diff --git a/.devcontainer/development/uplosi_azure_notes.md b/.devcontainer/development/uplosi_azure_notes.md index ee8d275b2d..741bc2e69f 100644 --- a/.devcontainer/development/uplosi_azure_notes.md +++ b/.devcontainer/development/uplosi_azure_notes.md @@ -7,7 +7,7 @@ oci.guest-components.reference to desired tag found here (non sha) : [ghcr.io/co ## mkosi build debug podvm ```console -cd /workspaces/cloud-api-adaptor/src/cloud-api-adaptor/podvm-mkosi +cd /workspaces/cloud-api-adaptor/src/cloud-api-adaptor/podvm TEE_PLATFORM=az-cvm-vtpm make debug ``` diff --git a/.github/workflows/actionlint.yaml b/.github/workflows/actionlint.yaml index 043b0d5abf..100d275f65 100644 --- a/.github/workflows/actionlint.yaml +++ b/.github/workflows/actionlint.yaml @@ -19,10 +19,11 @@ permissions: {} jobs: run-actionlint: + name: run-actionlint runs-on: ubuntu-24.04 steps: - name: Checkout the code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 0 persist-credentials: false diff --git a/.github/workflows/azure-e2e-test.yml b/.github/workflows/azure-e2e-test.yml deleted file mode 100644 index b9a824a78d..0000000000 --- a/.github/workflows/azure-e2e-test.yml +++ /dev/null @@ -1,377 +0,0 @@ -name: azure-e2e-test - -permissions: {} - -env: - CLUSTER_NAME_TEMPLATE: "{0}-e2e-test-${{ github.run_id }}-${{ github.run_attempt }}" - CLOUD_PROVIDER: "azure" - BUILTIN_CLOUD_PROVIDERS: "azure" - TEST_E2E_CREATE_RG: "no" - ACR_URL: "${{ vars.AZURE_ACR_URL }}" - MI_NAME: "${{ vars.AZURE_MANAGED_IDENTITY_NAME }}" - RG_NAME: "${{ vars.AZURE_RESOURCE_GROUP }}" - RUN: "${{ github.run_id }}-${{ github.run_attempt }}" - TEST_PROVISION_FILE: "${{ github.workspace }}/test_provision.properties" - -on: - workflow_call: - inputs: - podvm-image-id: - type: string - description: prebuilt podvm image - secrets: - AZURE_CLIENT_ID: - required: true - AZURE_SUBSCRIPTION_ID: - required: true - AZURE_TENANT_ID: - required: true - - workflow_dispatch: - inputs: - podvm-image-id: - type: string - description: prebuilt podvm image - -jobs: - build-caa-container-image: - runs-on: ubuntu-24.04 - defaults: - run: - working-directory: src/cloud-api-adaptor - outputs: - caa-image: "${{ steps.build-container.outputs.caa-image }}" - permissions: - id-token: write - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - - uses: azure/login@532459ea530d8321f2fb9bb10d1e0bcf23869a43 # v3.0.0 - name: 'Az CLI login' - with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - - - name: Build container image - id: build-container - env: - ARCHES: "linux/amd64" - RELEASE_BUILD: "false" - run: | - az acr login --name "$ACR_URL" - # This builds image in the following format: - # $ACR_URL/cloud-api-adaptor:dev-COMMIT- - make image "registry=${ACR_URL}" - echo "caa-image=${ACR_URL}/cloud-api-adaptor:dev-${GITHUB_SHA}-amd64" >> "$GITHUB_OUTPUT" - - install-aks: - runs-on: ubuntu-24.04 - defaults: - run: - working-directory: src/cloud-api-adaptor - needs: - - build-caa-container-image - # when none of required steps failed, skipped is ok - if: always() && !failure() && !cancelled() - strategy: - matrix: - parameters: - - id: "tdx" - machine_type: "Standard_DC2es_v6" - location: "eastus" - jitter: 0 - - id: "snp" - machine_type: "Standard_DC2as_v5" - location: "northeurope" - jitter: 10 - permissions: - id-token: write - env: - LOCATION: "${{ matrix.parameters.location }}" - - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - - name: Extract go version number - run: echo "GO_VERSION=$(yq -e '.tools.golang' versions.yaml)" >> "$GITHUB_ENV" - - - name: Set up Go environment - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - with: - go-version: "${{ env.GO_VERSION }}" - cache-dependency-path: "**/go.sum" - cache: false - - - name: Create provisioner file - env: - CAA_IMAGE: "${{ needs.build-caa-container-image.outputs.caa-image }}" - CLIENT_ID: "${{ secrets.AZURE_CLIENT_ID }}" - CLUSTER_NAME: "${{ format(env.CLUSTER_NAME_TEMPLATE, matrix.parameters.id) }}" - IMAGE_ID: "${{ inputs.podvm-image-id }}" - INSTANCE_SIZE: "${{ matrix.parameters.machine_type }}" - MI_NAME: "${{ vars.AZURE_MANAGED_IDENTITY_NAME }}" - SUBSCRIPTION_ID: "${{ secrets.AZURE_SUBSCRIPTION_ID }}" - TEST_TAGS: "owner=github-actions,run=${{ env.RUN }}" - run: | - cat << EOF > "$TEST_PROVISION_FILE" - AZURE_CLIENT_ID="$CLIENT_ID" - AZURE_IMAGE_ID="$IMAGE_ID" - AZURE_INSTANCE_SIZE="$INSTANCE_SIZE" - AZURE_SUBSCRIPTION_ID="$SUBSCRIPTION_ID" - CAA_IMAGE="$CAA_IMAGE" - CLUSTER_NAME="$CLUSTER_NAME" - FEDERATED_CREDENTIAL_NAME="$CLUSTER_NAME" - IS_CI_MANAGED_CLUSTER="true" - LOCATION="$LOCATION" - MANAGED_IDENTITY_NAME="$MI_NAME" - RESOURCE_GROUP_NAME="$RG_NAME" - TAGS="$TEST_TAGS" - EOF - cat "$TEST_PROVISION_FILE" - # assert that no variable is unset - ! grep -E '=x$|=""$' "$TEST_PROVISION_FILE" - - - name: Save the configuration created here - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - path: ${{ env.TEST_PROVISION_FILE }} - name: e2e-configuration-${{ matrix.parameters.id }} - - - uses: azure/login@532459ea530d8321f2fb9bb10d1e0bcf23869a43 # v3.0.0 - name: 'Az CLI login' - with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - - # Install AKS cluster in parallel with the podvm image build. - - name: Run provisioner - working-directory: src/cloud-api-adaptor/test/tools - run: | - sleep ${{ matrix.parameters.jitter }} - make caa-provisioner-cli - ./caa-provisioner-cli -action=createcluster - - run-e2e-test: - runs-on: ubuntu-24.04 - defaults: - run: - working-directory: src/cloud-api-adaptor - needs: - - install-aks - # when none of required steps failed, build-podvm-image can be skipped - if: always() && !failure() && !cancelled() - strategy: - matrix: - parameters: - - id: "tdx" - machine_type: "Standard_DC2es_v6" - location: "eastus" - - id: "snp" - machine_type: "Standard_DC2as_v5" - location: "northeurope" - permissions: - id-token: write - env: - TEE: "${{ matrix.parameters.id }}" - LOCATION: "${{ matrix.parameters.location }}" - - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - - name: Extract version numbers - run: | - { - echo "GO_VERSION=$(yq -e '.tools.golang' versions.yaml)" - echo "ORAS_VERSION=$(yq -e '.tools.oras' versions.yaml)" - echo "HELM_VERSION=$(yq -e '.tools.helm.version' versions.yaml)" - echo "HELM_CHECKSUM=$(yq -e '.tools.helm.sha256' versions.yaml)" - } >> "$GITHUB_ENV" - - - uses: oras-project/setup-oras@38de303aac69abb66f3e6255b7198bff35f323e3 # v2.0.0 - with: - version: ${{ env.ORAS_VERSION }} - - - name: Set up Go environment - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - with: - go-version: ${{ env.GO_VERSION }} - cache-dependency-path: "**/go.sum" - cache: false - - - name: Install cidr calculator - run: | - sudo apt-get update - sudo apt-get install -y sipcalc - - - name: Install kustomize - run: | - command -v kustomize >/dev/null || \ - curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | \ - sudo bash -s /usr/local/bin - - - name: Install Helm - run: | - curl -fsSL -o helm.tar.gz "https://get.helm.sh/helm-${HELM_VERSION}-linux-amd64.tar.gz" - echo "${HELM_CHECKSUM} helm.tar.gz" | sha256sum --check --strict - tar -xzf helm.tar.gz - sudo mv linux-amd64/helm /usr/local/bin/helm - rm -rf helm.tar.gz linux-amd64 - helm version - - - name: Restore the configuration created before - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: e2e-configuration-${{ matrix.parameters.id }} - - - uses: azure/login@532459ea530d8321f2fb9bb10d1e0bcf23869a43 # v3.0.0 - name: 'Az CLI login' - with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - - - name: Create peerpod subnet - env: - CLUSTER_NAME: "${{ format(env.CLUSTER_NAME_TEMPLATE, matrix.parameters.id) }}" - run: | - node_rg="$(az aks show -g "$RG_NAME" -n "$CLUSTER_NAME" --query nodeResourceGroup -o tsv)" - vnet_name="$(az network vnet list -g "$node_rg" --query '[].name' -o tsv)" - node_cidr="$(az network vnet show -n "$vnet_name" -g "$node_rg" --query "subnets[?name == 'aks-subnet'].addressPrefix" -o tsv)" - mask="${node_cidr#*/}" - peerpod_cidr="$(sipcalc "$node_cidr" -n 2 | grep ^Network | grep -v current | cut -d' ' -f2)/${mask}" - az network public-ip create -g "$node_rg" -n peerpod - az network nat gateway create -g "$node_rg" -l "$LOCATION" --public-ip-addresses peerpod -n peerpod - az network vnet subnet create -g "$node_rg" --vnet-name "$vnet_name" --nat-gateway peerpod --address-prefixes "$peerpod_cidr" -n peerpod - subnet_id="$(az network vnet subnet show -g "$node_rg" --vnet-name "$vnet_name" -n peerpod --query id -o tsv)" - - cat << EOF >> "$TEST_PROVISION_FILE" - AZURE_SUBNET_ID="$subnet_id" - EOF - - - name: Checkout KBS Repository - run: test/utils/checkout_kbs.sh - - - name: Run e2e test - id: runTests - env: - TEST_PROVISION: "no" - DEPLOY_KBS: "yes" - CLUSTER_NAME: "${{ format(env.CLUSTER_NAME_TEMPLATE, matrix.parameters.id) }}" - run: | - az aks get-credentials --resource-group "$RG_NAME" --name "$CLUSTER_NAME" - make test-e2e RUN_TESTS="^Test\(CreateSimplePodAzure\|RemoteAttestation\)$" - - - name: Debug on failure - if: failure() && steps.runTests.outcome == 'failure' - timeout-minutes: 15 - working-directory: ./ - env: - CLUSTER_NAME: "${{ format(env.CLUSTER_NAME_TEMPLATE, matrix.parameters.id) }}" - run: | - az aks get-credentials --resource-group "$RG_NAME" --name "$CLUSTER_NAME" || true - ./hack/ci-e2e-debug-fail.sh - shell: bash {0} - - cleanup: - runs-on: ubuntu-24.04 - needs: - - run-e2e-test - if: always() - strategy: - matrix: - parameters: - - id: "tdx" - jitter: 0 - - id: "snp" - jitter: 10 - permissions: - id-token: write - env: - JITTER: ${{ matrix.parameters.jitter }} - - steps: - - uses: azure/login@532459ea530d8321f2fb9bb10d1e0bcf23869a43 # v3.0.0 - name: 'Az CLI login' - with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - - - name: Delete coco namespace - # We want to delete the coco namespace because CAA might still spawn resources - # which prevents deletion of the AKS cluster - env: - CLUSTER_NAME: "${{ format(env.CLUSTER_NAME_TEMPLATE, matrix.parameters.id) }}" - run: | - az aks get-credentials --resource-group "$RG_NAME" --name "$CLUSTER_NAME" || true - namespace="confidential-containers-system" - kubectl patch namespace "$namespace" -p '{"metadata":{"finalizers": null }}' || true - kubectl delete namespace "$namespace" || true - - - name: Remove container image - run: | - # Delete the CAA container image built for this run. - suffix=".azurecr.io" - registry_name="${ACR_URL%"${suffix}"}" - - az acr repository delete \ - --name "${registry_name}" \ - --image "${ACR_URL}/cloud-api-adaptor:dev-${GITHUB_SHA}" \ - --yes || true - - - name: Remove dangling VMs - # Remove any VMs that might have been left behind in failed test runs - run: | - vms=$(az resource list \ - --tag owner=github-actions \ - --tag run="$RUN" \ - -o tsv --query "[?type == 'Microsoft.Compute/virtualMachines'].name") - for vm in $vms; do - az vm delete -n "$vm" -g "$RG_NAME" --yes || true - done - - - name: Remove dangling NICs - # Remove any NICs that might have been left behind in failed test runs - # NICs are reserved for 180s for VMs, even if they never launched - run: | - nics=$(az resource list \ - --tag owner=github-actions \ - --tag run="$RUN" \ - -o tsv --query "[?type == 'Microsoft.Network/networkInterfaces'].name") - sleep 180 - for nic in $nics; do - az network nic delete -n "$nic" -g "$RG_NAME" || true - done - - - name: Remove AKS cluster - env: - CLUSTER_NAME: "${{ format(env.CLUSTER_NAME_TEMPLATE, matrix.parameters.id) }}" - run: | - # Delete the cluster even if it has been deleted already or does not exists. - az aks delete \ - --name "$CLUSTER_NAME" \ - --resource-group "$RG_NAME" \ - --no-wait \ - --yes || true - - - name: Remove federated credential - env: - CLUSTER_NAME: "${{ format(env.CLUSTER_NAME_TEMPLATE, matrix.parameters.id) }}" - run: | - sleep "$JITTER" - az identity federated-credential delete \ - --identity-name "$MI_NAME" \ - --name "$CLUSTER_NAME" \ - --resource-group "$RG_NAME" \ - --yes || true diff --git a/.github/workflows/azure-podvm-image-build.yml b/.github/workflows/azure-podvm-image-build.yml deleted file mode 100644 index b63b2f8792..0000000000 --- a/.github/workflows/azure-podvm-image-build.yml +++ /dev/null @@ -1,192 +0,0 @@ -name: azure-podvm-image-build - -on: - workflow_call: - inputs: - image-version: - type: string - required: true - image-variant: - type: string - default: production - image-gallery: - type: string - image-definition: - type: string - community-gallery-name: - type: string - resource-group: - type: string - git-ref: - type: string - default: 'main' - secrets: - AZURE_CLIENT_ID: - required: true - AZURE_SUBSCRIPTION_ID: - required: true - AZURE_TENANT_ID: - required: true - outputs: - image-id: - description: "The PodVM image id" - value: ${{ jobs.build-podvm-image.outputs.image-id }} - - workflow_dispatch: - inputs: - image-version: - type: string - description: x.y.z - required: true - image-variant: - type: string - default: production - description: image variant - git-ref: - type: string - default: 'main' - description: tag, branch, sha - -permissions: {} - -jobs: - build-podvm-image: - runs-on: ubuntu-24.04 - defaults: - run: - working-directory: cloud-api-adaptor/src/cloud-api-adaptor/podvm-mkosi - outputs: - image-id: "${{ steps.upload-image.outputs.image-id }}" - permissions: - id-token: write - contents: read - packages: write - attestations: write - artifact-metadata: write - steps: - - name: Clone cloud-api-adaptor repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - path: cloud-api-adaptor - ref: ${{ inputs.git-ref }} - persist-credentials: false - - - name: Install build dependencies - run: | - sudo apt-get update - sudo apt-get install -y qemu-utils swtpm qemu-system-x86 - sudo snap install yq - - - name: Read properties from versions.yaml - run: | - echo "MKOSI_VERSION=$(yq -e '.tools.mkosi' ../versions.yaml)" >> "$GITHUB_ENV" - - - name: Install uplosi - env: - UPLOSI_VERSION: "0.3.0" - UPLOSI_SHA256: "687bcab7398ab0fda65a3809492e8cd4d6a25aad1573927be5ec75ac1c4cbc35" - run: | - wget -q "https://github.com/edgelesssys/uplosi/releases/download/v${UPLOSI_VERSION}/uplosi_${UPLOSI_VERSION}_linux_amd64.tar.gz" - sha256sum -c <(echo "$UPLOSI_SHA256" "uplosi_${UPLOSI_VERSION}_linux_amd64.tar.gz") - tar xzf uplosi_0.3.0_linux_amd64.tar.gz uplosi - sudo mv uplosi /usr/local/bin - - - name: Build binaries - env: - TEE_PLATFORM: az-cvm-vtpm - VERIFY_PROVENANCE: yes - run: | - make binaries - - - name: Build image - if: inputs.image-variant == 'production' - run: make image - - - name: Build debug image - if: inputs.image-variant != 'production' - run: make image-debug - - - uses: azure/login@532459ea530d8321f2fb9bb10d1e0bcf23869a43 # v3.0.0 - name: 'Az CLI login' - with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - - - name: upload image - id: upload-image - env: - SUBSCRIPTION_ID: "${{ secrets.AZURE_SUBSCRIPTION_ID }}" - IMAGE_VERSION: "${{ inputs.image-version }}" - RESOURCE_GROUP: "${{ inputs.resource-group || vars.AZURE_RESOURCE_GROUP }}" - IMAGE_GALLERY: "${{ inputs.image-gallery || vars.AZURE_PODVM_GALLERY_NAME }}" - IMAGE_DEFINITION: "${{ inputs.image-definition || vars.AZURE_PODVM_IMAGE_DEF_NAME }}" - COMMUNITY_GALLERY_NAME: "${{ inputs.community-gallery-name || vars.AZURE_COMMUNITY_GALLERY_NAME }}" - run: | - IMAGE_ID="/CommunityGalleries/${COMMUNITY_GALLERY_NAME}/Images/${IMAGE_DEFINITION}/Versions/${IMAGE_VERSION}" - SHARING_NAME_PREFIX="$(echo "$COMMUNITY_GALLERY_NAME" | cut -d'-' -f1)" - cat < uplosi.conf - [base] - imageVersion = "$IMAGE_VERSION" - name = "$IMAGE_DEFINITION" - - [base.azure] - subscriptionID = "$SUBSCRIPTION_ID" - location = "eastus" - resourceGroup = "$RESOURCE_GROUP" - sharedImageGallery = "$IMAGE_GALLERY" - sharingNamePrefix = "$SHARING_NAME_PREFIX" - - [variant.default] - provider = "azure" - - [variant.default.azure] - replicationRegions = ["eastus","eastus2","westeurope","northeurope"] - EOF - - uplosi upload build/system.raw - echo "successfully built $IMAGE_ID" - echo "image-id=${IMAGE_ID}" >> "$GITHUB_OUTPUT" - - - uses: oras-project/setup-oras@38de303aac69abb66f3e6255b7198bff35f323e3 # v2.0.0 - with: - version: 1.2.0 - - - name: Scrape PodVM measurements - run: | - wget -q http://security.debian.org/debian-security/pool/updates/main/e/edk2/ovmf_2022.11-6+deb12u1_all.deb - mkdir -p ovmf-pkg - dpkg-deb -x ovmf_2022.11-6+deb12u1_all.deb ovmf-pkg/ - cp ovmf-pkg/usr/share/OVMF/OVMF_CODE.fd . - ../hack/podvm-measure.sh swtpm & - sudo ../hack/podvm-measure.sh -i build/system.raw launch & - ../hack/podvm-measure.sh wait - ../hack/podvm-measure.sh scrape > measurements.json - ../hack/podvm-measure.sh stop - # verify json - jq -e . measurements.json - - - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Publish measurements - if: inputs.image-variant == 'production' - id: publish-measurements - env: - OCI_NAME: ghcr.io/${{ github.repository }}/measurements/azure/podvm - OCI_TAG: ${{ inputs.image-version }} - run: | - oras push "${OCI_NAME}:${OCI_TAG}" measurements.json - OCI_DIGEST=$(oras resolve "${OCI_NAME}:${OCI_TAG}") - echo "oci-name=${OCI_NAME}" >> "$GITHUB_OUTPUT" - echo "oci-digest=${OCI_DIGEST}" >> "$GITHUB_OUTPUT" - - - uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0 - if: inputs.image-variant == 'production' - with: - subject-name: ${{ steps.publish-measurements.outputs.oci-name }} - subject-digest: ${{ steps.publish-measurements.outputs.oci-digest }} - push-to-registry: true diff --git a/.github/workflows/azure-podvm-release.yml b/.github/workflows/azure-podvm-release.yml deleted file mode 100644 index 26d9c452fc..0000000000 --- a/.github/workflows/azure-podvm-release.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: azure-podvm-release - -on: - workflow_dispatch: - inputs: - version: - type: string - description: x.y.z - -permissions: {} - -jobs: - build-podvm-image: - strategy: - matrix: - parameters: - - variant: production - - variant: debug - uses: ./.github/workflows/azure-podvm-image-build.yml - permissions: - id-token: write # for OIDC to Azure - contents: read # to fetch the code - packages: write # to push measurements to OCI - attestations: write # to create the attestation - artifact-metadata: write # required by the attest wf - with: - git-ref: "v${{ github.event.inputs.version }}" - image-version: ${{ github.event.inputs.version }} - image-variant: ${{ matrix.parameters.variant }} - image-gallery: ${{ vars.AZURE_RELEASE_PODVM_GALLERY_NAME }} - image-definition: ${{ matrix.parameters.variant == 'production' && vars.AZURE_RELEASE_PODVM_IMAGE_DEF_NAME || vars.AZURE_RELEASE_PODVM_IMAGE_DEF_NAME_DEBUG }} - community-gallery-name: ${{ vars.AZURE_RELEASE_COMMUNITY_GALLERY_NAME }} - resource-group: ${{ vars.AZURE_RELEASE_RESOURCE_GROUP }} - secrets: - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 619bf959b4..73c2c3b2f1 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -32,7 +32,7 @@ jobs: working-directory: src/cloud-api-adaptor steps: - name: Checkout the pull request code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - name: Read properties from versions.yaml @@ -52,9 +52,11 @@ jobs: sudo apt-get update -y sudo apt-get install -y libvirt-dev - name: Build + env: + BUILD_TYPE: ${{ matrix.type }} run: | # Build the binaries - if [ ${{ matrix.type }} == "release" ]; then + if [ "${BUILD_TYPE}" == "release" ]; then RELEASE_BUILD=true make build else RELEASE_BUILD=false make build @@ -89,7 +91,7 @@ jobs: working-directory: src/${{ matrix.controller }} steps: - name: Checkout the pull request code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false @@ -124,70 +126,15 @@ jobs: - name: Build the controller image run: make docker-build - volumes: - name: volume controllers - runs-on: ubuntu-24.04 - strategy: - fail-fast: false - matrix: - controller: - - csi-wrapper - defaults: - run: - working-directory: src/${{ matrix.controller }} - steps: - - name: Checkout the pull request code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - - name: Read properties from versions.yaml - run: | - go_version="$(yq '.tools.golang' ../cloud-api-adaptor/versions.yaml)" - [ -n "$go_version" ] - echo "GO_VERSION=${go_version}" >> "$GITHUB_ENV" - - - name: Setup Golang version ${{ env.GO_VERSION }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - with: - go-version: ${{ env.GO_VERSION }} - cache-dependency-path: "**/go.sum" - cache: false - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - - - name: Build the controllers - run: make build - - - name: Run unit tests - run: make test - - - name: Build checks - run: make check - - - name: Build docker images - run: | - echo "::group::Build csi-node-wrapper-docker" - make csi-node-wrapper-docker - echo "::endgroup::" - - echo "::group::Build csi-controller-wrapper-docker" - make csi-controller-wrapper-docker - echo "::endgroup::" - - echo "::group::Build csi-podvm-wrapper-docker" - make csi-podvm-wrapper-docker - echo "::endgroup::" - webhook: + name: webhook runs-on: ubuntu-24.04 defaults: run: working-directory: src/webhook steps: - name: Checkout the pull request code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false @@ -205,7 +152,7 @@ jobs: cache: false - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Install kind run: | @@ -234,13 +181,14 @@ jobs: make test-e2e cloud-provider: + name: cloud-provider runs-on: ubuntu-24.04 defaults: run: working-directory: src/cloud-providers steps: - name: Checkout the pull request code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false diff --git a/.github/workflows/caa_build_and_push.yaml b/.github/workflows/caa_build_and_push.yaml index 900fea85bc..16ef7a4a8d 100644 --- a/.github/workflows/caa_build_and_push.yaml +++ b/.github/workflows/caa_build_and_push.yaml @@ -64,7 +64,7 @@ jobs: packages: write # Needed to push the images to GHCR steps: - name: Checkout the code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 0 persist-credentials: false @@ -95,7 +95,7 @@ jobs: architecture: ${{ contains(inputs.runner, 'ppc64le') && 'ppc64le' || '' }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Install build dependencies if: matrix.type == 'dev' run: | @@ -103,7 +103,7 @@ jobs: sudo apt-get install -y libvirt-dev - name: Login to quay Container Registry if: ${{ startsWith(inputs.registry, 'quay.io') }} - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: ${{ inputs.registry }} username: ${{ vars.QUAY_USERNAME }} @@ -111,7 +111,7 @@ jobs: - name: Login to Github Container Registry if: ${{ startsWith(inputs.registry, 'ghcr.io') }} - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: ${{ inputs.registry }} username: ${{ github.repository_owner }} diff --git a/.github/workflows/caa_build_and_push_all_arches.yaml b/.github/workflows/caa_build_and_push_all_arches.yaml index 6497848fb0..545bf954ef 100644 --- a/.github/workflows/caa_build_and_push_all_arches.yaml +++ b/.github/workflows/caa_build_and_push_all_arches.yaml @@ -63,18 +63,18 @@ jobs: needs: [build_push_job] steps: - name: Checkout the code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 0 ref: "${{ inputs.git_ref }}" persist-credentials: false - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Login to quay Container Registry if: ${{ startsWith(inputs.registry, 'quay.io') }} - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: ${{ inputs.registry }} username: ${{ vars.QUAY_USERNAME }} @@ -82,7 +82,7 @@ jobs: - name: Login to Github Container Registry if: ${{ startsWith(inputs.registry, 'ghcr.io') }} - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: ${{ inputs.registry }} username: ${{ github.repository_owner }} diff --git a/.github/workflows/csi_wrapper_images.yaml b/.github/workflows/csi_wrapper_images.yaml deleted file mode 100644 index 634f759439..0000000000 --- a/.github/workflows/csi_wrapper_images.yaml +++ /dev/null @@ -1,98 +0,0 @@ -# (C) Copyright Confidential Containers Contributors 2023. -# SPDX-License-Identifier: Apache-2.0 -# -# Build and push csi wrapper images for each arch. ---- -name: (Callable) Build and push CSI wrapper images - -on: - workflow_call: - inputs: - registry: - default: 'quay.io/confidential-containers' - description: 'Image registry (e.g. "ghcr.io/confidential-containers") where the built image will be pushed to' - required: false - type: string - image_tags: - description: 'Comma-separated list of tags for the dev built image (e.g. latest,ci-dev). By default uses the values from src/cloud-api-adaptor/hack/build.sh' - required: true - type: string - git_ref: - default: 'main' - description: Git ref to checkout the cloud-api-adaptor repository. Defaults to main. - required: false - type: string - secrets: - QUAY_PASSWORD: - required: true - -permissions: {} - -jobs: - build_push_job: - name: build and push - runs-on: ubuntu-24.04 - strategy: - fail-fast: false - matrix: - binary: - - csi-controller-wrapper - - csi-node-wrapper - - csi-podvm-wrapper - permissions: - contents: read - packages: write - steps: - - name: Checkout the code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - ref: "${{ inputs.git_ref }}" - persist-credentials: false - - name: Read properties from versions.yaml - run: | - go_version="$(yq '.tools.golang' src/cloud-api-adaptor/versions.yaml)" - [ -n "$go_version" ] - echo "GO_VERSION=${go_version}" >> "$GITHUB_ENV" - - name: Setup Golang version ${{ env.GO_VERSION }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - with: - go-version: ${{ env.GO_VERSION }} - cache-dependency-path: "**/go.sum" - cache: false - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - - name: Login to quay Container Registry - if: ${{ startsWith(inputs.registry, 'quay.io') }} - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 - with: - registry: quay.io - username: ${{ vars.QUAY_USERNAME }} - password: ${{ secrets.QUAY_PASSWORD }} - - name: Login to Github Container Registry - if: ${{ startsWith(inputs.registry, 'ghcr.io') }} - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Determine tags - id: tags - run: | - tags="${{ inputs.image_tags }}" - tags_new="" - for t in ${tags/,/ }; do - tags_new+="${{ inputs.registry }}/${{matrix.binary}}:${t}," - done - echo "tags=${tags_new}" >> "$GITHUB_OUTPUT" - - name: Build and push - uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 - with: - tags: ${{ steps.tags.outputs.tags }} - push: true - context: src - platforms: linux/amd64, linux/s390x, linux/ppc64le, linux/arm64 - file: src/csi-wrapper/Dockerfile.csi_wrappers - build-args: | - "BINARY=${{matrix.binary}}" - "SOURCE_FROM=local" diff --git a/.github/workflows/daily-e2e-tests.yaml b/.github/workflows/daily-e2e-tests.yaml index d620798576..37f4de394a 100644 --- a/.github/workflows/daily-e2e-tests.yaml +++ b/.github/workflows/daily-e2e-tests.yaml @@ -33,11 +33,11 @@ jobs: podvm_image_tag: latest registry: ghcr.io/${{ github.repository_owner }} permissions: - id-token: write - contents: read - packages: write - attestations: write - artifact-metadata: write + id-token: write # Required for OIDC authentication + contents: read # Required to checkout code + packages: write # Required to push container images + attestations: write # Required to write attestations + artifact-metadata: write # Required to write artifact metadata secrets: AWS_IAM_ROLE_ARN: ${{ secrets.AWS_IAM_ROLE_ARN }} QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }} diff --git a/.github/workflows/e2e_aws.yaml b/.github/workflows/e2e_aws.yaml index 0ae44e9f07..d879224b84 100644 --- a/.github/workflows/e2e_aws.yaml +++ b/.github/workflows/e2e_aws.yaml @@ -89,7 +89,7 @@ jobs: contents: read # Required by aws-actions/configure-aws-credentials steps: - name: Checkout Code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 0 ref: ${{ inputs.git_ref }} diff --git a/.github/workflows/e2e_byom.yaml b/.github/workflows/e2e_byom.yaml index 648ab120f0..c38f2d81f5 100644 --- a/.github/workflows/e2e_byom.yaml +++ b/.github/workflows/e2e_byom.yaml @@ -50,7 +50,7 @@ jobs: WEBHOOK_IMAGE: "${{ inputs.webhook_image }}" steps: - name: Checkout Code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 0 ref: ${{ inputs.git_ref }} diff --git a/.github/workflows/e2e_docker.yaml b/.github/workflows/e2e_docker.yaml deleted file mode 100644 index a4744a7d9b..0000000000 --- a/.github/workflows/e2e_docker.yaml +++ /dev/null @@ -1,166 +0,0 @@ -# (C) Copyright Confidential Containers Contributors 2024. -# SPDX-License-Identifier: Apache-2.0 -# -# Run docker e2e tests. -name: (Callable) docker e2e tests - -on: - workflow_call: - inputs: - podvm_image: - required: true - type: string - caa_image: - description: The cloud-api-adaptor OCI image (including tag) to test - type: string - git_ref: - default: 'main' - description: Git ref to checkout the cloud-api-adaptor repository. Defaults to main. - required: false - type: string - container_runtime: - default: 'containerd' - description: Name of the container runtime. Either containerd or crio. - required: false - type: string - peerpod_ctrl_image: - description: The peerpod-ctrl OCI image (including tag) - required: false - default: '' - type: string - webhook_image: - description: The peer-pods-webhook OCI image (including tag) - required: false - default: '' - type: string - secrets: - QUAY_PASSWORD: - required: true - -env: - CLOUD_PROVIDER: docker - CLUSTER_NAME: peer-pods - DEBIAN_FRONTEND: noninteractive - -defaults: - run: - working-directory: src/cloud-api-adaptor - -permissions: {} - -jobs: - test-docker: - name: "Docker e2e tests: (${{ inputs.container_runtime }})" - runs-on: ubuntu-22.04 - env: - CAA_IMAGE: "${{ inputs.caa_image }}" - PEERPOD_CTRL_IMAGE: "${{ inputs.peerpod_ctrl_image }}" - WEBHOOK_IMAGE: "${{ inputs.webhook_image }}" - steps: - - name: Checkout Code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - ref: ${{ inputs.git_ref }} - persist-credentials: false - - - name: Rebase the code - if: github.event_name == 'pull_request_target' - working-directory: ./ - run: | - ./hack/ci-helper.sh rebase-atop-of-the-latest-target-branch - - - name: Remove unnecessary directories to free up space - working-directory: ./ - run: | - sudo ./hack/ci-helper.sh clean-up-runner - - - name: Login to quay Container Registry - if: ${{ startsWith(inputs.podvm_image, 'quay.io') }} - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 - with: - registry: quay.io - username: ${{ vars.QUAY_USERNAME }} - password: ${{ secrets.QUAY_PASSWORD }} - - - name: Login to the ghcr Container registry - if: ${{ startsWith(inputs.podvm_image, 'ghcr.io') }} - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Read properties from versions.yaml - run: | - sudo snap install yq - go_version="$(yq '.tools.golang' versions.yaml)" - [ -n "$go_version" ] - { - echo "GO_VERSION=${go_version}" - echo "HELM_VERSION=$(yq -e '.tools.helm.version' versions.yaml)" - echo "HELM_CHECKSUM=$(yq -e '.tools.helm.sha256' versions.yaml)" - } >> "$GITHUB_ENV" - - - name: Setup Golang version ${{ env.GO_VERSION }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - with: - go-version: ${{ env.GO_VERSION }} - - - name: Install kustomize - run: | - command -v kustomize >/dev/null || \ - curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | \ - sudo bash -s /usr/local/bin - - - name: Install Helm - run: | - curl -fsSL -o helm.tar.gz "https://get.helm.sh/helm-${HELM_VERSION}-linux-amd64.tar.gz" - echo "${HELM_CHECKSUM} helm.tar.gz" | sha256sum --check --strict - tar -xzf helm.tar.gz - sudo mv linux-amd64/helm /usr/local/bin/helm - rm -rf helm.tar.gz linux-amd64 - helm version - - - name: Config docker - run: | - cat <<- EOF > docker.properties - DOCKER_PODVM_IMAGE="${{ inputs.podvm_image }}" - DOCKER_HOST="unix:///var/run/docker.sock" - DOCKER_NETWORK_NAME="kind" - CONTAINER_RUNTIME="${{ inputs.container_runtime }}" - DOCKER_API_VERSION="1.44" - EOF - - # Split CAA_IMAGE into CAA_IMAGE_NAME and CAA_IMAGE_TAG - CAA_IMAGE_TAG="${CAA_IMAGE##*:}" - CAA_IMAGE_NAME="${CAA_IMAGE%:*}" - echo "CAA_IMAGE=\"${CAA_IMAGE_NAME}\"" >> docker.properties - echo "CAA_IMAGE_TAG=\"${CAA_IMAGE_TAG}\"" >> docker.properties - - # For debugging - cat docker.properties - - - name: run tests - id: runTests - run: | - export CLOUD_PROVIDER=docker - export CONTAINER_RUNTIME="${{ inputs.container_runtime }}" - export DEPLOY_KBS=false - export TEST_PROVISION=yes - export TEST_TEARDOWN=no - export TEST_PROVISION_FILE="$PWD/docker.properties" - export TEST_PODVM_IMAGE="${{ inputs.podvm_image }}" - export TEST_E2E_TIMEOUT="50m" - - make test-e2e - - - name: Debug tests failure - if: failure() && steps.runTests.outcome == 'failure' - working-directory: ./ - run: | - export KUBECONFIG="${HOME}/kube_${CLUSTER_NAME}" - kind get kubeconfig -n "$CLUSTER_NAME" > "$KUBECONFIG" - ./hack/ci-e2e-debug-fail.sh - # Avoid running with `set -e` as command fails should be allowed - shell: bash {0} diff --git a/.github/workflows/e2e_libvirt.yaml b/.github/workflows/e2e_libvirt.yaml index 6bd64ec8ec..357d72156f 100644 --- a/.github/workflows/e2e_libvirt.yaml +++ b/.github/workflows/e2e_libvirt.yaml @@ -58,7 +58,7 @@ jobs: name: "Libvirt e2e tests: (${{ inputs.runner }}, ${{ inputs.container_runtime }}) on mkosi" steps: - name: Checkout Code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 0 ref: ${{ inputs.git_ref }} @@ -109,11 +109,13 @@ jobs: - name: Use oras to get qcow2 from ${{ inputs.podvm_image }} run: | - oras pull ${{ inputs.podvm_image }} + oras pull "${PODVM_IMAGE}" tar xvJpf podvm.tar.xz qcow2=$(find ./*.qcow2) echo "PODVM_QCOW2=$(pwd)/${qcow2}" >> "$GITHUB_ENV" working-directory: src/cloud-api-adaptor/podvm + env: + PODVM_IMAGE: ${{ inputs.podvm_image }} - name: Config Libvirt env: @@ -183,7 +185,7 @@ jobs: export TEST_PROVISION="yes" export TEST_TEARDOWN="no" export TEST_PROVISION_FILE="$PWD/libvirt.properties" - export TEST_PODVM_IMAGE="${{ env.PODVM_QCOW2 }}" + export TEST_PODVM_IMAGE="${PODVM_QCOW2}" export TEST_E2E_TIMEOUT="75m" make test-e2e diff --git a/.github/workflows/e2e_on_pull.yaml b/.github/workflows/e2e_on_pull.yaml index cb8ff5afe9..b3aab1b7f7 100644 --- a/.github/workflows/e2e_on_pull.yaml +++ b/.github/workflows/e2e_on_pull.yaml @@ -16,8 +16,8 @@ on: # triggering this workflow). pull_request_target: types: - # This workflow will be run if the pull request is labeled test_e2e_libvirt, or - # test_e2e_docker, so adding 'labeled' to the list of activity types. + # This workflow will be run if the pull request is labeled test_e2e_libvirt, + # so adding 'labeled' to the list of activity types. # - opened - synchronize @@ -34,11 +34,11 @@ permissions: {} jobs: authorize: + name: authorize runs-on: ubuntu-24.04 if: | contains(github.event.pull_request.labels.*.name, 'test_e2e_aws') || contains(github.event.pull_request.labels.*.name, 'test_e2e_libvirt') || - contains(github.event.pull_request.labels.*.name, 'test_e2e_docker') || contains(github.event.pull_request.labels.*.name, 'test_e2e_byom') steps: - run: "true" @@ -51,11 +51,11 @@ jobs: podvm_image_tag: ci-pr${{ github.event.number }} registry: ghcr.io/${{ github.repository_owner }} permissions: - id-token: write - contents: read - packages: write - attestations: write - artifact-metadata: write + id-token: write # Required for OIDC authentication + contents: read # Required to checkout code + packages: write # Required to push container images + attestations: write # Required to write attestations + artifact-metadata: write # Required to write artifact metadata secrets: AWS_IAM_ROLE_ARN: ${{ secrets.AWS_IAM_ROLE_ARN }} QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }} diff --git a/.github/workflows/e2e_run_all.yaml b/.github/workflows/e2e_run_all.yaml index 852a44c7ba..3466f1df1d 100644 --- a/.github/workflows/e2e_run_all.yaml +++ b/.github/workflows/e2e_run_all.yaml @@ -18,7 +18,7 @@ on: required: false type: string podvm_image_tag: - description: set the podvm_builder/podvm_binaries/podvm image tag + description: set the podvm image tag required: true type: string registry: @@ -48,44 +48,6 @@ defaults: permissions: {} jobs: - podvm_builder: - uses: ./.github/workflows/podvm_builder.yaml - with: - registry: ${{ inputs.registry }} - image_tag: ${{ inputs.podvm_image_tag }} - git_ref: ${{ inputs.git_ref }} - permissions: - contents: read - packages: write - secrets: - QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }} - - podvm_binaries: - needs: [podvm_builder] - uses: ./.github/workflows/podvm_binaries.yaml - with: - registry: ${{ inputs.registry }} - image_tag: ${{ inputs.podvm_image_tag }} - git_ref: ${{ inputs.git_ref }} - permissions: - contents: read - packages: write - secrets: - QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }} - - podvm: - needs: [podvm_binaries] - uses: ./.github/workflows/podvm.yaml - with: - registry: ${{ inputs.registry }} - image_tag: ${{ inputs.podvm_image_tag }} - git_ref: ${{ inputs.git_ref }} - permissions: - contents: read - packages: write - secrets: - QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }} - podvm_mkosi_amd64: uses: ./.github/workflows/podvm_mkosi.yaml with: @@ -95,10 +57,10 @@ jobs: arch: amd64 debug: true permissions: - contents: read - packages: write - id-token: write - attestations: write + contents: read # Required to checkout code + packages: write # Required to push container images + id-token: write # Required for OIDC authentication + attestations: write # Required to write attestations artifact-metadata: write # Required by podvm_mkosi.yaml to write attestation metadata secrets: QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }} @@ -112,10 +74,10 @@ jobs: arch: s390x debug: true permissions: - contents: read - packages: write - attestations: write - id-token: write + contents: read # Required to checkout code + packages: write # Required to push container images + attestations: write # Required to write attestations + id-token: write # Required for OIDC authentication artifact-metadata: write # Required by podvm_mkosi.yaml to write attestation metadata secrets: QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }} @@ -164,8 +126,8 @@ jobs: release_tags: ${{ inputs.caa_image_tag }} git_ref: ${{ inputs.git_ref }} permissions: - contents: read - packages: write + contents: read # Required to checkout code + packages: write # Required to push container images secrets: QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }} @@ -180,8 +142,8 @@ jobs: git_ref: ${{ inputs.git_ref }} runner: 'ubuntu-24.04-s390x' permissions: - contents: read - packages: write + contents: read # Required to checkout code + packages: write # Required to push container images secrets: QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }} @@ -255,10 +217,11 @@ jobs: AWS_IAM_ROLE_ARN: ${{ secrets.AWS_IAM_ROLE_ARN }} libvirt_e2e_arch_prep: + name: libvirt_e2e_arch_prep runs-on: ubuntu-24.04 steps: - name: Checkout Code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 0 persist-credentials: false @@ -350,36 +313,6 @@ jobs: secrets: REGISTRY_CREDENTIAL_ENCODED: ${{ secrets.REGISTRY_CREDENTIAL_ENCODED }} - # Run docker e2e tests if pull request labeled 'test_e2e_docker' - docker: - name: docker - if: | - github.event_name == 'schedule' || - github.event_name == 'workflow_dispatch' || - contains(github.event.pull_request.labels.*.name, 'test_e2e_docker') - needs: [podvm_mkosi_amd64, caa_image_amd64, peerpod_ctrl_image_amd64, webhook_image] - strategy: - fail-fast: false - matrix: - container_runtime: - - crio - os: - - fedora - provider: - - docker - arch: - - amd64 - uses: ./.github/workflows/e2e_docker.yaml - with: - caa_image: ${{ inputs.registry }}/cloud-api-adaptor:${{ inputs.caa_image_tag }}-dev-amd64 - container_runtime: ${{ matrix.container_runtime }} - podvm_image: ${{ needs.podvm_mkosi_amd64.outputs.docker_oci_image }} - git_ref: ${{ inputs.git_ref }} - peerpod_ctrl_image: ${{ inputs.registry }}/peerpod-ctrl:${{ inputs.caa_image_tag }}-amd64 - webhook_image: ${{ inputs.registry }}/peer-pods-webhook:${{ inputs.caa_image_tag }} - secrets: - QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }} - # Run BYOM e2e tests if pull request labeled 'test_e2e_byom' byom: name: byom diff --git a/.github/workflows/lib-codeql.yaml b/.github/workflows/lib-codeql.yaml index a68849ebd3..6bc516bd0e 100644 --- a/.github/workflows/lib-codeql.yaml +++ b/.github/workflows/lib-codeql.yaml @@ -12,11 +12,11 @@ jobs: timeout-minutes: 360 permissions: - security-events: write + security-events: write # Required to upload CodeQL results steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -25,11 +25,11 @@ jobs: check-latest: true - name: Initialize CodeQL - uses: github/codeql-action/init@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 + uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 with: languages: 'go' - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 + uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 with: category: "/language:go" diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 0880638fdc..22cd36ae3d 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -26,7 +26,7 @@ jobs: contents: read steps: - name: Checkout the pull request code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - name: Read properties from versions.yaml @@ -56,7 +56,7 @@ jobs: contents: read steps: - name: Checkout the pull request code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - name: Read properties from versions.yaml @@ -87,9 +87,9 @@ jobs: codeql: permissions: - actions: read - contents: read - security-events: write + actions: read # Required to read workflow artifacts + contents: read # Required to checkout code + security-events: write # Required to upload CodeQL results needs: - vet-and-fmt uses: "./.github/workflows/lib-codeql.yaml" @@ -101,7 +101,7 @@ jobs: contents: read steps: - name: Checkout the pull request code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - name: Run shellcheck @@ -112,7 +112,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Checkout the pull request code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - name: Read properties from versions.yaml @@ -134,7 +134,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Checkout the pull request code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - name: Read properties from versions.yaml @@ -157,24 +157,13 @@ jobs: - name: Run govulncheck run: make govulncheck - packer: - name: Packer check - runs-on: ubuntu-24.04 - steps: - - name: Checkout the pull request code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - name: Run packer check - run: make packer-check - terraform: name: Terraform check # The 24.04 image doesn't have terraform installed, so stick with 22.04 here runs-on: ubuntu-22.04 steps: - name: Checkout the pull request code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - name: Run terraform check @@ -187,7 +176,7 @@ jobs: contents: read steps: - name: Checkout the pull request code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false diff --git a/.github/workflows/peerpod-ctrl_build_and_push.yaml b/.github/workflows/peerpod-ctrl_build_and_push.yaml index 75810585ee..beba891aaa 100644 --- a/.github/workflows/peerpod-ctrl_build_and_push.yaml +++ b/.github/workflows/peerpod-ctrl_build_and_push.yaml @@ -49,18 +49,18 @@ jobs: packages: write # Required for pushing to GitHub Container Registry steps: - name: Checkout Code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 0 persist-credentials: false ref: "${{ inputs.git_ref }}" - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Login to Quay container Registry if: ${{ startsWith(inputs.registry, 'quay.io') }} - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: quay.io username: ${{ vars.QUAY_USERNAME }} @@ -68,7 +68,7 @@ jobs: - name: Login to Github Container Registry if: ${{ startsWith(inputs.registry, 'ghcr.io') }} - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: ghcr.io username: ${{ github.repository_owner }} @@ -101,7 +101,7 @@ jobs: echo "tags=${tags_new}" >> "$GITHUB_OUTPUT" - name: Build and push - uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 + uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 with: tags: ${{ steps.tags.outputs.tags }} push: true diff --git a/.github/workflows/peerpod-ctrl_build_and_push_all_arches.yaml b/.github/workflows/peerpod-ctrl_build_and_push_all_arches.yaml index c11bcf3f6b..17b98b5caa 100644 --- a/.github/workflows/peerpod-ctrl_build_and_push_all_arches.yaml +++ b/.github/workflows/peerpod-ctrl_build_and_push_all_arches.yaml @@ -60,18 +60,18 @@ jobs: packages: write # Required for pushing to GitHub Container Registry steps: - name: Checkout the code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 0 ref: "${{ inputs.git_ref }}" persist-credentials: false - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Login to Quay Container Registry if: ${{ startsWith(inputs.registry, 'quay.io') }} - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: ${{ inputs.registry }} username: ${{ vars.QUAY_USERNAME }} @@ -79,7 +79,7 @@ jobs: - name: Login to Github Container Registry if: ${{ startsWith(inputs.registry, 'ghcr.io') }} - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: ${{ inputs.registry }} username: ${{ github.repository_owner }} diff --git a/.github/workflows/peerpods-chart_image.yaml b/.github/workflows/peerpods-chart_image.yaml index 87b68a0067..021bcba5fe 100644 --- a/.github/workflows/peerpods-chart_image.yaml +++ b/.github/workflows/peerpods-chart_image.yaml @@ -53,7 +53,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: ref: ${{ inputs.git_ref || github.ref }} persist-credentials: false @@ -138,7 +138,7 @@ jobs: helm version - name: Log in to GitHub Container Registry - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: ghcr.io username: ${{ github.actor }} diff --git a/.github/workflows/podvm.yaml b/.github/workflows/podvm.yaml deleted file mode 100644 index 25d24e1ec8..0000000000 --- a/.github/workflows/podvm.yaml +++ /dev/null @@ -1,86 +0,0 @@ -name: (Callable) Build and push podvm images -on: - workflow_call: - inputs: - registry: - default: 'quay.io/confidential-containers' - required: false - type: string - image_tag: - default: '' - required: false - type: string - git_ref: - description: Git ref to checkout the cloud-api-adaptor repository. - required: true - type: string - secrets: - QUAY_PASSWORD: - required: true - -defaults: - run: - working-directory: src/cloud-api-adaptor - -permissions: {} - -jobs: - build: - name: Create pod vm image - runs-on: ubuntu-24.04 - strategy: - fail-fast: false - matrix: - os: - - ubuntu - arch: [amd64, s390x, arm64] - provider: [generic] - include: - - os: ubuntu - - permissions: - contents: read - packages: write - steps: - - name: Checkout Code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - persist-credentials: false - ref: "${{ inputs.git_ref }}" - - - name: Rebase the code - if: github.event_name == 'pull_request_target' - working-directory: ./ - run: | - ./hack/ci-helper.sh rebase-atop-of-the-latest-target-branch - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - - - name: Login to Quay container Registry - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 - if: ${{ startsWith(inputs.registry, 'quay.io') }} - with: - registry: quay.io - username: ${{ vars.QUAY_USERNAME }} - password: ${{ secrets.QUAY_PASSWORD }} - - - name: Login to Github Container Registry - if: ${{ startsWith(inputs.registry, 'ghcr.io') }} - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push - run: make podvm-image - env: - PUSH: true - REGISTRY: ${{ inputs.registry }} - ARCH: ${{ matrix.arch }} - PODVM_TAG: ${{ inputs.image_tag }} - PODVM_DISTRO: ${{ matrix.os }} - CLOUD_PROVIDER: ${{ matrix.provider }} - PACKER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/podvm_binaries.yaml b/.github/workflows/podvm_binaries.yaml deleted file mode 100644 index f905d4cf6c..0000000000 --- a/.github/workflows/podvm_binaries.yaml +++ /dev/null @@ -1,81 +0,0 @@ -name: (Callable) Build and push podvm_binaries image -on: - workflow_call: - inputs: - registry: - default: 'quay.io/confidential-containers' - required: false - type: string - image_tag: - default: '' - required: false - type: string - git_ref: - description: Git ref to checkout the cloud-api-adaptor repository. - required: true - type: string - secrets: - QUAY_PASSWORD: - required: true - -defaults: - run: - working-directory: src/cloud-api-adaptor - -permissions: {} - -jobs: - build: - name: Create pod vm binaries image - runs-on: ubuntu-24.04 - strategy: - fail-fast: false - matrix: - os: [ubuntu] - arch: [amd64, s390x, arm64] - include: - - os: ubuntu - permissions: - contents: read - packages: write - steps: - - name: Checkout Code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - persist-credentials: false - ref: "${{ inputs.git_ref }}" - - - name: Rebase the code - if: github.event_name == 'pull_request_target' - working-directory: ./ - run: | - ./hack/ci-helper.sh rebase-atop-of-the-latest-target-branch - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - - - name: Login to Quay container Registry - if: ${{ startsWith(inputs.registry, 'quay.io') }} - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 - with: - registry: quay.io - username: ${{ vars.QUAY_USERNAME }} - password: ${{ secrets.QUAY_PASSWORD }} - - - name: Login to Github Container Registry - if: ${{ startsWith(inputs.registry, 'ghcr.io') }} - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push - run: make podvm-binaries - env: - PUSH: true - REGISTRY: ${{ inputs.registry }} - ARCH: ${{ matrix.arch }} - PODVM_TAG: ${{ inputs.image_tag }} - PODVM_DISTRO: ${{ matrix.os }} diff --git a/.github/workflows/podvm_builder.yaml b/.github/workflows/podvm_builder.yaml deleted file mode 100644 index 669f668d3b..0000000000 --- a/.github/workflows/podvm_builder.yaml +++ /dev/null @@ -1,81 +0,0 @@ -name: (Callable) Build and push podvm_builder images -on: - workflow_call: - inputs: - registry: - default: 'quay.io/confidential-containers' - required: false - type: string - image_tag: - default: '' - required: false - type: string - git_ref: - description: Git ref to checkout the cloud-api-adaptor repository. - required: true - type: string - secrets: - QUAY_PASSWORD: - required: true - -defaults: - run: - working-directory: src/cloud-api-adaptor - -permissions: {} - -jobs: - build: - name: Create pod vm builder image - runs-on: ${{ matrix.runner }} - strategy: - fail-fast: false - matrix: - os: - # Please keep this list in alphabetical order. - - ubuntu - runner: - - ubuntu-24.04 - permissions: - contents: read - packages: write - steps: - - name: Checkout Code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - persist-credentials: false - ref: "${{ inputs.git_ref }}" - - - name: Rebase the code - if: github.event_name == 'pull_request_target' - working-directory: ./ - run: | - ./hack/ci-helper.sh rebase-atop-of-the-latest-target-branch - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - - - name: Login to Quay container Registry - if: ${{ startsWith(inputs.registry, 'quay.io') }} - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 - with: - registry: quay.io - username: ${{ vars.QUAY_USERNAME }} - password: ${{ secrets.QUAY_PASSWORD }} - - - name: Login to Github Container Registry - if: ${{ startsWith(inputs.registry, 'ghcr.io') }} - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push - run: make podvm-builder - env: - PUSH: true - REGISTRY: ${{ inputs.registry }} - PODVM_TAG: ${{ inputs.image_tag }} - PODVM_DISTRO: ${{ matrix.os }} diff --git a/.github/workflows/podvm_mkosi.yaml b/.github/workflows/podvm_mkosi.yaml index a9e252dce2..8f69058349 100644 --- a/.github/workflows/podvm_mkosi.yaml +++ b/.github/workflows/podvm_mkosi.yaml @@ -57,21 +57,21 @@ on: qcow2_oras_image: description: The location of the qcow2 oras container this workflow pushed value: ${{ jobs.build-image.outputs.qcow2_oras_image }} - docker_oci_image: - description: The location of the docker oci container image this workflow pushed - value: ${{ jobs.build-image.outputs.docker_oci_image }} defaults: run: working-directory: src/cloud-api-adaptor +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-mkosi-${{ toJSON(inputs) }} + cancel-in-progress: true + permissions: {} jobs: build-image: name: Build mkosi image for ${{ inputs.arch }} runs-on: ${{ inputs.arch == 's390x' && 's390x' || inputs.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }} - continue-on-error: ${{ inputs.arch == 's390x' }} # Remove once s390x image build is stable permissions: contents: read # Required for checkout on forks packages: write # Required to publish the oras package to ghcr @@ -80,10 +80,9 @@ jobs: artifact-metadata: write # Required by actions/attest to write attestation metadata outputs: qcow2_oras_image: ${{ steps.publish_oras_qcow2.outputs.image }}:${{ steps.publish_oras_qcow2.outputs.tag }} - docker_oci_image: ${{ steps.build_docker_oci.outputs.image }} steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 0 persist-credentials: false @@ -101,11 +100,11 @@ jobs: ./hack/ci-helper.sh rebase-atop-of-the-latest-target-branch - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Login to quay Container Registry if: ${{ startsWith(inputs.registry, 'quay.io') }} - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: quay.io username: ${{ vars.QUAY_USERNAME }} @@ -113,7 +112,7 @@ jobs: - name: Login to the ghcr Container registry if: ${{ startsWith(inputs.registry, 'ghcr.io') }} - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: ghcr.io username: ${{ github.actor }} @@ -143,41 +142,45 @@ jobs: - name: Build binaries id: build_binaries - working-directory: src/cloud-api-adaptor/podvm-mkosi - run: make binaries + working-directory: src/cloud-api-adaptor/podvm + run: make podvm-binaries env: ARCH: ${{ inputs.arch }} VERIFY_PROVENANCE: yes - name: Build mkosi debug image if: ${{ inputs.debug == true }} - working-directory: src/cloud-api-adaptor/podvm-mkosi + working-directory: src/cloud-api-adaptor/podvm run: make image-debug + env: + MKOSI_VERSION: ${{ env.MKOSI_VERSION }} - name: Build mkosi image if: ${{ inputs.debug == false }} - working-directory: src/cloud-api-adaptor/podvm-mkosi + working-directory: src/cloud-api-adaptor/podvm run: make image + env: + MKOSI_VERSION: ${{ env.MKOSI_VERSION }} - name: Upload the qcow2 with oras id: publish_oras_qcow2 - working-directory: src/cloud-api-adaptor/podvm-mkosi + working-directory: src/cloud-api-adaptor/podvm run: | mkdir oras cd oras cp ../build/podvm-*.qcow2 . tar cJf podvm.tar.xz podvm-*.qcow2 - image=${{ inputs.registry }}/podvm-generic-fedora + image=${REGISTRY}/podvm-generic-fedora if [ "${DEBUG}" = "true" ]; then image=${image}-debug fi - image=${image}-${{ inputs.arch }} + image=${image}-${ARCH} tag=$(git rev-parse --short HEAD) oras push "${image}:${tag}" podvm.tar.xz # If the input has a different image-tag then also push it with that tag - if [ -n "${{ inputs.image_tag }}" ] && [ "${{ inputs.image_tag }}" != "${tag}" ];then - oras push "${image}:${{ inputs.image_tag }}" podvm.tar.xz + if [ -n "${IMAGE_TAG}" ] && [ "${IMAGE_TAG}" != "${tag}" ];then + oras push "${image}:${IMAGE_TAG}" podvm.tar.xz fi # add image and digest to output for attestation @@ -187,30 +190,12 @@ jobs: echo "tag=${tag}" >> "$GITHUB_OUTPUT" env: DEBUG: ${{ inputs.debug == true && 'true' || 'false' }} + REGISTRY: ${{ inputs.registry }} + ARCH: ${{ inputs.arch }} + IMAGE_TAG: ${{ inputs.image_tag }} - uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0 with: subject-name: ${{ steps.publish_oras_qcow2.outputs.image }} subject-digest: ${{ steps.publish_oras_qcow2.outputs.digest }} push-to-registry: true - - - name: Clean up some space for the docker provider build - working-directory: src/cloud-api-adaptor/podvm-mkosi - run: | - sudo du --max-depth=2 /home/runner || true - sudo du --max-depth=2 /var/lib || true - sudo rm -rf ./build - sudo rm -rf ./mkosi.cache - - - name: Build and push image for docker provider - id: build_docker_oci - working-directory: src/cloud-api-adaptor/podvm-mkosi - run: | - tag=$(git rev-parse --short HEAD) - PODVM_TAG=${tag} make image-container - PODVM_TAG=${tag} make push-image-container - arch=$(uname -m) - arch=${arch/x86_64/amd64} - echo "image=${{ inputs.registry }}/podvm-docker-image-${arch}:${tag}" >> "$GITHUB_OUTPUT" - env: - REGISTRY: ${{ inputs.registry }} diff --git a/.github/workflows/podvm_mkosi_ubuntu.yaml b/.github/workflows/podvm_mkosi_ubuntu.yaml index d60df61869..08ed973327 100644 --- a/.github/workflows/podvm_mkosi_ubuntu.yaml +++ b/.github/workflows/podvm_mkosi_ubuntu.yaml @@ -65,7 +65,7 @@ on: value: ${{ jobs.build-image.outputs.byom_e2e_image }} concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-mkosi-ubuntu-${{ inputs.arch }} + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-mkosi-ubuntu-${{ toJSON(inputs) }} cancel-in-progress: true defaults: @@ -77,7 +77,7 @@ permissions: {} jobs: build-image: name: Build mkosi Ubuntu image for ${{ inputs.arch }} - runs-on: ${{ inputs.arch == 's390x' && 's390x' || 'ubuntu-24.04' }} + runs-on: ${{ inputs.arch == 's390x' && 's390x' || (inputs.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-24.04') }} permissions: contents: read # Required if we want to run on a fork? packages: write # Required to publish the oras package to ghcr @@ -90,7 +90,7 @@ jobs: byom_e2e_image: ${{ steps.build_byom_e2e.outputs.image }} steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 0 persist-credentials: false @@ -108,11 +108,11 @@ jobs: ./hack/ci-helper.sh rebase-atop-of-the-latest-target-branch - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Login to quay Container Registry if: ${{ startsWith(inputs.registry, 'quay.io') }} - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: quay.io username: ${{ vars.QUAY_USERNAME }} @@ -120,7 +120,7 @@ jobs: - name: Login to the ghcr Container registry if: ${{ startsWith(inputs.registry, 'ghcr.io') }} - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: ghcr.io username: ${{ github.actor }} @@ -150,8 +150,8 @@ jobs: - name: Build binaries for Ubuntu id: build_binaries - working-directory: src/cloud-api-adaptor/podvm-mkosi - run: make binaries + working-directory: src/cloud-api-adaptor/podvm + run: make podvm-binaries env: ARCH: ${{ inputs.arch }} PODVM_DISTRO: ubuntu @@ -159,21 +159,23 @@ jobs: - name: Build mkosi debug image if: ${{ inputs.debug == true }} - working-directory: src/cloud-api-adaptor/podvm-mkosi + working-directory: src/cloud-api-adaptor/podvm run: make image-debug env: PODVM_DISTRO: ubuntu + MKOSI_VERSION: ${{ env.MKOSI_VERSION }} - name: Build mkosi image if: ${{ inputs.debug == false }} - working-directory: src/cloud-api-adaptor/podvm-mkosi + working-directory: src/cloud-api-adaptor/podvm run: make image env: PODVM_DISTRO: ubuntu + MKOSI_VERSION: ${{ env.MKOSI_VERSION }} - name: Upload the qcow2 with oras id: publish_oras_qcow2 - working-directory: src/cloud-api-adaptor/podvm-mkosi + working-directory: src/cloud-api-adaptor/podvm run: | mkdir oras cd oras @@ -209,17 +211,17 @@ jobs: subject-digest: ${{ steps.publish_oras_qcow2.outputs.digest }} push-to-registry: true - - name: Clean up some space for the docker provider build - working-directory: src/cloud-api-adaptor/podvm-mkosi + - name: Clean up some space for the docker podvm image build + working-directory: src/cloud-api-adaptor/podvm run: | sudo du --max-depth=2 /home/runner || true sudo du --max-depth=2 /var/lib || true sudo rm -rf ./build sudo rm -rf ./mkosi.cache - - name: Build and push image for docker provider + - name: Build and push image for docker podvm image id: build_docker_oci - working-directory: src/cloud-api-adaptor/podvm-mkosi + working-directory: src/cloud-api-adaptor/podvm run: | tag=$(git rev-parse --short HEAD) PODVM_TAG=${tag} make image-container @@ -233,7 +235,7 @@ jobs: - name: Build and push BYOM e2e image id: build_byom_e2e - working-directory: src/cloud-api-adaptor/podvm-mkosi + working-directory: src/cloud-api-adaptor/podvm run: | # Build BYOM e2e image (depends on docker image built in previous step) PODVM_TAG=${IMAGE_TAG} make image-byom-e2e-container diff --git a/.github/workflows/podvm_publish.yaml b/.github/workflows/podvm_publish.yaml index 372f80d753..eca9c882cf 100644 --- a/.github/workflows/podvm_publish.yaml +++ b/.github/workflows/podvm_publish.yaml @@ -36,51 +36,13 @@ on: permissions: {} jobs: - podvm_builder: - uses: ./.github/workflows/podvm_builder.yaml - with: - git_ref: ${{ inputs.git_ref || github.sha }} - image_tag: ${{ inputs.image_tag || github.sha }} - registry: ${{ inputs.registry != '' && inputs.registry || (github.repository == 'confidential-containers/cloud-api-adaptor' && 'quay.io/confidential-containers' || format('ghcr.io/{0}', github.repository_owner)) }} - permissions: - contents: read - packages: write - secrets: - QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }} - - podvm_binaries: - needs: [podvm_builder] - uses: ./.github/workflows/podvm_binaries.yaml - with: - git_ref: ${{ inputs.git_ref || github.sha }} - image_tag: ${{ inputs.image_tag || github.sha }} - registry: ${{ inputs.registry != '' && inputs.registry || (github.repository == 'confidential-containers/cloud-api-adaptor' && 'quay.io/confidential-containers' || format('ghcr.io/{0}', github.repository_owner)) }} - - permissions: - contents: read - packages: write - secrets: - QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }} podvm: - needs: [podvm_binaries] - uses: ./.github/workflows/podvm.yaml - with: - git_ref: ${{ inputs.git_ref || github.sha }} - image_tag: ${{ inputs.image_tag || github.sha }} - registry: ${{ inputs.registry != '' && inputs.registry || (github.repository == 'confidential-containers/cloud-api-adaptor' && 'quay.io/confidential-containers' || format('ghcr.io/{0}', github.repository_owner)) }} - permissions: - contents: read - packages: write - secrets: - QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }} - - podvm-mkosi: uses: ./.github/workflows/podvm_mkosi.yaml permissions: - contents: read - packages: write - id-token: write - attestations: write + contents: read # Required to checkout code + packages: write # Required to push container images + id-token: write # Required for OIDC authentication + attestations: write # Required to write attestations artifact-metadata: write # Required by podvm_mkosi.yaml to write attestation metadata strategy: fail-fast: false @@ -95,13 +57,14 @@ jobs: secrets: QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }} - podvm-ubuntu-mkosi: + podvm-ubuntu: uses: ./.github/workflows/podvm_mkosi_ubuntu.yaml permissions: contents: read # Required if we want to run on a fork? packages: write # Required to publish the oras package to ghcr id-token: write # Required to publish the attestation provenance to ghcr attestations: write # Required to publish the attestation provenance to ghcr + artifact-metadata: write # Required by podvm_mkosi_ubuntu.yaml to write attestation metadata strategy: fail-fast: false matrix: diff --git a/.github/workflows/podvm_smoketest.yaml b/.github/workflows/podvm_smoketest.yaml index 15141b454b..c186d233fb 100644 --- a/.github/workflows/podvm_smoketest.yaml +++ b/.github/workflows/podvm_smoketest.yaml @@ -3,18 +3,23 @@ name: smoke test on: workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + permissions: {} jobs: build: + name: build runs-on: 'ubuntu-24.04' defaults: run: - working-directory: src/cloud-api-adaptor/podvm-mkosi + working-directory: src/cloud-api-adaptor/podvm steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false @@ -45,10 +50,10 @@ jobs: version: ${{ env.ORAS_VERSION }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Build binaries - run: make binaries + run: make podvm-binaries - name: Disable TLS for agent-protocol-forwarder run: | @@ -63,9 +68,10 @@ jobs: uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: podvm-build - path: src/cloud-api-adaptor/podvm-mkosi/build/podvm-fedora-amd64.qcow2 + path: src/cloud-api-adaptor/podvm/build/podvm-fedora-amd64.qcow2 test: + name: test # We're pinning the runner to 22.04 b/c libvirt struggles with the # OVMF_CODE_4M firmware that is default on 24.04. runs-on: 'ubuntu-22.04' @@ -74,12 +80,12 @@ jobs: strategy: matrix: test-mode: - - name: podvm-mkosi + - name: podvm mode: basic - - name: podvm-mkosi-with-scratch-space + - name: podvm-with-scratch-space mode: scratch-space steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false @@ -97,8 +103,8 @@ jobs: working-directory: src/cloud-api-adaptor run: | { - echo "KATA_REF=$(yq -e '.oci.kata-agent-ctl.reference' versions.yaml)"; - echo "KATA_REG=$(yq -e '.oci.kata-agent-ctl.registry' versions.yaml)"; + echo "KATA_REF=$(yq -e '.oci.kata-containers.reference' versions.yaml)"; + echo "KATA_REG=$(yq -e '.oci.kata-containers.registry' versions.yaml)"; } >> "$GITHUB_ENV" - name: Install kata-agent-ctl diff --git a/.github/workflows/podvm_smoketest_ubuntu.yaml b/.github/workflows/podvm_smoketest_ubuntu.yaml index 7b16282c6a..e52c7ef3e9 100644 --- a/.github/workflows/podvm_smoketest_ubuntu.yaml +++ b/.github/workflows/podvm_smoketest_ubuntu.yaml @@ -16,10 +16,10 @@ jobs: defaults: run: - working-directory: src/cloud-api-adaptor/podvm-mkosi + working-directory: src/cloud-api-adaptor/podvm steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false @@ -49,10 +49,10 @@ jobs: version: ${{ env.ORAS_VERSION }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Build binaries - run: make binaries + run: make podvm-binaries env: PODVM_DISTRO: ubuntu @@ -71,7 +71,7 @@ jobs: uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: podvm-build-ubuntu - path: src/cloud-api-adaptor/podvm-mkosi/build/podvm-ubuntu-amd64.qcow2 + path: src/cloud-api-adaptor/podvm/build/podvm-ubuntu-amd64.qcow2 test: name: Test the image @@ -83,12 +83,12 @@ jobs: strategy: matrix: test-mode: - - name: podvm-mkosi-ubuntu + - name: podvm-ubuntu mode: basic - - name: podvm-mkosi-with-scratch-space-ubuntu + - name: podvm-with-scratch-space-ubuntu mode: scratch-space steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false @@ -106,8 +106,8 @@ jobs: run: | { echo "ORAS_VERSION=$(./hack/versions.py -q tools.oras)"; - echo "KATA_AGENT_CTL_REF=$(./hack/versions.py -q oci.kata-agent-ctl.reference)"; - echo "KATA_AGENT_CTL_REG=$(./hack/versions.py -q oci.kata-agent-ctl.registry)"; + echo "KATA_REF=$(./hack/versions.py -q oci.kata-containers.reference)"; + echo "KATA_REG=$(./hack/versions.py -q oci.kata-containers.registry)"; } >> "$GITHUB_ENV" - uses: oras-project/setup-oras@38de303aac69abb66f3e6255b7198bff35f323e3 # v2.0.0 @@ -116,7 +116,7 @@ jobs: - name: Install kata-agent-ctl run: | - oras pull "${KATA_AGENT_CTL_REG}/agent-ctl:${KATA_AGENT_CTL_REF}-x86_64" + oras pull "${KATA_REG}/agent-ctl:${KATA_REF}-x86_64" tar --zstd -xf kata-static-agent-ctl.tar.zst cp opt/kata/bin/kata-agent-ctl /usr/local/bin diff --git a/.github/workflows/publish_images_on_push.yaml b/.github/workflows/publish_images_on_push.yaml index dc0ce9da80..baf8d8d4bc 100644 --- a/.github/workflows/publish_images_on_push.yaml +++ b/.github/workflows/publish_images_on_push.yaml @@ -32,20 +32,8 @@ jobs: release_tags: ${{ github.sha }} registry: ${{ inputs.registry != '' && inputs.registry || (github.repository == 'confidential-containers/cloud-api-adaptor' && 'quay.io/confidential-containers' || format('ghcr.io/{0}', github.repository_owner)) }} permissions: - contents: read - packages: write - secrets: - QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }} - - csi-wrapper: - uses: ./.github/workflows/csi_wrapper_images.yaml - with: - image_tags: latest,${{ github.sha }} - git_ref: ${{ github.sha }} - registry: ${{ inputs.registry != '' && inputs.registry || (github.repository == 'confidential-containers/cloud-api-adaptor' && 'quay.io/confidential-containers' || format('ghcr.io/{0}', github.repository_owner)) }} - permissions: - contents: read - packages: write + contents: read # Required to checkout code + packages: write # Required to push container images secrets: QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }} @@ -67,8 +55,8 @@ jobs: git_ref: ${{ github.sha }} registry: ${{ inputs.registry != '' && inputs.registry || (github.repository == 'confidential-containers/cloud-api-adaptor' && 'quay.io/confidential-containers' || format('ghcr.io/{0}', github.repository_owner)) }} permissions: - contents: read - packages: write + contents: read # Required to checkout code + packages: write # Required to push container images secrets: QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index e024d90f19..12c7e9ef74 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -18,19 +18,8 @@ jobs: release_tags: ${{ github.event.release.tag_name }} git_ref: ${{ github.ref }} permissions: - contents: read - packages: write - secrets: - QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }} - - csi-wrapper: - uses: ./.github/workflows/csi_wrapper_images.yaml - with: - image_tags: ${{ github.event.release.tag_name }} - git_ref: ${{ github.ref }} - permissions: - contents: read - packages: write + contents: read # Required to checkout code + packages: write # Required to push container images secrets: QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }} @@ -64,8 +53,8 @@ jobs: image_tags: ${{ github.event.release.tag_name }} git_ref: ${{ github.ref }} permissions: - contents: read - packages: write + contents: read # Required to checkout code + packages: write # Required to push container images secrets: QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }} diff --git a/.github/workflows/scorecard.yaml b/.github/workflows/scorecard.yaml index 1f8fb9507c..73ebf616a1 100644 --- a/.github/workflows/scorecard.yaml +++ b/.github/workflows/scorecard.yaml @@ -11,6 +11,10 @@ on: branches: [ "main" ] workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + permissions: {} jobs: @@ -20,14 +24,12 @@ jobs: # `publish_results: true` only works when run from the default branch. conditional can be removed if disabled. if: github.event.repository.default_branch == github.ref_name || github.event_name == 'pull_request' permissions: - # Needed to upload the results to code-scanning dashboard. - security-events: write - # Needed to publish results and get a badge (see publish_results below). - id-token: write + security-events: write # Needed to upload the results to code-scanning dashboard + id-token: write # Needed to publish results and get a badge (see publish_results below) steps: - name: "Checkout code" - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false @@ -55,6 +57,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 + uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 with: sarif_file: results.sarif diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml index 5955ccf59e..203ecd5209 100644 --- a/.github/workflows/stale.yaml +++ b/.github/workflows/stale.yaml @@ -18,7 +18,7 @@ jobs: actions: write # Needed to manage caches for state persistence across runs pull-requests: write # Needed to add/remove labels, post comments, or close PRs steps: - - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0 + - uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0 with: stale-pr-message: 'This PR has been opened without any activity for 90 days. Please comment on the issue otherwise it will be closed in 7 days' days-before-pr-stale: 90 diff --git a/.github/workflows/test-images.yaml b/.github/workflows/test-images.yaml index 0ead0633ee..505c6d0f50 100644 --- a/.github/workflows/test-images.yaml +++ b/.github/workflows/test-images.yaml @@ -13,15 +13,20 @@ defaults: run: working-directory: src/cloud-api-adaptor +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + permissions: {} jobs: list-dockerfiles: + name: list-dockerfiles runs-on: ubuntu-24.04 outputs: matrix: ${{ env.MATRIX }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - id: set-matrix @@ -30,25 +35,26 @@ jobs: name: Create Test Images needs: list-dockerfiles runs-on: ubuntu-24.04 + environment: production strategy: fail-fast: false matrix: targets: ${{ fromJson(needs.list-dockerfiles.outputs.matrix) }} arch: [amd64, s390x, arm64] permissions: - contents: read - packages: write + contents: read # Required to checkout code + packages: write # Required to push container images steps: - name: Checkout Code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Login to Quay container Registry - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: quay.io username: ${{ vars.QUAY_USERNAME }} @@ -56,11 +62,13 @@ jobs: - name: determine docker tag by splitting branch name on slash id: docker-tag shell: bash + env: + MATRIX_TARGET: ${{ matrix.targets }} run: | - file_name=$(basename "${{matrix.targets}}") + file_name=$(basename "${MATRIX_TARGET}") echo "DOCKER_TAG=${file_name##*.}" >> "$GITHUB_ENV" - name: Build and push - uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 + uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 with: tags: | quay.io/confidential-containers/test-images:${{env.DOCKER_TAG}} diff --git a/.github/workflows/validate-config-extractor.yaml b/.github/workflows/validate-config-extractor.yaml index 80c7a3ec94..e169926aee 100644 --- a/.github/workflows/validate-config-extractor.yaml +++ b/.github/workflows/validate-config-extractor.yaml @@ -30,7 +30,7 @@ jobs: working-directory: src/cloud-providers steps: - name: Checkout the pull request code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false diff --git a/.github/workflows/versions.yaml b/.github/workflows/versions.yaml index f5af189ee2..d116d19fcf 100644 --- a/.github/workflows/versions.yaml +++ b/.github/workflows/versions.yaml @@ -38,7 +38,7 @@ jobs: contents: read steps: - name: Checkout the pull request code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false diff --git a/.github/workflows/webhook_image.yaml b/.github/workflows/webhook_image.yaml index 1fc972eca8..5437fe803f 100644 --- a/.github/workflows/webhook_image.yaml +++ b/.github/workflows/webhook_image.yaml @@ -35,11 +35,11 @@ jobs: run: working-directory: src/webhook permissions: - contents: read - packages: write + contents: read # Required to checkout code + packages: write # Required to push container images steps: - name: Checkout the code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 0 persist-credentials: false @@ -56,36 +56,39 @@ jobs: cache-dependency-path: "**/go.sum" cache: false - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Login to quay Container Registry if: ${{ startsWith(inputs.registry, 'quay.io') }} - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: quay.io username: ${{ vars.QUAY_USERNAME }} password: ${{ secrets.QUAY_PASSWORD }} - name: Login to Github Container Registry if: ${{ startsWith(inputs.registry, 'ghcr.io') }} - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push image + env: + IMAGE_TAGS: ${{ inputs.image_tags }} + REGISTRY: ${{ inputs.registry }} run: | - tags="${{ inputs.image_tags }}" + tags="${IMAGE_TAGS}" latest=0 for t in ${tags/,/ }; do if [ "$t" = "latest" ]; then latest=1 else echo "::group::Build and push tag ${t}" - make docker-load docker-push IMG="${{ inputs.registry }}/peer-pods-webhook:${t}" + make docker-load docker-push IMG="${REGISTRY}/peer-pods-webhook:${t}" echo "::endgroup::" fi done if [ $latest -eq 1 ]; then echo "::group::Push latest" - make docker-push IMG=${{ inputs.registry }}/peer-pods-webhook:latest + make docker-push IMG="${REGISTRY}/peer-pods-webhook:latest" echo "::endgroup::" fi diff --git a/.github/workflows/zizmor.yaml b/.github/workflows/zizmor.yaml index 43c9d33467..c7effc92cf 100644 --- a/.github/workflows/zizmor.yaml +++ b/.github/workflows/zizmor.yaml @@ -16,18 +16,17 @@ concurrency: jobs: zizmor: name: Run zizmor - runs-on: ubuntu-22.04 - permissions: - security-events: write # Require writing security events to upload results to security tab - + runs-on: ubuntu-24.04 steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false fetch-depth: 0 - name: Run zizmor - uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3 + uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6 with: + advanced-security: false + annotations: true persona: auditor diff --git a/CITATION.cff b/CITATION.cff index 382841f58e..cb053f4e34 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -5,6 +5,6 @@ authors: title: Cloud API Adaptor abstract: Ability to create Kata pods using cloud provider APIs aka the peer-pods approach license: Apache-2.0 -version: 0.20.0 -date-released: 2026-04-28 -commit: cc5a1fbf8fcd5458405a9f7a1d56cd4811cea1d9 +version: 0.21.1 +date-released: 2026-05-29 +commit: ea3ea1f063b10073a551d97e11d31169ca4648f5 diff --git a/Makefile b/Makefile index f105af6d8e..8746bb1178 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ help: ## Display this help. ## Run formatters and linters against the code. .PHONY: check -check: fmt golangci-lint shellcheck tidy-check govulncheck packer-check terraform-check +check: fmt golangci-lint shellcheck tidy-check govulncheck terraform-check .PHONY: fmt fmt: ## Run go fmt against code. @@ -51,12 +51,6 @@ govulncheck: ./hack/govulncheck.sh .PHONY: packer-format -packer-format: - ./hack/packer-check.sh - -.PHONY: packer-check -packer-check: - ./hack/packer-check.sh --check .PHONY: terraform-format terraform-format: diff --git a/README.md b/README.md index 44ac47f218..0a6d78a4e0 100644 --- a/README.md +++ b/README.md @@ -16,18 +16,19 @@ or nested virtualisation support. The background and description of the components involved in 'peer pods' can be found in the [architecture documentation](./docs/architecture.md). ## Cloud Providers + [cloud-providers](./src/cloud-providers/) Cloud Providers for Kata remote hypervisor ## PeerPod controller + [peerpod-ctrl](./src/peerpod-ctrl/) PeerPod controller is watching PeerPod events and deleting dangling resources that were not deleted by the cloud-api-adaptor at Pod deletion time. ## Cloud API Adaptor -[cloud-api-adaptor](./src/cloud-api-adaptor/) Ability to create Kata pods using cloud provider APIs aka the peer-pods approach -## CSI Wrapper -[csi-wrapper](./src/csi-wrapper/) CSI Wrapper solution for Peer Pod Storage +[cloud-api-adaptor](./src/cloud-api-adaptor/) Ability to create Kata pods using cloud provider APIs aka the peer-pods approach ## Webhook + [webhook](./src/webhook/) This mutating webhook modifies a POD spec using specific runtimeclass to remove all `resources` entries and replace it with peer-pod extended resource. ## Contribution @@ -37,5 +38,5 @@ This project uses [the Apache 2.0 license](./LICENSE). Contribution to this proj ## Collaborations * Slack: [#confidential-containers-peerpod](https://cloud-native.slack.com/archives/C04A2EJ70BX) in [CNCF](https://communityinviter.com/apps/cloud-native/cncf) -* Zoom meeting: https://zoom.us/j/94601737867?pwd=MEF5NkN5ZkRDcUtCV09SQllMWWtzUT09 - * 14:00 - 15:00 UTC on each `Wednesday` +* Zoom meeting: + * 14:00 - 15:00 UTC on each `Wednesday` diff --git a/docs/Release-Process.md b/docs/Release-Process.md index 4acc2c0ebb..0f615a26aa 100644 --- a/docs/Release-Process.md +++ b/docs/Release-Process.md @@ -26,12 +26,14 @@ The flow of releases should roughly be: - We should already be in sync with the guest-components and trustee, from the previous step, but now we should update: - The kata-containers source branch that we use in [versions.yaml](../src/cloud-api-adaptor/versions.yaml) to the kata-containers release version. - - The `kata-containers/src/runtime` go module that we include in the main `cloud-api-adaptor` [`go.mod`](../src/cloud-api-adaptor/go.mod) and the `csi-wrapper` [`go.mod`](../src/csi-wrapper/go.mod). This can be done by running + - The `kata-containers/src/runtime` go module that we include in the main `cloud-api-adaptor` [`go.mod`](../src/cloud-api-adaptor/go.mod). This can be done by running + ``` go get github.com/kata-containers/kata-containers/src/runtime@ go mod tidy ``` - in the [cloud-api-adaptor](../src/cloud-api-adaptor/) directory and [csi-wrapper](../src/csi-wrapper/) directory. + + in the [cloud-api-adaptor](../src/cloud-api-adaptor/) directory. - Update the `dependencies.kata-deploy` version in [`Chart.yaml`](../src/cloud-api-adaptor/install/charts/peerpods/Chart.yaml) to the Kata release version and then run `helm dependency update src/cloud-api-adaptor/install/charts/peerpods/` to update [`Chart.lock`](../src/cloud-api-adaptor/install/charts/peerpods/Chart.lock). @@ -66,6 +68,7 @@ In the same PR, bump the Helm chart versions in the [peerpod-ctrl](../src/peerpod-ctrl/chart/Chart.yaml) and [webhook](../src/webhook/chart/Chart.yaml), `Chart.yaml`s for: + - `version`: bump to the new chart release version (e.g. `0.2.0` => `0.3.0`) - `appVersion`: set to the CAA release version (e.g. `v0.18.0`) @@ -88,8 +91,6 @@ git tag src/cloud-api-adaptor/v0.8.0 main git push upstream src/cloud-api-adaptor/v0.8.0 git tag src/cloud-providers/v0.8.0 main git push upstream src/cloud-providers/v0.8.0 -git tag src/csi-wrapper/v0.8.0 main -git push upstream src/csi-wrapper/v0.8.0 git tag src/peerpod-ctrl/v0.8.0 main git push upstream src/peerpod-ctrl/v0.8.0 git tag src/webhook/v0.8.0 main @@ -106,9 +107,6 @@ Total 0 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0) To github.com:confidential-containers/cloud-api-adaptor.git * [new tag] src/cloud-providers/v0.8.0 -> src/cloud-providers/v0.8.0 Total 0 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0) -To github.com:confidential-containers/cloud-api-adaptor.git - * [new tag] src/csi-wrapper/v0.8.0 -> src/csi-wrapper/v0.8.0 -Total 0 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0) To github.com:confidential-containers/cloud-api-adaptor.git * [new tag] src/peerpod-ctrl/v0.8.0 -> src/peerpod-ctrl/v0.8.0 Total 0 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0) @@ -131,7 +129,6 @@ Update the `dependencies.kata-deploy` version in [`Chart.yaml`](../src/cloud-api Revert `image.tag` back to `"latest"` in [`values.yaml`](../src/cloud-api-adaptor/install/charts/peerpods/values.yaml), [`providers/libvirt.yaml`](../src/cloud-api-adaptor/install/charts/peerpods/providers/libvirt.yaml), -[`providers/docker.yaml`](../src/cloud-api-adaptor/install/charts/peerpods/providers/docker.yaml), [`peerpod-ctrl/chart/values.yaml`](../src/peerpod-ctrl/chart/values.yaml), and [`webhook/chart/values.yaml`](../src/webhook/chart/values.yaml). diff --git a/docs/architecture.md b/docs/architecture.md index 80b3ee0510..d32b079ac0 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -13,6 +13,7 @@ or nested virtualisation support. With the Confidential Containers (CoCo) projects we are aiming to deliver a confidential computing enabled Kubernetes Platform providing: + - **Security and Confidentiality** - We want to provide highly secure solutions to our users' applications, limiting the actions of the cloud service provider (CSP), or Kubernetes Admins and providing assurance that their data and workloads remain confidential and safe from modification. @@ -23,6 +24,7 @@ minimal changes. The way that we do this is utilise Trusted Execution Environments (TEEs). These are areas of hardware where code is executed with the following features: + - Hardware root-of-trust - Remote Attestation - Trusted launch @@ -44,7 +46,6 @@ can run our pod sandboxes. Although this is the traditional way to currently configure a Kata Containers VMs in the cloud, there is a big drawback of having to have a separate bare metal box per Kubernetes node, which is expensive and difficult to scale. - ### Nested Virtualisation Most current cloud providers need to have the scalabilty and cost savings that bare metal nodes cannot provide, so @@ -54,7 +55,6 @@ Kubernetes node map to a virtual server, so each physical server can run multipl multiple pod sandboxes. Currently there are limitation of not being able to run as a second-level VM in a performant way for most TEEs and CSPs, means we can't put these pod sandboxes inside a TEE boundary in these scenarios. - ### Peer pods The peer pod approach is to compromise between the two previous models - using a TEE environment as a first-level KVM, @@ -77,6 +77,7 @@ Using a virtual network to communicate between the node (host) and the pod sandb peer pods is more complicated as well. #### Advantages + Due to the lack of wide availability of running TEEs in nested VM, if we want to use TEEs, then compared to having a bare metal machine per node, we can scale more easily and cheaply. There is also some advantages to the independence of peer pods, where each pod sandbox has it's own dedicated resources @@ -102,8 +103,8 @@ The [`shim v2`](https://github.com/kata-containers/kata-containers/blob/main/doc part of the Kata Containers runtime, implements the containerd runtime v2 API for Kata Containers and allows it to receive commands from Kubernetes, via a CRI client to launch and run Pods. - ### Cloud API Adaptor + The [cloud-api-adaptor](../src/cloud-api-adaptor/cmd/cloud-api-adaptor) implements the remote hypervisor. It runs as a daemonset on each node and is responsible for receiving commands from the `containerd-shim-kata-v2` process and implementing them for peer pods. For example, when it receives commands that were related to Pod lifecycle that are traditionally @@ -114,6 +115,7 @@ When the VMs are created they make a CNI compatible network tunnel using VxLAN t peer pods VM to flow other commands like `CreateContainer` through to the the remote sandbox. ### Webhook + The [webhook](../src/webhook/) is an mutating admission controller that modifies a pod spec using specific runtimeclass to remove all resources entries and replace it with peer-pod extended resource. This is needed as unlike a standard pod, a peer pod will not consume the worker node's resources in the same way. @@ -124,12 +126,6 @@ The [peerpod-ctrl](../src/peerpod-ctrl/) is a Kubernetes controller that is used the peer pods. It is responsible for watching PeerPod events and ensuring that any resources that weren't cleaned up by the cloud-api-adaptor (e.g. in the case of a network error) are deleted. -### CSI wrapper - -The [CSI wrapper](../src/csi-wrapper/) plugins (which run on the control place and worker nodes and the peer pod -VM) allows Kubernetes to use Container Storage Interface (CSI) to attach and export persistent volumes to a pod and -have them be attached to the peer pod. - ## Peer Pod VM components In order to create a peer pod VM, each cloud provider needs to have an [image](../src/cloud-api-adaptor/podvm/) created that provides some diff --git a/hack/govulncheck.sh b/hack/govulncheck.sh index b270992d5b..6aacfc769f 100755 --- a/hack/govulncheck.sh +++ b/hack/govulncheck.sh @@ -81,6 +81,15 @@ fi statuscode=0 for module in "${goModules[@]}"; do + # Check if the module has any Go packages before running govulncheck + # Some modules have go.mod but no Go source files + if go list -C "${module}" ./... 2>&1 | grep -q "matched no packages"; then + if [ "$verbose" = true ]; then + echo "Skipping ${module}: no Go packages found" + fi + continue + fi + govulncheck -C "${module}" ./... || statuscode=$? done diff --git a/hack/packer-check.sh b/hack/packer-check.sh deleted file mode 100755 index 812c7966fd..0000000000 --- a/hack/packer-check.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -check=false -if [[ "${1-}" == "--check" ]]; then - check=true -fi - -# Find top level packer directories - -readarray -t <<< "$( - find "$(pwd)" -type f -name "*.pkr.hcl" -exec dirname "{}" \; | - sort -ud -)" -packerPaths=("${MAPFILE[@]}") -pathPrefix="${packerPaths[0]}" -packerModules=("${pathPrefix}") -for ((i = 1; i < ${#packerPaths[@]}; i++)); do - path="${packerPaths[i]}" - if [[ ${path} == "${pathPrefix}"* ]]; then - continue - fi - packerModules+=("${pathPrefix}") - pathPrefix="${path}" -done - -fmtFlags=() -if [ "$check" = true ]; then - fmtFlags+=("-check" "-write=true" "-diff") -fi - -exitcode=0 - -echo "Checking Packer modules:" -for module in "${packerModules[@]}"; do - echo " $module" - # packer init "${module}" - packer fmt "${fmtFlags[@]}" "${module}" || exitcode=$? - # packer validate "${module}" || exitcode=$? -done - -exit $exitcode diff --git a/hack/release-helper.sh b/hack/release-helper.sh index 85cdc92b87..3390637f34 100755 --- a/hack/release-helper.sh +++ b/hack/release-helper.sh @@ -69,13 +69,11 @@ update_provider_charts() { sed "${sed_inplace[@]}" "s/^\( tag:\).*/\1 \"${image_tag}\"/" "${chart_dir}/values.yaml" echo "Updated ${chart_dir}/values.yaml -> ${image_tag}" - for provider in libvirt docker; do - provider_file="${chart_dir}/providers/${provider}.yaml" - if [ -f "${provider_file}" ]; then - sed "${sed_inplace[@]}" "s/^\( tag:\).*/\1 \"${image_tag}-dev\"/" "${provider_file}" - echo "Updated ${provider_file} -> ${image_tag}-dev" - fi - done + provider_file="${chart_dir}/providers/libvirt.yaml" + if [ -f "${provider_file}" ]; then + sed "${sed_inplace[@]}" "s/^\( tag:\).*/\1 \"${image_tag}-dev\"/" "${provider_file}" + echo "Updated ${provider_file} -> ${image_tag}-dev" + fi # Pin peerpod-ctrl and webhook image tags peerpodctrl_values="src/peerpod-ctrl/chart/values.yaml" @@ -104,7 +102,7 @@ usage() { "caa-image-tag": Updates the Helm chart values to a specific image tag of the cloud-api-adaptor, peerpod-ctrl and webhook to use for the release, to provide a pinned and stable version. - Providers libvirt and docker get the dev- prefix. + Provider libvirt gets the dev- prefix. - Parameters: where - image_tag corresponds to the tag of the pre-release tested version of the quay.io/confidential-containers/cloud-api-adaptor image diff --git a/src/cloud-api-adaptor/Dockerfile b/src/cloud-api-adaptor/Dockerfile index 20323ca5ec..94be72b18a 100644 --- a/src/cloud-api-adaptor/Dockerfile +++ b/src/cloud-api-adaptor/Dockerfile @@ -1,9 +1,9 @@ ARG BUILD_TYPE=dev -# golang:1.25.10 (based on Debian 13 trixie) -ARG BUILDER_BASE=golang@sha256:3965b9511a9bed199f0b4eb146f99c31971a5c0adb3240d9f25e233cbd9e01c8 -# debian:trixie-slim (matches golang:1.25.10 base for library compatibility) -# Multi-arch manifest list digest supporting amd64, arm64, ppc64le,, s390x -ARG BASE=debian@sha256:109e2c65005bf160609e4ba6acf7783752f8502ad218e298253428690b9eaa4b +# golang:1.25.11 (based on Debian 13 trixie) +ARG BUILDER_BASE=golang@sha256:dd7d32e19b28621cd982082397fc0510d396805b717d5e77466aa2dd692340de +# debian:trixie-slim (matches golang:1.25.11 base for library compatibility) +# Multi-arch manifest list digest supporting amd64, arm64, ppc64le, s390x +ARG BASE=debian@sha256:b6e2a152f22a40ff69d92cb397223c906017e1391a73c952b588e51af8883bf8 # This dockerfile uses Go cross-compilation to build the binary, # we build on the host platform ($BUILDPLATFORM) and then copy the @@ -59,7 +59,7 @@ RUN echo "Building iptables-wrapper... " \ FROM $BASE AS base-release RUN apt-get update && \ - apt-get install -y iptables && \ + apt-get install -y ca-certificates iptables && \ rm -rf /var/lib/apt/lists/* RUN --mount=type=cache,target=/iptables,from=iptables,source=/iptables,readonly \ cp /iptables/iptables-wrapper /usr/sbin \ diff --git a/src/cloud-api-adaptor/Makefile b/src/cloud-api-adaptor/Makefile index d6b49afc82..6b97029e71 100644 --- a/src/cloud-api-adaptor/Makefile +++ b/src/cloud-api-adaptor/Makefile @@ -28,7 +28,7 @@ WEBHOOK_ENABLED ?= true ifeq ($(RELEASE_BUILD),true) BUILTIN_CLOUD_PROVIDERS ?= alibabacloud aws azure byom gcp ibmcloud ibmcloud_powervs else - BUILTIN_CLOUD_PROVIDERS ?= alibabacloud aws azure byom gcp ibmcloud ibmcloud_powervs libvirt docker + BUILTIN_CLOUD_PROVIDERS ?= alibabacloud aws azure byom gcp ibmcloud ibmcloud_powervs libvirt endif all: build @@ -130,9 +130,7 @@ image: .git-commit ## Build and push docker image to $registry deploy: ## Deploy cloud-api-adaptor using Helm charts. Assumes provider secrets file exists at install/charts/peerpods/providers/$(CLOUD_PROVIDER)-secrets.yaml ifneq ($(CLOUD_PROVIDER),) $(eval HELM_VALUES_FILES := install/charts/peerpods/providers/$(CLOUD_PROVIDER).yaml) -ifeq ($(filter $(CLOUD_PROVIDER),docker),) $(eval HELM_VALUES_FILES := $(HELM_VALUES_FILES) install/charts/peerpods/providers/$(CLOUD_PROVIDER)-secrets.yaml) -endif ifeq ($(WEBHOOK_ENABLED),true) @if ! kubectl get svc -n cert-manager cert-manager-webhook > /dev/null 2>&1; then \ @@ -160,96 +158,6 @@ delete: ## Delete cloud-api-adaptor using Helm charts. echo "Release 'peerpods' not found in namespace 'confidential-containers-system'(either not installed or already uninstalled)"; \ fi -### PODVM IMAGE BUILDING ### - -REGISTRY ?= quay.io/confidential-containers - -PODVM_DISTRO ?= ubuntu -PODVM_TAG ?= $(VERSIONS_HASH) - -PODVM_BUILDER_IMAGE ?= $(REGISTRY)/podvm-builder-$(PODVM_DISTRO):$(PODVM_TAG) -PODVM_BINARIES_IMAGE ?= $(REGISTRY)/podvm-binaries-$(PODVM_DISTRO)-$(DISTRO_ARCH):$(PODVM_TAG) -PODVM_BYOM_BINARIES_IMAGE ?= $(REGISTRY)/podvm-byom-binaries-$(PODVM_DISTRO)-$(DISTRO_ARCH):$(PODVM_TAG) -PODVM_IMAGE ?= $(REGISTRY)/podvm-$(or $(CLOUD_PROVIDER),generic)-$(PODVM_DISTRO)-$(DISTRO_ARCH):$(PODVM_TAG) - -PUSH ?= false -# If not pushing `--load` into the local docker cache -DOCKER_OPTS := $(if $(filter $(PUSH),true),--push,--load) $(EXTRA_DOCKER_OPTS) - -DOCKERFILE_SUFFIX := $(if $(filter $(PODVM_DISTRO),ubuntu alinux),,.$(PODVM_DISTRO)) -BUILDER_DOCKERFILE := Dockerfile.podvm_builder$(DOCKERFILE_SUFFIX) -BINARIES_DOCKERFILE := Dockerfile.podvm_binaries$(DOCKERFILE_SUFFIX) -BYOM_BINARIES_DOCKERFILE := Dockerfile.podvm_byom_binaries$(DOCKERFILE_SUFFIX) -PODVM_DOCKERFILE := Dockerfile.podvm$(DOCKERFILE_SUFFIX) - -podvm-builder: - docker buildx build -t $(PODVM_BUILDER_IMAGE) -f podvm/$(BUILDER_DOCKERFILE) \ - --build-arg GO_VERSION=$(GO_VERSION) \ - --build-arg ORG_ID=$(ORG_ID) \ - --build-arg ACTIVATION_KEY=$(ACTIVATION_KEY) \ - --build-arg ARCH=$(ARCH) \ - --build-arg GO_ARCH=$(GO_ARCH) \ - --build-arg DISTRO_ARCH=$(DISTRO_ARCH) \ - --build-arg PROTOC_VERSION=$(PROTOC_VERSION) \ - --build-arg YQ_VERSION=$(YQ_VERSION) \ - --build-arg YQ_CHECKSUM=${YQ_CHECKSUM_$(GO_ARCH)} \ - --build-arg YQ_ARCH=$(GO_ARCH) \ - --build-arg PROTOC_ARCH=$(PROTOC_ARCH) \ - --build-arg ORAS_VERSION=$(ORAS_VERSION) \ - --build-arg PACKER_VERSION=$(PACKER_VERSION) \ - $(DOCKER_OPTS) . - -podvm-binaries: - cp -rf ../../.git .git - cd ../ && docker buildx build -t $(PODVM_BINARIES_IMAGE) -f cloud-api-adaptor/podvm/$(BINARIES_DOCKERFILE) \ - --build-arg BUILDER_IMG=$(PODVM_BUILDER_IMAGE) \ - --build-arg PODVM_DISTRO=$(PODVM_DISTRO) \ - --build-arg ARCH=$(ARCH) \ - --build-arg GO_ARCH=$(GO_ARCH) \ - --build-arg DISTRO_ARCH=$(DISTRO_ARCH) \ - --build-arg TEE_PLATFORM=$(TEE_PLATFORM) \ - --build-arg PAUSE_REPO=$(PAUSE_REPO) \ - --build-arg PAUSE_VERSION=$(PAUSE_VERSION) \ - --build-arg PAUSE_BIN=$(PAUSE_BIN) \ - $(if $(AUTHFILE),--build-arg AUTHFILE=$(AUTHFILE),) \ - $(if $(DEFAULT_AGENT_POLICY_FILE),--build-arg DEFAULT_AGENT_POLICY_FILE=$(DEFAULT_AGENT_POLICY_FILE),) \ - $(DOCKER_OPTS) . - rm -rf .git - -podvm-image: -ifeq ($(PODVM_DISTRO),rhel) -ifeq ($(shell test -f $(IMAGE_URL) && echo 1 || echo 0),1) - cp ${IMAGE_URL} ./rhel.img - $(eval IMAGE_URL=cloud-api-adaptor/rhel.img) - @echo "### Copied IMAGE_URL file to $(IMAGE_URL) in order to suite the docker context" -endif -endif - cp -rf ../../.git .git - cd ../ && docker buildx build -t $(PODVM_IMAGE) -f cloud-api-adaptor/podvm/$(PODVM_DOCKERFILE) \ - --build-arg BUILDER_IMG=$(PODVM_BUILDER_IMAGE) \ - --build-arg BINARIES_IMG=$(PODVM_BINARIES_IMAGE) \ - --build-arg PODVM_DISTRO=$(PODVM_DISTRO) \ - --build-arg ORG_ID=$(ORG_ID) \ - --build-arg ACTIVATION_KEY=$(ACTIVATION_KEY) \ - --build-arg ARCH=$(ARCH) \ - --build-arg CLOUD_PROVIDER=$(or $(CLOUD_PROVIDER),generic) \ - --build-arg IMAGE_URL=$(IMAGE_URL) \ - --build-arg IMAGE_CHECKSUM=$(IMAGE_CHECKSUM) \ - --build-arg SE_BOOT=$(SE_BOOT) \ - --build-arg SE_VERIFY=$(SE_VERIFY) \ - --build-arg PAUSE_REPO=$(PAUSE_REPO) \ - --build-arg PAUSE_VERSION=$(PAUSE_VERSION) \ - --build-arg PAUSE_BIN=$(PAUSE_BIN) \ - --build-arg PACKER_GITHUB_API_TOKEN=$(PACKER_GITHUB_API_TOKEN) \ - $(if $(AUTHFILE),--build-arg AUTHFILE=$(AUTHFILE),) \ - $(DOCKER_OPTS) . - rm -rf .git - -podvm-byom-binaries-image: - docker buildx build -t $(PODVM_BYOM_BINARIES_IMAGE) -f podvm-mkosi/$(BYOM_BINARIES_DOCKERFILE) \ - --build-arg BINARIES_IMG=$(PODVM_BINARIES_IMAGE) \ - $(DOCKER_OPTS) . - ##@ Helm .PHONY: static-helm-check diff --git a/src/cloud-api-adaptor/Makefile.defaults b/src/cloud-api-adaptor/Makefile.defaults index 24944bb648..fca24d431e 100644 --- a/src/cloud-api-adaptor/Makefile.defaults +++ b/src/cloud-api-adaptor/Makefile.defaults @@ -112,4 +112,3 @@ GUEST_COMPONENTS_REGISTRY := $(or $(GUEST_COMPONENTS_REGISTRY), $(call query,oci GUEST_COMPONENTS_REF := $(or $(GUEST_COMPONENTS_REF),$(call query,oci.guest-components.reference)) PAUSE_REPO := $(or $(PAUSE_REPO),$(call query,oci.pause.registry)) PAUSE_VERSION := $(or $(PAUSE_VERSION),$(call query,oci.pause.tag)) -PACKER_VERSION := $(or $(PACKER_VERSION),$(call query,tools.packer)) diff --git a/src/cloud-api-adaptor/README.md b/src/cloud-api-adaptor/README.md index 39511f3a0a..9fff51327a 100644 --- a/src/cloud-api-adaptor/README.md +++ b/src/cloud-api-adaptor/README.md @@ -38,22 +38,29 @@ A custom VM image, which contains the required components, must be available in each provider in their respective directories. You can also find further information in the podvm [README.md](./podvm/README.md) about how to build your own image using Docker to build the required components and create the image. -> At time of writing the project is moving towards using [mkosi](https://github.com/systemd/mkosi) as our build approach, more information on this can be found -> in the podvm-mkosi [README.md](./podvm-mkosi/README.md). +> The podvm build uses [mkosi](https://github.com/systemd/mkosi); see the podvm [README.md](./podvm/README.md) for details. ### VM Image Build Quick Start -To create a QCOW2 image which can be imported into your provider of choice, you can use the following command. +To create a bootable image which can be imported into your provider of choice, you can use the mkosi-based build system in the `podvm/` directory. ```bash -# default ubuntu based, x86 architecture image -make podvm-builder podvm-binaries podvm-image -# or to produce an s390x architecture image -ARCH=s390x make podvm-builder podvm-binaries podvm-image -# or to produce a rhel distribution image -PODVM_DISTRO=rhel IMAGE_URL=/path/to/rhel.qcow2 make podvm-builder podvm-binaries podvm-image +# Build Fedora-based image (default) +cd podvm +make # builds builder, binaries, and OS image + +# Build Ubuntu 24.04 image +cd podvm +PODVM_DISTRO=ubuntu make + +# Build with specific TEE platform support (e.g., SNP) +cd podvm +TEE_PLATFORM=snp make image + +# Convert to QCOW2 format for libvirt +qemu-img convert -f raw -O qcow2 build/system.raw build/system.qcow2 ``` -> N.B. This will populate the image using the component versions found in [versions.yaml](./versions.yaml) (for RHEL image download it and set the path as the IMAGE_URL). +> N.B. This will populate the image using the component versions found in [versions.yaml](./versions.yaml). See [podvm/README.md](./podvm/README.md) for detailed build instructions and customization options. You can find provider specific instructions on how to import the QCOW2 image for each cloud provider in their respective directories. diff --git a/src/cloud-api-adaptor/aws/image/.gitignore b/src/cloud-api-adaptor/aws/image/.gitignore deleted file mode 100644 index 08211c67fd..0000000000 --- a/src/cloud-api-adaptor/aws/image/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -agent-protocol-forwarder -kata-agent -skopeo -umoci diff --git a/src/cloud-api-adaptor/aws/image/Dockerfile b/src/cloud-api-adaptor/aws/image/Dockerfile deleted file mode 100644 index deccc41450..0000000000 --- a/src/cloud-api-adaptor/aws/image/Dockerfile +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright Confidential Containers Contributors -# -# SPDX-License-Identifier: Apache-2.0 -# -# Builds pod vm image inside container -# -# syntax=docker/dockerfile:1.3 -ARG RELEASE_TAG="v0.9.0" -ARG BUILDER_IMG="quay.io/confidential-containers/podvm-builder-ubuntu:${RELEASE_TAG}" -ARG BINARIES_IMG="quay.io/confidential-containers/podvm-binaries-ubuntu-amd64:${RELEASE_TAG}" - -FROM ${BINARIES_IMG} AS podvm_binaries -FROM ${BUILDER_IMG} AS podvm_builder - -ARG CLOUD_PROVIDER=aws -ARG PODVM_DISTRO=ubuntu -# If not provided, uses system architecture -ARG ARCH=x86_64 -ARG CAA_SRC="https://github.com/confidential-containers/cloud-api-adaptor" -ARG CAA_SRC_REF=${RELEASE_TAG} - -ENV CLOUD_PROVIDER=${CLOUD_PROVIDER} -ENV PODVM_DISTRO=${PODVM_DISTRO} - -RUN if [ -n "${CAA_SRC}" ]; then \ - rm -rf cloud-api-adaptor && \ - git clone ${CAA_SRC} cloud-api-adaptor;\ - fi && \ - if [ -n "${CAA_SRC_REF}" ]; then \ - cd cloud-api-adaptor && \ - git fetch origin ${CAA_SRC_REF} && \ - git checkout FETCH_HEAD -b ${CAA_SRC_REF} ;\ - fi - -# Copy the binaries to podvm/files folder -COPY --from=podvm_binaries /podvm-binaries.tar.gz /src/cloud-api-adaptor/podvm/files/ -RUN tar xvf /src/cloud-api-adaptor/podvm/files/podvm-binaries.tar.gz -C /src/cloud-api-adaptor/podvm/files - -# Copy the pause_bundle to podvm/files folder -COPY --from=podvm_binaries /pause-bundle.tar.gz /src/cloud-api-adaptor/podvm/files/ -RUN tar xvf /src/cloud-api-adaptor/podvm/files/pause-bundle.tar.gz -C /src/cloud-api-adaptor/podvm/files - -ARG AWS_REGION -ARG VPC_ID -ARG SUBNET_ID -ARG INSTANCE_TYPE=t3.small -ARG IMAGE_NAME=peer-pod-ami -ARG VOLUME_SIZE=30 - -ENV AWS_REGION=${AWS_REGION} -ENV VPC_ID=${VPC_ID} -ENV SUBNET_ID=${SUBNET_ID} -ENV INSTANCE_TYPE=${INSTANCE_TYPE} -ENV IMAGE_NAME=${IMAGE_NAME} -ENV VOLUME_SIZE=${VOLUME_SIZE} - -# Install AWS cli which will be used for default-vpc creation if required -RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" && \ - unzip awscliv2.zip && \ - ./aws/install - - -RUN --mount=type=secret,id=AWS_ACCESS_KEY_ID \ - --mount=type=secret,id=AWS_SECRET_ACCESS_KEY \ - --mount=type=secret,id=AWS_SESSION_TOKEN \ - export AWS_ACCESS_KEY_ID=$(cat /run/secrets/AWS_ACCESS_KEY_ID) && \ - export AWS_SECRET_ACCESS_KEY=$(cat /run/secrets/AWS_SECRET_ACCESS_KEY) && \ - export AWS_SESSION_TOKEN=$(cat /run/secrets/AWS_SESSION_TOKEN 2>/dev/null || echo "") && \ - cd cloud-api-adaptor/src/cloud-api-adaptor/aws/image && \ - BINARIES= PAUSE_BUNDLE= CLOUD_PROVIDER=aws PODVM_DISTRO=$PODVM_DISTRO make image diff --git a/src/cloud-api-adaptor/aws/image/Makefile b/src/cloud-api-adaptor/aws/image/Makefile deleted file mode 100644 index 97e40f8ea4..0000000000 --- a/src/cloud-api-adaptor/aws/image/Makefile +++ /dev/null @@ -1,53 +0,0 @@ -# -# SPDX-License-Identifier: Apache-2.0 -# -ROOT_DIR := $(dir $(lastword $(MAKEFILE_LIST)))../../ -include $(ROOT_DIR)podvm/Makefile.inc - - -.PHONY: image clean default-vpc - -INSTANCE_TYPE ?= c4.xlarge - -VOLUME_SIZE ?= 30 - -ENABLE_NVIDIA_GPU ?= yes -export ENABLE_NVIDIA_GPU - -image: $(IMAGE_FILE) - -$(IMAGE_FILE): setopts $(BINARIES) $(FILES) - mkdir -p toupload - packer init ./${PODVM_DISTRO}/ - packer build ${OPTS} \ - -var region=${AWS_REGION} \ - -var instance_type=${INSTANCE_TYPE} \ - -var volume_size=${VOLUME_SIZE} \ - -var config_script_src=$(ROOT_DIR)/podvm/qcow2 \ - -var addons_script_src=$(ROOT_DIR)/podvm/addons \ - -var ami_name=${IMAGE_NAME} ./${PODVM_DISTRO}/ - rm -fr toupload - -setopts: -ifeq ($(PODVM_DISTRO),) - $(error PODVM_DISTRO is not defined) -endif -ifeq ($(AWS_REGION),) - $(error AWS_REGION is not defined) -endif -ifeq ($(VPC_ID),) - aws ec2 create-default-vpc --region ${AWS_REGION} || true - aws ec2 wait vpc-available --region ${AWS_REGION} -else ifneq ($(SUBNET_ID),) - $(eval OPTS += -var vpc_id=${VPC_ID} -var subnet_id=${SUBNET_ID} ) -else - $(error VPC_ID or SUBNET_ID not defined) -endif - - -clean: - rm -f "$(IMAGE_FILE)" "$(UBUNTU_IMAGE_FILE)" $(BINARIES) - rm -fr "$(SKOPEO_SRC)" "$(UMOCI_SRC)" "$(PAUSE_SRC)" "$(FILES_DIR)/$(PAUSE_BUNDLE)" - -.PHONY: force -force: diff --git a/src/cloud-api-adaptor/aws/image/rhel/aws-rhel.pkr.hcl b/src/cloud-api-adaptor/aws/image/rhel/aws-rhel.pkr.hcl deleted file mode 100644 index 59b0621ebf..0000000000 --- a/src/cloud-api-adaptor/aws/image/rhel/aws-rhel.pkr.hcl +++ /dev/null @@ -1,138 +0,0 @@ -packer { - required_plugins { - amazon = { - version = "= 1.3.1" - source = "github.com/hashicorp/amazon" - } - } -} - -source "amazon-ebs" "rhel" { - ami_name = "${var.ami_name}" - instance_type = "${var.instance_type}" - region = "${var.region}" - vpc_id = "${var.vpc_id}" - subnet_id = "${var.subnet_id}" - source_ami_filter { - filters = { - name = "RHEL-9.4.0_HVM-*" - root-device-type = "ebs" - virtualization-type = "hvm" - architecture = "x86_64" - } - - most_recent = true - owners = ["309956199498"] - } - ami_block_device_mappings { - device_name = "/dev/sda1" - delete_on_termination = "true" - volume_size = "${var.volume_size}" - } - - ssh_username = "ec2-user" - shutdown_behavior = "terminate" -} - -build { - name = "peer-pod-rhel" - sources = [ - "source.amazon-ebs.rhel" - ] - - provisioner "shell-local" { - command = "tar cf toupload/files.tar -C ../../podvm files" - } - - provisioner "file" { - source = "./toupload" - destination = "/tmp/" - } - - provisioner "shell" { - inline = [ - "cd /tmp && tar xf toupload/files.tar", - "rm toupload/files.tar" - ] - } - - provisioner "file" { - source = "${var.config_script_src}/copy-files.sh" - destination = "~/copy-files.sh" - } - - provisioner "shell" { - remote_folder = "~" - inline = [ - "sudo bash ~/copy-files.sh" - ] - } - - # relabel copied files right after copy-files.sh - # to prevent other commands from failing - provisioner "file" { - source = "${var.config_script_src}/selinux_relabel.sh" - destination = "~/selinux_relabel.sh" - } - - provisioner "shell" { - remote_folder = "~" - inline = [ - "sudo bash ~/selinux_relabel.sh" - ] - } - - provisioner "file" { - source = "${var.config_script_src}/misc-settings.sh" - destination = "~/misc-settings.sh" - } - - provisioner "shell" { - remote_folder = "~" - environment_vars = [ - "CLOUD_PROVIDER=${var.cloud_provider}", - "PODVM_DISTRO=${var.podvm_distro}", - "FORWARDER_PORT=${var.forwarder_port}" - ] - inline = [ - "sudo -E bash ~/misc-settings.sh" - ] - } - - # Addons - # To avoid multiple conditionals, copying the entire addons directory - # Individual addons are installed based on environment_vars by setup_addons.sh - provisioner "shell-local" { - command = "tar cf toupload/addons.tar -C ../../podvm addons" - } - - provisioner "file" { - source = "toupload" - destination = "/tmp/" - } - - provisioner "shell" { - inline = [ - "cd /tmp && tar xf toupload/addons.tar", - "rm toupload/addons.tar" - ] - } - - provisioner "file" { - source = "${var.addons_script_src}/setup_addons.sh" - destination = "~/setup_addons.sh" - } - - provisioner "shell" { - remote_folder = "~" - environment_vars = [ - "CLOUD_PROVIDER=${var.cloud_provider}", - "PODVM_DISTRO=${var.podvm_distro}", - "ENABLE_NVIDIA_GPU=${var.enable_nvidia_gpu}" - ] - inline = [ - "sudo -E bash ~/setup_addons.sh" - ] - } - -} diff --git a/src/cloud-api-adaptor/aws/image/rhel/variables.pkr.hcl b/src/cloud-api-adaptor/aws/image/rhel/variables.pkr.hcl deleted file mode 100644 index 222b293880..0000000000 --- a/src/cloud-api-adaptor/aws/image/rhel/variables.pkr.hcl +++ /dev/null @@ -1,64 +0,0 @@ -// variables.pkr.hcl - -// For those variables that you don't provide a default for, you must -// set them from the command line, a var-file, or the environment. - -variable "ami_name" { - type = string - default = "peer-pod-ami" -} - -variable "instance_type" { - type = string - default = "t3.small" -} - -variable "region" { - type = string -} - -variable "vpc_id" { - type = string - default = null -} - -variable "subnet_id" { - type = string - default = null -} - -variable "podvm_distro" { - type = string - default = env("PODVM_DISTRO") -} - -variable "cloud_provider" { - type = string - default = env("CLOUD_PROVIDER") -} - -variable "volume_size" { - type = string - // Size in GiBs - default = 30 -} - -variable "config_script_src" { - type = string - default = "" -} - -variable "addons_script_src" { - type = string - default = "" -} - -variable "enable_nvidia_gpu" { - type = string - default = env("ENABLE_NVIDIA_GPU") -} - -variable "forwarder_port" { - type = string - default = env("FORWARDER_PORT") -} diff --git a/src/cloud-api-adaptor/aws/image/ubuntu/aws-ubuntu.pkr.hcl b/src/cloud-api-adaptor/aws/image/ubuntu/aws-ubuntu.pkr.hcl deleted file mode 100644 index 786728a615..0000000000 --- a/src/cloud-api-adaptor/aws/image/ubuntu/aws-ubuntu.pkr.hcl +++ /dev/null @@ -1,122 +0,0 @@ -packer { - required_plugins { - amazon = { - version = "= 1.3.1" - source = "github.com/hashicorp/amazon" - } - } -} - -source "amazon-ebs" "ubuntu" { - ami_name = "${var.ami_name}" - instance_type = "${var.instance_type}" - region = "${var.region}" - vpc_id = "${var.vpc_id}" - subnet_id = "${var.subnet_id}" - source_ami_filter { - filters = { - name = "ubuntu/images/*ubuntu*jammy*" - root-device-type = "ebs" - virtualization-type = "hvm" - architecture = "x86_64" - } - - most_recent = true - owners = ["self", "aws-marketplace", "amazon"] - } - ami_block_device_mappings { - device_name = "/dev/sda1" - delete_on_termination = "true" - volume_size = "${var.volume_size}" - } - ssh_username = "ubuntu" -} - - -build { - name = "peer-pod-ubuntu" - sources = [ - "source.amazon-ebs.ubuntu" - ] - - provisioner "shell-local" { - command = "tar cf toupload/files.tar -C ../../podvm files" - } - - provisioner "file" { - source = "./toupload" - destination = "/tmp/" - } - - provisioner "shell" { - inline = [ - "cd /tmp && tar xf toupload/files.tar", - "rm toupload/files.tar" - ] - } - - provisioner "file" { - source = "${var.config_script_src}/copy-files.sh" - destination = "~/copy-files.sh" - } - - provisioner "shell" { - remote_folder = "~" - inline = [ - "sudo bash ~/copy-files.sh" - ] - } - - provisioner "file" { - source = "${var.config_script_src}/misc-settings.sh" - destination = "~/misc-settings.sh" - } - - provisioner "shell" { - remote_folder = "~" - environment_vars = [ - "CLOUD_PROVIDER=${var.cloud_provider}", - "PODVM_DISTRO=${var.podvm_distro}", - "FORWARDER_PORT=${var.forwarder_port}" - ] - inline = [ - "sudo -E bash ~/misc-settings.sh" - ] - } - - # Addons - # To avoid multiple conditionals, copying the entire addons directory - # Individual addons are installed based on environment_vars by setup_addons.sh - provisioner "shell-local" { - command = "tar cf toupload/addons.tar -C ../../podvm addons" - } - - provisioner "file" { - source = "toupload" - destination = "/tmp/" - } - - provisioner "shell" { - inline = [ - "cd /tmp && tar xf toupload/addons.tar", - "rm toupload/addons.tar" - ] - } - - provisioner "file" { - source = "${var.addons_script_src}/setup_addons.sh" - destination = "~/setup_addons.sh" - } - - provisioner "shell" { - remote_folder = "~" - environment_vars = [ - "CLOUD_PROVIDER=${var.cloud_provider}", - "PODVM_DISTRO=${var.podvm_distro}", - "ENABLE_NVIDIA_GPU=${var.enable_nvidia_gpu}" - ] - inline = [ - "sudo -E bash ~/setup_addons.sh" - ] - } -} diff --git a/src/cloud-api-adaptor/aws/image/ubuntu/variables.pkr.hcl b/src/cloud-api-adaptor/aws/image/ubuntu/variables.pkr.hcl deleted file mode 100644 index 222b293880..0000000000 --- a/src/cloud-api-adaptor/aws/image/ubuntu/variables.pkr.hcl +++ /dev/null @@ -1,64 +0,0 @@ -// variables.pkr.hcl - -// For those variables that you don't provide a default for, you must -// set them from the command line, a var-file, or the environment. - -variable "ami_name" { - type = string - default = "peer-pod-ami" -} - -variable "instance_type" { - type = string - default = "t3.small" -} - -variable "region" { - type = string -} - -variable "vpc_id" { - type = string - default = null -} - -variable "subnet_id" { - type = string - default = null -} - -variable "podvm_distro" { - type = string - default = env("PODVM_DISTRO") -} - -variable "cloud_provider" { - type = string - default = env("CLOUD_PROVIDER") -} - -variable "volume_size" { - type = string - // Size in GiBs - default = 30 -} - -variable "config_script_src" { - type = string - default = "" -} - -variable "addons_script_src" { - type = string - default = "" -} - -variable "enable_nvidia_gpu" { - type = string - default = env("ENABLE_NVIDIA_GPU") -} - -variable "forwarder_port" { - type = string - default = env("FORWARDER_PORT") -} diff --git a/src/cloud-api-adaptor/docker/kind_cluster.sh b/src/cloud-api-adaptor/byom/e2e/kind_cluster.sh similarity index 91% rename from src/cloud-api-adaptor/docker/kind_cluster.sh rename to src/cloud-api-adaptor/byom/e2e/kind_cluster.sh index 50fda020f9..4199563fa5 100755 --- a/src/cloud-api-adaptor/docker/kind_cluster.sh +++ b/src/cloud-api-adaptor/byom/e2e/kind_cluster.sh @@ -29,11 +29,6 @@ if [ "$1" == "create" ]; then # Create a kind cluster echo "runtime: " "\$CONTAINER_RUNTIME" - if [ "$CONTAINER_RUNTIME" == "crio" ]; then - echo "Creating a kind cluster with crio runtime" - KIND_CONFIG_FILE="kind-config-crio.yaml" - fi - # Create a kind cluster echo "Creating a kind cluster" kind create cluster --name "\$CLUSTER_NAME" --config "\$KIND_CONFIG_FILE" || exit 1 diff --git a/src/cloud-api-adaptor/cmd/cloud-api-adaptor/docker.go b/src/cloud-api-adaptor/cmd/cloud-api-adaptor/docker.go deleted file mode 100644 index 9e48dfff45..0000000000 --- a/src/cloud-api-adaptor/cmd/cloud-api-adaptor/docker.go +++ /dev/null @@ -1,10 +0,0 @@ -//go:build docker - -// (C) Copyright Confidential Containers Contributors -// SPDX-License-Identifier: Apache-2.0 - -package main - -import ( - _ "github.com/confidential-containers/cloud-api-adaptor/src/cloud-providers/docker" -) diff --git a/src/cloud-api-adaptor/docker/.gitignore b/src/cloud-api-adaptor/docker/.gitignore deleted file mode 100644 index 4675001d9d..0000000000 --- a/src/cloud-api-adaptor/docker/.gitignore +++ /dev/null @@ -1 +0,0 @@ -image/resources/* diff --git a/src/cloud-api-adaptor/docker/README.md b/src/cloud-api-adaptor/docker/README.md deleted file mode 100644 index 485aeaaa04..0000000000 --- a/src/cloud-api-adaptor/docker/README.md +++ /dev/null @@ -1,266 +0,0 @@ -# Introduction - -The `docker` provider simulates a pod VM inside a docker container. - -> **Note**: Run the following commands from the root of this repository. - -## Prerequisites - -- Install `docker` [engine](https://docs.docker.com/engine/install/) on your K8s worker node - - Ensure docker engine supports API version 1.44+. You can verify it by running - `docker version`. - Docker engine version 26+ supports API 1.44. - - Ensure you complete the [post install steps](https://docs.docker.com/engine/install/linux-postinstall/) if using non-root user - -- Install [yq](https://github.com/mikefarah/yq/releases/download/v4.44.2/yq_linux_amd64), [kubectl](https://storage.googleapis.com/kubernetes-release/release/v1.29.4/bin/linux/amd64/kubectl), [kind](https://kind.sigs.k8s.io/dl/v0.23.0/kind-linux-amd64), [helm](https://helm.sh/docs/intro/install/) manually or using `prereqs.sh` helper script under `src/cloud-api-adaptor/docker`. - -- Kubernetes cluster -``` -# The default cluster name is peer-pods if CLUSTER_NAME variable not set -export CLUSTER_NAME={your_cluster_name} -``` -use below command to create a kind cluster before deploy CAA -``` -./kind_cluster.sh create -``` - -## Build CAA pod-VM image - -- Set environment variables - -```bash -export CLOUD_PROVIDER=docker -``` - -- Build the required pod VM binaries - -The same binaries built for the mkosi image is used for the podvm docker image - -```bash -cd src/cloud-api-adaptor/podvm-mkosi -make container -``` - -This will build the required binaries inside a container and place -it under `resources/binaries-tree` and also build the pod VM container image - -By default the image is named `quay.io/confidential-containers/podvm-docker-image`. - -For quick changes you can just build the binaries of podvm components and -update `./resources/binaries-tree/usr/local/bin` with the new components and -run `make image-container` to build a new podvm container image. - -You can download a ready-to-use image on your worker node. - -```bash -docker pull quay.io/confidential-containers/podvm-docker-image -``` - -Note that before you can spin up a pod, the podvm image must be available on the K8s worker node -with the docker engine installed. - -## Build CAA container image - -> **Note**: If you have made changes to the CAA code and you want to deploy those changes then follow [these instructions](https://github.com/confidential-containers/cloud-api-adaptor/blob/main/src/cloud-api-adaptor/install/README.md#building-custom-cloud-api-adaptor-image) to build the container image from the root of this repository. - -## Deploy CAA - -The following [`docker.yaml`](../install/charts/peerpods/providers/docker.yaml) -is used, and you can edit it to customize the installation. - -### Deploy CAA on the Kubernetes cluster - -Run the following command to deploy CAA: - -```bash -CLOUD_PROVIDER=docker make deploy -``` - -Generic CAA deployment instructions are also described [here](../install/README.md). - -For changing the CAA image to your custom built image (eg. `quay.io/myuser/cloud-api-adaptor`), -you can use the following: - -```bash -export CAA_IMAGE=quay.io/myuser/cloud-api-adaptor -kubectl set image ds/cloud-api-adaptor-daemonset -n confidential-containers-system cloud-api-adaptor-con="$CAA_IMAGE" -``` - -## Running the CAA e2e tests - -### Test Prerequisites - -To run the tests, use a test system with at least 8GB RAM and 4vCPUs. -Ubuntu 22.04 has been tested. Other Linux distros should work, but it has not -been tested. - -Following software prerequisites are needed on the test system: - -- helm -- make -- go -- yq -- kubectl -- kind -- docker - -A `prereqs.sh` helper script is available under `src/cloud-api-adaptor/docker` to install/uninstall the prerequisites. - - -> **Note:** If using the `prereqs.sh` helper script to install the -> prerequisites, then reload the shell to ensure new permissions -are in place to run `docker` and other commands. - -### Test Execution - -In order to run the tests, edit the file `src/cloud-api-adaptor/test/provisioner/docker/provision_docker.properties` -and update the `CAA_IMAGE` and `CAA_IMAGE_TAG` variables with your custom CAA image and tag. - -You can run the CAA e2e [tests/e2e/README.md](../test/e2e/README.md) by running the following command: - -```sh -make TEST_PODVM_IMAGE= TEST_PROVISION=yes CLOUD_PROVIDER=docker TEST_PROVISION_FILE=$(pwd)/test/provisioner/docker/provision_docker.properties test-e2e -``` - -This will create a two node kind cluster, automatically download the pod VM -image mentioned in the `provision_docker.properties` file and run the tests. On -completion of the test, the kind cluster will be automatically deleted. - -If you want to run the tests on a crio based kind cluster, then update `CONTAINER_RUNTIME` to `crio` -in the `provision_docker.properties` file. - -> **Note:** To overcome docker rate limiting issue or to download images from private registries, -create a `config.json` file under `/tmp` with your registry secrets. - -For example: -If your docker registry user is `someuser` and password is `somepassword` then create the auth string -as shown below: - -```sh -echo -n "someuser:somepassword" | base64 -c29tZXVzZXI6c29tZXBhc3N3b3Jk -``` - -This auth string needs to be used in `/tmp/config.json` as shown below: - -```sh -{ - "auths": { - "https://index.docker.io/v1/": { - "auth": "c29tZXVzZXI6c29tZXBhc3N3b3Jk" - } - } -} -``` - -If you want to use a different location for the registry secret, then remember to update the same -in the `src/cloud-api-adaptor/docker/kind-config.yaml` file if using `containerd` or -in the `src/cloud-api-adaptor/docker/kind-config-crio.yaml` file if using `crio`. - -> **Note:** If you have executed the tests with `TEST_TEARDOWN=no`, then you'll -> need to manually delete the `kind` created cluster by running the following -> command: - -```sh -kind delete cluster --name peer-pods -``` - - -## Run sample application - -### Ensure runtimeclass is present - -Verify that the `runtimeclass` is created after deploying CAA: - -```bash -kubectl get runtimeclass -``` - -Once you find a `runtimeclass` named `kata-remote` then you can be sure that the deployment was successful. A successful deployment will look like this: - -```console -$ kubectl get runtimeclass -NAME HANDLER AGE -kata-remote kata-remote 7m18s -``` - -### Deploy workload - -Create an `nginx` deployment: - -```yaml -echo ' -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: nginx - name: nginx -spec: - replicas: 1 - selector: - matchLabels: - app: nginx - template: - metadata: - labels: - app: nginx - annotations: - io.containerd.cri.runtime-handler: kata-remote - spec: - runtimeClassName: kata-remote - containers: - - image: nginx@sha256:9700d098d545f9d2ee0660dfb155fe64f4447720a0a763a93f2cf08997227279 - name: nginx -' | kubectl apply -f - -``` - -Ensure that the pod is up and running: - -```bash -kubectl get pods -n default -``` - -You could run `docker ps` command to view the docker container running. -The docker container name will be the pod name prefixed with `podvm`. - -Example: - -``` -$ kubectl get pods -NAME READY STATUS RESTARTS AGE -nginx-dbc79c87-jt49h 1/1 Running 1 (3m22s ago) 3m29s - -$ docker ps -CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES -e60b768b847d quay.io/confidential-containers/podvm-docker-image "/usr/local/bin/entr…" 3 minutes ago Up 3 minutes 15150/tcp podvm-nginx-dbc79c87-jt49h-b9361aef -``` - -For debugging you can use docker commands like `docker ps`, `docker logs`, `docker exec`. - -### Delete workload - -```sh -kubectl delete deployment nginx -``` - -## Troubleshooting - -When using `containerd` and `nydus-snapshotter` you might encounter pod creation failure due to -issues with unpacking of image. Check the `nydus-snapshotter` troubleshooting [doc](../docs/troubleshooting/nydus-snapshotter.md). - -In order to login to the worker node you can use either of the following approaches - -```sh -kubectl debug node/peer-pods-worker -it --image=busybox - -# chroot /host -``` - -or - -```sh -docker exec -it peer-pods-worker bash -``` diff --git a/src/cloud-api-adaptor/docker/kind-config-crio.yaml b/src/cloud-api-adaptor/docker/kind-config-crio.yaml deleted file mode 100644 index df071ce224..0000000000 --- a/src/cloud-api-adaptor/docker/kind-config-crio.yaml +++ /dev/null @@ -1,41 +0,0 @@ -kind: Cluster -apiVersion: kind.x-k8s.io/v1alpha4 -networking: - disableDefaultCNI: true # disable kindnet - podSubnet: 192.168.0.0/16 # set to Calico's default subnet -nodes: - - role: control-plane - # Instructions to build - https://github.com/cri-o/cri-o/blob/main/tutorials/crio-in-kind.md#build-node-image - image: quay.io/confidential-containers/kind-crio:v1.29.4 - extraMounts: - # The config.json file contains the registry secrets that you might - # need to pull images from a private registry or docker registry to avoid - # rate limiting. - - hostPath: /tmp/config.json - containerPath: /var/lib/kubelet/config.json - kubeadmConfigPatches: - - | - kind: InitConfiguration - nodeRegistration: - criSocket: unix:///var/run/crio/crio.sock - - | - kind: JoinConfiguration - nodeRegistration: - criSocket: unix:///var/run/crio/crio.sock - - role: worker - image: quay.io/confidential-containers/kind-crio:v1.29.4 - extraMounts: - - hostPath: /var/run/docker.sock - containerPath: /var/run/docker.sock - - hostPath: /var/lib/docker - containerPath: /var/lib/docker - # The config.json file contains the registry secrets that you might - # need to pull images from a private registry or docker registry to avoid - # rate limiting. - - hostPath: /tmp/config.json - containerPath: /var/lib/kubelet/config.json - kubeadmConfigPatches: - - | - kind: JoinConfiguration - nodeRegistration: - criSocket: unix:///var/run/crio/crio.sock diff --git a/src/cloud-api-adaptor/docker/kind-config.yaml b/src/cloud-api-adaptor/docker/kind-config.yaml deleted file mode 100644 index 3b12ea5118..0000000000 --- a/src/cloud-api-adaptor/docker/kind-config.yaml +++ /dev/null @@ -1,27 +0,0 @@ -kind: Cluster -apiVersion: kind.x-k8s.io/v1alpha4 -networking: - disableDefaultCNI: true # disable kindnet - podSubnet: 192.168.0.0/16 # set to Calico's default subnet -nodes: - - role: control-plane - # Same image version as used for pod VM base image - image: kindest/node:v1.29.4 - extraMounts: - # The config.json file contains the registry secrets that you might - # need to pull images from a private registry or docker registry to avoid - # rate limiting. - - hostPath: /tmp/config.json - containerPath: /var/lib/kubelet/config.json - - role: worker - image: kindest/node:v1.29.4 - extraMounts: - - hostPath: /var/run/docker.sock - containerPath: /var/run/docker.sock - - hostPath: /var/lib/docker - containerPath: /var/lib/docker - # The config.json file contains the registry secrets that you might - # need to pull images from a private registry or docker registry to avoid - # rate limiting. - - hostPath: /tmp/config.json - containerPath: /var/lib/kubelet/config.json diff --git a/src/cloud-api-adaptor/docker/prereqs.sh b/src/cloud-api-adaptor/docker/prereqs.sh deleted file mode 100755 index 6fc15831e4..0000000000 --- a/src/cloud-api-adaptor/docker/prereqs.sh +++ /dev/null @@ -1,369 +0,0 @@ -#!/bin/bash - -# Two options: install - to check and install some prerequisites -# uninstall - to uninstall the prerequisites - -# GLOBAL VARIABLES -# Install directory for binary packages -install_dir="/usr/local/bin" -# File to record installed OS packages -os_packages_file=".installed_os_packages" -# File to record installed binary packages -bin_packages_file=".installed_bin_packages" -# File to record docker installation -docker_file=".installed_docker" -# File to record go installation -go_file=".installed_go" - -# Get the directory where the script is located -SCRIPT_DIR=$(dirname "$(realpath "$0")") - -# Determine the path to versions.yaml relative to the script's directory -VERSIONS_YAML_PATH=$(realpath "${SCRIPT_DIR}/../versions.yaml") - -# function to trap errors and exit -function error_exit() { - echo "$1" 1>&2 - exit 1 -} - -# function to check if passwordless sudo is enabled -check_sudo() { - if sudo -n true 2>/dev/null; then - echo "Passwordless sudo is enabled." - else - error_exit "Passwordless sudo is not enabled. Please enable passwordless sudo." - fi -} - -# function to compare versions -# returns true if $1 < $2 -version_lt() { - [ "$1" != "$2" ] && [ "$(printf '%s\n' "$@" | sort -V | head -n 1)" == "$1" ] -} - -# function to install OS packages -# the packages are available in the variable REQUIRED_OS_PACKAGES -# the function will install the packages using the package manager -# Following are the packages that are installed: -# make -install_os_packages() { - # Define the required OS packages - REQUIRED_OS_PACKAGES=( - "make" - ) - - # Install the required OS packages - for package in "${REQUIRED_OS_PACKAGES[@]}"; do - if [[ -x "$(command -v "${package}")" ]]; then - echo "Package ${package} is already installed. Skipping." - continue - else - echo "Installing ${package}..." - # Update a hidden file to record what was installed. This is useful for uninstallation - echo "${package}" >>"${os_packages_file}" - if [[ -x "$(command -v apt-get)" ]]; then - # shellcheck disable=SC2015 - sudo apt-get update && - sudo apt-get install -y "${package}" || - error_exit "Failed to install ${package}" - elif [[ -x "$(command -v dnf)" ]]; then - sudo dnf install -y "${package}" || - error_exit "Failed to install ${package}" - else - error_exit "Unsupported OS" - fi - fi - done - - echo "All OS packages installed successfully." - -} - -# function to download and install binary packages. -# the packages, their respective download locations and compression -# are available in the variable REQUIRED_BINARY_PACKAGES -# the function will download the packages, extract them and install them in /usr/local/bin -# Following are the packages that are installed: -# yq=https://github.com/mikefarah/yq/releases/download/v4.44.2/yq_linux_amd64 -# kubectl=https://storage.googleapis.com/kubernetes-release/release/v1.29.4/bin/linux/amd64/kubectl -# kind=https://kind.sigs.k8s.io/dl/v0.23.0/kind-linux-amd64 - -install_binary_packages() { - # Define the required binary packages - REQUIRED_BINARY_PACKAGES=( - "yq=https://github.com/mikefarah/yq/releases/download/v4.44.2/yq_linux_amd64" - "kubectl=https://storage.googleapis.com/kubernetes-release/release/v1.29.4/bin/linux/amd64/kubectl" - "kind=https://kind.sigs.k8s.io/dl/v0.23.0/kind-linux-amd64" - "helm=https://get.helm.sh/helm-v4.0.4-linux-amd64.tar.gz" - ) - - # Specify the installation directory - local install_dir="/usr/local/bin" - - # Install the required binary packages - for package_info in "${REQUIRED_BINARY_PACKAGES[@]}"; do - IFS='=' read -r package_name package_url <<<"${package_info}" - download_path="/tmp/${package_name}" - - if [[ -x "${install_dir}/${package_name}" ]]; then - echo "Package ${package_name} is already installed. Skipping." - continue - else - echo "Downloading ${package_name}..." - # Update a hidden file to record what was installed. This is useful for uninstallation - echo "${package_name}" >>"${bin_packages_file}" - curl -sSL "${package_url}" -o "${download_path}" || - error_exit "Failed to download ${package_name}" - - echo "Extracting ${package_name}..." - if [[ "${package_url}" == *.tar.gz ]]; then - sudo tar -xf "${download_path}" -C "${install_dir}" || - error_exit "Failed to extract ${package_name}" - if [ "${package_name}" = "helm" ]; then - sudo mv "${install_dir}/linux-amd64/helm" "${install_dir}" - sudo rm -rf "${install_dir}/linux-amd64" - fi - # If not a tar.gz file, then it is a binary file - else - echo "${package_name} is binary file." - sudo mv "${download_path}" "${install_dir}/${package_name}" || - error_exit "Failed to move ${package_name} to ${install_dir}" - fi - - echo "Marking ${install_dir}/${package_name} executable" - sudo chmod +x "${install_dir}/${package_name}" || - error_exit "Failed to mark ${package_name} executable" - - echo "Cleaning up..." - rm -f "${download_path}" - fi - done - - echo "All binary packages installed successfully." - -} - -# function to install golang -install_golang() { - echo "Path to versions.yaml: $VERSIONS_YAML_PATH" - - # When installing, use the required min go version from the versions.yaml file - REQUIRED_GO_VERSION="$(yq '.tools.golang' "$VERSIONS_YAML_PATH")" - # Check if Go is already installed - if [[ -x "$(command -v go)" ]]; then - echo "Go is already installed" - # Check if the installed version is less than the required version - installed_go_version=$(v=$(go version | awk '{print $3}') && echo "${v#go}") - - if version_lt "$installed_go_version" "$REQUIRED_GO_VERSION"; then - echo "Warning: Found ${installed_go_version}, is lower than our required $REQUIRED_GO_VERSION" - echo "Please remove the existing go version and run this script again." - exit 1 - else - echo "Found ${installed_go_version}, good to go" - fi - else - # Install Go - echo "Installing Go" - curl -fsSL https://go.dev/dl/go${REQUIRED_GO_VERSION}.linux-amd64.tar.gz -o go.tar.gz || error_exit "Failed to download Go" - sudo tar -C /usr/local -xzf go.tar.gz || error_exit "Failed to extract Go" - touch "$go_file" - rm -f go.tar.gz - fi - -} - -# function to uninstall golang -uninstall_golang() { - # Check if Go is installed - if [[ ! -x "$(command -v go)" ]]; then - echo "Go is not installed" - return - fi - - # Uninstall Go - echo "Uninstalling Go" - - # Uninstall only if go_file exists - if [[ ! -f "$go_file" ]]; then - echo "Go was not installed using this script. Skipping uninstallation." - return - fi - - sudo rm -rf /usr/local/go - - # Remove the file that records the installed packages - rm -f "$go_file" -} - -# function to install docker -install_docker() { - # Check if Docker is already installed - if [[ -x "$(command -v docker)" ]]; then - echo "Docker is already installed" - else - # Install Docker - echo "Installing Docker" - curl -fsSL https://get.docker.com -o get-docker.sh || error_exit "Failed to download Docker installation script" - sudo sh get-docker.sh || error_exit "Failed to install Docker" - touch "$docker_file" - rm -f get-docker.sh - sudo groupadd docker - sudo usermod -aG docker "$USER" - fi -} - -#function to uninstall the binary packages -uninstall_binary_packages() { - # Remove the installed binary packages - # The packages are available under bin_packages_file file - # The function will remove the packages from install_dir - - # Check if the file exists - if [[ ! -f "${bin_packages_file}" ]]; then - echo "No binary packages to uninstall." - return - fi - - # Read the file and uninstall the packages - while IFS= read -r package_name; do - if [[ -x "${install_dir}/${package_name}" ]]; then - echo "Uninstalling ${package_name}..." - sudo rm -f "${install_dir}/${package_name}" || - error_exit "Failed to uninstall ${package_name}" - - else - echo "Package ${package_name} is not installed. Skipping." - fi - done <"${bin_packages_file}" - - # Remove the file that records the installed packages - rm -f "${bin_packages_file}" - - echo "All binary packages uninstalled successfully." -} - -# function to uninstall the OS packages -uninstall_os_packages() { - # Remove the installed Os packages - # The packages are available under os_packages_file file - # The function will remove the packages using the package manager - - # Check if the file exists - if [[ ! -f "${os_packages_file}" ]]; then - echo "No OS packages to uninstall." - return - fi - - # Read the file and uninstall the packages - while IFS= read -r package_name; do - if [[ -x "$(command -v "${package_name}")" ]]; then - echo "Uninstalling ${package_name}..." - if [[ -x "$(command -v apt-get)" ]]; then - sudo apt-get purge -y "${package_name}" || - error_exit "Failed to uninstall ${package_name}" - elif [[ -x "$(command -v dnf)" ]]; then - sudo dnf remove -y "${package_name}" || - error_exit "Failed to uninstall ${package_name}" - else - error_exit "Unsupported OS" - fi - else - echo "Package ${package_name} is not installed. Skipping." - fi - done <"${os_packages_file}" - - # Remove the file that records the installed packages - rm -f "${os_packages_file}" - - echo "All OS packages uninstalled successfully." -} - -# function to uninstall docker -uninstall_docker() { - # Check if Docker is installed - if [[ ! -x "$(command -v docker)" ]]; then - echo "Docker is not installed" - return - fi - - # Uninstall Docker - echo "Uninstalling Docker" - - # Uninstall only if docker_file exists - if [[ ! -f "$docker_file" ]]; then - echo "Docker was not installed using this script. Skipping uninstallation." - return - fi - # Check if OS is Ubuntu - if [[ -x "$(command -v apt-get)" ]]; then - sudo apt-get purge -y docker-ce docker-ce-cli containerd.io \ - docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras || error_exit "Failed to uninstall Docker" - sudo rm -rf /var/lib/docker - sudo rm -rf /var/lib/containerd - elif [[ -x "$(command -v dnf)" ]]; then - sudo dnf remove -y docker-ce docker-ce-cli containerd.io \ - docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras || error_exit "Failed to uninstall Docker" - sudo rm -rf /var/lib/docker - sudo rm -rf /var/lib/containerd - fi - - # Remove the file that records the installed packages - rm -f "$docker_file" -} - -#function to set PATH to include /usr/local/bin and /usr/local/go/bin -set_path() { - - # If bin_packages_file exists, then add /usr/local/bin to PATH - if [[ -f "${bin_packages_file}" ]]; then - # Add /usr/local/bin to PATH - if [[ ":$PATH:" == *":/usr/local/bin:"* ]]; then - echo "/usr/local/bin is already in PATH. Skipping." - else - echo "Adding /usr/local/bin to PATH..." - # shellcheck disable=SC2016 - echo 'export PATH=$PATH:/usr/local/bin' >>"$HOME"/.bashrc - fi - fi - - # If go_file exists, then add /usr/local/go/bin to PATH - if [[ -f "${go_file}" ]]; then - # Add /usr/local/go/bin to PATH - if [[ ":$PATH:" == *":/usr/local/go/bin:"* ]]; then - echo "/usr/local/go/bin is already in PATH. Skipping." - else - echo "Adding /usr/local/go/bin to PATH..." - # shellcheck disable=SC2016 - echo 'export PATH=$PATH:/usr/local/go/bin' >>"$HOME"/.bashrc - fi - fi - - # Reload the bashrc file - # shellcheck source=/dev/null - source "$HOME"/.bashrc - -} - -if [[ "$(uname)" != "Linux" || "$(uname -m)" != "x86_64" ]]; then - error_exit "This script is only for Linux x86_64 OS." -fi - -# Main function -if [[ "$1" == "install" ]]; then - check_sudo - install_os_packages - install_binary_packages - install_golang - install_docker - set_path -elif [[ "$1" == "uninstall" ]]; then - check_sudo - uninstall_binary_packages - uninstall_os_packages - uninstall_golang - uninstall_docker -else - error_exit "Invalid argument. Please provide either 'install' or 'uninstall'." -fi diff --git a/src/cloud-api-adaptor/docs/addnewprovider.md b/src/cloud-api-adaptor/docs/addnewprovider.md index 6e367b8267..6200a1c113 100644 --- a/src/cloud-api-adaptor/docs/addnewprovider.md +++ b/src/cloud-api-adaptor/docs/addnewprovider.md @@ -219,7 +219,7 @@ go mod tidy ```bash cat > Dockerfile < **EKS-Specific Implementation** +> +> This guide is designed for **Amazon EKS** clusters, which automatically inject IRSA environment variables (`AWS_WEB_IDENTITY_TOKEN_FILE`, `AWS_ROLE_ARN`) and mount service account tokens when you annotate a ServiceAccount. +> +> **For self-managed Kubernetes or other platforms:** +> - You must manually configure projected service account token volumes +> - Set `AWS_WEB_IDENTITY_TOKEN_FILE` and `AWS_ROLE_ARN` environment variables in the ConfigMap/Secret +> - Mount the OIDC token at the path specified in `AWS_WEB_IDENTITY_TOKEN_FILE` +> - See the [Alibaba Cloud RRSA implementation](https://github.com/confidential-containers/cloud-api-adaptor/blob/main/src/cloud-api-adaptor/install/charts/peerpods/templates/daemonset.yaml#L69-L128) in the Helm charts as a reference for manual OIDC token projection + +## Benefits + +- **No static credentials** - No long-lived AWS access keys in Kubernetes secrets +- **Automatic credential rotation** - Temporary credentials that auto-rotate +- **Fine-grained permissions** - Each service account can have different IAM roles +- **AWS best practice** - Follows AWS security recommendations for EKS workloads + +## Prerequisites + +- EKS cluster version 1.13 or later +- `kubectl` configured to access your cluster +- `aws` CLI version 2.0 or later +- `eksctl` +- Appropriate AWS IAM permissions to create roles and OIDC providers + +## Step 1: Enable OIDC Provider for Your EKS Cluster + +### Check if OIDC Issuer Exists + +```bash +# Set your cluster name +export CLUSTER_NAME="my-eks-cluster" +export AWS_REGION="us-east-2" + +# Get the OIDC issuer URL +aws eks describe-cluster \ + --name ${CLUSTER_NAME} \ + --region ${AWS_REGION} \ + --query "cluster.identity.oidc.issuer" \ + --output text +``` + +This should output something like: +``` +https://oidc.eks.us-east-2.amazonaws.com/id/31D259E489F3F8CDF111109DB7105368 +``` + +### Check if IAM OIDC Provider is Registered + +```bash +# Extract the OIDC ID +OIDC_ID=$(aws eks describe-cluster \ + --name ${CLUSTER_NAME} \ + --region ${AWS_REGION} \ + --query "cluster.identity.oidc.issuer" \ + --output text | awk -F'/' '{print $NF}') + +# Check if provider exists in IAM +aws iam list-open-id-connect-providers | grep ${OIDC_ID} +``` + +**If the command returns empty**, the OIDC provider is not registered in IAM and must be created. + +#### Create OIDC Provider (if needed) + +```bash +eksctl utils associate-iam-oidc-provider \ + --cluster ${CLUSTER_NAME} \ + --region ${AWS_REGION} \ + --approve + +# Verify the OIDC provider was created successfully +aws iam list-open-id-connect-providers | grep ${OIDC_ID} +``` + +### Get Account ID and OIDC Provider + +These values will be used for both cloud-api-adaptor and peerpod-ctrl: + +```bash +export ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) + +export OIDC_PROVIDER=$(aws eks describe-cluster \ + --name ${CLUSTER_NAME} \ + --region ${AWS_REGION} \ + --query "cluster.identity.oidc.issuer" \ + --output text | sed 's|https://||') + +echo "Account ID: ${ACCOUNT_ID}" +echo "OIDC Provider: ${OIDC_PROVIDER}" +``` + +## Step 2: Create IAM Role and Attach Permissions for cloud-api-adaptor + +### Define Variables + +```bash +export NAMESPACE="confidential-containers-system" +export CAA_SERVICE_ACCOUNT="cloud-api-adaptor" +export CAA_ROLE_NAME="CAA-IRSA-Role" +export CAA_POLICY_NAME="CAA-EC2-Policy" +``` + +### Create Trust Policy for cloud-api-adaptor + +```bash +cat > /tmp/caa-trust-policy.json < /tmp/caa-ec2-policy.json < /tmp/peerpod-ctrl-trust-policy.json < /tmp/peerpod-ctrl-ec2-policy.json <Note: The published images doesn't have any TEE support. For specific TEE support, you'll need to build your own pod VM image or used the published Azure, AWS and GCP images. Refer to the coco [website](https://confidentialcontainers.org/docs/examples/) for more details. -You can find the packer images available at https://quay.io/organization/confidential-containers with the *podvm-generic-ubuntu-[ARCH]* name pattern. -You can find the mkosi images available at https://quay.io/organization/confidential-containers with the *podvm-generic-fedora-[ARCH]* name pattern. +You can find the mkosi images available at with the *podvm-generic-ubuntu-[ARCH]* name pattern. For example: -- https://quay.io/repository/confidential-containers/podvm-generic-ubuntu-amd64 hosts the Ubuntu images that can be used with all providers. -- https://quay.io/repository/confidential-containers/podvm-generic-ubuntu-s390x hosts the Ubuntu images that can be used for s390x architecture. -- https://quay.io/repository/confidential-containers/podvm-generic-fedora-amd64 hosts the Fedora images that can be used with all providers. -- https://quay.io/repository/confidential-containers/podvm-generic-fedora-s390x hosts the Ubuntu images that can be used for s390x architecture. - -## Downloading the packer based images - -The easiest way to extract the packer generated qcow2 file from the podvm container image is using the [`download-image.sh`](../podvm/hack/download-image.sh) script. For example, to extract the file from the *podvm-generic-ubuntu-amd64* image: - -```sh -$ export CAA_VERSION=v0.13.0 -$ ./src/cloud-api-adaptor/podvm/hack/download-image.sh quay.io/confidential-containers/podvm-generic-ubuntu-amd64:$CAA_VERSION . -o podvm.qcow2 -``` - ->Note: images can be checked from https://quay.io/repository/confidential-containers/podvm-generic-ubuntu-amd64?tab=tags, to get the available tags. The `latest` tag is not available. +- hosts the Ubuntu images that can be used with all providers. +- hosts the Ubuntu images that can be used for s390x architecture. ## Downloading the mkosi based images The mkosi based images are OCI artifacts, so you'll need to use [oras](https://oras.land/docs/installation/) to download the image ```sh -$ export CAA_VERSION=v0.13.0 -$ oras pull quay.io/confidential-containers/podvm-generic-fedora-amd64:$CAA_VERSION -$ tar xvJpf podvm.tar.xz +export CAA_VERSION=v0.13.0 +oras pull quay.io/confidential-containers/podvm-generic-fedora-amd64:$CAA_VERSION +tar xvJpf podvm.tar.xz ``` diff --git a/src/cloud-api-adaptor/entrypoint.sh b/src/cloud-api-adaptor/entrypoint.sh index 847a58a01c..a42254a2c2 100755 --- a/src/cloud-api-adaptor/entrypoint.sh +++ b/src/cloud-api-adaptor/entrypoint.sh @@ -42,7 +42,16 @@ one_of() { } aws() { - test_vars AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY + # Check that at least one authentication method is configured + one_of AWS_SECRET_ACCESS_KEY AWS_ROLE_ARN + + # If using web identity, require role ARN and token file + if [ -n "${AWS_ROLE_ARN}" ]; then + test_vars AWS_WEB_IDENTITY_TOKEN_FILE AWS_ROLE_ARN + else + # If using access keys, require both key and secret + test_vars AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY + fi set -x exec cloud-api-adaptor aws ${optionals} @@ -101,12 +110,6 @@ libvirt() { } -docker() { - set -x - exec cloud-api-adaptor docker ${optionals} - -} - byom() { test_vars VM_POOL_IPS @@ -118,9 +121,9 @@ byom() { help_msg() { cat <" echo " -r " echo " [-g] (optional. fetch attestation using github api)" + echo " [-s] (optional. deny attestations produced on self-hosted runners)" exit 1 } @@ -30,9 +31,10 @@ oci_artifact="" expected_digest="" repository="" github="0" +assert_runner="0" # Parse options using getopts -while getopts ":a:d:r:g" opt; do +while getopts ":a:d:r:gs" opt; do case "${opt}" in a) oci_artifact="${OPTARG}" @@ -46,6 +48,9 @@ while getopts ":a:d:r:g" opt; do g) github="1" ;; + s) + assert_runner="1" + ;; *) usage ;; @@ -104,6 +109,7 @@ claims=$( workflowDigest: .githubWorkflowSHA, workflowTrigger: .githubWorkflowTrigger, workflowRef: .githubWorkflowRef, + runner: .runnerEnvironment, }' ) @@ -111,6 +117,7 @@ digest=$(echo "$claims" | jq -r '.digest') workflow_digest=$(echo "$claims" | jq -r '.workflowDigest') workflow_trigger=$(echo "$claims" | jq -r '.workflowTrigger') workflow_ref=$(echo "$claims" | jq -r '.workflowRef') +runner=$(echo "$claims" | jq -r '.runner') verification_failed="" @@ -134,6 +141,11 @@ if [ "$workflow_ref" != "refs/heads/main" ]; then verification_failed="1" fi +if [ "$assert_runner" == "1" ] && [ "$runner" != "github-hosted" ]; then + echo "Runner mismatch: expected github-hosted, got $runner" + verification_failed="1" +fi + if [ "$verification_failed" != "" ]; then echo "Verification failed" exit 1 diff --git a/src/cloud-api-adaptor/ibmcloud/IMPORT_PODVM_TO_VPC.md b/src/cloud-api-adaptor/ibmcloud/IMPORT_PODVM_TO_VPC.md index 1a46313e7b..6351b72f04 100644 --- a/src/cloud-api-adaptor/ibmcloud/IMPORT_PODVM_TO_VPC.md +++ b/src/cloud-api-adaptor/ibmcloud/IMPORT_PODVM_TO_VPC.md @@ -1,39 +1,34 @@ # Importing Public PODVM images into IBM Cloud VPC -As part of the release process pre-built images are published as container images to the confidential-containers quay repository. e.g. `quay.io/confidential-containers/podvm-ibmcloud-ubuntu-amd64` that contain a single qcow2 file that can be extracted. Alternatively images can be built and distributed directly as qcow2 files. These qcow2 that needs to be uploaded to ibmcloud to use as a vpc image. +As part of the release process pre-built images are published as OCI artifacts using oras to the confidential-containers quay repository. e.g. `quay.io/confidential-containers/podvm-generic-ubuntu-amd64`. Alternatively images can be built and distributed directly as qcow2 files. These qcow2 files need to be uploaded to IBM Cloud to use as a VPC image. -To simpify this process a script has been created to aid this. `ibmcloud/image/import.sh`. +To simplify this process a script has been created to aid this: `ibmcloud/image/import.sh`. ## Prerequisites ### Tools - jq `apt install jq` -- ibmcloud `curl -fsSL https://clis.cloud.ibm.com/install/linux | sh` (https://cloud.ibm.com/docs/cli?topic=cli-getting-started) -- docker/podman `apt install docker.io` -It's failed to install `docker` on Ubuntu20.04 with above single command. `docker` can be installed with `snap` as below. - ``` - sudo snap install docker - sudo systemctl start snap.docker.dockerd - sudo systemctl enable snap.docker.dockerd - ``` +- ibmcloud `curl -fsSL https://clis.cloud.ibm.com/install/linux | sh` () +- oras `snap install oras --classic` () ### Cloud-Object-Storage -To create the VPC image you need to first import the file to ibmcloud COS. The script will do this step but a bucket must already be available. +To create the VPC image you need to first import the file to IBM Cloud Object Storage (COS). The script will do this step but a bucket must already be available. You may follow the [offical documentation](https://cloud.ibm.com/docs/cloud-object-storage?topic=cloud-object-storage-getting-started-cloud-object-storage) to create a bucket, a free tier is sufficient. Bucket region can be different from the VPC region. ## Running + ``` -Usage: ./import.sh [--bucket --region --instance --endpoint --api --os ] +Usage: ./import.sh [--bucket --region --instance --endpoint --api --os ] ``` ### Arguments The first two arguments are positional: -1. The name of the container image to extract the qcow2 file, or the file itself +1. The name of the OCI artifact to extract the qcow2 file, or the file itself 1. The VPC region to create the image in (the process can be repeated for multiple regions) The later options: @@ -50,9 +45,9 @@ The script will sanitise `.` and `_` into `-` and lowercase the image name. Only ### Examples -- Extracting and uploading a qcow2 image from a container image: +- Extracting and uploading a qcow2 image from an OCI artifact: -`./import.sh quay.io/confidential-containers/podvm-ibmcloud-ubuntu-amd64 ca-tor --instance jt-cos-instance --bucket podvm-image-cos-bucket-jt --region jp-tok` +`./import.sh quay.io/confidential-containers/podvm-generic-ubuntu-amd64 ca-tor --instance jt-cos-instance --bucket podvm-image-cos-bucket-jt --region jp-tok` - Uploading a qcow2 file directly: diff --git a/src/cloud-api-adaptor/ibmcloud/ROKS_SETUP.md b/src/cloud-api-adaptor/ibmcloud/ROKS_SETUP.md index ebfec918cf..228e8e0e5d 100644 --- a/src/cloud-api-adaptor/ibmcloud/ROKS_SETUP.md +++ b/src/cloud-api-adaptor/ibmcloud/ROKS_SETUP.md @@ -5,7 +5,7 @@ This guide describes how to set up a simple peer pod demo environment with a Red 1. [Set up an OpenShift Kubernetes cluster for PeerPod VMs](#set-up-an-openshift-kubernetes-cluster-for-peerpod-vms) 1. [Upload a PeerPod VM Custom Image](#upload-a-peerpod-vm-custom-image) 1. [Deploy the PeerPod Webhook](#deploy-the-peerpod-webhook) -1. [Deploy kata-deploy and cloud-api-adaptor ](#deploy-confidential-containers) +1. [Deploy kata-deploy and cloud-api-adaptor](#deploy-confidential-containers) 1. [Run a Helloworld sample](#run-a-helloworld-sample) ## Pre-reqs @@ -13,19 +13,26 @@ This guide describes how to set up a simple peer pod demo environment with a Red Before proceeding you will need to install: 1. [IBM Cloud CLI](https://cloud.ibm.com/docs/cli?topic=cli-install-ibmcloud-cli) and `container-service[kubernetes-service/ks]` and `vpc-infrastructure[infrastructure-service/is]` plugins + > **Tips** +> > - If you are using Ubuntu linux, you can run follow commands simply: +> > ```bash -> $ curl -fsSL https://clis.cloud.ibm.com/install/linux | sh -> $ ibmcloud plugin install kubernetes-service -> $ ibmcloud plugin install vpc-infrastructure +> curl -fsSL https://clis.cloud.ibm.com/install/linux | sh +> ibmcloud plugin install kubernetes-service +> ibmcloud plugin install vpc-infrastructure > ``` -2. [`jq`](https://stedolan.github.io/jq/download/) +> +1. [`jq`](https://stedolan.github.io/jq/download/) + > **Tip:** If you are using Ubuntu linux, you can run follow command: +> > ```bash -> $ sudo apt-get install jq +> sudo apt-get install jq > ``` -3. [go](https://go.dev/doc/install) +> +1. [go](https://go.dev/doc/install) 1. `make` 1. the OpenShift [oc CLI](https://cloud.ibm.com/docs/openshift?topic=openshift-cli-install#install-kubectl-cli) 1. [helm](https://helm.sh/docs/intro/install/) @@ -140,19 +147,23 @@ By default, your Red Hat OpenShift cluster will not work with the peer pod compo A peer pod VM image needs to be available as a VPC custom image in IBM Cloud to create the peer pod instances with. If you want to run the full confidential containers end-to-end demo, including TDX attestation with a [Trustee](https://github.com/confidential-containers/trustee), you will need to make sure that your peer pod VM image is configured with the TDX attestation agent and kernel modules. -If you don't have a suitable peer pod image, you will need to [build one](./README.md#peer-pod-vm-image). For example, you can use the following command to build a TDX enabled RHEL image: +If you don't have a suitable peer pod image, you can either use a prebuilt image or build one yourself using mkosi. To build a custom Ubuntu image with TDX support: + ```bash # Run this command in directory src/cloud-api-adaptor -PODVM_DISTRO=rhel TEE_PLATFORM=tdx ACTIVATION_KEY= ORG_ID= IMAGE_URL= make podvm-builder podvm-binaries podvm-image +cd podvm +make ARCH=amd64 PODVM_DISTRO=ubuntu TEE_PLATFORM=tdx image ``` -This will create the RHEL image wrapped in a docker container image. You can then upload the RHEL image to IBM Cloud by running the following command from the root directory of the `cloud-api-adaptor` repository: +This will create an Ubuntu image with TDX support. The resulting qcow2 image will be in `podvm/build/podvm-ubuntu-amd64.qcow2`. You can then upload the image to IBM Cloud by running the following command from the root directory of the `cloud-api-adaptor` repository: + ```bash -src/cloud-api-adaptor/ibmcloud/image/import.sh : "$REGION" --pull never --os red-9-amd64 +src/cloud-api-adaptor/ibmcloud/image/import.sh podvm/build/podvm-ubuntu-amd64.qcow2 "$REGION" ``` > [!TIP] > If you don't have a TDX enabled image and are unable to build one, you can still run the peer pod demo without attestation. Run the following command to import a prebuilt non-TDX demo image: +> > ```bash > src/cloud-api-adaptor/ibmcloud/image/import.sh ghcr.io/confidential-containers/podvm-generic-ubuntu-amd64:latest "$REGION" --platform linux/amd64 > ``` @@ -313,11 +324,14 @@ Hello version: v1, instance: helloworld > If you have a Trustee configured, you can also test that attestation is working by running curl inside the helloworld pod to retrieve a key from the confidential data hub (CDH). > > For example, if your Trustee is configured with the same example key as used in the [test Trustee](#deploy-a-test-trustee), you can retrieve the value of key1 using the following command: +> > ```bash > oc exec -n default -it helloworld -- bash > curl http://127.0.0.1:8006/cdh/resource/default/kbsres1/key1 > ``` +> > If it is working, this will output the key's configured value: +> > ``` > res1val1 > ``` @@ -353,6 +367,7 @@ Otherwise: The following instructions can be used to set up a simple Trustee with an HTTP endpoint for testing attestation. 1. Create a Ubuntu VSI in the same VPC as your ROKS cluster. + ``` ibmcloud is instance-create "$CLUSTER_NAME-trustee" "$VPC_ID" "$ZONE" "bx2-2x8" "$SUBNET_ID" --image "r014-85b1a9ec-369b-41d6-b921-39666d4139d1" --keys "$SSH_KEY_ID" --allow-ip-spoofing false ``` @@ -361,6 +376,7 @@ The following instructions can be used to set up a simple Trustee with an HTTP e > Tip: you can SSH to the private IP of the VSI from a pod or node in your ROKS cluster, since they are running in the same VPC. First, run the following commands to install docker and oras: + ``` sudo apt-get update sudo apt-get install ca-certificates curl @@ -377,6 +393,7 @@ The following instructions can be used to set up a simple Trustee with an HTTP e ``` Next, deploy the Trustee services: + ``` git clone https://github.com/confidential-containers/trustee.git cd trustee @@ -386,6 +403,7 @@ The following instructions can be used to set up a simple Trustee with an HTTP e ``` Finally, configure an example key that can be used for CDH testing: + ``` oras pull ghcr.io/confidential-containers/staged-images/kbs-client:latest chmod +x kbs-client @@ -398,11 +416,13 @@ The following instructions can be used to set up a simple Trustee with an HTTP e 1. Configure your confidential containers environment to use the Trustee. First, use the VSI IP to set the `KBS_SERVICE_ENDPOINT` environment variable to the URL of the Trustee: + ``` export KBS_SERVICE_ENDPOINT="http://$(ibmcloud is instance "$CLUSTER_NAME-trustee" --output JSON | jq -r '.network_interfaces[].primary_ip.address'):8080" ``` Then, set the `INITDATA` environment variable to the compressed and encoded Trustee configuration: + ``` export INITDATA=$(cat <> ~/peerpods-cluster.properties ``` Alternatively, you can configure the Trustee for a specific peer pod, by including the `io.katacontainers.config.hypervisor.cc_init_data` annotation on the pod. For example: + ``` apiVersion: v1 kind: Pod diff --git a/src/cloud-api-adaptor/ibmcloud/image/import.sh b/src/cloud-api-adaptor/ibmcloud/image/import.sh index a821634158..67eaac0d82 100755 --- a/src/cloud-api-adaptor/ibmcloud/image/import.sh +++ b/src/cloud-api-adaptor/ibmcloud/image/import.sh @@ -1,5 +1,5 @@ #!/bin/bash -# import.sh takes a podvm docker image reference and a cloud region +# import.sh takes a podvm oras artifact reference or qcow2 file and a cloud region # and creates a ibmcloud vpc image of the qcow2 image. # Requires IBMCLOUD_API_KEY to be set. # Will default to first bucket it can find, by specifying @@ -12,7 +12,7 @@ error(){ script_dir=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) function usage() { - echo "Usage: $0 [--bucket --region --instance --endpoint --api --os --pull always(default)|missing|never]" + echo "Usage: $0 [--bucket --region --instance --endpoint --api --os ]" } image_file=$1 @@ -22,9 +22,7 @@ bucket_region=$region instance= endpoint= api=${IBMCLOUD_API_ENDPOINT-https://cloud.ibm.com} -platform= operating_system= -pull=always shift 2 while (( "$#" )); do @@ -35,8 +33,6 @@ while (( "$#" )); do --region) bucket_region=$2 ;; --os) operating_system=$2 ;; --api) api=$2 ;; - --platform) platform=$2 ;; - --pull) pull=$2 ;; --help) usage; exit 0 ;; *) usage 1>&2; exit 1;; esac @@ -83,9 +79,12 @@ fi if [ -f ${image_file} ]; then file=${image_file} else - # Download image echo "Downloading file from image ${image_file}" - file=$($script_dir/../../podvm/hack/download-image.sh ${image_file} . --platform "${platform}" --pull "${pull}") || error "Unable to download ${image_file}" + # mkosi images are oras artifacts + oras pull ${image_file} || error "Unable to download ${image_file}" + tar xvJf podvm.tar.xz + file=$(ls podvm-*.qcow2 2>/dev/null | head -n1) + [ -z "$file" ] && error "No qcow2 file found in downloaded artifact" fi echo "Uploading file ${file}" diff --git a/src/cloud-api-adaptor/install/README.md b/src/cloud-api-adaptor/install/README.md index 1bf391e115..4dbc820c24 100644 --- a/src/cloud-api-adaptor/install/README.md +++ b/src/cloud-api-adaptor/install/README.md @@ -40,13 +40,13 @@ For development, the easiest way to install it to a given `PROVIDER` is: - Copy `charts/peerpods/providers/PROVIDER-secrets.yaml.template` to `charts/peerpods/providers/PROVIDER-secrets.yaml` and edit the secrets - properly, unless you are installing for docker. + properly. - Fill `charts/peerpods/providers/PROVIDER.yaml` with required values and any customizations - Then run the `make deploy` command: ```sh - export CLOUD_PROVIDER= + export CLOUD_PROVIDER= make deploy ``` diff --git a/src/cloud-api-adaptor/install/charts/peerpods/Chart.yaml b/src/cloud-api-adaptor/install/charts/peerpods/Chart.yaml index 647d9c294d..7ca342aae7 100644 --- a/src/cloud-api-adaptor/install/charts/peerpods/Chart.yaml +++ b/src/cloud-api-adaptor/install/charts/peerpods/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: peerpods description: Cloud API Adaptor (Peerpods) Helm Chart type: application -version: 0.2.0 -appVersion: "v0.20.0" +version: 0.3.1 +appVersion: "v0.21.1" keywords: - confidential-containers @@ -25,12 +25,12 @@ dependencies: repository: "oci://ghcr.io/kata-containers/kata-deploy-charts" condition: kata-deploy.enabled - name: peerpodctrl - version: "0.2.0" + version: "0.3.1" repository: "file://../../../../peerpod-ctrl/chart" alias: resourceCtrl condition: resourceCtrl.enabled - name: peerpods-webhook - version: "0.2.0" + version: "0.3.1" repository: "file://../../../../webhook/chart" alias: webhook condition: webhook.enabled diff --git a/src/cloud-api-adaptor/install/charts/peerpods/README.md b/src/cloud-api-adaptor/install/charts/peerpods/README.md index 8d5cda6891..a6d05dedd5 100644 --- a/src/cloud-api-adaptor/install/charts/peerpods/README.md +++ b/src/cloud-api-adaptor/install/charts/peerpods/README.md @@ -137,13 +137,13 @@ Two image variants are published: | Variant | Tag Format | Includes CGO | Used By | |---------|----------------------------|--------------|---------------------------------| -| Dev | `latest` or `dev-` | Yes | libvirt, docker | +| Dev | `latest` or `dev-` | Yes | libvirt | | Release | `` | No | aws, azure, gcp, ibmcloud, etc. | - **During development**: `latest` works for all providers (default in values.yaml) -- **At release**: `values.yaml` is updated to ``, and libvirt/docker provider files override with `dev-` +- **At release**: `values.yaml` is updated to ``, and libvirt provider files override with `dev-` -The libvirt and docker provider files automatically include an `image.tag` override +The libvirt provider files automatically include an `image.tag` override since they require the dev image with CGO bindings. ### File Structure diff --git a/src/cloud-api-adaptor/install/charts/peerpods/providers/docker-secrets.yaml.template b/src/cloud-api-adaptor/install/charts/peerpods/providers/docker-secrets.yaml.template deleted file mode 100644 index 0843fde7c9..0000000000 --- a/src/cloud-api-adaptor/install/charts/peerpods/providers/docker-secrets.yaml.template +++ /dev/null @@ -1 +0,0 @@ -# No cloud credentials required for docker diff --git a/src/cloud-api-adaptor/install/charts/peerpods/providers/docker.yaml b/src/cloud-api-adaptor/install/charts/peerpods/providers/docker.yaml deleted file mode 100644 index 46bc5755bd..0000000000 --- a/src/cloud-api-adaptor/install/charts/peerpods/providers/docker.yaml +++ /dev/null @@ -1,105 +0,0 @@ -# Auto-generated by: make sync-chart-values -# Avoid editing manually. You can, but CI will check for drift. -# Provider: docker - -provider: docker - -# Dev image required for this provider (includes CGO bindings) -# Update to dev- at release time -image: - tag: "latest" - -providerConfigs: - docker: {} - # CA certificate file for custom TLS (e.g. /etc/certificates/ca.crt) - # (default: "") - # CACERT_FILE: "" - - # Client certificate file for custom TLS (e.g. /etc/certificates/client.crt) - # (default: "") - # CERT_FILE: "" - - # Client key file for custom TLS (e.g. /etc/certificates/client.key) - # (default: "") - # CERT_KEY: "" - - # Enable cloud config verify - should use it for production - # (default: "false") - # CLOUD_CONFIG_VERIFY: "false" - - # Docker API version - # (default: "1.44") - # DOCKER_API_VERSION: "1.44" - - # Path to directory with Docker TLS certificates - # (default: "") - # DOCKER_CERT_PATH: "" - - # Docker host - # (default: "unix:///var/run/docker.sock") - # DOCKER_HOST: "unix:///var/run/docker.sock" - - # Docker network name to connect to - # (default: "bridge") - # DOCKER_NETWORK_NAME: "bridge" - - # Docker image to use for podvm - # (default: "quay.io/confidential-containers/podvm-docker-image") - # DOCKER_PODVM_IMAGE: "quay.io/confidential-containers/podvm-docker-image" - - # Use TLS and verify the remote server certificate - # (default: "false") - # DOCKER_TLS_VERIFY: "false" - - # Enable encrypted scratch space for pod VMs - # (default: "false") - # ENABLE_SCRATCH_SPACE: "false" - - # [EXPERIMENTAL] Enable external networking via pod VM - # (default: "false") - # EXTERNAL_NETWORK_VIA_PODVM: "false" - - # port number of agent protocol forwarder - # (default: "") - # FORWARDER_PORT: "" - - # Default initdata for all Pods - # (default: "") - # INITDATA: "" - - # pause image to be used for the pods - # (default: "") - # PAUSE_IMAGE: "" - - # peer pods limit per node (default=10) - # (default: "10") - # PEERPODS_LIMIT_PER_NODE: "10" - - # base directory for pod directories - # (default: "") - # PODS_DIR: "" - - # [EXPERIMENTAL] Comma separated CIDRs for local pod subnets - # (default: "") - # POD_SUBNET_CIDRS: "" - - # Maximum timeout in minutes for establishing agent proxy connection - # (default: "") - # PROXY_TIMEOUT: "" - - # Unix domain socket path of remote hypervisor service - # (default: "") - # REMOTE_HYPERVISOR_ENDPOINT: "" - - # Skip TLS certificate verification - use it only for testing - # (default: "false") - # TLS_SKIP_VERIFY: "false" - - # Tunnel provider - # (default: "") - # TUNNEL_TYPE: "" - - # VXLAN UDP port number (VXLAN tunnel mode only - # (default: "") - # VXLAN_PORT: "" - diff --git a/src/cloud-api-adaptor/install/charts/peerpods/providers/libvirt.yaml b/src/cloud-api-adaptor/install/charts/peerpods/providers/libvirt.yaml index fd4b7f531c..0f742949a8 100644 --- a/src/cloud-api-adaptor/install/charts/peerpods/providers/libvirt.yaml +++ b/src/cloud-api-adaptor/install/charts/peerpods/providers/libvirt.yaml @@ -5,7 +5,7 @@ provider: libvirt # Dev image required for this provider (includes CGO bindings) -# Update to dev- at release time +# Update to -dev at release time image: tag: "latest" diff --git a/src/cloud-api-adaptor/install/charts/peerpods/templates/daemonset.yaml b/src/cloud-api-adaptor/install/charts/peerpods/templates/daemonset.yaml index 8d0cf707ab..ef5cc7d0ef 100644 --- a/src/cloud-api-adaptor/install/charts/peerpods/templates/daemonset.yaml +++ b/src/cloud-api-adaptor/install/charts/peerpods/templates/daemonset.yaml @@ -85,15 +85,9 @@ spec: # - mountPath: /cloud-providers # name: provider-dir # # setting for cloud provider external plugin -{{- if eq .Values.provider "docker" }} - - mountPath: /var/run/docker.sock - name: docker-socket - - mountPath: /var/lib/docker - name: docker-dir -{{- end }} -{{- if eq .Values.provider "ibmcloud" }} - mountPath: /run/kata-containers/shared/direct-volumes name: kata-direct-volumes-dir +{{- if eq .Values.provider "ibmcloud" }} - mountPath: /var/run/secrets/tokens name: vault-token {{- end }} @@ -146,19 +140,11 @@ spec: # type: Directory # name: provider-dir # # setting for cloud provider external plugin -{{- if eq .Values.provider "docker" }} - - name: docker-socket - hostPath: - path: /var/run/docker.sock - - name: docker-dir - hostPath: - path: /var/lib/docker -{{- end }} -{{- if eq .Values.provider "ibmcloud" }} - name: kata-direct-volumes-dir hostPath: path: /run/kata-containers/shared/direct-volumes type: DirectoryOrCreate +{{- if eq .Values.provider "ibmcloud" }} - name: vault-token projected: sources: diff --git a/src/cloud-api-adaptor/install/charts/peerpods/values.yaml b/src/cloud-api-adaptor/install/charts/peerpods/values.yaml index 61ad244fa2..e2653e1428 100644 --- a/src/cloud-api-adaptor/install/charts/peerpods/values.yaml +++ b/src/cloud-api-adaptor/install/charts/peerpods/values.yaml @@ -72,12 +72,12 @@ secrets: # # Two image variants are published: # - Release image (): smaller, no CGO/libvirt bindings -# - Dev image (dev- or "latest"): includes CGO bindings for libvirt/docker +# - Dev image (dev- or "latest"): includes CGO bindings for libvirt # # During development: "latest" works for all providers -# At release: most providers use , libvirt/docker use dev- +# At release: most providers use , libvirt uses dev- # -# Note: libvirt/docker provider files override this with the dev image. +# Note: libvirt provider files override this with the dev image. image: name: quay.io/confidential-containers/cloud-api-adaptor tag: "latest" diff --git a/src/cloud-api-adaptor/pkg/adaptor/cloud/cloud.go b/src/cloud-api-adaptor/pkg/adaptor/cloud/cloud.go index c2895b73fc..cd5a6e4991 100644 --- a/src/cloud-api-adaptor/pkg/adaptor/cloud/cloud.go +++ b/src/cloud-api-adaptor/pkg/adaptor/cloud/cloud.go @@ -193,6 +193,12 @@ func (s *cloudService) CreateVM(ctx context.Context, req *pb.CreateVMRequest) (r // Get Pod VM image from annotations image := util.GetImageFromAnnotation(req.Annotations) + // Get CSI volumes that need to be attached to the PodVM + csiVolumes := util.GetCSIVolumesForPod(req.Annotations) + if len(csiVolumes) > 0 { + logger.Printf("Found %d CSI volumes to attach to PodVM", len(csiVolumes)) + } + netNSPath := req.NetworkNamespacePath podNetworkConfig, err := s.workerNode.Inspect(netNSPath) @@ -212,6 +218,7 @@ func (s *cloudService) CreateVM(ctx context.Context, req *pb.CreateVMRequest) (r GPUs: gpus, Image: image, MultiNic: podNetworkConfig.ExternalNetViaPodVM, + Volumes: csiVolumes, } // TODO: server name is also generated in each cloud provider, and possibly inconsistent diff --git a/src/cloud-api-adaptor/pkg/adaptor/proxy/cloud_volumes_test.go b/src/cloud-api-adaptor/pkg/adaptor/proxy/cloud_volumes_test.go new file mode 100644 index 0000000000..cc13c2089f --- /dev/null +++ b/src/cloud-api-adaptor/pkg/adaptor/proxy/cloud_volumes_test.go @@ -0,0 +1,367 @@ +// (C) Copyright Confidential Containers Contributors +// SPDX-License-Identifier: Apache-2.0 + +package proxy + +import ( + "context" + b64 "encoding/base64" + "encoding/json" + "os" + "path/filepath" + "testing" + + "github.com/confidential-containers/cloud-api-adaptor/src/cloud-api-adaptor/pkg/util" + pb "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/agent/protocols/grpc" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func writeTestMountInfo(t *testing.T, dir, volumePath string, info map[string]interface{}) { + t.Helper() + encoded := b64.URLEncoding.EncodeToString([]byte(volumePath)) + volDir := filepath.Join(dir, encoded) + require.NoError(t, os.MkdirAll(volDir, 0o755)) + + data, err := json.Marshal(info) + require.NoError(t, err) + require.NoError(t, os.WriteFile(filepath.Join(volDir, "mountInfo.json"), data, 0o644)) +} + +func overrideKataDirectVolumesDir(t *testing.T, dir string) { + t.Helper() + origDir := util.KataDirectVolumesDir + util.KataDirectVolumesDir = dir + t.Cleanup(func() { util.KataDirectVolumesDir = origDir }) +} + +func TestCloudVolumes_SingleVolumeAnnotation(t *testing.T) { + dir := t.TempDir() + overrideKataDirectVolumesDir(t, dir) + + service, cleanup := setupMockAgentAndService(t) + defer cleanup() + + podUID := "pod-uid-111" + volPath := "/var/lib/kubelet/pods/" + podUID + "/volumes/kubernetes.io~csi/pvc-test/mount" + + writeTestMountInfo(t, dir, volPath, map[string]interface{}{ + "device": "/subscriptions/sub/disks/csi-vol-pvc-test", + "fstype": "ext4", + "metadata": map[string]interface{}{ + "cloud-volume-path": "/subscriptions/sub/disks/csi-vol-pvc-test", + }, + }) + + req := newCreateContainerRequest("test-cloud-vol"). + withAnnotations(map[string]string{ + "io.kubernetes.cri.sandbox-uid": podUID, + }). + withMounts(&pb.Mount{ + Destination: "/mnt/data", + Source: volPath, + Type: "bind", + }). + build() + + _, err := service.CreateContainer(context.Background(), req) + require.NoError(t, err) + + cvJSON, ok := req.OCI.Annotations["io.confidentialcontainers.org.cloud_volumes"] + require.True(t, ok, "cloud_volumes annotation should be set") + + var cloudVolumes map[string]map[string]string + require.NoError(t, json.Unmarshal([]byte(cvJSON), &cloudVolumes)) + + require.Contains(t, cloudVolumes, "vol-0") + assert.Equal(t, "/mnt/data", cloudVolumes["vol-0"]["mount_point"]) + assert.Equal(t, "ext4", cloudVolumes["vol-0"]["fs_type"]) + assert.Equal(t, "0", cloudVolumes["vol-0"]["lun"]) + assert.Equal(t, "/subscriptions/sub/disks/csi-vol-pvc-test", cloudVolumes["vol-0"]["disk_id"]) +} + +func TestCloudVolumes_MultipleVolumes(t *testing.T) { + dir := t.TempDir() + overrideKataDirectVolumesDir(t, dir) + + service, cleanup := setupMockAgentAndService(t) + defer cleanup() + + podUID := "pod-uid-222" + volPathA := "/var/lib/kubelet/pods/" + podUID + "/volumes/kubernetes.io~csi/pvc-alpha/mount" + volPathB := "/var/lib/kubelet/pods/" + podUID + "/volumes/kubernetes.io~csi/pvc-bravo/mount" + + writeTestMountInfo(t, dir, volPathA, map[string]interface{}{ + "device": "disk-alpha", + "fstype": "ext4", + }) + writeTestMountInfo(t, dir, volPathB, map[string]interface{}{ + "device": "disk-bravo", + "fstype": "xfs", + }) + + req := newCreateContainerRequest("test-multi-vol"). + withAnnotations(map[string]string{ + "io.kubernetes.cri.sandbox-uid": podUID, + }). + withMounts( + &pb.Mount{Destination: "/data/a", Source: volPathA, Type: "bind"}, + &pb.Mount{Destination: "/data/b", Source: volPathB, Type: "bind"}, + ). + build() + + _, err := service.CreateContainer(context.Background(), req) + require.NoError(t, err) + + cvJSON := req.OCI.Annotations["io.confidentialcontainers.org.cloud_volumes"] + require.NotEmpty(t, cvJSON) + + var cloudVolumes map[string]map[string]string + require.NoError(t, json.Unmarshal([]byte(cvJSON), &cloudVolumes)) + assert.Len(t, cloudVolumes, 2) +} + +func TestCloudVolumes_FsTypeFromMountInfo(t *testing.T) { + dir := t.TempDir() + overrideKataDirectVolumesDir(t, dir) + + service, cleanup := setupMockAgentAndService(t) + defer cleanup() + + podUID := "pod-uid-333" + volPath := "/var/lib/kubelet/pods/" + podUID + "/volumes/kubernetes.io~csi/pvc-xfs/mount" + + writeTestMountInfo(t, dir, volPath, map[string]interface{}{ + "device": "disk-xfs", + "fstype": "xfs", + }) + + req := newCreateContainerRequest("test-fstype"). + withAnnotations(map[string]string{ + "io.kubernetes.cri.sandbox-uid": podUID, + }). + withMounts(&pb.Mount{ + Destination: "/mnt/xfs", + Source: volPath, + Type: "bind", + }). + build() + + _, err := service.CreateContainer(context.Background(), req) + require.NoError(t, err) + + var cloudVolumes map[string]map[string]string + require.NoError(t, json.Unmarshal([]byte(req.OCI.Annotations["io.confidentialcontainers.org.cloud_volumes"]), &cloudVolumes)) + assert.Equal(t, "xfs", cloudVolumes["vol-0"]["fs_type"]) +} + +func TestCloudVolumes_FsTypeFallsBackToExt4(t *testing.T) { + dir := t.TempDir() + overrideKataDirectVolumesDir(t, dir) + + service, cleanup := setupMockAgentAndService(t) + defer cleanup() + + podUID := "pod-uid-444" + volPath := "/var/lib/kubelet/pods/" + podUID + "/volumes/kubernetes.io~csi/pvc-nofs/mount" + + writeTestMountInfo(t, dir, volPath, map[string]interface{}{ + "device": "disk-nofs", + }) + + req := newCreateContainerRequest("test-fstype-default"). + withAnnotations(map[string]string{ + "io.kubernetes.cri.sandbox-uid": podUID, + }). + withMounts(&pb.Mount{ + Destination: "/mnt/default", + Source: volPath, + Type: "bind", + }). + build() + + _, err := service.CreateContainer(context.Background(), req) + require.NoError(t, err) + + var cloudVolumes map[string]map[string]string + require.NoError(t, json.Unmarshal([]byte(req.OCI.Annotations["io.confidentialcontainers.org.cloud_volumes"]), &cloudVolumes)) + assert.Equal(t, "ext4", cloudVolumes["vol-0"]["fs_type"]) +} + +func TestCloudVolumes_NoAnnotationWhenNoCSIVolumes(t *testing.T) { + dir := t.TempDir() + overrideKataDirectVolumesDir(t, dir) + + service, cleanup := setupMockAgentAndService(t) + defer cleanup() + + req := newCreateContainerRequest("test-no-vol"). + withMounts(&pb.Mount{ + Destination: "/mnt/regular", + Source: "/some/regular/path", + Type: "bind", + }). + build() + + _, err := service.CreateContainer(context.Background(), req) + require.NoError(t, err) + + _, ok := req.OCI.Annotations["io.confidentialcontainers.org.cloud_volumes"] + assert.False(t, ok, "cloud_volumes annotation should not be set for non-CSI volumes") +} + +func TestCloudVolumes_SkipsVolumesFromOtherPods(t *testing.T) { + dir := t.TempDir() + overrideKataDirectVolumesDir(t, dir) + + service, cleanup := setupMockAgentAndService(t) + defer cleanup() + + writeTestMountInfo(t, dir, + "/var/lib/kubelet/pods/other-pod-uid/volumes/kubernetes.io~csi/pvc-other/mount", + map[string]interface{}{"device": "other-disk", "fstype": "ext4"}) + + req := newCreateContainerRequest("test-other-pod"). + withAnnotations(map[string]string{ + "io.kubernetes.cri.sandbox-uid": "my-pod-uid", + }). + build() + + _, err := service.CreateContainer(context.Background(), req) + require.NoError(t, err) + + _, ok := req.OCI.Annotations["io.confidentialcontainers.org.cloud_volumes"] + assert.False(t, ok, "should not include volumes from other pods") +} + +func TestCloudVolumes_LUNIndexSkippedVolumeConsistency(t *testing.T) { + dir := t.TempDir() + overrideKataDirectVolumesDir(t, dir) + + service, cleanup := setupMockAgentAndService(t) + defer cleanup() + + podUID := "pod-uid-555" + + // vol-a has matching OCI mount + volPathA := "/var/lib/kubelet/pods/" + podUID + "/volumes/kubernetes.io~csi/pvc-alpha/mount" + writeTestMountInfo(t, dir, volPathA, map[string]interface{}{ + "device": "disk-alpha", "fstype": "ext4", + }) + + // vol-b has NO matching OCI mount (will be skipped in annotation but still counted) + volPathB := "/var/lib/kubelet/pods/" + podUID + "/volumes/kubernetes.io~csi/pvc-bravo/mount" + writeTestMountInfo(t, dir, volPathB, map[string]interface{}{ + "device": "disk-bravo", "fstype": "ext4", + }) + + // vol-c has matching OCI mount + volPathC := "/var/lib/kubelet/pods/" + podUID + "/volumes/kubernetes.io~csi/pvc-charlie/mount" + writeTestMountInfo(t, dir, volPathC, map[string]interface{}{ + "device": "disk-charlie", "fstype": "ext4", + }) + + req := newCreateContainerRequest("test-lun-skip"). + withAnnotations(map[string]string{ + "io.kubernetes.cri.sandbox-uid": podUID, + }). + withMounts( + &pb.Mount{Destination: "/data/a", Source: volPathA, Type: "bind"}, + // No mount for volPathB + &pb.Mount{Destination: "/data/c", Source: volPathC, Type: "bind"}, + ). + build() + + _, err := service.CreateContainer(context.Background(), req) + require.NoError(t, err) + + cvJSON := req.OCI.Annotations["io.confidentialcontainers.org.cloud_volumes"] + require.NotEmpty(t, cvJSON) + + var cloudVolumes map[string]map[string]string + require.NoError(t, json.Unmarshal([]byte(cvJSON), &cloudVolumes)) + + // Should only have 2 entries (vol-b is skipped because no OCI mount) + assert.Len(t, cloudVolumes, 2) + + // Collect LUN values + luns := make(map[string]string) + for _, vol := range cloudVolumes { + luns[vol["lun"]] = vol["disk_id"] + } + + // vol-a should get LUN 0 (canonical index 0) + // vol-b gets canonical index 1 (skipped in annotation but index still incremented) + // vol-c should get LUN 2 (canonical index 2) + assert.Contains(t, luns, "0", "vol-a should be at LUN 0") + assert.Contains(t, luns, "2", "vol-c should be at LUN 2, not LUN 1") + assert.NotContains(t, luns, "1", "LUN 1 should be skipped (vol-b had no OCI mount)") + + assert.Equal(t, "disk-alpha", luns["0"]) + assert.Equal(t, "disk-charlie", luns["2"]) +} + +func TestCloudVolumes_SkipsVolumeWithNoDiskID(t *testing.T) { + dir := t.TempDir() + overrideKataDirectVolumesDir(t, dir) + + service, cleanup := setupMockAgentAndService(t) + defer cleanup() + + podUID := "pod-uid-666" + volPath := "/var/lib/kubelet/pods/" + podUID + "/volumes/kubernetes.io~csi/pvc-nodisk/mount" + + writeTestMountInfo(t, dir, volPath, map[string]interface{}{ + "fstype": "ext4", + }) + + req := newCreateContainerRequest("test-no-disk"). + withAnnotations(map[string]string{ + "io.kubernetes.cri.sandbox-uid": podUID, + }). + withMounts(&pb.Mount{ + Destination: "/mnt/nodisk", + Source: volPath, + Type: "bind", + }). + build() + + _, err := service.CreateContainer(context.Background(), req) + require.NoError(t, err) + + _, ok := req.OCI.Annotations["io.confidentialcontainers.org.cloud_volumes"] + assert.False(t, ok, "annotation should not be set when volume has no disk ID") +} + +func TestCloudVolumes_SkipsInvalidMountInfoJSON(t *testing.T) { + dir := t.TempDir() + overrideKataDirectVolumesDir(t, dir) + + service, cleanup := setupMockAgentAndService(t) + defer cleanup() + + podUID := "pod-uid-777" + volPath := "/var/lib/kubelet/pods/" + podUID + "/volumes/kubernetes.io~csi/pvc-badjson/mount" + + encoded := b64.URLEncoding.EncodeToString([]byte(volPath)) + volDir := filepath.Join(dir, encoded) + require.NoError(t, os.MkdirAll(volDir, 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(volDir, "mountInfo.json"), []byte("not json"), 0o644)) + + req := newCreateContainerRequest("test-bad-json"). + withAnnotations(map[string]string{ + "io.kubernetes.cri.sandbox-uid": podUID, + }). + withMounts(&pb.Mount{ + Destination: "/mnt/badjson", + Source: volPath, + Type: "bind", + }). + build() + + _, err := service.CreateContainer(context.Background(), req) + require.NoError(t, err) + + _, ok := req.OCI.Annotations["io.confidentialcontainers.org.cloud_volumes"] + assert.False(t, ok, "annotation should not be set with invalid JSON") +} diff --git a/src/cloud-api-adaptor/pkg/adaptor/proxy/service.go b/src/cloud-api-adaptor/pkg/adaptor/proxy/service.go index a1541d1645..21a70b613c 100644 --- a/src/cloud-api-adaptor/pkg/adaptor/proxy/service.go +++ b/src/cloud-api-adaptor/pkg/adaptor/proxy/service.go @@ -6,11 +6,14 @@ package proxy import ( "context" b64 "encoding/base64" + "encoding/json" + "fmt" "net" "os" "path/filepath" "strings" + "github.com/confidential-containers/cloud-api-adaptor/src/cloud-api-adaptor/pkg/util" "github.com/confidential-containers/cloud-api-adaptor/src/cloud-api-adaptor/pkg/util/agentproto" pb "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/agent/protocols/grpc" "google.golang.org/protobuf/types/known/emptypb" @@ -22,14 +25,12 @@ type proxyService struct { } const ( - defaultPauseImage = "registry.k8s.io/pause:3.7" - kataDirectVolumesDir = "/run/kata-containers/shared/direct-volumes" - volumeTargetPathKey = "io.confidentialcontainers.org.peerpodvolumes.target_path" - csiPluginEscapeQualifiedName = "kubernetes.io~csi" - imageGuestPull = "image_guest_pull" - cdiAnnotationKey = "cdi.k8s.io/peer-pods" - defaultCDIType = "nvidia.com/gpu=all" - defaultGPUsAnnotation = "io.katacontainers.config.hypervisor.default_gpus" + defaultPauseImage = "registry.k8s.io/pause:3.7" + volumeTargetPathKey = "io.confidentialcontainers.org.peerpodvolumes.target_path" + imageGuestPull = "image_guest_pull" + cdiAnnotationKey = "cdi.k8s.io/peer-pods" + defaultCDIType = "nvidia.com/gpu=all" + defaultGPUsAnnotation = "io.katacontainers.config.hypervisor.default_gpus" ) func newProxyService(dialer func(context.Context) (net.Conn, error), pauseImage string) *proxyService { @@ -47,12 +48,16 @@ func newProxyService(dialer func(context.Context) (net.Conn, error), pauseImage func (s *proxyService) CreateContainer(ctx context.Context, req *pb.CreateContainerRequest) (*emptypb.Empty, error) { var pullImageInGuest bool logger.Printf("CreateContainer: containerID:%s", req.ContainerId) + if req.OCI.Annotations == nil { + req.OCI.Annotations = make(map[string]string) + } + if len(req.OCI.Mounts) > 0 { logger.Print(" mounts:") for i, m := range req.OCI.Mounts { logger.Printf(" destination:%s source:%s type:%s", m.Destination, m.Source, m.Type) - if isNodePublishVolumeTargetPath(m.Source, kataDirectVolumesDir) { + if isNodePublishVolumeTargetPath(m.Source, util.KataDirectVolumesDir) { if i > 0 { req.OCI.Annotations[volumeTargetPathKey] += "," } @@ -94,6 +99,101 @@ func (s *proxyService) CreateContainer(ctx context.Context, req *pb.CreateContai logger.Printf("Pulling image separately not support on main. It is required to use the nydus-snapshotter, which isn't configured properly here.") } + // Detect cloud volumes by scanning the direct-volumes directory in canonical + // order. The scan order matches GetCSIVolumesForPod (os.ReadDir sorts by + // name), so the LUN index here is consistent with the cloud provider's + // disk attachment order. + cloudVolumes := make(map[string]util.CloudVolumeAnnotation) + podUID := req.OCI.Annotations["io.kubernetes.cri.sandbox-uid"] + + dirEntries, dirErr := os.ReadDir(util.KataDirectVolumesDir) + if dirErr == nil { + canonicalIdx := 0 + for _, entry := range dirEntries { + if !entry.IsDir() { + continue + } + decodedBytes, err := b64.URLEncoding.DecodeString(entry.Name()) + if err != nil { + continue + } + decodedPath := string(decodedBytes) + + if !strings.Contains(decodedPath, "/volumes/"+util.CSIPluginEscapeQualifiedName+"/") { + continue + } + if podUID != "" && !strings.Contains(decodedPath, "/pods/"+podUID+"/") { + continue + } + + mountInfoPath := filepath.Join(util.KataDirectVolumesDir, entry.Name(), "mountInfo.json") + data, err := os.ReadFile(mountInfoPath) + if err != nil { + logger.Printf("could not read mountInfo.json for %s: %v", decodedPath, err) + continue + } + var mountInfo map[string]interface{} + if err := json.Unmarshal(data, &mountInfo); err != nil { + logger.Printf("could not parse mountInfo.json for %s: %v", decodedPath, err) + continue + } + + diskID := "" + if md, ok := mountInfo["metadata"].(map[string]interface{}); ok { + if cp, ok := md["cloud-volume-path"].(string); ok && cp != "" { + diskID = cp + } + } + if diskID == "" { + if d, ok := mountInfo["device"].(string); ok { + diskID = d + } + } + if diskID == "" { + logger.Printf("cloud volume at %s has no disk ID, skipping", decodedPath) + continue + } + + fsType := "ext4" + if ft, ok := mountInfo["fstype"].(string); ok && ft != "" { + fsType = ft + } + + mountDest := "" + for _, m := range req.OCI.Mounts { + if m.Source == decodedPath { + mountDest = m.Destination + break + } + } + if mountDest == "" { + logger.Printf("cloud volume disk %s has no matching mount in container spec, skipping", diskID) + canonicalIdx++ + continue + } + + volKey := fmt.Sprintf("vol-%d", canonicalIdx) + cloudVolumes[volKey] = util.CloudVolumeAnnotation{ + MountPoint: mountDest, + FSType: fsType, + LUN: fmt.Sprintf("%d", canonicalIdx), + DiskID: diskID, + } + logger.Printf("Detected cloud volume %s -> %s (lun=%d, disk=%s, fs=%s)", volKey, mountDest, canonicalIdx, diskID, fsType) + canonicalIdx++ + } + } + + if len(cloudVolumes) > 0 { + cvJSON, err := json.Marshal(cloudVolumes) + if err != nil { + logger.Printf("failed to marshal cloud_volumes annotation: %v", err) + } else { + req.OCI.Annotations[util.CloudVolumesAnnotationKey] = string(cvJSON) + logger.Printf("Set cloud_volumes annotation: %s", string(cvJSON)) + } + } + res, err := s.Redirector.CreateContainer(ctx, req) if err != nil { @@ -104,7 +204,7 @@ func (s *proxyService) CreateContainer(ctx context.Context, req *pb.CreateContai } func isNodePublishVolumeTargetPath(volumePath, directVolumesDir string) bool { - if !strings.Contains(filepath.Clean(volumePath), "/volumes/"+csiPluginEscapeQualifiedName+"/") { + if !strings.Contains(filepath.Clean(volumePath), "/volumes/"+util.CSIPluginEscapeQualifiedName+"/") { return false } diff --git a/src/cloud-api-adaptor/pkg/forwarder/interceptor/interceptor.go b/src/cloud-api-adaptor/pkg/forwarder/interceptor/interceptor.go index bff1278b0f..6eab4e1326 100644 --- a/src/cloud-api-adaptor/pkg/forwarder/interceptor/interceptor.go +++ b/src/cloud-api-adaptor/pkg/forwarder/interceptor/interceptor.go @@ -5,11 +5,17 @@ package interceptor import ( "context" + "encoding/json" "fmt" "log" "net" "os" + "os/exec" + "path/filepath" + "sort" + "strconv" "strings" + "syscall" "time" retry "github.com/avast/retry-go/v4" @@ -18,15 +24,23 @@ import ( specs "github.com/opencontainers/runtime-spec/specs-go" "google.golang.org/protobuf/types/known/emptypb" + "github.com/confidential-containers/cloud-api-adaptor/src/cloud-api-adaptor/pkg/util" "github.com/confidential-containers/cloud-api-adaptor/src/cloud-api-adaptor/pkg/util/agentproto" ) const ( - volumeTargetPathKey = "io.confidentialcontainers.org.peerpodvolumes.target_path" - volumeCheckInterval = 5 * time.Second - volumeCheckTimeout = 3 * time.Minute + volumeTargetPathKey = "io.confidentialcontainers.org.peerpodvolumes.target_path" + cloudVolumeMountBase = "/run/cloud-volumes" + volumeCheckInterval = 5 * time.Second + volumeCheckTimeout = 3 * time.Minute ) +var allowedFSTypes = map[string]bool{ + "ext4": true, + "ext3": true, + "xfs": true, +} + var logger = log.New(log.Writer(), "[forwarder/interceptor] ", log.LstdFlags|log.Lmsgprefix) type Interceptor interface { @@ -36,7 +50,22 @@ type Interceptor interface { type interceptor struct { agentproto.Redirector - nsPath string + nsPath string + cloudMountPaths []string +} + +// unmountCloudVolumes unmounts all cloud volume mount points in reverse order. +// Called during DestroySandbox to clean up before the PodVM is terminated. +func (i *interceptor) unmountCloudVolumes() { + for idx := len(i.cloudMountPaths) - 1; idx >= 0; idx-- { + mp := i.cloudMountPaths[idx] + if err := syscall.Unmount(mp, 0); err != nil { + logger.Printf("WARNING: failed to unmount cloud volume %s: %v", mp, err) + } else { + logger.Printf("Unmounted cloud volume %s", mp) + } + } + i.cloudMountPaths = nil } func dial(ctx context.Context, agentSocket string) (net.Conn, error) { @@ -95,6 +124,95 @@ func (i *interceptor) CreateContainer(ctx context.Context, req *pb.CreateContain logger.Printf(" %s: %q", ns.Type, ns.Path) } + // Handle cloud volumes: detect device, format if needed, mount, and + // bind-mount into the container's mount namespace. + if cvJSON, ok := req.OCI.Annotations[util.CloudVolumesAnnotationKey]; ok && cvJSON != "" { + var cloudVolumes map[string]util.CloudVolumeAnnotation + if err := json.Unmarshal([]byte(cvJSON), &cloudVolumes); err != nil { + return nil, fmt.Errorf("corrupt cloud_volumes annotation (pod would start without volumes): %w", err) + } + + volNames := make([]string, 0, len(cloudVolumes)) + for k := range cloudVolumes { + volNames = append(volNames, k) + } + sort.Strings(volNames) + + for _, volName := range volNames { + volInfo := cloudVolumes[volName] + mountPoint := volInfo.MountPoint + fsType := volInfo.FSType + lunStr := volInfo.LUN + if mountPoint == "" || lunStr == "" { + return nil, fmt.Errorf("cloud volume %s missing required mount_point or lun field", volName) + } + + safeName := filepath.Base(volName) + if safeName != volName || safeName == "." || safeName == ".." { + return nil, fmt.Errorf("cloud volume %q has unsafe name", volName) + } + + if fsType == "" { + fsType = "ext4" + } + if !allowedFSTypes[fsType] { + return nil, fmt.Errorf("cloud volume %s requests unsupported filesystem type %q (allowed: ext4, ext3, xfs)", volName, fsType) + } + + lunIdx, err := strconv.Atoi(lunStr) + if err != nil { + return nil, fmt.Errorf("cloud volume %s has invalid lun %q: %w", volName, lunStr, err) + } + + diskID := volInfo.DiskID + device, err := findDataDiskDevice(lunIdx, diskID) + if err != nil { + return nil, fmt.Errorf("cloud volume %s: %w", volName, err) + } + logger.Printf("cloud volume %s: LUN %d -> device %s", volName, lunIdx, device) + + hostMountPoint := filepath.Join(cloudVolumeMountBase, safeName) + if err := os.MkdirAll(hostMountPoint, 0o755); err != nil { + return nil, fmt.Errorf("creating mount point for %s: %w", volName, err) + } + + if err := waitForDevice(device); err != nil { + return nil, fmt.Errorf("cloud volume %s device %s not available: %w", volName, device, err) + } + + if err := formatAndMount(device, hostMountPoint, fsType); err != nil { + return nil, fmt.Errorf("failed to mount cloud volume %s at %s: %w", volName, hostMountPoint, err) + } + i.cloudMountPaths = append(i.cloudMountPaths, hostMountPoint) + + if fsGroupStr := volInfo.FSGroup; fsGroupStr != "" { + if gid, err := strconv.Atoi(fsGroupStr); err == nil { + logger.Printf("cloud volume %s: applying fsGroup %d to %s", volName, gid, hostMountPoint) + if err := os.Chown(hostMountPoint, -1, gid); err != nil { + logger.Printf("WARNING: failed to chown %s to gid %d: %v", hostMountPoint, gid, err) + } + if err := os.Chmod(hostMountPoint, 0o2775); err != nil { + logger.Printf("WARNING: failed to chmod %s: %v", hostMountPoint, err) + } + } + } + + rewrote := false + for idx, m := range req.OCI.Mounts { + if m.Destination == mountPoint { + req.OCI.Mounts[idx].Source = hostMountPoint + req.OCI.Mounts[idx].Type = "bind" + logger.Printf("cloud volume %s: rewrote mount source to %s", volName, hostMountPoint) + rewrote = true + break + } + } + if !rewrote { + logger.Printf("WARNING: cloud volume %s mount_point %q not found in container mounts", volName, mountPoint) + } + } + } + volumeTargetPath := req.OCI.Annotations[volumeTargetPathKey] volumeTargetPathSlice := strings.Split(volumeTargetPath, ",") if len(req.OCI.Mounts) > 0 { @@ -221,6 +339,8 @@ func (i *interceptor) DestroySandbox(ctx context.Context, req *pb.DestroySandbox logger.Printf("DestroySandbox") + i.unmountCloudVolumes() + res, err := i.Redirector.DestroySandbox(ctx, req) if err != nil { @@ -229,3 +349,433 @@ func (i *interceptor) DestroySandbox(ctx context.Context, req *pb.DestroySandbox return res, err } + +// detectCloudProvider determines the cloud platform by probing +// provider-specific device paths inside the PodVM. +func detectCloudProvider() string { + if _, err := os.Stat("/dev/disk/azure"); err == nil { + return "azure" + } + // Azure VMs have a well-known DMI chassis asset tag. This is more + // specific than checking /sys/bus/vmbus which exists on all Hyper-V + // VMs (including on-prem and Azure Stack). + if data, err := os.ReadFile("/sys/class/dmi/id/chassis_asset_tag"); err == nil { + if strings.TrimSpace(string(data)) == "7783-7084-3265-9085-8269-3286-77" { + return "azure" + } + } + if matches, _ := filepath.Glob("/dev/disk/by-id/nvme-Amazon_Elastic_Block_Store_*"); len(matches) > 0 { + return "aws" + } + if matches, _ := filepath.Glob("/dev/vd[a-z]"); len(matches) > 0 { + return "libvirt" + } + return "generic" +} + +// findDataDiskDevice locates the block device for the given LUN index. +// It auto-detects the cloud provider and uses provider-specific paths, +// then falls back to sysfs HCTL-based LUN matching. +// diskID is the cloud-provider volume identifier (e.g. EBS vol-xxx); it +// may be empty for providers that rely purely on LUN-index matching. +// +// Data disks may take several seconds to appear in the PodVM after boot +// (SCSI rescan, udev rules), so the entire detection is retried. +func findDataDiskDevice(lunIdx int, diskID string) (string, error) { + provider := detectCloudProvider() + logger.Printf("Cloud provider detected: %s (LUN %d, diskID %s)", provider, lunIdx, diskID) + + maxAttempts := 15 + retryDelay := 2 * time.Second + if v := os.Getenv("CAA_DISK_DETECT_MAX_ATTEMPTS"); v != "" { + if n, err := strconv.Atoi(v); err == nil && n > 0 { + maxAttempts = n + } + } + if v := os.Getenv("CAA_DISK_DETECT_RETRY_DELAY"); v != "" { + if d, err := time.ParseDuration(v); err == nil && d > 0 { + retryDelay = d + } + } + + if provider == "azure" { + triggerUdevRescan() + } + + for attempt := 1; attempt <= maxAttempts; attempt++ { + if attempt > 1 { + logger.Printf("Retry %d/%d for LUN %d...", attempt, maxAttempts, lunIdx) + time.Sleep(retryDelay) + } + + switch provider { + case "azure": + if dev, err := findAzureDataDisk(lunIdx); err == nil { + return dev, nil + } + case "aws": + if dev, err := findAWSDataDisk(lunIdx, diskID); err == nil { + return dev, nil + } + case "libvirt": + if dev, err := findLibvirtDataDisk(lunIdx); err == nil { + return dev, nil + } + } + + if dev, err := findDataDiskBySysfsHCTL(lunIdx); err == nil { + return dev, nil + } + } + + dumpBlockDeviceDiagnostics() + return "", fmt.Errorf("no data disk found for LUN %d (provider=%s) after %d attempts", lunIdx, provider, maxAttempts) +} + +// triggerUdevRescan triggers udev to process pending block device events and +// create symlinks (e.g. /dev/disk/azure/*, /dev/disk/by-path/*). +// Called once before the retry loop rather than on every attempt. +func triggerUdevRescan() { + if _, err := exec.Command("udevadm", "trigger", "--subsystem-match=block").CombinedOutput(); err == nil { + exec.Command("udevadm", "settle", "--timeout=5").CombinedOutput() //nolint:errcheck + } +} + +func findAzureDataDisk(lunIdx int) (string, error) { + azurePaths := []string{ + fmt.Sprintf("/dev/disk/azure/data/by-lun/%d", lunIdx), + fmt.Sprintf("/dev/disk/azure/scsi1/lun%d", lunIdx), + } + for _, p := range azurePaths { + if target, err := filepath.EvalSymlinks(p); err == nil { + logger.Printf("Found Azure data disk: %s -> %s", p, target) + return target, nil + } + } + + byPathDir := "/dev/disk/by-path" + if entries, err := os.ReadDir(byPathDir); err == nil { + lunSuffix := fmt.Sprintf("-lun-%d", lunIdx) + for _, e := range entries { + name := e.Name() + if strings.Contains(name, "vmbus") && strings.HasSuffix(name, lunSuffix) && !strings.Contains(name, "part") { + fullPath := filepath.Join(byPathDir, name) + if target, err := filepath.EvalSymlinks(fullPath); err == nil { + if isOnSCSIHost0(filepath.Base(target)) { + logger.Printf("Skipping by-path %s -> %s (SCSI host 0, OS disk area)", fullPath, target) + continue + } + logger.Printf("Found Azure data disk via by-path: %s -> %s", fullPath, target) + return target, nil + } + } + } + } + return "", fmt.Errorf("Azure data disk LUN %d not found", lunIdx) +} + +func findAWSDataDisk(lunIdx int, diskID string) (string, error) { + entries, err := filepath.Glob("/dev/disk/by-id/nvme-Amazon_Elastic_Block_Store_*") + if err != nil || len(entries) == 0 { + return "", fmt.Errorf("no AWS EBS NVMe devices found") + } + + var candidates []string + for _, e := range entries { + if !strings.Contains(e, "-part") { + candidates = append(candidates, e) + } + } + + // Prefer matching by EBS volume ID for multi-disk correctness. + // The NVMe by-id symlink encodes the volume ID with hyphens removed, + // e.g. vol-0abc123 becomes nvme-Amazon_Elastic_Block_Store_vol0abc123. + if diskID != "" { + volIDNormalized := strings.ReplaceAll(diskID, "-", "") + for _, c := range candidates { + if strings.Contains(c, volIDNormalized) { + target, err := filepath.EvalSymlinks(c) + if err != nil { + return "", err + } + logger.Printf("Found AWS EBS disk by volume ID: %s -> %s (diskID=%s)", c, target, diskID) + return target, nil + } + } + return "", fmt.Errorf("AWS EBS disk with volume ID %s not found in %d by-id symlinks", diskID, len(candidates)) + } + + // Index-based selection only when diskID is empty (legacy/testing). + if lunIdx >= len(candidates) { + return "", fmt.Errorf("AWS EBS disk index %d out of range (have %d)", lunIdx, len(candidates)) + } + target, err := filepath.EvalSymlinks(candidates[lunIdx]) + if err != nil { + return "", err + } + logger.Printf("Found AWS EBS disk by index: %s -> %s", candidates[lunIdx], target) + return target, nil +} + +func findLibvirtDataDisk(lunIdx int) (string, error) { + if lunIdx < 0 || lunIdx > 24 { + return "", fmt.Errorf("LUN index %d out of range for virtio devices (0-24)", lunIdx) + } + devLetter := 'b' + rune(lunIdx) + device := fmt.Sprintf("/dev/vd%c", devLetter) + if _, err := os.Stat(device); err == nil { + logger.Printf("Found libvirt virtio disk: %s", device) + return device, nil + } + return "", fmt.Errorf("libvirt device %s not found", device) +} + +// isOnSCSIHost0 checks if a block device is on SCSI host controller 0, +// where Azure/Hyper-V places the OS and temp disks. +func isOnSCSIHost0(devName string) bool { + devicePath := filepath.Join("/sys/block", devName, "device") + realPath, err := filepath.EvalSymlinks(devicePath) + if err != nil { + return false + } + for _, part := range strings.Split(realPath, "/") { + hctl := strings.Split(part, ":") + if len(hctl) == 4 { + host, err := strconv.Atoi(hctl[0]) + if err == nil { + return host == 0 + } + } + } + return false +} + +// findDataDiskBySysfsHCTL matches block devices by their SCSI HCTL +// (Host:Channel:Target:Lun) address in sysfs rather than relying on +// directory listing order. On Azure/Hyper-V, SCSI controller 0 holds +// the OS and temp disks while data disks live on controller 1+, so +// controller 0 is skipped. If no match is found on non-zero hosts, +// a second pass looks at all hosts but filters out any device that +// is mounted or has partitions (indicating it is the OS/temp disk). +func findDataDiskBySysfsHCTL(lunIdx int) (string, error) { + entries, err := os.ReadDir("/sys/block") + if err != nil { + return "", fmt.Errorf("cannot read /sys/block: %w", err) + } + + type devInfo struct { + name string + host int + lun int + hctl string + } + + var allMatches []devInfo + + for _, e := range entries { + name := e.Name() + if !strings.HasPrefix(name, "sd") && !strings.HasPrefix(name, "nvme") && !strings.HasPrefix(name, "vd") { + continue + } + + devicePath := filepath.Join("/sys/block", name, "device") + realPath, err := filepath.EvalSymlinks(devicePath) + if err != nil { + continue + } + + parts := strings.Split(realPath, "/") + for _, part := range parts { + hctl := strings.Split(part, ":") + if len(hctl) == 4 { + host, hostErr := strconv.Atoi(hctl[0]) + lun, lunErr := strconv.Atoi(hctl[3]) + if hostErr != nil || lunErr != nil { + continue + } + logger.Printf("sysfs scan: /dev/%s HCTL=%s (host=%d lun=%d)", name, part, host, lun) + if lun == lunIdx { + allMatches = append(allMatches, devInfo{name: name, host: host, lun: lun, hctl: part}) + } + } + } + } + + // Pass 1: prefer devices on non-zero hosts (standard Azure data disk topology) + for _, m := range allMatches { + if m.host == 0 { + continue + } + dev := "/dev/" + m.name + if isRootOrMountedDevice(m.name) { + logger.Printf("Skipping %s (LUN %d host %d): device is in use as root/mounted", dev, m.lun, m.host) + continue + } + logger.Printf("Found data disk via sysfs HCTL (host>0): %s (LUN %d from %s)", dev, lunIdx, m.hctl) + return dev, nil + } + + // Pass 2: if no non-zero host matched, check host 0 devices that are + // NOT mounted and have NO partitions (i.e. raw data disks on host 0). + for _, m := range allMatches { + if m.host != 0 { + continue + } + dev := "/dev/" + m.name + if isRootOrMountedDevice(m.name) { + logger.Printf("Skipping %s (LUN %d host 0): device is mounted", dev, m.lun) + continue + } + if hasPartitions(m.name) { + logger.Printf("Skipping %s (LUN %d host 0): device has partitions (likely OS/temp disk)", dev, m.lun) + continue + } + logger.Printf("Found data disk via sysfs HCTL (host=0, raw): %s (LUN %d from %s)", dev, lunIdx, m.hctl) + return dev, nil + } + + return "", fmt.Errorf("no data disk found for LUN %d via sysfs HCTL (%d candidates scanned)", lunIdx, len(allMatches)) +} + +// hasPartitions checks whether a block device has any partition sub-devices +// in /sys/block//N (e.g. sda1, sda2). +func hasPartitions(devName string) bool { + entries, err := os.ReadDir(filepath.Join("/sys/block", devName)) + if err != nil { + return false + } + for _, e := range entries { + if strings.HasPrefix(e.Name(), devName) { + return true + } + } + return false +} + +// isRootOrMountedDevice returns true if the device or any of its partitions +// is mounted as a filesystem (especially / or /boot). This prevents +// accidentally selecting the OS disk as a data disk when HCTL host +// numbering doesn't match expectations. +func isRootOrMountedDevice(devName string) bool { + data, err := os.ReadFile("/proc/mounts") + if err != nil { + return false + } + for _, line := range strings.Split(string(data), "\n") { + fields := strings.Fields(line) + if len(fields) < 2 { + continue + } + mountDev := fields[0] + if strings.HasPrefix(mountDev, "/dev/"+devName) { + return true + } + } + return false +} + +func dumpBlockDeviceDiagnostics() { + logger.Printf("=== Block Device Diagnostics ===") + if out, err := exec.Command("lsblk", "-o", "NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE").CombinedOutput(); err == nil { + logger.Printf("lsblk:\n%s", string(out)) + } + for _, dir := range []string{"/dev/disk/azure", "/dev/disk/by-path", "/dev/disk/by-id"} { + if entries, err := os.ReadDir(dir); err == nil { + for _, e := range entries { + full := filepath.Join(dir, e.Name()) + if target, err := os.Readlink(full); err == nil { + logger.Printf(" %s -> %s", full, target) + } + } + } + } +} + +func waitForDevice(device string) error { + for attempt := 0; attempt < 30; attempt++ { + if strings.HasPrefix(device, "/dev/disk/") { + if _, err := os.Lstat(device); err == nil { + return nil + } + } else { + devName := filepath.Base(device) + if _, err := os.Stat(filepath.Join("/sys/block", devName)); err == nil { + return nil + } + } + time.Sleep(2 * time.Second) + } + return fmt.Errorf("device %s not available after 60s", device) +} + +func formatAndMount(device, mountPoint, fsType string) error { + if fsType == "" { + fsType = "ext4" + } + if !allowedFSTypes[fsType] { + return fmt.Errorf("unsupported filesystem type %q", fsType) + } + + if alreadyMounted, err := mountinfo.Mounted(mountPoint); err == nil && alreadyMounted { + logger.Printf("Mount point %s is already mounted, treating as success", mountPoint) + return nil + } + + // Try mounting first — if the disk already has a valid filesystem, this + // avoids any risk of accidentally reformatting it. + mountCmd := exec.Command("mount", "-t", fsType, device, mountPoint) + if mountOut, mountErr := mountCmd.CombinedOutput(); mountErr == nil { + logger.Printf("Mounted existing filesystem on %s at %s (type=%s)", device, mountPoint, fsType) + return nil + } else { + logger.Printf("Initial mount of %s failed (expected for new disks): %s", device, strings.TrimSpace(string(mountOut))) + } + + // Mount failed — check if there's any filesystem using blkid with retries. + needsFormat := false + for attempt := 0; attempt < 3; attempt++ { + out, err := exec.Command("blkid", "-p", device).CombinedOutput() + outStr := strings.TrimSpace(string(out)) + if err == nil && len(outStr) > 0 { + autoMount := exec.Command("mount", device, mountPoint) + if autoOut, autoErr := autoMount.CombinedOutput(); autoErr == nil { + logger.Printf("Mounted %s at %s (auto-detected type from: %s)", device, mountPoint, outStr) + return nil + } else { + return fmt.Errorf("device %s has filesystem signature (%s) but mount failed: %s", device, outStr, strings.TrimSpace(string(autoOut))) + } + } + if err != nil { + // Exit code 2 = no valid filesystem found (safe to format) + if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() == 2 { + needsFormat = true + break + } + logger.Printf("blkid attempt %d for %s failed: %v (output: %s)", attempt+1, device, err, outStr) + time.Sleep(2 * time.Second) + continue + } + // err == nil but empty output — ambiguous state, retry + logger.Printf("blkid returned empty output for %s (attempt %d), retrying...", device, attempt+1) + time.Sleep(2 * time.Second) + continue + } + + if !needsFormat { + return fmt.Errorf("cannot determine filesystem state of %s after retries; refusing to format to protect data", device) + } + + logger.Printf("No filesystem on %s, formatting as %s", device, fsType) + mkfsCmd := exec.Command("mkfs."+fsType, device) + if mkfsOut, mkfsErr := mkfsCmd.CombinedOutput(); mkfsErr != nil { + return fmt.Errorf("mkfs.%s on %s failed: %s: %w", fsType, device, strings.TrimSpace(string(mkfsOut)), mkfsErr) + } + logger.Printf("Formatted %s as %s", device, fsType) + + mountCmd2 := exec.Command("mount", "-t", fsType, device, mountPoint) + if mountOut, mountErr := mountCmd2.CombinedOutput(); mountErr != nil { + return fmt.Errorf("mount after format %s -> %s failed: %s: %w", device, mountPoint, strings.TrimSpace(string(mountOut)), mountErr) + } + logger.Printf("Mounted %s at %s (type=%s)", device, mountPoint, fsType) + return nil +} diff --git a/src/cloud-api-adaptor/pkg/forwarder/interceptor/interceptor_test.go b/src/cloud-api-adaptor/pkg/forwarder/interceptor/interceptor_test.go index b116e60358..970f78be4f 100644 --- a/src/cloud-api-adaptor/pkg/forwarder/interceptor/interceptor_test.go +++ b/src/cloud-api-adaptor/pkg/forwarder/interceptor/interceptor_test.go @@ -7,8 +7,10 @@ import ( "context" "os" "path/filepath" + "strings" "testing" + "github.com/confidential-containers/cloud-api-adaptor/src/cloud-api-adaptor/pkg/util" pb "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/agent/protocols/grpc" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/stretchr/testify/assert" @@ -955,6 +957,271 @@ func TestInterceptorWithComplexAnnotations(t *testing.T) { }) } +func TestCloudVolumesJSONParseError(t *testing.T) { + t.Run("corrupt JSON annotation returns error instead of silently skipping", func(t *testing.T) { + mock := &mockRedirector{} + i := &interceptor{ + Redirector: mock, + nsPath: "/run/netns/podns", + } + + req := &pb.CreateContainerRequest{ + ContainerId: "test-container", + OCI: &pb.Spec{ + Linux: &pb.Linux{ + Namespaces: []*pb.LinuxNamespace{}, + }, + Annotations: map[string]string{ + util.CloudVolumesAnnotationKey: "{invalid json", + }, + }, + } + + ctx := context.Background() + _, err := i.CreateContainer(ctx, req) + assert.Error(t, err) + assert.Contains(t, err.Error(), "corrupt cloud_volumes annotation") + assert.False(t, mock.createContainerCalled) + }) + + t.Run("missing mount_point field returns error", func(t *testing.T) { + mock := &mockRedirector{} + i := &interceptor{ + Redirector: mock, + nsPath: "/run/netns/podns", + } + + req := &pb.CreateContainerRequest{ + ContainerId: "test-container", + OCI: &pb.Spec{ + Linux: &pb.Linux{ + Namespaces: []*pb.LinuxNamespace{}, + }, + Annotations: map[string]string{ + util.CloudVolumesAnnotationKey: `{"vol-0":{"fs_type":"ext4","lun":"0"}}`, + }, + }, + } + + ctx := context.Background() + _, err := i.CreateContainer(ctx, req) + assert.Error(t, err) + assert.Contains(t, err.Error(), "missing required mount_point or lun") + }) + + t.Run("unsupported filesystem type returns error", func(t *testing.T) { + mock := &mockRedirector{} + i := &interceptor{ + Redirector: mock, + nsPath: "/run/netns/podns", + } + + req := &pb.CreateContainerRequest{ + ContainerId: "test-container", + OCI: &pb.Spec{ + Linux: &pb.Linux{ + Namespaces: []*pb.LinuxNamespace{}, + }, + Annotations: map[string]string{ + util.CloudVolumesAnnotationKey: `{"vol-0":{"mount_point":"/data","fs_type":"ntfs","lun":"0"}}`, + }, + }, + } + + ctx := context.Background() + _, err := i.CreateContainer(ctx, req) + assert.Error(t, err) + assert.Contains(t, err.Error(), "unsupported filesystem type") + }) + + t.Run("unsafe volume name returns error", func(t *testing.T) { + mock := &mockRedirector{} + i := &interceptor{ + Redirector: mock, + nsPath: "/run/netns/podns", + } + + req := &pb.CreateContainerRequest{ + ContainerId: "test-container", + OCI: &pb.Spec{ + Linux: &pb.Linux{ + Namespaces: []*pb.LinuxNamespace{}, + }, + Annotations: map[string]string{ + util.CloudVolumesAnnotationKey: `{"../escape":{"mount_point":"/data","fs_type":"ext4","lun":"0"}}`, + }, + }, + } + + ctx := context.Background() + _, err := i.CreateContainer(ctx, req) + assert.Error(t, err) + assert.Contains(t, err.Error(), "unsafe name") + }) + + t.Run("invalid lun value returns error", func(t *testing.T) { + mock := &mockRedirector{} + i := &interceptor{ + Redirector: mock, + nsPath: "/run/netns/podns", + } + + req := &pb.CreateContainerRequest{ + ContainerId: "test-container", + OCI: &pb.Spec{ + Linux: &pb.Linux{ + Namespaces: []*pb.LinuxNamespace{}, + }, + Annotations: map[string]string{ + util.CloudVolumesAnnotationKey: `{"vol-0":{"mount_point":"/data","fs_type":"ext4","lun":"abc"}}`, + }, + }, + } + + ctx := context.Background() + _, err := i.CreateContainer(ctx, req) + assert.Error(t, err) + assert.Contains(t, err.Error(), "invalid lun") + }) + + t.Run("valid cloud_volumes without device does not panic", func(t *testing.T) { + mock := &mockRedirector{} + i := &interceptor{ + Redirector: mock, + nsPath: "/run/netns/podns", + } + + req := &pb.CreateContainerRequest{ + ContainerId: "test-container", + OCI: &pb.Spec{ + Linux: &pb.Linux{ + Namespaces: []*pb.LinuxNamespace{}, + }, + Annotations: map[string]string{ + util.CloudVolumesAnnotationKey: `{"vol-0":{"mount_point":"/data","fs_type":"ext4","lun":"0","disk_id":"vol-fake"}}`, + }, + }, + } + + ctx := context.Background() + _, err := i.CreateContainer(ctx, req) + // Will error because the device doesn't exist, but should not panic + assert.Error(t, err) + assert.Contains(t, err.Error(), "cloud volume vol-0") + }) + + t.Run("empty cloud_volumes annotation is ignored", func(t *testing.T) { + mock := &mockRedirector{} + i := &interceptor{ + Redirector: mock, + nsPath: "/run/netns/podns", + } + + req := &pb.CreateContainerRequest{ + ContainerId: "test-container", + OCI: &pb.Spec{ + Linux: &pb.Linux{ + Namespaces: []*pb.LinuxNamespace{}, + }, + Annotations: map[string]string{ + util.CloudVolumesAnnotationKey: "", + }, + }, + } + + ctx := context.Background() + _, err := i.CreateContainer(ctx, req) + assert.NoError(t, err) + assert.True(t, mock.createContainerCalled) + }) +} + +func TestHasPartitions(t *testing.T) { + t.Run("returns false for nonexistent device", func(t *testing.T) { + assert.False(t, hasPartitions("nonexistent_device_xyz")) + }) + + t.Run("returns false when no partition entries exist", func(t *testing.T) { + tmpDir := t.TempDir() + devName := "sdtest" + sysBlockDir := filepath.Join(tmpDir, devName) + require.NoError(t, os.MkdirAll(sysBlockDir, 0o755)) + require.NoError(t, os.MkdirAll(filepath.Join(sysBlockDir, "queue"), 0o755)) + require.NoError(t, os.MkdirAll(filepath.Join(sysBlockDir, "power"), 0o755)) + // No sda1 etc. + // We can't directly test since hasPartitions reads from /sys/block, + // but we test the logic indirectly through mock paths + assert.False(t, hasPartitions("nonexistent_device_xyz")) + }) +} + +func TestIsRootOrMountedDevice(t *testing.T) { + t.Run("returns false for unknown device", func(t *testing.T) { + assert.False(t, isRootOrMountedDevice("zzznonexistent999")) + }) + + t.Run("returns true for root device if accessible", func(t *testing.T) { + data, err := os.ReadFile("/proc/mounts") + if err != nil { + t.Skip("cannot read /proc/mounts") + } + // Find actual root mount device + for _, line := range strings.Split(string(data), "\n") { + fields := strings.Fields(line) + if len(fields) >= 2 && fields[1] == "/" && strings.HasPrefix(fields[0], "/dev/") { + devName := strings.TrimPrefix(fields[0], "/dev/") + // Strip partition number (e.g., sda1 -> sda) + base := strings.TrimRight(devName, "0123456789") + assert.True(t, isRootOrMountedDevice(base), + "expected device %s (from %s) to be detected as mounted", base, fields[0]) + return + } + } + t.Skip("could not find root device in /proc/mounts") + }) +} + +func TestDetectCloudProvider(t *testing.T) { + provider := detectCloudProvider() + // On a regular test machine, this won't be a cloud provider + // We just verify it returns one of the known values + validProviders := map[string]bool{ + "azure": true, "aws": true, "libvirt": true, "generic": true, + } + assert.True(t, validProviders[provider], + "detectCloudProvider returned unexpected value: %s", provider) +} + +func TestFindLibvirtDataDisk(t *testing.T) { + t.Run("rejects out of range LUN index", func(t *testing.T) { + _, err := findLibvirtDataDisk(25) + assert.Error(t, err) + assert.Contains(t, err.Error(), "out of range") + }) + + t.Run("rejects negative LUN index", func(t *testing.T) { + _, err := findLibvirtDataDisk(-1) + assert.Error(t, err) + assert.Contains(t, err.Error(), "out of range") + }) + + t.Run("returns error when device does not exist", func(t *testing.T) { + // Unless /dev/vdb exists, this should fail + _, err := findLibvirtDataDisk(0) + if err != nil { + assert.Contains(t, err.Error(), "not found") + } + }) +} + +func TestFindDataDiskBySysfsHCTL(t *testing.T) { + t.Run("returns error for impossible LUN on test machine", func(t *testing.T) { + // LUN 999 should never exist + _, err := findDataDiskBySysfsHCTL(999) + assert.Error(t, err) + }) +} + func TestIsTargetPathEdgeCases(t *testing.T) { t.Run("handles paths with trailing slashes", func(t *testing.T) { assert.False(t, isTargetPath("/path/to/target/", "/path/to/target")) diff --git a/src/cloud-api-adaptor/pkg/util/cloud.go b/src/cloud-api-adaptor/pkg/util/cloud.go index 1e14dbec1d..7f59fb7dbb 100644 --- a/src/cloud-api-adaptor/pkg/util/cloud.go +++ b/src/cloud-api-adaptor/pkg/util/cloud.go @@ -1,11 +1,17 @@ package util import ( + b64 "encoding/base64" + "encoding/json" "fmt" + "log" + "os" + "path/filepath" "strconv" "strings" "github.com/confidential-containers/cloud-api-adaptor/src/cloud-api-adaptor/pkg/initdata" + provider "github.com/confidential-containers/cloud-api-adaptor/src/cloud-providers" cri "github.com/containerd/containerd/pkg/cri/annotations" hypannotations "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/annotations" ) @@ -114,3 +120,95 @@ func Contains(slice []string, s string) bool { } return false } + +var logger = log.New(log.Writer(), "[util/cloud] ", log.LstdFlags|log.Lmsgprefix) + +type mountInfoJSON struct { + VolumeType string `json:"volume-type"` + Device string `json:"device"` + FsType string `json:"fstype"` + Metadata map[string]string `json:"metadata"` + Options []string `json:"options"` +} + +var KataDirectVolumesDir = "/run/kata-containers/shared/direct-volumes" + +const CSIPluginEscapeQualifiedName = "kubernetes.io~csi" + +const CloudVolumesAnnotationKey = "io.confidentialcontainers.org.cloud_volumes" + +// CloudVolumeAnnotation is the schema for each entry in the cloud_volumes +// annotation. It is serialized by the proxy and deserialized by the interceptor. +type CloudVolumeAnnotation struct { + MountPoint string `json:"mount_point"` + FSType string `json:"fs_type"` + LUN string `json:"lun"` + DiskID string `json:"disk_id"` + FSGroup string `json:"fs_group,omitempty"` +} + +// GetCSIVolumesForPod scans the shared direct-volumes directory for +// mountInfo.json files written by the CSI block driver. Each file +// describes a cloud volume that should be attached to the PodVM. +// Volumes are filtered by pod UID (from annotations) to prevent +// cross-pod volume leakage on multi-tenant nodes. +func GetCSIVolumesForPod(annotations map[string]string) []provider.CloudVolume { + var volumes []provider.CloudVolume + + podUID := annotations[cri.SandboxUID] + + entries, err := os.ReadDir(KataDirectVolumesDir) + if err != nil { + return nil + } + + for _, entry := range entries { + if !entry.IsDir() { + continue + } + + decodedPath, err := b64.URLEncoding.DecodeString(entry.Name()) + if err != nil { + continue + } + decodedStr := string(decodedPath) + + if !strings.Contains(decodedStr, "/volumes/"+CSIPluginEscapeQualifiedName+"/") { + continue + } + + if podUID != "" && !strings.Contains(decodedStr, "/pods/"+podUID+"/") { + continue + } + + mountInfoPath := filepath.Join(KataDirectVolumesDir, entry.Name(), "mountInfo.json") + data, err := os.ReadFile(mountInfoPath) + if err != nil { + continue + } + + var info mountInfoJSON + if err := json.Unmarshal(data, &info); err != nil { + logger.Printf("WARNING: invalid mountInfo.json in %s: %v", entry.Name(), err) + continue + } + + volPath := info.Device + if info.Metadata != nil { + if cp, ok := info.Metadata["cloud-volume-path"]; ok && cp != "" { + volPath = cp + } + } + + if volPath == "" { + logger.Printf("WARNING: no disk ID in mountInfo.json for %s", decodedStr) + continue + } + + volumes = append(volumes, provider.CloudVolume{ + DiskID: volPath, + }) + } + + return volumes +} diff --git a/src/cloud-api-adaptor/pkg/util/cloud_csi_test.go b/src/cloud-api-adaptor/pkg/util/cloud_csi_test.go new file mode 100644 index 0000000000..8eeaffdb56 --- /dev/null +++ b/src/cloud-api-adaptor/pkg/util/cloud_csi_test.go @@ -0,0 +1,227 @@ +package util + +import ( + b64 "encoding/base64" + "encoding/json" + "fmt" + "os" + "path/filepath" + "testing" + + provider "github.com/confidential-containers/cloud-api-adaptor/src/cloud-providers" + cri "github.com/containerd/containerd/pkg/cri/annotations" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func setupDirectVolumesDir(t *testing.T) string { + t.Helper() + dir := t.TempDir() + origDir := KataDirectVolumesDir + + t.Cleanup(func() { + KataDirectVolumesDir = origDir + }) + KataDirectVolumesDir = dir + return dir +} + +func writeMountInfo(t *testing.T, dir, volumePath string, info map[string]interface{}) { + t.Helper() + encoded := b64.URLEncoding.EncodeToString([]byte(volumePath)) + volDir := filepath.Join(dir, encoded) + require.NoError(t, os.MkdirAll(volDir, 0o755)) + + data, err := json.Marshal(info) + require.NoError(t, err) + require.NoError(t, os.WriteFile(filepath.Join(volDir, "mountInfo.json"), data, 0o644)) +} + +func TestGetCSIVolumesForPod_EmptyDirectory(t *testing.T) { + setupDirectVolumesDir(t) + volumes := GetCSIVolumesForPod(map[string]string{}) + assert.Nil(t, volumes) +} + +func TestGetCSIVolumesForPod_NonExistentDirectory(t *testing.T) { + origDir := KataDirectVolumesDir + KataDirectVolumesDir = "/nonexistent/path" + defer func() { KataDirectVolumesDir = origDir }() + + volumes := GetCSIVolumesForPod(map[string]string{}) + assert.Nil(t, volumes) +} + +func TestGetCSIVolumesForPod_SingleVolume(t *testing.T) { + dir := setupDirectVolumesDir(t) + volPath := "/var/lib/kubelet/pods/pod-uid-123/volumes/kubernetes.io~csi/pvc-abc/mount" + + writeMountInfo(t, dir, volPath, map[string]interface{}{ + "device": "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/disks/csi-vol-pvc-abc", + "fstype": "ext4", + }) + + volumes := GetCSIVolumesForPod(map[string]string{}) + require.Len(t, volumes, 1) + assert.Equal(t, "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/disks/csi-vol-pvc-abc", volumes[0].DiskID) +} + +func TestGetCSIVolumesForPod_CloudVolumePathTakesPrecedence(t *testing.T) { + dir := setupDirectVolumesDir(t) + volPath := "/var/lib/kubelet/pods/pod-uid-123/volumes/kubernetes.io~csi/pvc-abc/mount" + + writeMountInfo(t, dir, volPath, map[string]interface{}{ + "device": "fallback-device", + "fstype": "ext4", + "metadata": map[string]interface{}{ + "cloud-volume-path": "/subscriptions/sub1/disks/preferred-disk", + }, + }) + + volumes := GetCSIVolumesForPod(map[string]string{}) + require.Len(t, volumes, 1) + assert.Equal(t, "/subscriptions/sub1/disks/preferred-disk", volumes[0].DiskID) +} + +func TestGetCSIVolumesForPod_PodUIDFiltering(t *testing.T) { + dir := setupDirectVolumesDir(t) + + writeMountInfo(t, dir, + "/var/lib/kubelet/pods/pod-uid-AAA/volumes/kubernetes.io~csi/pvc-1/mount", + map[string]interface{}{"device": "disk-A", "fstype": "ext4"}) + + writeMountInfo(t, dir, + "/var/lib/kubelet/pods/pod-uid-BBB/volumes/kubernetes.io~csi/pvc-2/mount", + map[string]interface{}{"device": "disk-B", "fstype": "ext4"}) + + writeMountInfo(t, dir, + "/var/lib/kubelet/pods/pod-uid-AAA/volumes/kubernetes.io~csi/pvc-3/mount", + map[string]interface{}{"device": "disk-C", "fstype": "ext4"}) + + annotations := map[string]string{ + cri.SandboxUID: "pod-uid-AAA", + } + volumes := GetCSIVolumesForPod(annotations) + require.Len(t, volumes, 2) + + diskIDs := []string{volumes[0].DiskID, volumes[1].DiskID} + assert.Contains(t, diskIDs, "disk-A") + assert.Contains(t, diskIDs, "disk-C") + assert.NotContains(t, diskIDs, "disk-B") +} + +func TestGetCSIVolumesForPod_EmptyPodUIDReturnsAll(t *testing.T) { + dir := setupDirectVolumesDir(t) + + writeMountInfo(t, dir, + "/var/lib/kubelet/pods/pod-uid-AAA/volumes/kubernetes.io~csi/pvc-1/mount", + map[string]interface{}{"device": "disk-A", "fstype": "ext4"}) + + writeMountInfo(t, dir, + "/var/lib/kubelet/pods/pod-uid-BBB/volumes/kubernetes.io~csi/pvc-2/mount", + map[string]interface{}{"device": "disk-B", "fstype": "ext4"}) + + volumes := GetCSIVolumesForPod(map[string]string{}) + assert.Len(t, volumes, 2) +} + +func TestGetCSIVolumesForPod_SkipsNonCSIVolumes(t *testing.T) { + dir := setupDirectVolumesDir(t) + + writeMountInfo(t, dir, + "/var/lib/kubelet/pods/pod-uid-123/volumes/kubernetes.io~csi/pvc-1/mount", + map[string]interface{}{"device": "csi-disk", "fstype": "ext4"}) + + writeMountInfo(t, dir, + "/var/lib/kubelet/pods/pod-uid-123/volumes/kubernetes.io~configmap/config/mount", + map[string]interface{}{"device": "configmap-device", "fstype": "ext4"}) + + volumes := GetCSIVolumesForPod(map[string]string{}) + require.Len(t, volumes, 1) + assert.Equal(t, "csi-disk", volumes[0].DiskID) +} + +func TestGetCSIVolumesForPod_SkipsMissingDiskID(t *testing.T) { + dir := setupDirectVolumesDir(t) + + writeMountInfo(t, dir, + "/var/lib/kubelet/pods/pod-uid-123/volumes/kubernetes.io~csi/pvc-no-disk/mount", + map[string]interface{}{"fstype": "ext4"}) + + volumes := GetCSIVolumesForPod(map[string]string{}) + assert.Empty(t, volumes) +} + +func TestGetCSIVolumesForPod_SkipsInvalidJSON(t *testing.T) { + dir := setupDirectVolumesDir(t) + volPath := "/var/lib/kubelet/pods/pod-uid-123/volumes/kubernetes.io~csi/pvc-bad/mount" + encoded := b64.URLEncoding.EncodeToString([]byte(volPath)) + volDir := filepath.Join(dir, encoded) + require.NoError(t, os.MkdirAll(volDir, 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(volDir, "mountInfo.json"), []byte("{invalid json"), 0o644)) + + volumes := GetCSIVolumesForPod(map[string]string{}) + assert.Empty(t, volumes) +} + +func TestGetCSIVolumesForPod_SkipsNonBase64Directories(t *testing.T) { + dir := setupDirectVolumesDir(t) + + require.NoError(t, os.MkdirAll(filepath.Join(dir, "not-base64-encoded"), 0o755)) + + writeMountInfo(t, dir, + "/var/lib/kubelet/pods/pod-uid-123/volumes/kubernetes.io~csi/pvc-ok/mount", + map[string]interface{}{"device": "good-disk", "fstype": "ext4"}) + + volumes := GetCSIVolumesForPod(map[string]string{}) + require.Len(t, volumes, 1) + assert.Equal(t, "good-disk", volumes[0].DiskID) +} + +func TestGetCSIVolumesForPod_SkipsRegularFiles(t *testing.T) { + dir := setupDirectVolumesDir(t) + + volPath := "/var/lib/kubelet/pods/pod-uid-123/volumes/kubernetes.io~csi/pvc-file/mount" + encoded := b64.URLEncoding.EncodeToString([]byte(volPath)) + require.NoError(t, os.WriteFile(filepath.Join(dir, encoded), []byte("not a dir"), 0o644)) + + volumes := GetCSIVolumesForPod(map[string]string{}) + assert.Empty(t, volumes) +} + +func TestGetCSIVolumesForPod_MultipleVolumesCanonicalOrder(t *testing.T) { + dir := setupDirectVolumesDir(t) + + paths := []string{ + "/var/lib/kubelet/pods/pod-uid-123/volumes/kubernetes.io~csi/pvc-charlie/mount", + "/var/lib/kubelet/pods/pod-uid-123/volumes/kubernetes.io~csi/pvc-alpha/mount", + "/var/lib/kubelet/pods/pod-uid-123/volumes/kubernetes.io~csi/pvc-bravo/mount", + } + + for i, p := range paths { + writeMountInfo(t, dir, p, map[string]interface{}{ + "device": fmt.Sprintf("disk-%d", i), + "fstype": "ext4", + }) + } + + volumes := GetCSIVolumesForPod(map[string]string{}) + require.Len(t, volumes, 3) + + for i := 0; i < len(volumes)-1; i++ { + assert.NotEqual(t, volumes[i].DiskID, volumes[i+1].DiskID, + "volumes should be distinct") + } +} + +func TestGetCSIVolumesForPod_ReturnType(t *testing.T) { + dir := setupDirectVolumesDir(t) + + writeMountInfo(t, dir, + "/var/lib/kubelet/pods/pod-uid-123/volumes/kubernetes.io~csi/pvc-test/mount", + map[string]interface{}{"device": "test-disk", "fstype": "ext4"}) + + volumes := GetCSIVolumesForPod(map[string]string{}) + require.Len(t, volumes, 1) + assert.IsType(t, provider.CloudVolume{}, volumes[0]) +} diff --git a/src/cloud-api-adaptor/podvm-mkosi/Dockerfile.podvm_docker_provider b/src/cloud-api-adaptor/podvm-mkosi/Dockerfile.podvm_docker_provider deleted file mode 100644 index 2dca7d83e1..0000000000 --- a/src/cloud-api-adaptor/podvm-mkosi/Dockerfile.podvm_docker_provider +++ /dev/null @@ -1,59 +0,0 @@ -# Adapted from https://github.com/kubernetes-sigs/kind/blob/main/images/base/Dockerfile - -ARG BASE_IMAGE=registry.fedoraproject.org/fedora:43 - -FROM $BASE_IMAGE AS iptables - -COPY ./versions.yaml /versions.yaml - -RUN echo "Building iptables-wrapper... " \ - && dnf install -y git golang \ - && git clone https://github.com/kubernetes-sigs/iptables-wrappers.git /iptables \ - && cd /iptables \ - && version=$(grep 'iptables-wrapper:' /versions.yaml | awk '{print $2}') \ - && git checkout $version \ - && make BIN_DIR=. build - -FROM $BASE_IMAGE AS base - -RUN echo "Installing Packages ..." \ - && dnf install -y \ - systemd \ - conntrack iptables iptables-legacy iptables-nft nftables iproute ethtool util-linux kmod \ - libseccomp pigz fuse-overlayfs \ - nfs-utils which systemd-pam \ - bash ca-certificates curl jq procps \ - && rm -f /lib/systemd/system/multi-user.target.wants/* \ - && rm -f /etc/systemd/system/*.wants/* \ - && rm -f /lib/systemd/system/local-fs.target.wants/* \ - && rm -f /lib/systemd/system/sockets.target.wants/*udev* \ - && rm -f /lib/systemd/system/sockets.target.wants/*initctl* \ - && rm -f /lib/systemd/system/basic.target.wants/* \ - && echo "ReadKMsg=no" >> /etc/systemd/journald.conf - -RUN echo "Enabling / Disabling services ... " \ - && systemctl mask systemd-binfmt.service \ - && systemctl enable systemd-logind dbus.socket - -RUN --mount=type=cache,target=/iptables,from=iptables,source=/iptables,readonly \ - cp /iptables/iptables-wrapper /usr/sbin \ - && /usr/sbin/iptables-wrapper install - -# Add podvm resources -COPY ./podvm-mkosi/resources/binaries-tree/etc/ /etc/ -COPY ./podvm-mkosi/resources/binaries-tree/usr/ /usr/ -COPY ./podvm-mkosi/resources/binaries-tree/pause_bundle/ /pause_bundle/ - -RUN curl -LO https://raw.githubusercontent.com/confidential-containers/cloud-api-adaptor/main/src/cloud-api-adaptor/podvm/qcow2/misc-settings.sh - -RUN PODVM_DISTRO=ubuntu CLOUD_PROVIDER=generic bash ./misc-settings.sh - -COPY --chmod=0755 ./podvm-mkosi/entrypoint.sh /usr/local/bin/ - -# https://systemd.io/CONTAINER_INTERFACE/ -ENV container=docker - -# systemd exits on SIGRTMIN+3, not SIGTERM (which re-executes it) -# https://bugzilla.redhat.com/show_bug.cgi?id=1201657 -STOPSIGNAL SIGRTMIN+3 -ENTRYPOINT [ "/usr/local/bin/entrypoint.sh", "/sbin/init" ] diff --git a/src/cloud-api-adaptor/podvm-mkosi/Makefile b/src/cloud-api-adaptor/podvm-mkosi/Makefile deleted file mode 100644 index 791965af60..0000000000 --- a/src/cloud-api-adaptor/podvm-mkosi/Makefile +++ /dev/null @@ -1,224 +0,0 @@ -include ../Makefile.defaults - -SE_BOOT ?= false -IS_DEBIAN := $(shell if grep -q 'ID_LIKE=debian' /etc/os-release; then echo "true"; else echo "false"; fi) - -REGISTRY ?= quay.io/confidential-containers -PODVM_DISTRO ?= fedora -ifeq ($(PODVM_TAG),) - PODVM_TAG := $(VERSIONS_HASH) -endif -PODVM_BUILDER_IMAGE ?= $(REGISTRY)/podvm-builder-$(PODVM_DISTRO)-$(DISTRO_ARCH):$(PODVM_TAG) -PODVM_BINARIES_IMAGE ?= $(REGISTRY)/podvm-binaries-$(PODVM_DISTRO)-$(DISTRO_ARCH):$(PODVM_TAG) -PODVM_IMAGE ?= $(REGISTRY)/podvm-generic-$(PODVM_DISTRO)$(if $(filter $(SE_BOOT),true),-se,)-$(DISTRO_ARCH):$(PODVM_TAG) -PODVM_CONTAINER_NAME ?= $(REGISTRY)/podvm-docker-image-$(DISTRO_ARCH) -PODVM_BYOM_E2E_CONTAINER_NAME ?= $(REGISTRY)/podvm-byom-e2e-image-$(DISTRO_ARCH) -VERIFY_PROVENANCE ?= no -MKOSI_VERSION ?= v26 - -_SHA := $(shell git rev-parse --short HEAD) -_SHA_DIRTY := $(shell [ -n "$$(git status --porcelain 2>/dev/null)" ] && printf -- '-dirty') -IMAGE_VERSION ?= $(_SHA)$(_SHA_DIRTY) - -export IMAGE_VERSION -export MKOSI_VERSION - -.DEFAULT_GOAL := all -.PHONY: all -all: binaries image - -.PHONY: debug -debug:binaries image-debug - -.PHONY: sftp -sftp:binaries image-sftp - -.PHONY: container -container: binaries image-container - -.PHONY: byom-e2e-container -byom-e2e-container: image-byom-e2e-container - -ifeq ($(PODVM_DISTRO),ubuntu) -DISTRO_NAME := ubuntu -DISTRO_IMAGE := ubuntu:24.04 -DISTRO_RELEASE := noble -DISTRO_REPOS := --repositories=universe -PKG_INSTALL := apt-get update && apt-get install -y debian-archive-keyring -else -DISTRO_NAME := fedora -DISTRO_IMAGE := fedora:43 -DISTRO_RELEASE := 43 -DISTRO_REPOS := -PKG_INSTALL := dnf install -y distribution-gpg-keys libseccomp -endif - -define run_mkosi_in_container - docker run --rm --privileged \ - -v "$(shell pwd)":/workspace \ - -w /workspace \ - $(DISTRO_IMAGE) \ - bash -c "$(PKG_INSTALL) bubblewrap coreutils git python3; mkdir -p /build; git clone -b $(MKOSI_VERSION) https://github.com/systemd/mkosi /build/mkosi; export PATH="/build/mkosi/bin:$$PATH"; mkosi --tools-tree=default --tools-tree-release=$(DISTRO_RELEASE) --tools-tree-distribution=$(DISTRO_NAME) --distribution=$(DISTRO_NAME) --release=$(DISTRO_RELEASE) $(DISTRO_REPOS) $(1)" -endef - -binaries: - @echo "Building binaries..." - rm -rf ./resources/binaries-tree -ifeq ($(IS_DEBIAN),true) - docker buildx use default -endif - docker buildx build \ - -t $(PODVM_BINARIES_IMAGE) \ - --progress=plain \ - --build-arg GO_VERSION=$(GO_VERSION) \ - --build-arg ARCH=$(ARCH) \ - --build-arg GO_ARCH=$(GO_ARCH) \ - --build-arg DISTRO_ARCH=$(DISTRO_ARCH) \ - --build-arg PROTOC_VERSION=$(PROTOC_VERSION) \ - --build-arg PROTOC_ARCH=$(PROTOC_ARCH) \ - --build-arg YQ_VERSION=$(YQ_VERSION) \ - --build-arg YQ_CHECKSUM=$(YQ_CHECKSUM_$(YQ_ARCH)) \ - --build-arg YQ_ARCH=$(YQ_ARCH) \ - --build-arg ORAS_VERSION=$(ORAS_VERSION) \ - --build-arg TEE_PLATFORM=$(TEE_PLATFORM) \ - --build-arg PAUSE_REPO=$(PAUSE_REPO) \ - --build-arg PAUSE_VERSION=$(PAUSE_VERSION) \ - --build-arg PAUSE_BIN=$(PAUSE_BIN) \ - --build-arg IMAGE_NAME=mkosi-podvm-binaries \ - --build-arg VERIFY_PROVENANCE=$(VERIFY_PROVENANCE) \ - $(if $(AUTHFILE),--build-arg AUTHFILE=$(AUTHFILE),) \ - $(if $(DEFAULT_AGENT_POLICY_FILE),--build-arg DEFAULT_AGENT_POLICY_FILE=$(DEFAULT_AGENT_POLICY_FILE),) \ - $(if $(filter $(PUSH),true),,-o type=local,dest="./resources/binaries-tree") \ - $(DOCKER_OPTS) \ - -f ../podvm/Dockerfile.podvm_binaries.$(PODVM_DISTRO) ../../ - -PHONY: insecure-builder -insecure-builder: - docker buildx ls | grep "^insecure-builder" || docker buildx create \ - --driver-opt image=moby/buildkit:master \ - --name insecure-builder \ - --buildkitd-flags '--allow-insecure-entitlement security.insecure' - -PHONY: image -image: insecure-builder - rm -rf ./build - @echo "Building image..." -ifeq ($(SE_BOOT),true) - $(call run_mkosi_in_container,--profile production --image system) - sudo -E ../hack/build-s390x-se-image.sh -else ifeq ($(ARCH),s390x) - $(call run_mkosi_in_container,--profile production --image system) - sudo -E ../hack/build-s390x-image.sh -else - mkdir -p build - docker buildx build \ - -f Dockerfile.mkosi$(if $(filter $(PODVM_DISTRO),ubuntu),.ubuntu,) \ - -t $(PODVM_IMAGE) \ - --build-arg PROFILE=production \ - --build-arg IMAGE_VERSION \ - --build-arg MKOSI_VERSION \ - --progress=plain \ - -o type=local,dest=./build \ - --builder insecure-builder \ - --allow security.insecure \ - . - qemu-img convert -f raw -O qcow2 build/system.raw build/podvm-$(PODVM_DISTRO)-$(DISTRO_ARCH).qcow2 -endif - -PHONY: image-debug -image-debug: insecure-builder - rm -rf ./build - @echo "Building debug image..." -ifeq ($(SE_BOOT),true) - $(call run_mkosi_in_container,--profile debug) - sudo -E ../hack/build-s390x-se-image.sh -else ifeq ($(ARCH),s390x) - $(call run_mkosi_in_container,--profile debug) - sudo -E ../hack/build-s390x-image.sh -else - - mkdir -p build - docker buildx build \ - -f Dockerfile.mkosi$(if $(filter $(PODVM_DISTRO),ubuntu),.ubuntu,) \ - -t $(PODVM_IMAGE) \ - --build-arg PROFILE=debug \ - --build-arg IMAGE_VERSION \ - --build-arg MKOSI_VERSION \ - --progress=plain \ - -o type=local,dest=./build \ - --builder insecure-builder \ - --allow security.insecure \ - . - qemu-img convert -f raw -O qcow2 build/system.raw build/podvm-$(PODVM_DISTRO)-$(DISTRO_ARCH).qcow2 -endif - -PHONY: image-sftp -image-sftp: insecure-builder - rm -rf ./build - @echo "Building sftp image..." -ifeq ($(SE_BOOT),true) - $(call run_mkosi_in_container,--profile sftp) - sudo -E ../hack/build-s390x-se-image.sh -else ifeq ($(ARCH),s390x) - $(call run_mkosi_in_container,--profile sftp) - sudo -E ../hack/build-s390x-image.sh -else - mkdir -p build - docker buildx build \ - -f Dockerfile.mkosi$(if $(filter $(PODVM_DISTRO),ubuntu),.ubuntu,) \ - -t $(PODVM_IMAGE) \ - --build-arg PROFILE=sftp \ - --build-arg IMAGE_VERSION \ - --build-arg MKOSI_VERSION \ - --progress=plain \ - -o type=local,dest=./build \ - --builder insecure-builder \ - --allow security.insecure \ - . - qemu-img convert -f raw -O qcow2 build/system.raw build/podvm-$(PODVM_DISTRO)-$(DISTRO_ARCH).qcow2 -endif - -PHONY: image-container -image-container: - @echo "Building podvm container image..." - cd .. && docker buildx build \ - -t $(PODVM_CONTAINER_NAME):$(PODVM_TAG) \ - -t $(PODVM_CONTAINER_NAME):latest \ - -f podvm-mkosi/Dockerfile.podvm_docker_provider$(if $(filter $(PODVM_DISTRO),ubuntu),.ubuntu,) . - -.PHONY: image-byom-e2e-container -image-byom-e2e-container: - @echo "Building BYOM podvm container image with SSH support..." - docker buildx build \ - -t $(PODVM_BYOM_E2E_CONTAINER_NAME):$(PODVM_TAG) \ - -t $(PODVM_BYOM_E2E_CONTAINER_NAME):latest \ - --build-arg BASE_IMAGE=$(PODVM_CONTAINER_NAME):latest \ - -f Dockerfile.podvm_byom_docker_provider$(if $(filter $(PODVM_DISTRO),ubuntu),.ubuntu,) . - -PHONY: push-image -push-image: - @echo "Push podvm image to remote repository..." -ifeq ($(SE_BOOT),true) - docker push $(PODVM_NAME)-se:$(PODVM_TAG) - docker push $(PODVM_NAME)-se:latest -else - docker push $(PODVM_NAME):$(PODVM_TAG) - docker push $(PODVM_NAME):latest -endif - -PHONY: push-image-container -push-image-container: - @echo "Push podvm container image to remote repository..." - docker push $(PODVM_CONTAINER_NAME):$(PODVM_TAG) - docker push $(PODVM_CONTAINER_NAME):latest - -.PHONY: push-image-byom-e2e-container -push-image-byom-e2e-container: - @echo "Push BYOM podvm container image to remote repository..." - docker push $(PODVM_BYOM_E2E_CONTAINER_NAME):$(PODVM_TAG) - docker push $(PODVM_BYOM_E2E_CONTAINER_NAME):latest - -PHONY: clean -clean: - rm -rf ./build - rm -rf ./resources/binaries-tree diff --git a/src/cloud-api-adaptor/podvm-mkosi/README.md b/src/cloud-api-adaptor/podvm-mkosi/README.md deleted file mode 100644 index ddc6a7e30b..0000000000 --- a/src/cloud-api-adaptor/podvm-mkosi/README.md +++ /dev/null @@ -1,171 +0,0 @@ -# Podvm images with mkosi - -[mkosi](https://github.com/systemd/mkosi) builds a bootable OS image from scratch. This way, we have full control over every detail of the image, especially over the image format and boot process. On the long run, we will implement fully, bit-by-bit reproducible images with mkosi, and use measured boot and an immutable root FS to ensure the image integrity through remote attestation. - -## Prerequisites - -There are various ways to install mkosi documented on the [project page](https://github.com/systemd/mkosi). Different distributions also package mkosi in their repositories, alongside the dependencies. - -Refer to the [CI workflow](../../../.github/workflows/podvm_mkosi.yaml) to see which additional tools are required to build an image. - -### Building the image - -```sh -make # this will rebuild the builder, the binaries and the OS image -``` - -> [!WARNING] -> The `make` (and `make binaries`) targets will remove all customization made -> in the `./resources/binaries-tree` dirs - -```sh -make image # this will only rebuild the pod VM image -``` - -The default built pod VM image have support for only filesystem attester. - -Set the `TEE_PLATFORM` variable to build the image with specific attester support. - -For example, this will build the pod VM image with support for SNP attester. - -```sh -TEE_PLATFORM=snp make image -``` - -Likewise `TEE_PLATFORM=az-cvm-vtpm` will enable support for Azure vTPM attesters. - -Refer to the following [doc](https://github.com/confidential-containers/guest-components/tree/main?tab=readme-ov-file#build) for accepted values of TEE_PLATFORM. - -#### Building Ubuntu 24.04 images - -By default, Fedora images are built. To build Ubuntu 24.04 images: - -```sh -PODVM_DISTRO=ubuntu make # rebuild builder, binaries and OS image for Ubuntu -``` - -```sh -PODVM_DISTRO=ubuntu make image # only rebuild OS image for Ubuntu -``` - -### Upload the image to the desired cloud provider - -You can upload the image with the tool of your choice, but the recommended way is using [uplosi](https://github.com/edgelesssys/uplosi). Follow the uplosi readme to configure your upload for the desired cloud provider. Then run: - -```sh -# Using -i and a imageVersionFile to increment the image version after the upload. -uplosi -i build/system.raw -``` - -If you want to use the image with libvirt, run the following to convert to qcow2 format: - -```sh -qemu-img convert -f raw -O qcow2 build/system.raw build/system.qcow2 -``` - -## Debug image - -There is a debug variant of the image that provides a specific configuration to debug things within -the podvm. It has additional packages installed that are commonly needed for debugging. -Further, the image has access through the serial console enabled, you can access it through the portal -of the cloud provider. - -```sh -make debug # this will rebuild the builder, the binaries and the OS image -``` - -```sh -make image-debug # this will only rebuild the OS image -``` - -Set `TEE_PLATFORM` as explained previously, to add support for specific attester in the debug image. - -For Ubuntu debug images: -```sh -PODVM_DISTRO=ubuntu make debug -``` - -Notice that building a debug image will overwrite any previous existing debug or production image. - -For using SSH, create a file `resources/authorized_keys` with your SSH public key. Ensure the permissions -are set to `0400` for the `authorized_keys` file. SSH access is only possible for the `root` user. - -## Testing the image - -To verify the podvm image is bootable and responsive before deploying to -cloud one can use a simple [smoketest](../podvm/hack/smoke_test.sh). It -uses libvirt to run the image and kata-agent-ctl to establish connection -to the podvm. - -> [!WARNING] -> This script is intended for CI testing, expects tools in-place and contains -> only a rough cleanup. Use it on a disposable machine only. -> -> The smoke-test won't deploy tls certificates so the image must be built with -> `-disable-tls`: -> -> mkdir -p ./resources/binaries-tree/etc/default -> echo "TLS_OPTIONS=-disable-tls" > ./resources/binaries-tree/etc/default/agent-protocol-forwarder -> -> Do not forget to re-enable it before sending the image to production. - -## Custom image configuration - -You can easily place additional files in `resources/binaries-tree` after it has been populated by the -`make binaries` step. Notice that systemd units need to be enabled in the presets and links in the tree -won't be copied into the image. You can use `./mkosi.postinst` script to create symlinks. - -If you want to add additional packages to the image, you can define distribution-specific config files: - -For Fedora (`mkosi.presets/system/mkosi.conf.d/fedora-extra.conf`): -```ini -[Match] -Distribution=fedora - -[Content] -Packages= - cowsay -``` - -For Ubuntu (`mkosi.presets/system/mkosi.conf.d/ubuntu-extra.conf`): -```ini -[Match] -Distribution=ubuntu - -[Content] -Packages= - cowsay -``` - -## Limitations - -The following limitations apply to these images. Notice that the limitations are intentional to -reduce complexity of configuration and CI and shall not be seen as open to-dos. - -- Deployed images cannot be customized with cloud-init. Runtime configuration data is retrieved - from IMDS via the project's `process-user-data` tool. - -## Build s390x image -We can use the mkosi **ToolsTree** feature defined in `mkosi.conf` to download latest tools automatically: -``` -[Runtime] -ToolsTree=default -``` -And install **mkosi** from the repository: -```sh -git clone -b v26 https://github.com/systemd/mkosi -ln -s $PWD/mkosi/bin/mkosi /usr/local/bin/mkosi -mkosi --version -``` -Another issue is s390x does not support UEFI. Instead, we can first use **mkosi** to build non-bootable system files, then use **zipl** to generate the bootloader and finally create the bootable image. - -It requires a **s390x host** to build s390x image with make commands: -``` -TEE_PLATFORM=se-attester make binaries -make image -# SE_BOOT=true make image -# make image-debug -# SE_BOOT=true make image-debug -``` - -The final output is `build/podvm-s390x.qcow2` or `build/podvm-s390x-se.qcow2`, which can be used as the Pod VM image in libvirt environment. diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.postinst b/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.postinst deleted file mode 100755 index 5aa6fd239b..0000000000 --- a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.postinst +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash - -set -euxo pipefail - -{ - echo "IMAGE_ID=\"podvm\"" - echo "IMAGE_VERSION=\"${IMAGE_VERSION-v0.0.0}\"" - echo "VARIANT_ID=\"${VARIANT_ID}\"" -} >> "${BUILDROOT}/etc/os-release" diff --git a/src/cloud-api-adaptor/podvm-mkosi/resources/.gitkeep b/src/cloud-api-adaptor/podvm-mkosi/resources/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/cloud-api-adaptor/podvm-mkosi/.gitignore b/src/cloud-api-adaptor/podvm/.gitignore similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/.gitignore rename to src/cloud-api-adaptor/podvm/.gitignore diff --git a/src/cloud-api-adaptor/podvm-mkosi/Dockerfile.mkosi b/src/cloud-api-adaptor/podvm/Dockerfile.mkosi similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/Dockerfile.mkosi rename to src/cloud-api-adaptor/podvm/Dockerfile.mkosi diff --git a/src/cloud-api-adaptor/podvm-mkosi/Dockerfile.mkosi.ubuntu b/src/cloud-api-adaptor/podvm/Dockerfile.mkosi.ubuntu similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/Dockerfile.mkosi.ubuntu rename to src/cloud-api-adaptor/podvm/Dockerfile.mkosi.ubuntu diff --git a/src/cloud-api-adaptor/podvm/Dockerfile.podvm b/src/cloud-api-adaptor/podvm/Dockerfile.podvm deleted file mode 100644 index 61af7677b2..0000000000 --- a/src/cloud-api-adaptor/podvm/Dockerfile.podvm +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright Confidential Containers Contributors -# -# SPDX-License-Identifier: Apache-2.0 -# -# Builds pod vm image inside container -# -ARG BUILDER_IMG="quay.io/confidential-containers/podvm-builder-ubuntu" - -# Override it via build-args for different platforms -ARG BINARIES_IMG="quay.io/confidential-containers/podvm-binaries-ubuntu-amd64" - -FROM ${BINARIES_IMG} AS podvm_binaries -FROM ${BUILDER_IMG} AS podvm_builder - -ARG CLOUD_PROVIDER -ARG SE_BOOT -ARG PODVM_DISTRO=ubuntu -ARG UEFI=false - -# If not provided, uses system architecture -ARG ARCH -ARG AUTHFILE -ARG PAUSE_REPO -ARG PAUSE_VERSION -ARG PAUSE_BIN - -ENV AUTHFILE=${AUTHFILE} -ENV PAUSE_REPO=${PAUSE_REPO} -ENV PAUSE_VERSION=${PAUSE_VERSION} -ENV PAUSE_BIN=${PAUSE_BIN} -ENV CLOUD_PROVIDER=${CLOUD_PROVIDER} -ENV PODVM_DISTRO=${PODVM_DISTRO} -ENV ARCH=${ARCH} -ENV UEFI=${UEFI} -ENV SE_BOOT=${SE_BOOT} -ENV PACKER_GITHUB_API_TOKEN=${PACKER_GITHUB_API_TOKEN} - -# Defaults to Ubuntu noble amd64 release image. These variables can be overriden as needed -ARG IMAGE_URL -ARG IMAGE_CHECKSUM - -ENV IMAGE_URL=${IMAGE_URL} -ENV IMAGE_CHECKSUM=${IMAGE_CHECKSUM} - -# Copy the binaries to podvm/files folder -COPY --from=podvm_binaries /podvm-binaries.tar.gz /src/cloud-api-adaptor/podvm/files/podvm-binaries.tar.gz -RUN tar xvf /src/cloud-api-adaptor/podvm/files/podvm-binaries.tar.gz -C /src/cloud-api-adaptor/podvm/files - -# Copy the pause_bundle to podvm/files folder -COPY --from=podvm_binaries /pause-bundle.tar.gz /src/cloud-api-adaptor/podvm/files/pause-bundle.tar.gz -RUN tar xvf /src/cloud-api-adaptor/podvm/files/pause-bundle.tar.gz -C /src/cloud-api-adaptor/podvm/files - -COPY . /src - -WORKDIR /src/cloud-api-adaptor/podvm -# Installs add-ons for foreign target, if required -RUN ./hack/cross-build-extras.sh - -# Will rebuild only the local binaries (agent-protocol-forwarder/process-user-data) -# To rebuild everything update to "make FORCE=true image" -RUN make image - -FROM scratch -COPY --from=podvm_builder /src/cloud-api-adaptor/podvm/output/*.qcow2 / diff --git a/src/cloud-api-adaptor/podvm/Dockerfile.podvm.rhel b/src/cloud-api-adaptor/podvm/Dockerfile.podvm.rhel deleted file mode 100644 index 0dff771db3..0000000000 --- a/src/cloud-api-adaptor/podvm/Dockerfile.podvm.rhel +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright Confidential Containers Contributors -# -# SPDX-License-Identifier: Apache-2.0 -# -# Builds pod vm image inside container -# -ARG BUILDER_IMG="quay.io/confidential-containers/podvm-builder-rhel" -ARG BINARIES_IMG="quay.io/confidential-containers/podvm-binaries-rhel-amd64" - -FROM ${BINARIES_IMG} AS podvm_binaries -FROM ${BUILDER_IMG} AS podvm_builder - -ARG CLOUD_PROVIDER -ARG PODVM_DISTRO=rhel -ARG UEFI=false -ARG ORG_ID -ARG ACTIVATION_KEY - -# If not provided, uses system architecture -ARG ARCH -ARG CAA_SRC="" -ARG CAA_SRC_REF="" -ARG AUTHFILE -ARG PAUSE_REPO -ARG PAUSE_VERSION -ARG PAUSE_BIN - -ENV AUTHFILE=${AUTHFILE} -ENV PAUSE_REPO=${PAUSE_REPO} -ENV PAUSE_VERSION=${PAUSE_VERSION} -ENV PAUSE_BIN=${PAUSE_BIN} -ENV CLOUD_PROVIDER=${CLOUD_PROVIDER} -ENV PODVM_DISTRO=${PODVM_DISTRO} -ENV ARCH=${ARCH} -ENV UEFI=${UEFI} - -ARG SE_BOOT -ARG SE_VERIFY -ARG IMAGE_URL -ARG IMAGE_CHECKSUM - -ADD ${IMAGE_URL} /tmp/rhel.img -ENV IMAGE_URL=/tmp/rhel.img -ENV IMAGE_CHECKSUM=${IMAGE_CHECKSUM} - -ENV SE_BOOT=${SE_BOOT} -ENV SE_VERIFY=${SE_VERIFY} -# workaround to ensure hashicorp packer is called instead -# of cracklib packer which is installed by default -ENV PATH="/usr/bin:${PATH}" - -# Copy the binaries to podvm/files folder -COPY --from=podvm_binaries /podvm-binaries.tar.gz /src/cloud-api-adaptor/podvm/files/podvm-binaries.tar.gz -RUN tar xvf /src/cloud-api-adaptor/podvm/files/podvm-binaries.tar.gz -C /src/cloud-api-adaptor/podvm/files - -# Copy the pause_bundle to podvm/files folder -COPY --from=podvm_binaries /pause-bundle.tar.gz /src/cloud-api-adaptor/podvm/files/pause-bundle.tar.gz -RUN tar xvf /src/cloud-api-adaptor/podvm/files/pause-bundle.tar.gz -C /src/cloud-api-adaptor/podvm/files - -COPY . /src - -WORKDIR /src/cloud-api-adaptor/podvm - -RUN LIBC=gnu ORG_ID=${ORG_ID} ACTIVATION_KEY=${ACTIVATION_KEY} make image - -# The below instructions can be used if you prefer to rebuild all the binaries -#RUN make binaries -#RUN make image - -FROM scratch -COPY --from=podvm_builder /src/cloud-api-adaptor/podvm/output/*.qcow2 / diff --git a/src/cloud-api-adaptor/podvm/Dockerfile.podvm_binaries b/src/cloud-api-adaptor/podvm/Dockerfile.podvm_binaries deleted file mode 100644 index c8f601321b..0000000000 --- a/src/cloud-api-adaptor/podvm/Dockerfile.podvm_binaries +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright Confidential Containers Contributors -# -# SPDX-License-Identifier: Apache-2.0 -# -# Builds pod vm image inside container -# -ARG BUILDER_IMG="quay.io/confidential-containers/podvm-builder-ubuntu" - -FROM ${BUILDER_IMG} AS podvm_builder - -ARG CLOUD_PROVIDER -ARG PODVM_DISTRO=ubuntu -ARG GUEST_COMPONENTS_VERSION -ARG GUEST_COMPONENTS_REPO -# By default AA will be built with the `all-attesters` feature, -# which doesn't compile ootb on ubuntu. -ARG TEE_PLATFORM=none -# If not provided, uses system architecture -ARG ARCH -#This is the name of the policy file under -#files/etc/kata-opa -ARG DEFAULT_AGENT_POLICY_FILE=allow-all.rego -ARG AUTHFILE -ARG PAUSE_REPO -ARG PAUSE_VERSION -ARG PAUSE_BIN - -ENV AUTHFILE=${AUTHFILE} -ENV PAUSE_REPO=${PAUSE_REPO} -ENV PAUSE_VERSION=${PAUSE_VERSION} -ENV PAUSE_BIN=${PAUSE_BIN} -ENV CLOUD_PROVIDER=${CLOUD_PROVIDER} -ENV PODVM_DISTRO=${PODVM_DISTRO} -ENV GUEST_COMPONENTS_VERSION=${GUEST_COMPONENTS_VERSION} -ENV GUEST_COMPONENTS_REPO=${GUEST_COMPONENTS_REPO} -ENV TEE_PLATFORM=${TEE_PLATFORM} -ENV ARCH=${ARCH} -ENV DEFAULT_AGENT_POLICY_FILE=${DEFAULT_AGENT_POLICY_FILE} - -# Set these as they are required in the Makefile -ENV IMAGE_URL="none" -ENV IMAGE_CHECKSUM="none" - -COPY . /src - -WORKDIR /src/cloud-api-adaptor/podvm -# Installs add-ons for foreign target, if required -RUN ./hack/cross-build-extras.sh - -RUN LIBC=gnu make binaries - -RUN tar czvf /podvm-binaries.tar.gz -C /src/cloud-api-adaptor/podvm/files usr/ etc/ -RUN tar czvf /pause-bundle.tar.gz -C /src/cloud-api-adaptor/podvm/files pause_bundle/ - -FROM scratch -COPY --from=podvm_builder /podvm-binaries.tar.gz / -COPY --from=podvm_builder /pause-bundle.tar.gz / -COPY --from=podvm_builder /src/cloud-api-adaptor/podvm/files / diff --git a/src/cloud-api-adaptor/podvm/Dockerfile.podvm_binaries.rhel b/src/cloud-api-adaptor/podvm/Dockerfile.podvm_binaries.rhel deleted file mode 100644 index 1213d690d4..0000000000 --- a/src/cloud-api-adaptor/podvm/Dockerfile.podvm_binaries.rhel +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright Confidential Containers Contributors -# -# SPDX-License-Identifier: Apache-2.0 -# -# Builds pod vm image inside container -# -ARG BUILDER_IMG="quay.io/confidential-containers/podvm-builder-rhel" - -FROM ${BUILDER_IMG} AS podvm_builder - -ARG PODVM_DISTRO=rhel -# By default AA will be built with the `all-attesters` feature, -# which doesn't compile ootb on RHEL. -ARG TEE_PLATFORM=none -# If not provided, uses system architecture -ARG ARCH -#This is the name of the policy file under -#files/etc/kata-opa -ARG DEFAULT_AGENT_POLICY_FILE=allow-all.rego -ARG AUTHFILE -ARG PAUSE_REPO -ARG PAUSE_VERSION -ARG PAUSE_BIN - -ENV AUTHFILE=${AUTHFILE} -ENV PAUSE_REPO=${PAUSE_REPO} -ENV PAUSE_VERSION=${PAUSE_VERSION} -ENV PAUSE_BIN=${PAUSE_BIN} -ENV PODVM_DISTRO=${PODVM_DISTRO} -ENV TEE_PLATFORM=${TEE_PLATFORM} -ENV ARCH=${ARCH} -ENV DEFAULT_AGENT_POLICY_FILE=${DEFAULT_AGENT_POLICY_FILE} - -COPY . /src - -WORKDIR /src/cloud-api-adaptor/podvm - -RUN LIBC=gnu make binaries - -RUN tar czvf /podvm-binaries.tar.gz -C /src/cloud-api-adaptor/podvm/files usr/ etc/ -RUN tar czvf /pause-bundle.tar.gz -C /src/cloud-api-adaptor/podvm/files pause_bundle/ - -# registry.access.redhat.com/ubi10/ubi:10.1 -FROM registry.access.redhat.com/ubi10/ubi@sha256:f573194e8e5231f1c9340c497e1f8d9aa9dbb42b2849e60341e34f50eec9477e -COPY --from=podvm_builder /podvm-binaries.tar.gz / -COPY --from=podvm_builder /pause-bundle.tar.gz / diff --git a/src/cloud-api-adaptor/podvm/Dockerfile.podvm_builder b/src/cloud-api-adaptor/podvm/Dockerfile.podvm_builder deleted file mode 100644 index 5da5b0bd0b..0000000000 --- a/src/cloud-api-adaptor/podvm/Dockerfile.podvm_builder +++ /dev/null @@ -1,62 +0,0 @@ -# syntax=docker/dockerfile:1.5-labs -# Copyright Confidential Containers Contributors -# -# SPDX-License-Identifier: Apache-2.0 -# -# Creates a builder container image that should be used to build the Pod VM -# disk inside a container. -# -# ubuntu:24.04 -FROM ubuntu@sha256:0d39fcc8335d6d74d5502f6df2d30119ff4790ebbb60b364818d5112d9e3e932 - -ARG ARCH="x86_64" -ARG GO_ARCH="amd64" -ARG DISTRO_ARCH="amd64" -ARG YQ_ARCH="amd64" -ARG PROTOC_ARCH="x86_64" -ARG GO_VERSION -ARG PROTOC_VERSION -ARG YQ_VERSION -ARG YQ_CHECKSUM -ARG ORAS_VERSION -# Without setting ENV gh-action is failing to use the correct values -ENV GO_VERSION=${GO_VERSION} -ENV PROTOC_VERSION=${PROTOC_VERSION} -ENV ARCH=${ARCH} -ENV YQ_ARCH=${YQ_ARCH} -ENV YQ_VERSION=${YQ_VERSION} -ENV ORAS_VERSION=${ORAS_VERSION} - -ENV DEBIAN_FRONTEND=noninteractive - -RUN apt-get update -y && \ - apt-get install --no-install-recommends -y build-essential cloud-image-utils curl git gnupg \ - libdevmapper-dev libgpgme-dev lsb-release pkg-config jq \ - musl-tools unzip wget git zstd && \ - apt-get install -y qemu-kvm && \ - wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc && \ - echo "deb http://apt.llvm.org/$(lsb_release -cs)/ llvm-toolchain-$(lsb_release -cs)-17 main" | tee -a /etc/apt/sources.list && \ - apt-get update && apt-get install -y clang-17 && \ - curl -fsSL https://apt.releases.hashicorp.com/gpg | apt-key add - && \ - echo "deb [arch=${DISTRO_ARCH}] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | tee -a /etc/apt/sources.list && \ - apt-get update && apt-get install --no-install-recommends -y packer=1.9.4-1 && \ - apt-get clean - -ADD https://dl.google.com/go/go${GO_VERSION}.linux-${GO_ARCH}.tar.gz go${GO_VERSION}.linux-${GO_ARCH}.tar.gz -RUN rm -rf /usr/local/go && tar -C /usr/local -xzf go${GO_VERSION}.linux-${GO_ARCH}.tar.gz && rm -f go${GO_VERSION}.linux-${GO_ARCH}.tar.gz - -ADD https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_${YQ_ARCH} /usr/local/bin/yq -RUN echo "${YQ_CHECKSUM#sha256:} /usr/local/bin/yq" | sha256sum -c -RUN chmod a+x /usr/local/bin/yq - -ENV PATH="/usr/local/go/bin:$PATH" - -ADD https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-linux-${PROTOC_ARCH}.zip protoc-${PROTOC_VERSION}-linux-${PROTOC_ARCH}.zip -RUN unzip protoc-${PROTOC_VERSION}-linux-${PROTOC_ARCH}.zip -d /usr/local && rm -f protoc-${PROTOC_VERSION}-linux-${PROTOC_ARCH}.zip - -ADD https://github.com/oras-project/oras/releases/download/v${ORAS_VERSION}/oras_${ORAS_VERSION}_linux_${DISTRO_ARCH}.tar.gz oras_${ORAS_VERSION}_linux_${DISTRO_ARCH}.tar.gz -RUN rm -rf /usr/local/bin/oras && tar -C /usr/local/bin -xzf oras_${ORAS_VERSION}_linux_${DISTRO_ARCH}.tar.gz && rm -f oras_${ORAS_VERSION}_linux_${DISTRO_ARCH}.tar.gz - -WORKDIR /src - -ENV GOPATH=/src diff --git a/src/cloud-api-adaptor/podvm/Dockerfile.podvm_builder.rhel b/src/cloud-api-adaptor/podvm/Dockerfile.podvm_builder.rhel deleted file mode 100644 index c6cf711e37..0000000000 --- a/src/cloud-api-adaptor/podvm/Dockerfile.podvm_builder.rhel +++ /dev/null @@ -1,88 +0,0 @@ -# syntax=docker/dockerfile:1.5-labs -# Copyright Confidential Containers Contributors -# -# SPDX-License-Identifier: Apache-2.0 -# -# Creates a builder container image that should be used to build the Pod VM -# disk inside a container. -# -# registry.access.redhat.com/ubi10/ubi:10.1 -FROM registry.access.redhat.com/ubi10/ubi@sha256:f573194e8e5231f1c9340c497e1f8d9aa9dbb42b2849e60341e34f50eec9477e - -ARG ARCH="x86_64" -ARG GO_ARCH="amd64" -ARG DISTRO_ARCH="amd64" -ARG YQ_ARCH="amd64" -ARG PROTOC_ARCH="x86_64" -ARG GO_VERSION -ARG PROTOC_VERSION -ARG PACKER_VERSION -ARG ORAS_VERSION -ARG YQ_VERSION -ARG YQ_CHECKSUM -ARG ORG_ID -ARG ACTIVATION_KEY - -# Without setting ENV gh-action is failing to use the correct values -ENV GO_VERSION=${GO_VERSION} -ENV PROTOC_VERSION=${PROTOC_VERSION} -ENV PROTOC_ARCH=${PROTOC_ARCH} -ENV ARCH=${ARCH} -ENV YQ_ARCH=${YQ_ARCH} -ENV YQ_VERSION=${YQ_VERSION} -ENV ORAS_VERSION=${ORAS_VERSION} -ENV PACKER_VERSION=${PACKER_VERSION} - -# This registering RHEL when building on an unsubscribed system -# If you are running a UBI container on a registered and subscribed RHEL host, the main RHEL Server repository is enabled inside the standard UBI container -RUN if [[ -n "${ACTIVATION_KEY}" && -n "${ORG_ID}" ]]; then \ - rm -f /etc/rhsm-host && rm -f /etc/pki/entitlement-host; \ - subscription-manager register --org=${ORG_ID} --activationkey=${ACTIVATION_KEY}; \ - fi - -RUN subscription-manager repos --enable codeready-builder-for-rhel-10-${ARCH/amd64/x86_64}-rpms; \ - dnf -y group install 'Development Tools' && \ - dnf install -y yum-utils gnupg git --allowerasing curl pkg-config clang perl libseccomp-devel gpgme-devel \ - device-mapper-devel qemu-kvm unzip wget libassuan-devel xorriso cloud-utils-growpart cloud-init \ - perl-FindBin openssl-devel tpm2-tss-devel jq xz zstd - -RUN ln -s /usr/bin/xorrisofs /usr/local/bin/genisoimage - -RUN curl -L -o /usr/local/bin/yq https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_${YQ_ARCH} \ - && echo "${YQ_CHECKSUM#sha256:} /usr/local/bin/yq" | sha256sum -c - -RUN chmod a+x /usr/local/bin/yq && \ - curl https://dl.google.com/go/go${GO_VERSION}.linux-${GO_ARCH}.tar.gz -o go${GO_VERSION}.linux-${GO_ARCH}.tar.gz && \ - rm -rf /usr/local/go && tar -C /usr/local -xzf go${GO_VERSION}.linux-${GO_ARCH}.tar.gz && \ - rm -f go${GO_VERSION}.linux-${GO_ARCH}.tar.gz -ENV PATH="/usr/local/go/bin:/usr/local/bin:${PATH}" - -# Install packer. Packer doesn't have prebuilt s390x arch binaries beyond Packer version 0.1.5 -RUN if [ "${ARCH}" == "s390x" ]; then \ - git clone --depth 1 --single-branch https://github.com/hashicorp/packer.git -b ${PACKER_VERSION}; \ - cd packer; \ - sed -i -- "s/ALL_XC_ARCH=.*/ALL_XC_ARCH=\"${ARCH}\"/g" scripts/build.sh; \ - sed -i -- "s/ALL_XC_OS=.*/ALL_XC_OS=\"Linux\"/g" scripts/build.sh; \ - make bin && cp bin/packer /usr/local/bin/; \ - cd $OLDPWD; \ - else \ - yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo && \ - dnf install -y packer; \ - fi - -# set a correspond qemu-system-* named link to qemu-kvm -RUN ln -s /usr/libexec/qemu-kvm /usr/bin/qemu-system-$(uname -m) - -# cloud-utils package is not available for rhel. -RUN git clone https://github.com/canonical/cloud-utils -RUN cd cloud-utils && make install - -ENV PATH="/usr/local/go/bin:$PATH" - -RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-linux-${PROTOC_ARCH}.zip && \ - unzip protoc-${PROTOC_VERSION}-linux-${PROTOC_ARCH}.zip -d /usr/local && rm -f protoc-${PROTOC_VERSION}-linux-${PROTOC_ARCH}.zip - -ADD https://github.com/oras-project/oras/releases/download/v${ORAS_VERSION}/oras_${ORAS_VERSION}_linux_${DISTRO_ARCH}.tar.gz oras_${ORAS_VERSION}_linux_${DISTRO_ARCH}.tar.gz -RUN rm -rf /usr/local/bin/oras && tar -C /usr/local/bin -xzf oras_${ORAS_VERSION}_linux_${DISTRO_ARCH}.tar.gz && rm -f oras_${ORAS_VERSION}_linux_${DISTRO_ARCH}.tar.gz - -WORKDIR /src -ENV GOPATH=/src diff --git a/src/cloud-api-adaptor/podvm-mkosi/Dockerfile.podvm_byom_binaries b/src/cloud-api-adaptor/podvm/Dockerfile.podvm_byom_binaries similarity index 85% rename from src/cloud-api-adaptor/podvm-mkosi/Dockerfile.podvm_byom_binaries rename to src/cloud-api-adaptor/podvm/Dockerfile.podvm_byom_binaries index 8ace700851..0f79f2158b 100644 --- a/src/cloud-api-adaptor/podvm-mkosi/Dockerfile.podvm_byom_binaries +++ b/src/cloud-api-adaptor/podvm/Dockerfile.podvm_byom_binaries @@ -22,8 +22,8 @@ RUN tar xvf /src/pause-bundle.tar.gz -C /src/files COPY . /src -COPY podvm-mkosi/mkosi.skeleton-sftp/usr/lib/systemd/system /src/files/etc/systemd/system -COPY podvm/qcow2/copy-files.sh /src/files/copy-files.sh +COPY podvm/mkosi.skeleton-sftp/usr/lib/systemd/system /src/files/etc/systemd/system +COPY podvm/copy-files.sh /src/files/copy-files.sh RUN tar czvf /podvm-byom.tar.gz -C /src/files . diff --git a/src/cloud-api-adaptor/podvm-mkosi/Dockerfile.podvm_byom_docker_provider.ubuntu b/src/cloud-api-adaptor/podvm/Dockerfile.podvm_byom_docker_provider.ubuntu similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/Dockerfile.podvm_byom_docker_provider.ubuntu rename to src/cloud-api-adaptor/podvm/Dockerfile.podvm_byom_docker_provider.ubuntu diff --git a/src/cloud-api-adaptor/podvm-mkosi/Dockerfile.podvm_docker_provider.ubuntu b/src/cloud-api-adaptor/podvm/Dockerfile.podvm_docker_provider.ubuntu similarity index 73% rename from src/cloud-api-adaptor/podvm-mkosi/Dockerfile.podvm_docker_provider.ubuntu rename to src/cloud-api-adaptor/podvm/Dockerfile.podvm_docker_provider.ubuntu index f83514d7c8..36d9862083 100644 --- a/src/cloud-api-adaptor/podvm-mkosi/Dockerfile.podvm_docker_provider.ubuntu +++ b/src/cloud-api-adaptor/podvm/Dockerfile.podvm_docker_provider.ubuntu @@ -26,11 +26,11 @@ ARG DEBIAN_FRONTEND=noninteractive RUN echo "Installing Packages ..." \ && apt-get update \ && apt-get install -y \ - systemd \ - conntrack iptables nftables iproute2 ethtool util-linux kmod \ - libseccomp2 pigz fuse-overlayfs \ - nfs-common systemd-sysv \ - bash ca-certificates curl jq procps \ + systemd \ + conntrack iptables nftables iproute2 ethtool util-linux kmod \ + libseccomp2 pigz fuse-overlayfs \ + nfs-common systemd-sysv \ + bash ca-certificates curl jq procps \ && rm -f /lib/systemd/system/multi-user.target.wants/* \ && rm -f /etc/systemd/system/*.wants/* \ && rm -f /lib/systemd/system/local-fs.target.wants/* \ @@ -50,15 +50,15 @@ RUN --mount=type=cache,target=/iptables,from=iptables,source=/iptables,readonly && /usr/sbin/iptables-wrapper install # Add podvm resources -COPY ./podvm-mkosi/resources/binaries-tree/etc/ /etc/ -COPY ./podvm-mkosi/resources/binaries-tree/usr/ /usr/ -COPY ./podvm-mkosi/resources/binaries-tree/pause_bundle/ /pause_bundle/ +COPY ./podvm/resources/binaries-tree/etc/ /etc/ +COPY ./podvm/resources/binaries-tree/usr/ /usr/ +COPY ./podvm/resources/binaries-tree/pause_bundle/ /pause_bundle/ -RUN curl -LO https://raw.githubusercontent.com/confidential-containers/cloud-api-adaptor/main/src/cloud-api-adaptor/podvm/qcow2/misc-settings.sh +COPY ./podvm/misc-settings.sh /tmp/misc-settings.sh -RUN PODVM_DISTRO=ubuntu CLOUD_PROVIDER=generic bash ./misc-settings.sh +RUN PODVM_DISTRO=ubuntu CLOUD_PROVIDER=generic bash /tmp/misc-settings.sh -COPY --chmod=0755 ./podvm-mkosi/entrypoint.sh /usr/local/bin/ +COPY --chmod=0755 ./podvm/entrypoint.sh /usr/local/bin/ # https://systemd.io/CONTAINER_INTERFACE/ ENV container=docker diff --git a/src/cloud-api-adaptor/podvm/Makefile b/src/cloud-api-adaptor/podvm/Makefile index 7b55551836..5e1b56c4f3 100644 --- a/src/cloud-api-adaptor/podvm/Makefile +++ b/src/cloud-api-adaptor/podvm/Makefile @@ -1,113 +1,226 @@ -# -# SPDX-License-Identifier: Apache-2.0 -# +include ../Makefile.defaults +include ./Makefile.inc -SELF_DIR := $(dir $(lastword $(MAKEFILE_LIST))) +SE_BOOT ?= false +IS_DEBIAN := $(shell if grep -q 'ID_LIKE=debian' /etc/os-release; then echo "true"; else echo "false"; fi) -include $(SELF_DIR)Makefile.inc - -.PHONY: image clean clean_sources - -IMAGE_SUFFIX := .qcow2 -PODVM_DISTRO ?= ubuntu -IMAGE_URL := $(or $(IMAGE_URL),$($(PODVM_DISTRO)_$(DISTRO_ARCH)_IMAGE_URL)) -IMAGE_CHECKSUM := $(or $(IMAGE_CHECKSUM),$($(PODVM_DISTRO)_$(DISTRO_ARCH)_IMAGE_CHECKSUM)) - -ifndef IMAGE_URL -$(error "IMAGE_URL is not defined") -endif -ifndef IMAGE_CHECKSUM -$(error "IMAGE_CHECKSUM is not defined") +REGISTRY ?= quay.io/confidential-containers +PODVM_DISTRO ?= fedora +ifeq ($(PODVM_TAG),) + PODVM_TAG := $(VERSIONS_HASH) endif +PODVM_BUILDER_IMAGE ?= $(REGISTRY)/podvm-builder-$(PODVM_DISTRO)-$(DISTRO_ARCH):$(PODVM_TAG) +PODVM_BINARIES_IMAGE ?= $(REGISTRY)/podvm-binaries-$(PODVM_DISTRO)-$(DISTRO_ARCH):$(PODVM_TAG) +PODVM_IMAGE ?= $(REGISTRY)/podvm-generic-$(PODVM_DISTRO)$(if $(filter $(SE_BOOT),true),-se,)-$(DISTRO_ARCH):$(PODVM_TAG) +PODVM_CONTAINER_NAME ?= $(REGISTRY)/podvm-docker-image-$(DISTRO_ARCH) +PODVM_BYOM_E2E_CONTAINER_NAME ?= $(REGISTRY)/podvm-byom-e2e-image-$(DISTRO_ARCH) +VERIFY_PROVENANCE ?= no +MKOSI_VERSION ?= v26 + +_SHA := $(shell git rev-parse --short HEAD) +_SHA_DIRTY := $(shell [ -n "$$(git status --porcelain 2>/dev/null)" ] && printf -- '-dirty') +IMAGE_VERSION ?= $(_SHA)$(_SHA_DIRTY) -AGENT_PROTOCOL_FORWARDER_SRC := ../ +export IMAGE_VERSION +export MKOSI_VERSION -QEMU_MACHINE_TYPE_s390x := s390-ccw-virtio -QEMU_MACHINE_TYPE_aarch64 := virt +.DEFAULT_GOAL := all +.PHONY: all +all: podvm-binaries image -UEFI ?= false -UEFI_FIRMWARE_LOCATION ?= +.PHONY: debug +debug: podvm-binaries image-debug -SE_BOOT ?= 0 +.PHONY: sftp +sftp: podvm-binaries image-sftp -# Env variable for default qemu builder options across distros/arch -PACKER_DEFAULT_OPTS ?= -var qemu_image_name=${IMAGE_FILE} \ - -var cloud_image_url=${IMAGE_URL} \ - -var cloud_image_checksum=${IMAGE_CHECKSUM} +.PHONY: container +container: podvm-binaries image-container -image: $(IMAGE_FILE) +.PHONY: byom-e2e-container +byom-e2e-container: image-byom-e2e-container -setopts: ifeq ($(PODVM_DISTRO),ubuntu) - @echo defined - $(eval OPTS := -var se_boot=${SE_BOOT}) -ifneq ($(ARCH),x86_64) - $(eval OPTS += -var machine_type=${QEMU_MACHINE_TYPE_${ARCH}}) -ifndef QEMU_BINARY - $(eval OPTS += -var qemu_binary=qemu-system-${ARCH}) -endif -# UEFI for aarch64 on ubuntu -ifeq ($(ARCH),aarch64) - $(eval OPTS += -var is_uefi=true -var os_arch="aarch64") - $(eval OPTS += -var uefi_firmware="/usr/share/qemu-efi-aarch64/QEMU_EFI.fd") -endif -endif -else ifeq ($(PODVM_DISTRO),alinux) - @echo defined - $(eval OPTS := -var disk_size=22323) -else ifeq ($(PODVM_DISTRO),rhel) - @echo defined - $(eval OPTS := -var disk_size=11144) -ifeq ($(ARCH),s390x) - $(eval OPTS += -var se_boot=${SE_BOOT}) - $(eval OPTS += -var machine_type=${QEMU_MACHINE_TYPE_${ARCH}}) - $(eval OPTS += -var cpu_type=max) - $(eval OPTS += -var os_arch=s390x) -ifndef QEMU_BINARY - $(eval OPTS += -var qemu_binary=qemu-system-${ARCH}) +DISTRO_NAME := ubuntu +DISTRO_IMAGE := ubuntu:24.04 +DISTRO_RELEASE := noble +DISTRO_REPOS := --repositories=universe +PKG_INSTALL := apt-get update && apt-get install -y debian-archive-keyring +else +DISTRO_NAME := fedora +DISTRO_IMAGE := fedora:43 +DISTRO_RELEASE := 43 +DISTRO_REPOS := +PKG_INSTALL := dnf install -y distribution-gpg-keys libseccomp endif + +define run_mkosi_in_container + docker run --rm --privileged \ + -v "$(shell pwd)":/workspace \ + -w /workspace \ + $(DISTRO_IMAGE) \ + bash -c "$(PKG_INSTALL) bubblewrap coreutils git python3; mkdir -p /build; git clone -b $(MKOSI_VERSION) https://github.com/systemd/mkosi /build/mkosi; export PATH="/build/mkosi/bin:$$PATH"; mkosi --tools-tree=default --tools-tree-release=$(DISTRO_RELEASE) --tools-tree-distribution=$(DISTRO_NAME) --distribution=$(DISTRO_NAME) --release=$(DISTRO_RELEASE) $(DISTRO_REPOS) $(1)" +endef + +.PHONY: podvm-binaries +podvm-binaries: + @echo "Building podvm binaries using Docker..." + rm -rf ./resources/binaries-tree +ifeq ($(IS_DEBIAN),true) + docker buildx use default endif + docker buildx build \ + -t $(PODVM_BINARIES_IMAGE) \ + --progress=plain \ + --build-arg GO_VERSION=$(GO_VERSION) \ + --build-arg ARCH=$(ARCH) \ + --build-arg GO_ARCH=$(GO_ARCH) \ + --build-arg DISTRO_ARCH=$(DISTRO_ARCH) \ + --build-arg PROTOC_VERSION=$(PROTOC_VERSION) \ + --build-arg PROTOC_ARCH=$(PROTOC_ARCH) \ + --build-arg YQ_VERSION=$(YQ_VERSION) \ + --build-arg YQ_CHECKSUM=$(YQ_CHECKSUM_$(YQ_ARCH)) \ + --build-arg YQ_ARCH=$(YQ_ARCH) \ + --build-arg ORAS_VERSION=$(ORAS_VERSION) \ + --build-arg TEE_PLATFORM=$(TEE_PLATFORM) \ + --build-arg PAUSE_REPO=$(PAUSE_REPO) \ + --build-arg PAUSE_VERSION=$(PAUSE_VERSION) \ + --build-arg PAUSE_BIN=$(PAUSE_BIN) \ + --build-arg IMAGE_NAME=mkosi-podvm-binaries \ + --build-arg VERIFY_PROVENANCE=$(VERIFY_PROVENANCE) \ + $(if $(AUTHFILE),--build-arg AUTHFILE=$(AUTHFILE),) \ + $(if $(DEFAULT_AGENT_POLICY_FILE),--build-arg DEFAULT_AGENT_POLICY_FILE=$(DEFAULT_AGENT_POLICY_FILE),) \ + $(if $(filter $(PUSH),true),,-o type=local,dest="./resources/binaries-tree") \ + $(DOCKER_OPTS) \ + -f Dockerfile.podvm_binaries.$(PODVM_DISTRO) ../../ + +PHONY: insecure-builder +insecure-builder: + docker buildx ls | grep "^insecure-builder" || docker buildx create \ + --driver-opt image=moby/buildkit:master \ + --name insecure-builder \ + --buildkitd-flags '--allow-insecure-entitlement security.insecure' + +PHONY: image +image: insecure-builder + rm -rf ./build + @echo "Building image..." +ifeq ($(SE_BOOT),true) + $(call run_mkosi_in_container,--profile production) + sudo -E ../hack/build-s390x-se-image.sh +else ifeq ($(ARCH),s390x) + $(call run_mkosi_in_container,--profile production) + sudo -E ../hack/build-s390x-image.sh else - $(error PODVM_DISTRO is invalid or not defined) + mkdir -p build + docker buildx build \ + -f Dockerfile.mkosi$(if $(filter $(PODVM_DISTRO),ubuntu),.ubuntu,) \ + -t $(PODVM_IMAGE) \ + --build-arg PROFILE=production \ + --build-arg IMAGE_VERSION \ + --build-arg MKOSI_VERSION \ + --progress=plain \ + -o type=local,dest=./build \ + --builder insecure-builder \ + --allow security.insecure \ + . + qemu-img convert -f raw -O qcow2 build/system.raw build/podvm-$(PODVM_DISTRO)-$(DISTRO_ARCH).qcow2 endif -ifdef QEMU_BINARY - $(eval OPTS += -var qemu_binary=${QEMU_BINARY} ) +PHONY: image-debug +image-debug: insecure-builder + rm -rf ./build + @echo "Building debug image..." +ifeq ($(SE_BOOT),true) + $(call run_mkosi_in_container,--profile debug) + sudo -E ../hack/build-s390x-se-image.sh +else ifeq ($(ARCH),s390x) + $(call run_mkosi_in_container,--profile debug) + sudo -E ../hack/build-s390x-image.sh +else + + mkdir -p build + docker buildx build \ + -f Dockerfile.mkosi$(if $(filter $(PODVM_DISTRO),ubuntu),.ubuntu,) \ + -t $(PODVM_IMAGE) \ + --build-arg PROFILE=debug \ + --build-arg IMAGE_VERSION \ + --build-arg MKOSI_VERSION \ + --progress=plain \ + -o type=local,dest=./build \ + --builder insecure-builder \ + --allow security.insecure \ + . + qemu-img convert -f raw -O qcow2 build/system.raw build/podvm-$(PODVM_DISTRO)-$(DISTRO_ARCH).qcow2 endif -# UEFI for x86_64 -ifeq ($(UEFI),true) - $(eval OPTS += -var is_uefi=true -var os_arch="x86_64" ) -ifdef UEFI_FIRMWARE_LOCATION - $(eval OPTS += -var uefi_firmware=${UEFI_FIRMWARE_LOCATION} ) +PHONY: image-sftp +image-sftp: insecure-builder + rm -rf ./build + @echo "Building sftp image..." +ifeq ($(SE_BOOT),true) + $(call run_mkosi_in_container,--profile sftp) + sudo -E ../hack/build-s390x-se-image.sh +else ifeq ($(ARCH),s390x) + $(call run_mkosi_in_container,--profile sftp) + sudo -E ../hack/build-s390x-image.sh +else + mkdir -p build + docker buildx build \ + -f Dockerfile.mkosi$(if $(filter $(PODVM_DISTRO),ubuntu),.ubuntu,) \ + -t $(PODVM_IMAGE) \ + --build-arg PROFILE=sftp \ + --build-arg IMAGE_VERSION \ + --build-arg MKOSI_VERSION \ + --progress=plain \ + -o type=local,dest=./build \ + --builder insecure-builder \ + --allow security.insecure \ + . + qemu-img convert -f raw -O qcow2 build/system.raw build/podvm-$(PODVM_DISTRO)-$(DISTRO_ARCH).qcow2 endif + +PHONY: image-container +image-container: + @echo "Building podvm container image..." + cd .. && docker buildx build \ + -t $(PODVM_CONTAINER_NAME):$(PODVM_TAG) \ + -t $(PODVM_CONTAINER_NAME):latest \ + -f podvm/Dockerfile.podvm_docker_provider$(if $(filter $(PODVM_DISTRO),ubuntu),.ubuntu,) . + +.PHONY: image-byom-e2e-container +image-byom-e2e-container: + @echo "Building BYOM podvm container image with SSH support..." + docker buildx build \ + -t $(PODVM_BYOM_E2E_CONTAINER_NAME):$(PODVM_TAG) \ + -t $(PODVM_BYOM_E2E_CONTAINER_NAME):latest \ + --build-arg BASE_IMAGE=$(PODVM_CONTAINER_NAME):latest \ + -f Dockerfile.podvm_byom_docker_provider$(if $(filter $(PODVM_DISTRO),ubuntu),.ubuntu,) . + +PHONY: push-image +push-image: + @echo "Push podvm image to remote repository..." +ifeq ($(SE_BOOT),true) + docker push $(PODVM_NAME)-se:$(PODVM_TAG) + docker push $(PODVM_NAME)-se:latest +else + docker push $(PODVM_NAME):$(PODVM_TAG) + docker push $(PODVM_NAME):latest endif -$(IMAGE_FILE): $(BINARIES) $(FILES) setopts - rm -fr output - rm -f cloud-init.img - cloud-localds cloud-init.img qcow2/userdata.cfg - mkdir -p toupload - if [ "${SE_BOOT}" = "true" ] && [ "${ARCH}" = "s390x" ]; then \ - qemu-img create -f qcow2 "se-${IMAGE_FILE}" 100G; \ - # Temporary workaround for installing cryptsetup on RHEL 9.4 and below s390x base images for enabling se \ - # Due to issue: https://gitlab.com/qemu-project/qemu/-/issues/2054 \ - # Remove this if using the latest QEMU version (v9.0.0) \ - if [ "${PODVM_DISTRO}" = "rhel" ]; then \ - yum install -y cryptsetup; \ - cp /usr/sbin/cryptsetup ./files; \ - fi \ - fi - packer init ./qcow2/${PODVM_DISTRO} - if [ "${ARCH}" = "x86_64" ] || [ "${ARCH}" = "aarch64" ]; then \ - packer plugins install github.com/hashicorp/qemu v1.1.0; \ - fi - packer build ${PACKER_DEFAULT_OPTS} ${OPTS} qcow2/${PODVM_DISTRO} - rm -fr toupload - rm -f cloud-init.img +PHONY: push-image-container +push-image-container: + @echo "Push podvm container image to remote repository..." + docker push $(PODVM_CONTAINER_NAME):$(PODVM_TAG) + docker push $(PODVM_CONTAINER_NAME):latest -clean: - rm -f "$(IMAGE_FILE)" "$(UBUNTU_IMAGE_FILE)" $(BINARIES) - rm -fr "$(SKOPEO_SRC)" "$(UMOCI_SRC)" "$(PAUSE_SRC)" "$(FILES_DIR)/$(PAUSE_BUNDLE)" - rm -fr "$(OPA_SRC)" +.PHONY: push-image-byom-e2e-container +push-image-byom-e2e-container: + @echo "Push BYOM podvm container image to remote repository..." + docker push $(PODVM_BYOM_E2E_CONTAINER_NAME):$(PODVM_TAG) + docker push $(PODVM_BYOM_E2E_CONTAINER_NAME):latest +PHONY: clean +clean: + rm -rf ./build + rm -rf ./resources/binaries-tree diff --git a/src/cloud-api-adaptor/podvm/Makefile.inc b/src/cloud-api-adaptor/podvm/Makefile.inc index fb6ae370ee..3f7723d11c 100644 --- a/src/cloud-api-adaptor/podvm/Makefile.inc +++ b/src/cloud-api-adaptor/podvm/Makefile.inc @@ -103,6 +103,7 @@ define pull_agent_artifact $(eval OCI_DIGEST := $(shell oras resolve $(OCI_IMAGE):${tag})) $(eval OCI_REF := $(OCI_IMAGE)@$(OCI_DIGEST)) $(if $(filter yes,$(VERIFY_PROVENANCE)),$(ROOT_DIR)hack/verify-provenance.sh \ + $(if $(filter $(DISTRO_ARCH),amd64 arm64),-s,) \ -a $(OCI_REF) \ -r $(KATA_REPO) \ -d $(KATA_REF)) @@ -117,6 +118,7 @@ define pull_gc_artifact $(eval OCI_DIGEST := $(shell oras resolve $(OCI_IMAGE):${tag})) $(eval OCI_REF := $(OCI_IMAGE)@$(OCI_DIGEST)) $(if $(filter yes,$(VERIFY_PROVENANCE)),$(ROOT_DIR)hack/verify-provenance.sh \ + $(if $(filter $(DISTRO_ARCH),amd64 arm64),-s,) \ -a $(OCI_REF) \ -r $(GUEST_COMPONENTS_REPO) \ -d $(GUEST_COMPONENTS_REF)) diff --git a/src/cloud-api-adaptor/podvm/README.md b/src/cloud-api-adaptor/podvm/README.md index 99487e2102..496afdc752 100644 --- a/src/cloud-api-adaptor/podvm/README.md +++ b/src/cloud-api-adaptor/podvm/README.md @@ -1,204 +1,171 @@ -# Introduction +# Pod VM images -This directory contains the sources to build the podvm image (qcow2 file) for various Linux distributions and cloud providers. So use -the instructions in the next sections if you need to build your own image with changes to meet your requirements. Otherwise you can -find [here](../docs/consuming-prebuilt-podvm-images.md) information on how to consume pre-built images. +[mkosi](https://github.com/systemd/mkosi) builds a bootable OS image from scratch. This way, we have full control over every detail of the image, especially over the image format and boot process. On the long run, we will implement fully, bit-by-bit reproducible images with mkosi, and use measured boot and an immutable root FS to ensure the image integrity through remote attestation. -# How to build locally +## Prerequisites -In order to build locally it requires the source trees and softwares mentioned in the [developer's guide](../docs/DEVELOPMENT.md) to build this project binaries. It will also need [packer](https://www.packer.io/) (to build the qcow2) as well as the following packages: +There are various ways to install mkosi documented on the [project page](https://github.com/systemd/mkosi). Different distributions also package mkosi in their repositories, alongside the dependencies. -* On Ubuntu: +Refer to the [CI workflow](../../../.github/workflows/podvm_mkosi.yaml) to see which additional tools are required to build an image. - ```bash - $ apt-get install -y qemu-kvm cloud-utils qemu-utils protobuf-compiler pkg-config libdevmapper-dev libgpgme-dev - ``` +### Building the image -Finally run the following commands to build the qcow2 image: +```sh +make # this will rebuild the builder, the binaries and the OS image +``` + +> [!WARNING] +> The `make` (and `make podvm-binaries`) targets will remove all customization made +> in the `./resources/binaries-tree` dirs -```bash -$ export CLOUD_PROVIDER=[aws|azure|ibmcloud|libvirt|generic] -$ make image +```sh +make image # this will only rebuild the pod VM image ``` -**NOTE:** "generic" is a best-effort provider agnostic image creation -# How to build within container +The default built pod VM image have support for only filesystem attester. + +Set the `TEE_PLATFORM` variable to build the image with specific attester support. -This directory contains dockerfiles to build the podvm image entirely within container so that it only requires docker or podman installed on the host. +For example, this will build the pod VM image with support for SNP attester. -In general it is needed to follow these steps: +```sh +TEE_PLATFORM=snp make image +``` -1. Build a builder container image for a given Linux distribution -2. Build an image containing all the required podvm binaries for a given Linux distribution -3. Build an image containing the podvm qcow2 image for a given cloud provider -4. Extract the podvm image (qcow2 file) from the container image +Likewise `TEE_PLATFORM=az-cvm-vtpm` will enable support for Azure vTPM attesters. -The next sections describe that process in details. Note that although the following examples use docker, it can be carried out with podman too. +Refer to the following [doc](https://github.com/confidential-containers/guest-components/tree/main?tab=readme-ov-file#build) for accepted values of TEE_PLATFORM. -## Building a builder image +#### Building Ubuntu 24.04 images -The builder image packages the cloud-api-adaptor and Kata Containers sources as well as the softwares to build -the binaries (e.g. *kata-agent* and *agent-protocol-forwarder*) that should be installed in the podvm image. +By default, Fedora images are built. To build Ubuntu 24.04 images: -The builder image is agnostic to cloud providers in the sense that one can be used to build for multiple providers, however it is -dependent on the Linux distribution the image is built for. Therefore, in this directory you will find dockerfiles for each -supported distributions, which are currently Ubuntu 24.04 ([Dockerfile.podvm_builder](./Dockerfile.podvm_builder)), -Fedora 39 ([Dockerfile.podvm_builder.fedora](./Dockerfile.podvm_builder)) and RHEL 9 -([Dockerfile.podvm_builder.rhel](./Dockerfile.podvm_builder.rhel)). +```sh +PODVM_DISTRO=ubuntu make # rebuild builder, binaries and OS image for Ubuntu +``` -You can create the builder image using the make target by running: -```bash -$ make -C .. podvm-builder +```sh +PODVM_DISTRO=ubuntu make image # only rebuild OS image for Ubuntu ``` -You can optionally customize the builder image, by specify shell variables to the `make` command: -| Variable | Default value | Description | -| ------------------- | -------------- | --------------------------------------------------------------- | -| `ARCH` | `amd64`/`s390x`| Architecture of the podvm image to be built. Defaults to the architecture the of the current machine | -| `PODVM_DISTRO` | `ubuntu` | Valid options are `ubuntu`, `fedora` and `rhel` | -| `ORG_ID` | `""` | rhel only: the organization ID for Red Hat Subscription Management (RHSM) | -| `ACTIVATION_KEY` | `""` | rhel only: the activation key for Red Hat Subscription Management (RHSM) | +### Upload the image to the desired cloud provider -e.g. to produce an s390x architecture builder image -``` -ARCH=s390x make -C .. podvm-builder +You can upload the image with the tool of your choice, but the recommended way is using [uplosi](https://github.com/edgelesssys/uplosi). Follow the uplosi readme to configure your upload for the desired cloud provider. Then run: + +```sh +# Using -i and a imageVersionFile to increment the image version after the upload. +uplosi -i build/system.raw ``` -## Building the image containing the podvm binaries +If you want to use the image with libvirt, run the following to convert to qcow2 format: -Like the builder image, we have make targets for the binaries image in the parent directory. +```sh +qemu-img convert -f raw -O qcow2 build/system.raw build/system.qcow2 +``` -> **Note:** The `BUILDER_IMG` environment variable is crucial as it specifies -> the builder image, which is the result of the previous step build. Ensure you -> have built the builder image before proceeding. +## Debug image -To build the binaries image, use the following command: +There is a debug variant of the image that provides a specific configuration to debug things within +the podvm. It has additional packages installed that are commonly needed for debugging. +Further, the image has access through the serial console enabled, you can access it through the portal +of the cloud provider. -```bash -$ BUILDER_IMG= make -C .. podvm-binaries +```sh +make debug # this will rebuild the builder, the binaries and the OS image ``` -The build process can take significant time. +```sh +make image-debug # this will only rebuild the OS image +``` -You can customize the build, by using some environment variables like ARCH, -PODVM_DISTRO, and other things. For details visit the Makefile. +Set `TEE_PLATFORM` as explained previously, to add support for specific attester in the debug image. -## Building the podvm qcow2 image +For Ubuntu debug images: +```sh +PODVM_DISTRO=ubuntu make debug +``` -In order to build the podvm image you should be using the corresponding -dockerfile of the Linux distro for which the builder and binaries image were -built. For example, if the builder image was built with -*Dockerfile.podvm_builder.DISTRO* then you should use the -*Dockerfile.podvm.DISTRO* to build the podvm image. +Notice that building a debug image will overwrite any previous existing debug or production image. -The builder image has to be indicated via `BUILDER_IMG` build argument and -binaries image has to be indicated via `BINARIES_IMG` build argument to docker. +For using SSH, create a file `resources/authorized_keys` with your SSH public key. Ensure the permissions +are set to `0400` for the `authorized_keys` file. SSH access is only possible for the `root` user. -Below command will build the qcow2 image that can be used for all cloud providers -based on Ubuntu distro. +## Testing the image -```bash -$ docker build -t podvm \ - --build-arg BUILDER_IMG=podvm_builder \ - --build-arg BINARIES_IMG=podvm_binaries \ - -f Dockerfile.podvm . -``` +To verify the podvm image is bootable and responsive before deploying to +cloud one can use a simple [smoketest](./hack/smoke_test.sh). It +uses libvirt to run the image and kata-agent-ctl to establish connection +to the podvm. -This step will take several minutes to complete, mostly because `packer` will -use the QEMU builder in emulation mode when running within container. -> **Tip:** If you are using podman then you can speed up QEMU by enabling native -> virtualization, by passing the `--device=/dev/kvm` argument to enable KVM inside -> the container. +> [!WARNING] +> This script is intended for CI testing, expects tools in-place and contains +> only a rough cleanup. Use it on a disposable machine only. +> +> The smoke-test won't deploy tls certificates so the image must be built with +> `-disable-tls`: +> +> mkdir -p ./resources/binaries-tree/etc/default +> echo "TLS_OPTIONS=-disable-tls" > ./resources/binaries-tree/etc/default/agent-protocol-forwarder +> +> Do not forget to re-enable it before sending the image to production. -> **Note:** Beware that the process consume a bunch of memory and disk from the host. -If the build fails at the point QEMU was launched but packer couldn't -connect via ssh, with an error similar to: -> ``` -> Build 'qemu.ubuntu' errored after 5 minutes 57 seconds: Timeout waiting for SSH. -> ``` -> then it might indicate lack of memory, so try to increase the amount of memory if running on VM. +## Custom image configuration -The podvm image can be built for other architectures than `x86_64` by passing -the `ARCH` build argument to docker: +You can easily place additional files in `resources/binaries-tree` after it has been populated by the +`make podvm-binaries` step. Notice that systemd units need to be enabled in the presets and links in the tree +won't be copied into the image. You can use `./mkosi.postinst` script to create symlinks. -```bash -$ docker build -t podvm_s390x \ - --build-arg ARCH=s390x \ - --build-arg BUILDER_IMG=podvm_builder \ - --build-arg BINARIES_IMG=podvm_binaries_s390x \ - -f Dockerfile.podvm . -``` +If you want to add additional packages to the image, you can define distribution-specific config files: -The Secure Execution enabled podvm image can be built by passing the `SE_BOOT` build argument to docker. Currently this is only supported for Ubutu `s390x`, which also needs put the `HOST KEY documents` to the [files](files) folder, please follow the `Download host key document from Resource Link` section at [this document](../ibmcloud/SECURE_EXECUTION.md) to download `HOST KEY documents`. -```bash -$ tree -L 1 files -files -├── HKD-8562-1234567.crt -├── etc -└── usr -``` -Running below command will build the Secure Execution enabled qcow2 image: -```bash -$ docker build -t se_podvm_s390x \ - --build-arg ARCH=s390x \ - --build-arg SE_BOOT=true \ - --build-arg BUILDER_IMG=podvm_builder \ - --build-arg BINARIES_IMG=podvm_binaries_s390x \ - -f Dockerfile.podvm . +For Fedora (`mkosi.presets/system/mkosi.conf.d/fedora-extra.conf`): +```ini +[Match] +Distribution=fedora + +[Content] +Packages= + cowsay ``` -The podvm image can also be built using UEFI based images. For example if you want to build a -RHEL podvm image using UEFI based qcow2 image, then run the build using as shown below: +For Ubuntu (`mkosi.presets/system/mkosi.conf.d/ubuntu-extra.conf`): +```ini +[Match] +Distribution=ubuntu +[Content] +Packages= + cowsay ``` -# RHEL Dockerfile supports in passing an image file, file has to be in the docker context -$ docker build -t podvm-uefi \ - --build-arg BUILDER_IMG=podvm_builder \ - --build-arg BINARIES_IMG=podvm_binaries \ - --build-arg UEFI=true \ - --build-arg IMAGE_CHECKSUM="_qcow2_image_checksum" \ - --build-arg IMAGE_URL="uefi.qcow2" \ - -f Dockerfile.podvm.rhel . -``` - -## Extracting the qcow2 image -The final podvm image, i.e. the qcow2 file, is stored on the root of the podvm -container image. +## Limitations -There are a couple of ways to extract files from a container image using docker -or podman. However, to ease that task we have the -[hack/download-image.sh](hack/download-image.sh) script, which copy the qcow2 -file out of the podvm container image. +The following limitations apply to these images. Notice that the limitations are intentional to +reduce complexity of configuration and CI and shall not be seen as open to-dos. -Running the below command will extract the qcow2 image built in the previous step. +- Deployed images cannot be customized with cloud-init. Runtime configuration data is retrieved + from IMDS via the project's `process-user-data` tool. -```bash -$ ./hack/download-image.sh podvm:latest . -o podvm.qcow2 +## Build s390x image +We can use the mkosi **ToolsTree** feature defined in `mkosi.conf` to download latest tools automatically: ``` -Running the below command will extract the Secure Execution enabled qcow2 image built in the previous step. - -```bash -$ ./hack/download-image.sh se_podvm_s390x:latest . -o se_podvm.qcow2 +[Runtime] +ToolsTree=default ``` +And install **mkosi** from the repository: +```sh +git clone -b v26 https://github.com/systemd/mkosi +ln -s $PWD/mkosi/bin/mkosi /usr/local/bin/mkosi +mkosi --version +``` +Another issue is s390x does not support UEFI. Instead, we can first use **mkosi** to build non-bootable system files, then use **zipl** to generate the bootloader and finally create the bootable image. -# How to add support for a new Linux distribution - -In order to add a new Linux distribution essentially it is needed to create some dockerfiles and add the packer configuration files. - -Follow the steps below, replacing `DISTRO` with the name of the distribution being added: +It requires a **s390x host** to build s390x image with make commands: +``` +TEE_PLATFORM=se-attester make podvm-binaries +make image +# SE_BOOT=true make image +# make image-debug +# SE_BOOT=true make image-debug +``` -1. Create the builder dockerfile by copying `Dockerfile.podvm_builder` to `Dockerfile.podvm_builder.DISTRO` and - adjusting the file properly (e.g. replace `FROM ubuntu:24.04` with `FROM DISTRO`). Try to keep the same - software versions (e.g. Golang) as much as possible. -2. Create the podvm image dockerfile by copying `Dockerfile.podvm` to `Dockerfile.podvm.DISTRO` and adjusting the file - properly likewise. In particular, the *PODVM_DISTRO* and *BUILDER_IMG* arguments should be changed. -3. Create the podvm binaries dockerfile by copying `Dockerfile.podvm_binaries` - to `Dockerfile.podvm_binaries.DISTRO` and adjusting the file as needed. -4. Create the packer directory (`mkdir qcow2/DISTRO`) where the - `qemu-DISTRO.pkr.hcl` and `variables.pkr.hcl` files should be placed. Also on - this step you can also use an existing configuration (e.g. `qcow2/ubuntu`) as a - template. Ensure that common scripts and files like the - `qcow2/misc-settings.sh` are adjusted to support the DISTRO if needed. -5. Define the base image URL and checksum in the `Makefile` file. -6. Update this *README.md* properly in case that there are specific instructions and/or constraints for the DISTRO. +The final output is `build/podvm-s390x.qcow2` or `build/podvm-s390x-se.qcow2`, which can be used as the Pod VM image in libvirt environment. diff --git a/src/cloud-api-adaptor/podvm/addons/README.md b/src/cloud-api-adaptor/podvm/addons/README.md deleted file mode 100644 index 933e2f2c62..0000000000 --- a/src/cloud-api-adaptor/podvm/addons/README.md +++ /dev/null @@ -1,7 +0,0 @@ -## Introduction - -The addons directory is used to enable different addons for the podvm image. -Each addon and its associated files (binaries, configuration etc) should be under -specific sub-dir under `addons`. - -Each addon sub-dir needs to have `setup.sh` for setting up the addon. diff --git a/src/cloud-api-adaptor/podvm/addons/nvidia_gpu/README.md b/src/cloud-api-adaptor/podvm/addons/nvidia_gpu/README.md deleted file mode 100644 index b113b0312d..0000000000 --- a/src/cloud-api-adaptor/podvm/addons/nvidia_gpu/README.md +++ /dev/null @@ -1,76 +0,0 @@ -## Introduction - -This addon enables nvidia GPU support in the podvm image. - -You need to specify the GPU instance types in the cloud-api-adaptor configMap (peer-pods-cm). - -Here is an example. Replace it as appropriate depending on the specific provider and region - -``` -# For AWS -PODVM_INSTANCE_TYPES: "t3.small,c5.xlarge,p3.2xlarge" - -# For Azure -AZURE_INSTANCE_SIZES: "Standard_D8as_v5,Standard_D4as_v5,Standard_NC6s_v3,Standard_NC4as_T4_v3" - -``` - -Example pod definition: -``` -apiVersion: v1 -kind: Pod -metadata: - name: gpu-test - labels: - app: test - annotations: - io.katacontainers.config.hypervisor.machine_type: Standard_NC4as_T4_v3 - io.containerd.cri.runtime-handler: kata-remote -spec: - runtimeClassName: kata-remote - containers: - - name: ubuntu - image: ubuntu - command: ["sleep"] - args: ["infinity"] - env: - - name: NVIDIA_VISIBLE_DEVICES - value: "all" -``` - -You can verify the GPU devices by execing a shell in the pod as shown below: - -``` -$ kubectl exec -it gpu-test -- bash -root@gpu-test:/# nvidia-smi -Thu Nov 23 17:30:58 2023 -+---------------------------------------------------------------------------------------+ -| NVIDIA-SMI 535.129.03 Driver Version: 535.129.03 CUDA Version: 12.2 | -|-----------------------------------------+----------------------+----------------------+ -| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC | -| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. | -| | | MIG M. | -|=========================================+======================+======================| -| 0 Tesla T4 Off | 00000001:00:00.0 Off | Off | -| N/A 36C P8 9W / 70W | 2MiB / 16384MiB | 0% Default | -| | | N/A | -+-----------------------------------------+----------------------+----------------------+ - -+---------------------------------------------------------------------------------------+ -| Processes: | -| GPU GI CI PID Type Process name GPU Memory | -| ID ID Usage | -|=======================================================================================| -| No running processes found | -+---------------------------------------------------------------------------------------+ - -root@gpu-test:/# nvidia-smi -L -GPU 0: Tesla T4 (UUID: GPU-2b9a9945-a56c-fcf3-7156-8e380cf1d0cc) - -root@gpu-test:/# ls -l /dev/nvidia* -crw-rw-rw- 1 root root 235, 0 Nov 23 17:27 /dev/nvidia-uvm -crw-rw-rw- 1 root root 235, 1 Nov 23 17:27 /dev/nvidia-uvm-tools -crw-rw-rw- 1 root root 195, 0 Nov 23 17:27 /dev/nvidia0 -crw-rw-rw- 1 root root 195, 255 Nov 23 17:27 /dev/nvidiactl - -``` diff --git a/src/cloud-api-adaptor/podvm/addons/nvidia_gpu/setup.sh b/src/cloud-api-adaptor/podvm/addons/nvidia_gpu/setup.sh deleted file mode 100755 index cdd02859bb..0000000000 --- a/src/cloud-api-adaptor/podvm/addons/nvidia_gpu/setup.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/bash - -NVIDIA_DRIVER_VERSION=${NVIDIA_DRIVER_VERSION:-535} -NVIDIA_USERSPACE_VERSION=${NVIDIA_USERSPACE_VERSION:-1.13.5-1} - -NVIDIA_USERSPACE_PKGS=(nvidia-container-toolkit libnvidia-container1 libnvidia-container-tools) - -# Create the prestart hook directory -mkdir -p /usr/share/oci/hooks/prestart - -# Add hook script -cat <<'END' > /usr/share/oci/hooks/prestart/nvidia-container-toolkit.sh -#!/bin/bash -x - -# Log the o/p of the hook to a file -/usr/bin/nvidia-container-toolkit -debug "$@" > /var/log/nvidia-hook.log 2>&1 -END - -# Make the script executable -chmod +x /usr/share/oci/hooks/prestart/nvidia-container-toolkit.sh - - -# according to https://developer.download.nvidia.com/compute/cuda/repos/rhel9/x86_64/precompiled/ -# the correct way to install pre-compiled drivers is to update kernel and opt-into a precompiled modularity stream, -# however, it may take some time between latest kernel version is released until the precompiled pkgs are available; -# which may cause driver failure. -# This function tries to update the kernel to the latest version supported by the nvidia precompiled drivers, if fails -# it upgrades to the latest kernel version available. -rhel_kernel_version_matching() { - dnf -q -y module enable nvidia-driver:${NVIDIA_DRIVER_VERSION} - local latest_nv_version # latest nvidia driver available version - local latest_kmod_pkg_name # latest version of kmod-nvidia pkg name - local matching_kernel_version # extracted matching kernel version from kmod-nvidia name - latest_nv_version=$(repoquery --latest-limit 1 nvidia-driver --queryformat "%{version}" -q) - latest_kmod_pkg_name=$(repoquery --latest-limit 1 kmod-nvidia-${latest_nv_version}-* --queryformat "%{name}" -q | sort -V | tail -n 1) - matching_kernel_version=$(echo ${latest_kmod_pkg_name} | sed "s/kmod-nvidia-${latest_nv_version}-//") - echo "latest_nv_version: ${latest_nv_version}, latest_kmod_pkg_name: ${latest_kmod_pkg_name}, matching_kernel_version: ${matching_kernel_version}" - dnf -q -y install kernel-${matching_kernel_version}* kernel-core-${matching_kernel_version}* kernel-modules-core-${matching_kernel_version}* kernel-modules-${matching_kernel_version}* - if [ $? -eq 0 ]; then - return 0 - else - dnf -q -y update kernel kernel-core kernel-modules-core kernel-modules - fi -} - -# PODVM_DISTRO variable is set as part of the podvm image build process -# and available inside the packer VM -# Add NVIDIA packages -if [[ "$PODVM_DISTRO" == "ubuntu" ]]; then - export DEBIAN_FRONTEND=noninteractive - distribution=$(. /etc/os-release;echo $ID$VERSION_ID) - curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg - curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list - apt-get -q update -y - apt-get -q install -y "${NVIDIA_USERSPACE_PKGS[@]/%/-${NVIDIA_USERSPACE_VERSION}}" - apt-get -q install -y nvidia-driver-${NVIDIA_DRIVER_VERSION} -fi -if [[ "$PODVM_DISTRO" == "rhel" ]]; then - dnf config-manager --add-repo http://developer.download.nvidia.com/compute/cuda/repos/rhel9/x86_64/cuda-rhel9.repo - rhel_kernel_version_matching - - dnf install -q -y "${NVIDIA_USERSPACE_PKGS[@]/%/-${NVIDIA_USERSPACE_VERSION}}" - # This will use the default stream - dnf -q -y module install nvidia-driver:${NVIDIA_DRIVER_VERSION} -fi - -# Configure the settings for nvidia-container-runtime -sed -i "s/#debug/debug/g" /etc/nvidia-container-runtime/config.toml -sed -i "s|/var/log|/var/log/nvidia-kata-container|g" /etc/nvidia-container-runtime/config.toml -sed -i "s/#no-cgroups = false/no-cgroups = true/g" /etc/nvidia-container-runtime/config.toml -sed -i "/\[nvidia-container-cli\]/a no-pivot = true" /etc/nvidia-container-runtime/config.toml -sed -i "s/disable-require = false/disable-require = true/g" /etc/nvidia-container-runtime/config.toml -sed -i "s/info/debug/g" /etc/nvidia-container-runtime/config.toml - - diff --git a/src/cloud-api-adaptor/podvm/addons/setup_addons.sh b/src/cloud-api-adaptor/podvm/addons/setup_addons.sh deleted file mode 100755 index 74191df7fa..0000000000 --- a/src/cloud-api-adaptor/podvm/addons/setup_addons.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -set -euo pipefail - -# This is the dir in the pod vm image during build -ADDONS_DIR="/tmp/addons" - -# Check environment variables and execute corresponding scripts -if [[ "${ENABLE_NVIDIA_GPU}" == "yes" ]]; then - echo "Setting up Nvidia GPU" - ${ADDONS_DIR}/nvidia_gpu/setup.sh -fi diff --git a/src/cloud-api-adaptor/podvm/qcow2/copy-files.sh b/src/cloud-api-adaptor/podvm/copy-files.sh similarity index 100% rename from src/cloud-api-adaptor/podvm/qcow2/copy-files.sh rename to src/cloud-api-adaptor/podvm/copy-files.sh diff --git a/src/cloud-api-adaptor/podvm-mkosi/entrypoint.sh b/src/cloud-api-adaptor/podvm/entrypoint.sh similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/entrypoint.sh rename to src/cloud-api-adaptor/podvm/entrypoint.sh diff --git a/src/cloud-api-adaptor/podvm/hack/cross-build-extras.sh b/src/cloud-api-adaptor/podvm/hack/cross-build-extras.sh index f34f476c66..20ad7a29dd 100755 --- a/src/cloud-api-adaptor/podvm/hack/cross-build-extras.sh +++ b/src/cloud-api-adaptor/podvm/hack/cross-build-extras.sh @@ -6,8 +6,9 @@ # If ARCH is not set, exit [[ -z $ARCH ]] && exit 0 -# If ARCH is equal to HOST, exit -[[ $ARCH = $(uname -m) ]] && exit 0 +# If ARCH is equal to HOST, exit (no cross-compilation needed) +# Use replacement pattern to normalize arm64 to aarch64 for comparison +[[ "${ARCH/arm64/aarch64}" = "$(uname -m)" ]] && exit 0 # Only gnu is available for s390x and aarch64 libc=$([[ $ARCH =~ s390x || $ARCH =~ aarch64 ]] && echo "gnu" || echo "musl") @@ -17,7 +18,21 @@ rustup target add "$rustTarget" source /etc/os-release || source /usr/lib/os-release if [[ ${ID_LIKE:-} == *"debian"* ]]; then - apt install -y "qemu-system-$ARCH" + # Map architecture names to QEMU package names for Debian/Ubuntu + # Verified package names from: apt-cache search '^qemu-system-' + case "$ARCH" in + x86_64) + qemu_pkg="qemu-system-x86" + ;; + aarch64) + qemu_pkg="qemu-system-arm" + ;; + *) + # s390x and ppc64le match directly + qemu_pkg="qemu-system-$ARCH" + ;; + esac + apt install -y "$qemu_pkg" apt install -y "gcc-$ARCH-linux-$libc" elif [[ "${ID_LIKE:-}" =~ "fedora" ]] || [[ "${ID:-}" =~ "fedora" ]]; then dnf install -y "qemu-system-$ARCH" diff --git a/src/cloud-api-adaptor/podvm/hack/download-image.sh b/src/cloud-api-adaptor/podvm/hack/download-image.sh deleted file mode 100755 index 9818ed7b5f..0000000000 --- a/src/cloud-api-adaptor/podvm/hack/download-image.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/bin/bash -# download-image.sh -# takes an image reference and a directory and -# extracts the container image containing the packer generated qcow pod vm image into that directory -function usage() { - echo "Usage: $0 [-o ] [-p ] [--pull always(default)|missing|never] [--clean-up]" -} - -function error() { - echo "[ERROR] $1" 1>&2 && exit 1 -} - -image=$1 -directory=$2 -output= -platform= -clean_up_image= -pull=always - -shift 2 -while (( "$#" )); do - case "$1" in - -o) output=$2 ;; - --output) output=$2 ;; - -p) platform=$2 ;; - --platform) platform=$2 ;; - --pull) pull=$2 ;; - --clean-up) clean_up_image="yes"; shift 1 ;; - --help) usage; exit 0 ;; - *) usage 1>&2; exit 1;; - esac - shift 2 -done - -[[ -z "$image" || -z "$directory" ]] && 1>&2 usage && exit 1 - -container_name="podvm-exporter-$(date +%Y%m%d%H%M%s)" - -# Default to use docker, but check if podman is available -if docker --version >/dev/null 2>&1; then - container_binary=docker -elif podman --version >/dev/null 2>&1; then - container_binary=podman -fi - -[ -z "$container_binary" ] && error "please install docker or podman" - -platform_flag="" -if [[ -n "${platform}" ]]; then - platform_flag="--platform=${platform}" -elif [[ "${image}" == *"podvm-generic-ubuntu-s390x"* ]]; then - # The podvm-generic-ubuntu-s390x image (which will be deprecated soon) is built on amd64 - # and therefore has the incorrect platform type, so we need to override it to create the - # container to extract the image from. Others can use their default platform - platform_flag="--platform=amd64" -fi - -# Create a non-running container to extract image -$container_binary create --pull=${pull} ${platform_flag} --name "$container_name" "$image" /bin/sh >/dev/null 2>&1; -# Destory container after use -rm-container(){ - $container_binary rm -f "$container_name" >/dev/null 2>&1; - if [ -n "${clean_up_image:-}" ]; then - $container_binary rmi ${image} - fi -} -trap rm-container 0 - -podvm=$($container_binary export "$container_name" | tar t | grep podvm) -# Check if file is in image -[ -z "$podvm" ] && error "unable to find podvm qcow2 image" -# If output is not set default to podvm name -[ -z "$output" ] && output="$podvm" - -$container_binary cp "$container_name:/$podvm" "$directory/$output" -echo $output diff --git a/src/cloud-api-adaptor/podvm/hack/smoke_test.sh b/src/cloud-api-adaptor/podvm/hack/smoke_test.sh index f18cba36ce..7e0463139b 100755 --- a/src/cloud-api-adaptor/podvm/hack/smoke_test.sh +++ b/src/cloud-api-adaptor/podvm/hack/smoke_test.sh @@ -162,8 +162,8 @@ if [ -z "$KATACTL" ]; then echo "::error:: kata-agent-ctl command not cached for $(uname -m), please compile it yourself and put into PATH or current dir." exit 1 fi - KATA_REF=$(yq -e '.oci.kata-agent-ctl.reference' ${SCRIPTDIR}/../../versions.yaml) - KATA_REG=$(yq -e '.oci.kata-agent-ctl.registry' ${SCRIPTDIR}/../../versions.yaml) + KATA_REF=$(yq -e '.oci.kata-containers.reference' ${SCRIPTDIR}/../../versions.yaml) + KATA_REG=$(yq -e '.oci.kata-containers.registry' ${SCRIPTDIR}/../../versions.yaml) echo "::debug:: Pulling kata-ctl from ${KATA_REG}/agent-ctl:${KATA_REF}-x86_64" oras pull "${KATA_REG}/agent-ctl:${KATA_REF}-x86_64" tar --ztsd -xvf kata-static-agent-ctl.tar.zst ./opt/kata/bin/kata-agent-ctl --transform='s/opt\/kata\/bin\/kata-agent-ctl/kata-agent-ctl/' diff --git a/src/cloud-api-adaptor/podvm/qcow2/misc-settings.sh b/src/cloud-api-adaptor/podvm/misc-settings.sh similarity index 77% rename from src/cloud-api-adaptor/podvm/qcow2/misc-settings.sh rename to src/cloud-api-adaptor/podvm/misc-settings.sh index 0150e12126..88db325ba5 100755 --- a/src/cloud-api-adaptor/podvm/qcow2/misc-settings.sh +++ b/src/cloud-api-adaptor/podvm/misc-settings.sh @@ -27,28 +27,26 @@ if [[ "$CLOUD_PROVIDER" == "azure" || "$CLOUD_PROVIDER" == "generic" ]] && [[ "$ sudo apt-get install -y --no-install-recommends libtss2-tctildr0 fi -# Install iptables for all providers except docker. -if [[ "$CLOUD_PROVIDER" != "docker" ]]; then - if [ ! -x "$(command -v iptables)" ]; then - case $PODVM_DISTRO in - rhel) - if [[ "$ARCH" == "s390x" ]]; then - dnf -q install iptables-nft -y && dnf install -y kernel-modules-"$(uname -r)" kernel-modules-extra-"$(uname -r)" - dnf install -y 'dnf-command(versionlock)' - dnf versionlock add kernel-"$(uname -r)" - dnf versionlock add kernel-modules-"$(uname -r)" - else - dnf -q install iptables -y - fi - ;; - ubuntu) - apt-get -qq update && apt-get -qq install iptables -y - ;; - *) - echo "\"iptables\" is missing and cannot be installed, setup-nat-for-imds.sh is likely to fail 1>&2" - ;; - esac - fi +# Install iptables for all providers. +if [ ! -x "$(command -v iptables)" ]; then + case $PODVM_DISTRO in + rhel) + if [[ "$ARCH" == "s390x" ]]; then + dnf -q install iptables-nft -y && dnf install -y kernel-modules-"$(uname -r)" kernel-modules-extra-"$(uname -r)" + dnf install -y 'dnf-command(versionlock)' + dnf versionlock add kernel-"$(uname -r)" + dnf versionlock add kernel-modules-"$(uname -r)" + else + dnf -q install iptables -y + fi + ;; + ubuntu) + apt-get -qq update && apt-get -qq install iptables -y + ;; + *) + echo "\"iptables\" is missing and cannot be installed, setup-nat-for-imds.sh is likely to fail 1>&2" + ;; + esac fi if [ -e /etc/certificates/tls.crt ] && [ -e /etc/certificates/tls.key ] && [ -e /etc/certificates/ca.crt ]; then diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.cache/.gitkeep b/src/cloud-api-adaptor/podvm/mkosi.cache/.gitkeep similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.cache/.gitkeep rename to src/cloud-api-adaptor/podvm/mkosi.cache/.gitkeep diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.conf b/src/cloud-api-adaptor/podvm/mkosi.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.conf rename to src/cloud-api-adaptor/podvm/mkosi.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.conf.fedora b/src/cloud-api-adaptor/podvm/mkosi.conf.fedora similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.conf.fedora rename to src/cloud-api-adaptor/podvm/mkosi.conf.fedora diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.conf.ubuntu b/src/cloud-api-adaptor/podvm/mkosi.conf.ubuntu similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.conf.ubuntu rename to src/cloud-api-adaptor/podvm/mkosi.conf.ubuntu diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/initrd/mkosi.conf b/src/cloud-api-adaptor/podvm/mkosi.images/initrd/mkosi.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/initrd/mkosi.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/initrd/mkosi.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/initrd/mkosi.conf.d/fedora-s390x.conf b/src/cloud-api-adaptor/podvm/mkosi.images/initrd/mkosi.conf.d/fedora-s390x.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/initrd/mkosi.conf.d/fedora-s390x.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/initrd/mkosi.conf.d/fedora-s390x.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/initrd/mkosi.conf.d/fedora.conf b/src/cloud-api-adaptor/podvm/mkosi.images/initrd/mkosi.conf.d/fedora.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/initrd/mkosi.conf.d/fedora.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/initrd/mkosi.conf.d/fedora.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/initrd/mkosi.conf.d/ubuntu-s390x.conf b/src/cloud-api-adaptor/podvm/mkosi.images/initrd/mkosi.conf.d/ubuntu-s390x.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/initrd/mkosi.conf.d/ubuntu-s390x.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/initrd/mkosi.conf.d/ubuntu-s390x.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/initrd/mkosi.conf.d/ubuntu.conf b/src/cloud-api-adaptor/podvm/mkosi.images/initrd/mkosi.conf.d/ubuntu.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/initrd/mkosi.conf.d/ubuntu.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/initrd/mkosi.conf.d/ubuntu.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/initrd/mkosi.postinst b/src/cloud-api-adaptor/podvm/mkosi.images/initrd/mkosi.postinst similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/initrd/mkosi.postinst rename to src/cloud-api-adaptor/podvm/mkosi.images/initrd/mkosi.postinst diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/initrd/mkosi.skeleton/usr/lib/systemd/network/20-wired.network b/src/cloud-api-adaptor/podvm/mkosi.images/initrd/mkosi.skeleton/usr/lib/systemd/network/20-wired.network similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/initrd/mkosi.skeleton/usr/lib/systemd/network/20-wired.network rename to src/cloud-api-adaptor/podvm/mkosi.images/initrd/mkosi.skeleton/usr/lib/systemd/network/20-wired.network diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/initrd/mkosi.skeleton/usr/lib/systemd/network/21-azure.network b/src/cloud-api-adaptor/podvm/mkosi.images/initrd/mkosi.skeleton/usr/lib/systemd/network/21-azure.network similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/initrd/mkosi.skeleton/usr/lib/systemd/network/21-azure.network rename to src/cloud-api-adaptor/podvm/mkosi.images/initrd/mkosi.skeleton/usr/lib/systemd/network/21-azure.network diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/initrd/mkosi.skeleton/usr/lib/systemd/resolved.conf.d/fallback_dns.conf b/src/cloud-api-adaptor/podvm/mkosi.images/initrd/mkosi.skeleton/usr/lib/systemd/resolved.conf.d/fallback_dns.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/initrd/mkosi.skeleton/usr/lib/systemd/resolved.conf.d/fallback_dns.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/initrd/mkosi.skeleton/usr/lib/systemd/resolved.conf.d/fallback_dns.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/initrd/mkosi.skeleton/usr/lib/systemd/system-preset/20-coco-sys.preset b/src/cloud-api-adaptor/podvm/mkosi.images/initrd/mkosi.skeleton/usr/lib/systemd/system-preset/20-coco-sys.preset similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/initrd/mkosi.skeleton/usr/lib/systemd/system-preset/20-coco-sys.preset rename to src/cloud-api-adaptor/podvm/mkosi.images/initrd/mkosi.skeleton/usr/lib/systemd/system-preset/20-coco-sys.preset diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/initrd/mkosi.skeleton/usr/lib/systemd/system/afterburn-checkin.service.d/10-override.conf b/src/cloud-api-adaptor/podvm/mkosi.images/initrd/mkosi.skeleton/usr/lib/systemd/system/afterburn-checkin.service.d/10-override.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/initrd/mkosi.skeleton/usr/lib/systemd/system/afterburn-checkin.service.d/10-override.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/initrd/mkosi.skeleton/usr/lib/systemd/system/afterburn-checkin.service.d/10-override.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/initrd/mkosi.skeleton/usr/lib/systemd/system/process-user-data.service.d/10-override.conf b/src/cloud-api-adaptor/podvm/mkosi.images/initrd/mkosi.skeleton/usr/lib/systemd/system/process-user-data.service.d/10-override.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/initrd/mkosi.skeleton/usr/lib/systemd/system/process-user-data.service.d/10-override.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/initrd/mkosi.skeleton/usr/lib/systemd/system/process-user-data.service.d/10-override.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/fedora-bootable.conf b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/fedora-bootable.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/fedora-bootable.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/fedora-bootable.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/fedora-debug-aux.conf b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/fedora-debug-aux.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/fedora-debug-aux.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/fedora-debug-aux.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/fedora-debug-keys.conf b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/fedora-debug-keys.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/fedora-debug-keys.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/fedora-debug-keys.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/fedora-debug.conf b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/fedora-debug.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/fedora-debug.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/fedora-debug.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/fedora-s390x.conf b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/fedora-s390x.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/fedora-s390x.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/fedora-s390x.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/fedora-sftp.conf b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/fedora-sftp.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/fedora-sftp.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/fedora-sftp.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/fedora.conf b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/fedora.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/fedora.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/fedora.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/ubuntu-bootable.conf b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/ubuntu-bootable.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/ubuntu-bootable.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/ubuntu-bootable.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/ubuntu-debug-aux.conf b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/ubuntu-debug-aux.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/ubuntu-debug-aux.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/ubuntu-debug-aux.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/ubuntu-debug-keys.conf b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/ubuntu-debug-keys.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/ubuntu-debug-keys.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/ubuntu-debug-keys.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/ubuntu-debug.conf b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/ubuntu-debug.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/ubuntu-debug.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/ubuntu-debug.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/ubuntu-s390x.conf b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/ubuntu-s390x.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/ubuntu-s390x.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/ubuntu-s390x.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/ubuntu-sftp.conf b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/ubuntu-sftp.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/ubuntu-sftp.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/ubuntu-sftp.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/ubuntu.conf b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/ubuntu.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.conf.d/ubuntu.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.conf.d/ubuntu.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.finalize.chroot b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.finalize.chroot similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.finalize.chroot rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.finalize.chroot diff --git a/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.postinst b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.postinst new file mode 100755 index 0000000000..84a89e9538 --- /dev/null +++ b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.postinst @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +set -euxo pipefail + +{ + echo "IMAGE_ID=\"podvm\"" + echo "IMAGE_VERSION=\"${IMAGE_VERSION-v0.0.0}\"" + echo "VARIANT_ID=\"${VARIANT_ID}\"" +} >> "${BUILDROOT}/etc/os-release" + +# Install Azure storage udev rules so /dev/disk/azure/data/by-lun/N symlinks +# are created at boot without needing the full walinuxagent package. +mkdir -p "${BUILDROOT}/etc/udev/rules.d" +cat > "${BUILDROOT}/etc/udev/rules.d/66-azure-storage.rules" << 'UDEVRULES' +# Azure data disk symlinks by LUN +ACTION=="add|change", SUBSYSTEM=="block", ENV{ID_VENDOR}=="Msft", ENV{ID_MODEL}=="Virtual_Disk", PROGRAM="/bin/sh -c 'readlink /sys/block/%k/device 2>/dev/null | sed -n \"s/.*[:/]\\([0-9]*\\)$/\\1/p\"'", SYMLINK+="disk/azure/data/by-lun/%c" +UDEVRULES diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.profiles/debug.conf b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.profiles/debug.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.profiles/debug.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.profiles/debug.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.profiles/production.conf b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.profiles/production.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.profiles/production.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.profiles/production.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.profiles/sftp.conf b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.profiles/sftp.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.profiles/sftp.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.profiles/sftp.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.repart/00-esp.conf b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.repart/00-esp.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.repart/00-esp.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.repart/00-esp.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.repart/10-root.conf b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.repart/10-root.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.repart/10-root.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.repart/10-root.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.repart/20-root-verity.conf b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.repart/20-root-verity.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.repart/20-root-verity.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.repart/20-root-verity.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/etc/fastfetch/config.jsonc b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/etc/fastfetch/config.jsonc similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/etc/fastfetch/config.jsonc rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/etc/fastfetch/config.jsonc diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/etc/fastfetch/logo.txt b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/etc/fastfetch/logo.txt similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/etc/fastfetch/logo.txt rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/etc/fastfetch/logo.txt diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/etc/profile.d/10-alias.sh b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/etc/profile.d/10-alias.sh similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/etc/profile.d/10-alias.sh rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/etc/profile.d/10-alias.sh diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/etc/profile.d/20-ssh-banner.sh b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/etc/profile.d/20-ssh-banner.sh similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/etc/profile.d/20-ssh-banner.sh rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/etc/profile.d/20-ssh-banner.sh diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/usr/lib/repart.d/30-scratch.conf b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/usr/lib/repart.d/30-scratch.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/usr/lib/repart.d/30-scratch.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/usr/lib/repart.d/30-scratch.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/network/20-wired.network b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/network/20-wired.network similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/network/20-wired.network rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/network/20-wired.network diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/network/21-azure.network b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/network/21-azure.network similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/network/21-azure.network rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/network/21-azure.network diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/resolved.conf.d/fallback_dns.conf b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/resolved.conf.d/fallback_dns.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/resolved.conf.d/fallback_dns.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/resolved.conf.d/fallback_dns.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/system-preset/20-coco-sys.preset b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/system-preset/20-coco-sys.preset similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/system-preset/20-coco-sys.preset rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/system-preset/20-coco-sys.preset diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/system-preset/30-coco.preset b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/system-preset/30-coco.preset similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/system-preset/30-coco.preset rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/system-preset/30-coco.preset diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/system/afterburn-checkin.service.d/10-override.conf b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/system/afterburn-checkin.service.d/10-override.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/system/afterburn-checkin.service.d/10-override.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/system/afterburn-checkin.service.d/10-override.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/system/gen-issue.service b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/system/gen-issue.service similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/system/gen-issue.service rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/system/gen-issue.service diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/system/process-user-data.service.d/10-override.conf b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/system/process-user-data.service.d/10-override.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/system/process-user-data.service.d/10-override.conf rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/usr/lib/systemd/system/process-user-data.service.d/10-override.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/usr/libexec/gen-issue b/src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/usr/libexec/gen-issue similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.images/system/mkosi.skeleton/usr/libexec/gen-issue rename to src/cloud-api-adaptor/podvm/mkosi.images/system/mkosi.skeleton/usr/libexec/gen-issue diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-debug/etc/ssh/sshd_config b/src/cloud-api-adaptor/podvm/mkosi.skeleton-debug/etc/ssh/sshd_config similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-debug/etc/ssh/sshd_config rename to src/cloud-api-adaptor/podvm/mkosi.skeleton-debug/etc/ssh/sshd_config diff --git a/src/cloud-api-adaptor/podvm/mkosi.skeleton-debug/usr/lib/systemd/system-preset/21-coco-debug-ssh.preset b/src/cloud-api-adaptor/podvm/mkosi.skeleton-debug/usr/lib/systemd/system-preset/21-coco-debug-ssh.preset new file mode 100644 index 0000000000..2aeea38b8f --- /dev/null +++ b/src/cloud-api-adaptor/podvm/mkosi.skeleton-debug/usr/lib/systemd/system-preset/21-coco-debug-ssh.preset @@ -0,0 +1,4 @@ +# Ubuntu uses ssh.service (not sshd.service) for the OpenSSH daemon. +# ssh-host-keys.service is enabled via WantedBy=ssh.service (no preset needed). +# Explicitly enable ssh.service for Ubuntu debug images. +enable ssh.service diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-debug/usr/lib/systemd/system/api-server-rest.service.d/20-debug.conf b/src/cloud-api-adaptor/podvm/mkosi.skeleton-debug/usr/lib/systemd/system/api-server-rest.service.d/20-debug.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-debug/usr/lib/systemd/system/api-server-rest.service.d/20-debug.conf rename to src/cloud-api-adaptor/podvm/mkosi.skeleton-debug/usr/lib/systemd/system/api-server-rest.service.d/20-debug.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-debug/usr/lib/systemd/system/attestation-agent.service.d/20-debug.conf b/src/cloud-api-adaptor/podvm/mkosi.skeleton-debug/usr/lib/systemd/system/attestation-agent.service.d/20-debug.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-debug/usr/lib/systemd/system/attestation-agent.service.d/20-debug.conf rename to src/cloud-api-adaptor/podvm/mkosi.skeleton-debug/usr/lib/systemd/system/attestation-agent.service.d/20-debug.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-debug/usr/lib/systemd/system/confidential-data-hub.service.d/20-debug.conf b/src/cloud-api-adaptor/podvm/mkosi.skeleton-debug/usr/lib/systemd/system/confidential-data-hub.service.d/20-debug.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-debug/usr/lib/systemd/system/confidential-data-hub.service.d/20-debug.conf rename to src/cloud-api-adaptor/podvm/mkosi.skeleton-debug/usr/lib/systemd/system/confidential-data-hub.service.d/20-debug.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-debug/usr/lib/systemd/system/kata-agent.service.d/20-debug.conf b/src/cloud-api-adaptor/podvm/mkosi.skeleton-debug/usr/lib/systemd/system/kata-agent.service.d/20-debug.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-debug/usr/lib/systemd/system/kata-agent.service.d/20-debug.conf rename to src/cloud-api-adaptor/podvm/mkosi.skeleton-debug/usr/lib/systemd/system/kata-agent.service.d/20-debug.conf diff --git a/src/cloud-api-adaptor/podvm/mkosi.skeleton-debug/usr/lib/systemd/system/ssh-host-keys.service b/src/cloud-api-adaptor/podvm/mkosi.skeleton-debug/usr/lib/systemd/system/ssh-host-keys.service new file mode 100644 index 0000000000..b0aabfdebe --- /dev/null +++ b/src/cloud-api-adaptor/podvm/mkosi.skeleton-debug/usr/lib/systemd/system/ssh-host-keys.service @@ -0,0 +1,16 @@ +[Unit] +Description=Generate SSH host keys for /run/ssh +Documentation=man:sshd(8) +DefaultDependencies=no +Before=ssh.service + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=mkdir -p /run/ssh +ExecStart=ssh-keygen -q -t rsa -f /run/ssh/ssh_host_rsa_key -N "" -C "" +ExecStart=ssh-keygen -q -t ecdsa -f /run/ssh/ssh_host_ecdsa_key -N "" -C "" +ExecStart=ssh-keygen -q -t ed25519 -f /run/ssh/ssh_host_ed25519_key -N "" -C "" + +[Install] +WantedBy=ssh.service diff --git a/src/cloud-api-adaptor/podvm/mkosi.skeleton-debug/usr/lib/systemd/system/ssh.service.d/10-host-keys.conf b/src/cloud-api-adaptor/podvm/mkosi.skeleton-debug/usr/lib/systemd/system/ssh.service.d/10-host-keys.conf new file mode 100644 index 0000000000..69e639883a --- /dev/null +++ b/src/cloud-api-adaptor/podvm/mkosi.skeleton-debug/usr/lib/systemd/system/ssh.service.d/10-host-keys.conf @@ -0,0 +1,3 @@ +[Unit] +Wants=ssh-host-keys.service +After=ssh-host-keys.service diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-debug/usr/lib/systemd/system/sshd-keygen@.service.d/10-override.conf b/src/cloud-api-adaptor/podvm/mkosi.skeleton-debug/usr/lib/systemd/system/sshd-keygen@.service.d/10-override.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-debug/usr/lib/systemd/system/sshd-keygen@.service.d/10-override.conf rename to src/cloud-api-adaptor/podvm/mkosi.skeleton-debug/usr/lib/systemd/system/sshd-keygen@.service.d/10-override.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-debug/usr/libexec/openssh/sshd-keygen b/src/cloud-api-adaptor/podvm/mkosi.skeleton-debug/usr/libexec/openssh/sshd-keygen similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-debug/usr/libexec/openssh/sshd-keygen rename to src/cloud-api-adaptor/podvm/mkosi.skeleton-debug/usr/libexec/openssh/sshd-keygen diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-sftp/etc/ssh/sshd_config b/src/cloud-api-adaptor/podvm/mkosi.skeleton-sftp/etc/ssh/sshd_config similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-sftp/etc/ssh/sshd_config rename to src/cloud-api-adaptor/podvm/mkosi.skeleton-sftp/etc/ssh/sshd_config diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-sftp/usr/lib/systemd/system-preset/40-sftp-sys.preset b/src/cloud-api-adaptor/podvm/mkosi.skeleton-sftp/usr/lib/systemd/system-preset/40-sftp-sys.preset similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-sftp/usr/lib/systemd/system-preset/40-sftp-sys.preset rename to src/cloud-api-adaptor/podvm/mkosi.skeleton-sftp/usr/lib/systemd/system-preset/40-sftp-sys.preset diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-sftp/usr/lib/systemd/system/media-cidata.mount b/src/cloud-api-adaptor/podvm/mkosi.skeleton-sftp/usr/lib/systemd/system/media-cidata.mount similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-sftp/usr/lib/systemd/system/media-cidata.mount rename to src/cloud-api-adaptor/podvm/mkosi.skeleton-sftp/usr/lib/systemd/system/media-cidata.mount diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-sftp/usr/lib/systemd/system/reboot-watcher.path b/src/cloud-api-adaptor/podvm/mkosi.skeleton-sftp/usr/lib/systemd/system/reboot-watcher.path similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-sftp/usr/lib/systemd/system/reboot-watcher.path rename to src/cloud-api-adaptor/podvm/mkosi.skeleton-sftp/usr/lib/systemd/system/reboot-watcher.path diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-sftp/usr/lib/systemd/system/reboot-watcher.service b/src/cloud-api-adaptor/podvm/mkosi.skeleton-sftp/usr/lib/systemd/system/reboot-watcher.service similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-sftp/usr/lib/systemd/system/reboot-watcher.service rename to src/cloud-api-adaptor/podvm/mkosi.skeleton-sftp/usr/lib/systemd/system/reboot-watcher.service diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-sftp/usr/lib/systemd/system/sftp-dir.service b/src/cloud-api-adaptor/podvm/mkosi.skeleton-sftp/usr/lib/systemd/system/sftp-dir.service similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-sftp/usr/lib/systemd/system/sftp-dir.service rename to src/cloud-api-adaptor/podvm/mkosi.skeleton-sftp/usr/lib/systemd/system/sftp-dir.service diff --git a/src/cloud-api-adaptor/podvm/mkosi.skeleton-sftp/usr/lib/systemd/system/ssh-host-keys.service b/src/cloud-api-adaptor/podvm/mkosi.skeleton-sftp/usr/lib/systemd/system/ssh-host-keys.service new file mode 100644 index 0000000000..b0aabfdebe --- /dev/null +++ b/src/cloud-api-adaptor/podvm/mkosi.skeleton-sftp/usr/lib/systemd/system/ssh-host-keys.service @@ -0,0 +1,16 @@ +[Unit] +Description=Generate SSH host keys for /run/ssh +Documentation=man:sshd(8) +DefaultDependencies=no +Before=ssh.service + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=mkdir -p /run/ssh +ExecStart=ssh-keygen -q -t rsa -f /run/ssh/ssh_host_rsa_key -N "" -C "" +ExecStart=ssh-keygen -q -t ecdsa -f /run/ssh/ssh_host_ecdsa_key -N "" -C "" +ExecStart=ssh-keygen -q -t ed25519 -f /run/ssh/ssh_host_ed25519_key -N "" -C "" + +[Install] +WantedBy=ssh.service diff --git a/src/cloud-api-adaptor/podvm/mkosi.skeleton-sftp/usr/lib/systemd/system/ssh.service.d/10-host-keys.conf b/src/cloud-api-adaptor/podvm/mkosi.skeleton-sftp/usr/lib/systemd/system/ssh.service.d/10-host-keys.conf new file mode 100644 index 0000000000..69e639883a --- /dev/null +++ b/src/cloud-api-adaptor/podvm/mkosi.skeleton-sftp/usr/lib/systemd/system/ssh.service.d/10-host-keys.conf @@ -0,0 +1,3 @@ +[Unit] +Wants=ssh-host-keys.service +After=ssh-host-keys.service diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-sftp/usr/lib/systemd/system/sshd-keygen@.service.d/10-override.conf b/src/cloud-api-adaptor/podvm/mkosi.skeleton-sftp/usr/lib/systemd/system/sshd-keygen@.service.d/10-override.conf similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-sftp/usr/lib/systemd/system/sshd-keygen@.service.d/10-override.conf rename to src/cloud-api-adaptor/podvm/mkosi.skeleton-sftp/usr/lib/systemd/system/sshd-keygen@.service.d/10-override.conf diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-sftp/usr/libexec/openssh/sshd-keygen b/src/cloud-api-adaptor/podvm/mkosi.skeleton-sftp/usr/libexec/openssh/sshd-keygen similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.skeleton-sftp/usr/libexec/openssh/sshd-keygen rename to src/cloud-api-adaptor/podvm/mkosi.skeleton-sftp/usr/libexec/openssh/sshd-keygen diff --git a/src/cloud-api-adaptor/podvm-mkosi/mkosi.workspace/.gitkeep b/src/cloud-api-adaptor/podvm/mkosi.workspace/.gitkeep similarity index 100% rename from src/cloud-api-adaptor/podvm-mkosi/mkosi.workspace/.gitkeep rename to src/cloud-api-adaptor/podvm/mkosi.workspace/.gitkeep diff --git a/src/cloud-api-adaptor/podvm/qcow2/alinux/qemu-linux.pkr.hcl b/src/cloud-api-adaptor/podvm/qcow2/alinux/qemu-linux.pkr.hcl deleted file mode 100644 index bcf4fff352..0000000000 --- a/src/cloud-api-adaptor/podvm/qcow2/alinux/qemu-linux.pkr.hcl +++ /dev/null @@ -1,111 +0,0 @@ -locals { - machine_type = "${var.os_arch}" == "x86_64" && "${var.is_uefi}" ? "q35" : "${var.machine_type}" - use_pflash = "${var.os_arch}" == "x86_64" && "${var.is_uefi}" ? "true" : "false" - firmware = "${var.os_arch}" == "x86_64" && "${var.is_uefi}" ? "${var.uefi_firmware}" : "" - qemuargs = [ - ["-m", "${var.memory}"], - ["-smp", "cpus=${var.cpus}"], - ["-cdrom", "${var.cloud_init_image}"], - ["-serial", "mon:stdio"], - ["-cpu", "${var.cpu_type}"] - ] - final_qemuargs = local.qemuargs -} - -source "qemu" "alinux" { - disable_vnc = true - disk_compression = true - disk_image = true - disk_size = "${var.disk_size}" - format = "qcow2" - headless = true - iso_checksum = "${var.cloud_image_checksum}" - iso_url = "${var.cloud_image_url}" - output_directory = "${var.output_directory}" - qemuargs = "${local.final_qemuargs}" - ssh_password = "${var.ssh_password}" - ssh_port = 22 - ssh_username = "${var.ssh_username}" - ssh_timeout = "${var.ssh_timeout}" - boot_wait = "${var.boot_wait}" - vm_name = "${var.qemu_image_name}" - shutdown_command = "sudo shutdown -h now" - qemu_binary = "${var.qemu_binary}" - machine_type = "${local.machine_type}" - use_pflash = "${local.use_pflash}" - firmware = "${local.firmware}" -} - -build { - sources = ["source.qemu.alinux"] - - provisioner "shell-local" { - command = "tar cf toupload/files.tar files" - } - - provisioner "file" { - source = "./toupload" - destination = "/tmp/" - } - - provisioner "shell" { - inline = [ - "cd /tmp && tar xf toupload/files.tar", - "rm toupload/files.tar" - ] - } - - provisioner "file" { - source = "qcow2/copy-files.sh" - destination = "~/copy-files.sh" - } - - provisioner "shell" { - remote_folder = "~" - inline = [ - "sudo bash ~/copy-files.sh" - ] - } - - # relabel copied files right after copy-files.sh - # to prevent other commands from failing - provisioner "file" { - source = "qcow2/selinux_relabel.sh" - destination = "~/selinux_relabel.sh" - } - - provisioner "shell" { - remote_folder = "~" - inline = [ - "sudo bash ~/selinux_relabel.sh" - ] - } - - provisioner "file" { - source = "qcow2/misc-settings.sh" - destination = "~/misc-settings.sh" - } - - provisioner "shell" { - remote_folder = "~" - environment_vars = [ - "CLOUD_PROVIDER=${var.cloud_provider}", - "PODVM_DISTRO=${var.podvm_distro}", - "ACTIVATION_KEY=${var.activation_key}", - "ORG_ID=${var.org_id}", - "ARCH=${var.os_arch}" - ] - inline = [ - "sudo -E bash ~/misc-settings.sh" - ] - } - - # TODO: delete this item - # skip ioasid cgroup as kata-agent does not support it - provisioner "shell" { - inline = [ - "sudo sed -i 's/GRUB_CMDLINE_LINUX=\"/GRUB_CMDLINE_LINUX=\"cgroup_disable=ioasids/' /etc/default/grub", - "sudo grub2-mkconfig -o /boot/efi/EFI/alinux/grub.cfg" - ] - } -} diff --git a/src/cloud-api-adaptor/podvm/qcow2/alinux/variables.pkr.hcl b/src/cloud-api-adaptor/podvm/qcow2/alinux/variables.pkr.hcl deleted file mode 100644 index aa3f949c61..0000000000 --- a/src/cloud-api-adaptor/podvm/qcow2/alinux/variables.pkr.hcl +++ /dev/null @@ -1,114 +0,0 @@ -// variables.pkr.hcl - -// For those variables that you don't provide a default for, you must -// set them from the command line, a var-file, or the environment. - -variable "cloud_init_image" { - type = string - default = "cloud-init.img" -} - -variable "cpus" { - type = string - default = "2" -} - -variable "cpu_type" { - type = string - default = "Cascadelake-Server" -} - -variable "disk_size" { - type = string - default = "6144" -} - -variable "cloud_image_checksum" { - type = string - default = "73c7631c6a48b182e80c7c808d7e3adab3f07ad517fcf5d5eff8f3815306e37e" -} - -variable "cloud_image_url" { - type = string - default = "https://alinux3.oss-cn-hangzhou.aliyuncs.com/aliyun_3_x64_20G_nocloud_alibase_20250117.qcow2" -} - -variable "memory" { - type = string - default = "2048M" -} - -variable "qemu_binary" { - type = string - default = "qemu-system-x86_64" -} - -variable "ssh_password" { - type = string - default = "PeerP0d" -} - -variable "ssh_username" { - type = string - default = "peerpod" -} - -variable "ssh_timeout" { - type = string - default = "5m" -} - -variable "qemu_image_name" { - type = string - default = "peer-pod" -} - -variable "podvm_distro" { - type = string - default = env("PODVM_DISTRO") -} - -variable "cloud_provider" { - type = string - default = env("CLOUD_PROVIDER") -} - -variable "activation_key" { - type = string - default = env("ACTIVATION_KEY") -} - -variable "org_id" { - type = string - default = env("ORG_ID") -} - -variable "machine_type" { - type = string - default = "pc" -} - -variable "os_arch" { - type = string - default = "x86_64" -} - -variable "is_uefi" { - type = bool - default = false -} - -variable "uefi_firmware" { - type = string - default = "/usr/share/edk2/ovmf/OVMF_CODE.cc.fd" -} - -variable "boot_wait" { - type = string - default = "10s" -} - -variable "output_directory" { - type = string - default = "output" -} diff --git a/src/cloud-api-adaptor/podvm/qcow2/build-s390x-se-image-post.sh b/src/cloud-api-adaptor/podvm/qcow2/build-s390x-se-image-post.sh deleted file mode 100755 index 9b593e3d20..0000000000 --- a/src/cloud-api-adaptor/podvm/qcow2/build-s390x-se-image-post.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -if [ "${SE_BOOT:-false}" != "true" ]; then - exit 0 -elif [ "${ARCH}" != "s390x" ]; then - echo "Building of SE podvm image is only supported for s390x" - rm -f "se-${IMAGE_NAME}" - exit 0 -fi -[ ! -e "se-${IMAGE_NAME}" ] && exit 1 -rm -f "${OUTPUT_DIRECTORY}/${IMAGE_NAME}" -qemu-img convert -O qcow2 -c se-${IMAGE_NAME} ${OUTPUT_DIRECTORY}/se-${IMAGE_NAME} -rm -f se-${IMAGE_NAME} -echo "SE podvm image for s390x is built: ${OUTPUT_DIRECTORY}/se-${IMAGE_NAME}" diff --git a/src/cloud-api-adaptor/podvm/qcow2/build-s390x-se-image.sh b/src/cloud-api-adaptor/podvm/qcow2/build-s390x-se-image.sh deleted file mode 100755 index 95eac65a96..0000000000 --- a/src/cloud-api-adaptor/podvm/qcow2/build-s390x-se-image.sh +++ /dev/null @@ -1,261 +0,0 @@ -#!/bin/bash - -if [ "${SE_BOOT:-false}" != "true" ]; then - exit 0 -elif [ "${ARCH}" != "s390x" ]; then - echo "Building of SE podvm image is only supported for s390x" - exit 0 -fi -echo "Building SE podvm image for $ARCH" - -SE_VERIFY=${SE_VERIFY:-true} - -readonly BASE_DIR="/tmp/files" -if [ "${SE_VERIFY}" = "true" ]; then - required_files=("DigiCertCA.crt" "ibm-z-host-key-gen2.crl" "ibm-z-host-key-signing-gen2.crt") - for file in "${required_files[@]}"; do - local_path="${BASE_DIR}/${file}" - if [[ -f "${local_path}" ]]; then - echo "Found required file: ${local_path}" - else - echo "Missing required file: ${local_path}" >&2 - exit 1 - fi - done - signcert="${BASE_DIR}/ibm-z-host-key-signing-gen2.crt" - cacert="${BASE_DIR}/DigiCertCA.crt" - crl="${BASE_DIR}/ibm-z-host-key-gen2.crl" -fi - -declare -a host_key_args=() -shopt -s nullglob -found_hkd=0 -for file in "${BASE_DIR}"/*HKD.crt; do - if [[ -f "$file" ]]; then - echo "Found HKD file: \"${file}\"" - host_key_args+=("-k" "${file}") - found_hkd=1 - fi -done -shopt -u nullglob -if [[ "$found_hkd" -eq 0 ]]; then - echo "No HKD files found in ${BASE_DIR}." >&2 - exit 1 -fi -rm /tmp/files/.dummy.crt || true - -if [ "${PODVM_DISTRO}" = "rhel" ]; then - export LANG=C.UTF-8 - # Below is the tmp work-around to install cryptsetup on rhel9.4 and below s390x version base images, - #due to the issue : https://gitlab.com/qemu-project/qemu/-/issues/2054 - cp /tmp/files/cryptsetup /usr/bin/cryptsetup - chmod +x /usr/bin/cryptsetup - if ! command -v jq &> /dev/null || ! command -v cryptsetup &> /dev/null; then - if ! command -v jq &> /dev/null; then - echo >&2 "jq is required but it's not installed. Installing now..." - sudo yum install jq -y >/dev/null 2>&1 - if [ $? -ne 0 ]; then - echo >&2 "Failed to install jq. Aborting." - exit 1 - fi - fi - - if ! command -v cryptsetup &> /dev/null; then - echo >&2 "cryptsetup is required but it's not installed. Installing now..." - sudo yum install cryptsetup -y >/dev/null 2>&1 - if [ $? -ne 0 ]; then - echo >&2 "Failed to install cryptsetup. Aborting." - exit 1 - fi - fi - fi - sudo yum clean all - echo "jq and cryptsetup are installed. Proceeding with the script..." -else - echo "Installing jq" - export DEBIAN_FRONTEND=noninteractive - sudo apt-get update > /dev/null 2>&1 - sudo apt-get install jq -y > /dev/null 2>&1 - sudo apt-get remove unattended-upgrades -y - sudo apt-get autoremove - sudo apt-get clean - sudo rm -rf /var/lib/apt/lists/* -fi - -workdir=$(pwd) -disksize=100G -device=$(sudo lsblk --json | jq -r --arg disksize "$disksize" '.blockdevices[] | select(.size == $disksize and .children == null and .mountpoint == null) | .name') -echo "Found target device $device" -# /dev/vda or /dev/vdb -export tmp_nbd="/dev/$device" -export dst_mnt=$workdir/dst_mnt -export src_mnt=$workdir/src_mnt -echo "Creating boot-se and root partitions" - -sudo parted -a optimal ${tmp_nbd} mklabel gpt \ - mkpart boot-se ext4 1MiB 256MiB \ - mkpart root 256MiB 6400MiB \ - mkpart data 6400MiB ${disksize} \ - set 1 boot on - -echo "Waiting for the two partitions to show up" -while true; do -sleep 1 -[ -e ${tmp_nbd}2 ] && break -done - -echo "Formatting boot-se partition" -sudo mke2fs -t ext4 -L boot-se ${tmp_nbd}1 -boot_uuid=$(sudo blkid ${tmp_nbd}1 -s PARTUUID -o value) -export boot_uuid - -echo "Setting up encrypted root partition" -sudo mkdir ${workdir}/rootkeys -sudo mount -t tmpfs rootkeys ${workdir}/rootkeys -sudo dd if=/dev/random of=${workdir}/rootkeys/rootkey.bin bs=1 count=64 &> /dev/null -echo YES | sudo cryptsetup luksFormat --type luks2 ${tmp_nbd}2 --key-file ${workdir}/rootkeys/rootkey.bin -echo "Setting luks name for root partition" -LUKS_NAME="luks-$(sudo blkid -s UUID -o value ${tmp_nbd}2)" -export LUKS_NAME -echo "luks name is: $LUKS_NAME" -sudo cryptsetup open ${tmp_nbd}2 $LUKS_NAME --key-file ${workdir}/rootkeys/rootkey.bin - -echo "Copying the root filesystem" -sudo mkfs.ext4 -L "root" /dev/mapper/${LUKS_NAME} -sudo mkdir -p ${dst_mnt} -sudo mkdir -p ${src_mnt} -sudo mount /dev/mapper/$LUKS_NAME ${dst_mnt} -sudo mkdir ${dst_mnt}/boot-se -sudo mount -o norecovery ${tmp_nbd}1 ${dst_mnt}/boot-se -sudo mount --bind -o ro / ${src_mnt} -tar_opts=(--numeric-owner --preserve-permissions --acl --selinux --xattrs --xattrs-include='*' --sparse --one-file-system) -sudo tar -cf - "${tar_opts[@]}" --sort=none -C ${src_mnt} . | sudo tar -xf - "${tar_opts[@]}" --preserve-order -C "$dst_mnt" -sudo umount ${src_mnt} -echo "Partition copy complete" - -echo "Preparing secure execution boot image" -sudo rm -rf ${dst_mnt}/home/peerpod/* - -sudo mount -t sysfs sysfs ${dst_mnt}/sys -sudo mount -t proc proc ${dst_mnt}/proc -sudo mount --bind /dev ${dst_mnt}/dev - -sudo mkdir -p ${dst_mnt}/etc/keys -sudo mount -t tmpfs keys ${dst_mnt}/etc/keys -# ADD CONFIGURATION -echo "Adding fstab" -sudo -E bash -c 'cat < ${dst_mnt}/etc/fstab -#This file was auto-generated -/dev/mapper/$LUKS_NAME / ext4 defaults 1 1 -PARTUUID=$boot_uuid /boot-se ext4 norecovery 1 2 -END' - -echo "Adding luks keyfile for fs" -dev_uuid=$(sudo blkid -s UUID -o value "/dev/mapper/$LUKS_NAME") -sudo cp "${workdir}/rootkeys/rootkey.bin" "${dst_mnt}/etc/keys/luks-${dev_uuid}.key" -sudo chmod 600 "${dst_mnt}/etc/keys/luks-${dev_uuid}.key" - -sudo -E bash -c 'cat < ${dst_mnt}/etc/crypttab -#This file was auto-generated -$LUKS_NAME UUID=$(sudo blkid -s UUID -o value ${tmp_nbd}2) /etc/keys/luks-$(blkid -s UUID -o value /dev/mapper/$LUKS_NAME).key luks,discard,initramfs -END' -sudo chmod 744 "${dst_mnt}/etc/crypttab" - -# Disable virtio_rng -sudo -E bash -c 'cat < ${dst_mnt}/etc/modprobe.d/blacklist-virtio.conf -#do not trust rng from hypervisor -blacklist virtio_rng -END' - -sudo -E bash -c 'echo s390_trng >> ${dst_mnt}/etc/modules' - -echo "Preparing files needed for mkinitrd / initramfs" - -if [ "${PODVM_DISTRO}" = "rhel" ]; then - sudo -E bash -c 'echo "UMASK=0077" >> ${dst_mnt}/etc/dracut.conf.d/crypt.conf' - sudo -E bash -c 'echo "add_drivers+=\" dm_crypt \"" >> ${dst_mnt}/etc/dracut.conf.d/crypt.conf' - sudo -E bash -c 'echo "add_dracutmodules+=\" crypt \"" >> ${dst_mnt}/etc/dracut.conf.d/crypt.conf' - sudo -E bash -c 'echo "KEYFILE_PATTERN=\" /etc/keys/*.key \"" >> ${dst_mnt}/etc/dracut.conf.d/crypt.conf' - sudo -E bash -c 'echo "install_items+=\" /etc/keys/*.key \"" >> ${dst_mnt}/etc/dracut.conf.d/crypt.conf' - echo 'install_items+=" /etc/fstab "' >> ${dst_mnt}/etc/dracut.conf.d/crypt.conf - echo 'install_items+=" /etc/crypttab "' >> ${dst_mnt}/etc/dracut.conf.d/crypt.conf -else - sudo -E bash -c 'echo "KEYFILE_PATTERN=\"/etc/keys/*.key\"" >> ${dst_mnt}/etc/cryptsetup-initramfs/conf-hook' - sudo -E bash -c 'echo "UMASK=0077" >> ${dst_mnt}/etc/initramfs-tools/initramfs.conf' -fi - -sudo -E bash -c 'cat < ${dst_mnt}/etc/zipl.conf -[defaultboot] -default=linux -target=/boot-se - -targetbase=${tmp_nbd} -targettype=scsi -targetblocksize=512 -targetoffset=2048 - -[linux] -image = /boot-se/se.img -END' - -echo "Updating initial ram disk" -if [ "${PODVM_DISTRO}" = "rhel" ]; then - sudo cp "/boot/vmlinuz-$(uname -r)" "${dst_mnt}/boot/vmlinuz-$(uname -r)" - sudo cp "/boot/initramfs-$(uname -r).img" "${dst_mnt}/boot/initramfs-$(uname -r).img" - sleep 10 - sudo chroot ${dst_mnt} dracut -f -v - KERNEL_FILE="vmlinuz-$(uname -r)" - INITRD_FILE="initramfs-$(uname -r).img" -else - sudo chroot "${dst_mnt}" update-initramfs -u || true - # Clean up kernel names and make sure they are where we expect them - KERNEL_FILE=$(readlink ${dst_mnt}/boot/vmlinuz) - INITRD_FILE=$(readlink ${dst_mnt}/boot/initrd.img) -fi -echo "!!! Bootloader install errors prior to this line are intentional !!!!!" 1>&2 -echo "Generating an IBM Secure Execution image" -echo "Creating SE boot image" -export SE_PARMLINE="root=/dev/mapper/$LUKS_NAME rd.auto=1 rd.retry=30 console=ttysclp0 quiet panic=0 rd.shell=0 blacklist=virtio_rng swiotlb=262144" -sudo -E bash -c 'echo "${SE_PARMLINE}" > ${dst_mnt}/boot/parmfile' - -if [ "${SE_VERIFY}" = "true" ]; then - EXTRA_ARGS=" --cert=${cacert} --cert=${signcert} --crl=${crl} " -else - EXTRA_ARGS=" --no-verify " -fi - -sudo -E /usr/bin/genprotimg \ - -i ${dst_mnt}/boot/${KERNEL_FILE} \ - -r ${dst_mnt}/boot/${INITRD_FILE} \ - -p ${dst_mnt}/boot/parmfile \ - "${host_key_args[@]}" \ - ${EXTRA_ARGS} \ - -o ${dst_mnt}/boot-se/se.img - -# exit and throw an error if no se image was created -[ ! -e ${dst_mnt}/boot-se/se.img ] && exit 1 -# if building the image succeeded wipe /boot -sudo rm -rf ${dst_mnt}/boot/* -echo "Running zipl to prepare boot partition" -sudo chroot ${dst_mnt} zipl --targetbase ${tmp_nbd} \ - --targettype scsi \ - --targetblocksize 512 \ - --targetoffset 2048 \ - --target /boot-se \ - --image /boot-se/se.img - -echo "Cleaning luks keyfile" -sudo umount ${workdir}/rootkeys/ || true -sudo rm -rf ${workdir}/rootkeys -sudo umount ${dst_mnt}/etc/keys -sudo umount ${dst_mnt}/boot-se -sudo umount ${dst_mnt}/dev -sudo umount ${dst_mnt}/proc -sudo umount ${dst_mnt}/sys -sudo umount ${dst_mnt} -sudo rm -rf ${src_mnt} ${dst_mnt} - -echo "Closing encrypted root partition" -sudo cryptsetup close $LUKS_NAME -sleep 10 -echo "SE podvm qcow2 image build completed successfully" diff --git a/src/cloud-api-adaptor/podvm/qcow2/rhel/qemu-rhel.pkr.hcl b/src/cloud-api-adaptor/podvm/qcow2/rhel/qemu-rhel.pkr.hcl deleted file mode 100644 index bcb39198da..0000000000 --- a/src/cloud-api-adaptor/podvm/qcow2/rhel/qemu-rhel.pkr.hcl +++ /dev/null @@ -1,147 +0,0 @@ -locals { - machine_type = "${var.os_arch}" == "x86_64" && "${var.is_uefi}" ? "q35" : "${var.machine_type}" - use_pflash = "${var.os_arch}" == "x86_64" && "${var.is_uefi}" ? "true" : "false" - firmware = "${var.os_arch}" == "x86_64" && "${var.is_uefi}" ? "${var.uefi_firmware}" : "" - se_qemuargs = [ - ["-drive", "file=se-${var.qemu_image_name},if=none,cache=writeback,discard=ignore,format=qcow2,id=se-virtio-drive"], - ["-device", "virtio-blk,drive=se-virtio-drive,id=virtio-disk1"] - ] - qemuargs = "${var.se_boot}" == "true" ? ( - [ - ["-device", "virtio-blk,drive=virtio-drive,id=virtio-disk0,bootindex=1"], - ["-drive", "file=${var.output_directory}/${var.qemu_image_name},if=none,cache=writeback,discard=ignore,format=qcow2,id=virtio-drive"], - ["-cdrom", "${var.cloud_init_image}"], - ["-m", "${var.memory}"], - ["-smp", "cpus=${var.cpus}"], - ["-serial", "mon:stdio"], - ["-cpu", "${var.cpu_type}"] - ] - ) : ( - [ - ["-m", "${var.memory}"], - ["-smp", "cpus=${var.cpus}"], - ["-cdrom", "${var.cloud_init_image}"], - ["-serial", "mon:stdio"], - ["-cpu", "${var.cpu_type}"] - ] - ) - final_qemuargs = "${var.se_boot}" == "true" ? concat(local.qemuargs, local.se_qemuargs) : local.qemuargs -} - -source "qemu" "rhel" { - disable_vnc = true - disk_compression = true - disk_image = true - disk_size = "${var.disk_size}" - format = "qcow2" - headless = true - iso_checksum = "${var.cloud_image_checksum}" - iso_url = "${var.cloud_image_url}" - output_directory = "${var.output_directory}" - qemuargs = "${local.final_qemuargs}" - ssh_password = "${var.ssh_password}" - ssh_port = 22 - ssh_username = "${var.ssh_username}" - ssh_timeout = "${var.ssh_timeout}" - boot_wait = "${var.boot_wait}" - vm_name = "${var.qemu_image_name}" - shutdown_command = "sudo shutdown -h now" - qemu_binary = "${var.qemu_binary}" - machine_type = "${local.machine_type}" - use_pflash = "${local.use_pflash}" - firmware = "${local.firmware}" -} - -build { - sources = ["source.qemu.rhel"] - - provisioner "shell-local" { - command = "tar cf toupload/files.tar files" - } - - provisioner "file" { - source = "./toupload" - destination = "/tmp/" - } - - provisioner "shell" { - inline = [ - "cd /tmp && tar xf toupload/files.tar", - "rm toupload/files.tar" - ] - } - - provisioner "file" { - source = "qcow2/copy-files.sh" - destination = "~/copy-files.sh" - } - - provisioner "shell" { - remote_folder = "~" - inline = [ - "sudo bash ~/copy-files.sh" - ] - } - - # relabel copied files right after copy-files.sh - # to prevent other commands from failing - provisioner "file" { - source = "qcow2/selinux_relabel.sh" - destination = "~/selinux_relabel.sh" - } - - provisioner "shell" { - remote_folder = "~" - inline = [ - "sudo bash ~/selinux_relabel.sh" - ] - } - - provisioner "file" { - source = "qcow2/misc-settings.sh" - destination = "~/misc-settings.sh" - } - - provisioner "shell" { - remote_folder = "~" - environment_vars = [ - "CLOUD_PROVIDER=${var.cloud_provider}", - "PODVM_DISTRO=${var.podvm_distro}", - "ACTIVATION_KEY=${var.activation_key}", - "ORG_ID=${var.org_id}", - "ARCH=${var.os_arch}" - ] - inline = [ - "sudo -E bash ~/misc-settings.sh" - ] - } - - provisioner "file" { - source = "qcow2/build-s390x-se-image.sh" - destination = "~/build-s390x-se-image.sh" - } - - provisioner "shell" { - remote_folder = "~" - environment_vars = [ - "SE_BOOT=${var.se_boot}", - "PODVM_DISTRO=${var.podvm_distro}", - "ARCH=${var.os_arch}", - "SE_VERIFY=${var.se_verify}" - ] - inline = [ - "sudo -E bash ~/build-s390x-se-image.sh" - ] - } - - post-processor "shell-local" { - name = "post-build-se-image" - script = "qcow2/build-s390x-se-image-post.sh" - environment_vars = [ - "SE_BOOT=${var.se_boot}", - "ARCH=${var.os_arch}", - "OUTPUT_DIRECTORY=${var.output_directory}", - "IMAGE_NAME=${var.qemu_image_name}" - ] - } -} diff --git a/src/cloud-api-adaptor/podvm/qcow2/rhel/variables.pkr.hcl b/src/cloud-api-adaptor/podvm/qcow2/rhel/variables.pkr.hcl deleted file mode 100644 index 4398279dea..0000000000 --- a/src/cloud-api-adaptor/podvm/qcow2/rhel/variables.pkr.hcl +++ /dev/null @@ -1,124 +0,0 @@ -// variables.pkr.hcl - -// For those variables that you don't provide a default for, you must -// set them from the command line, a var-file, or the environment. - -variable "cloud_init_image" { - type = string - default = "cloud-init.img" -} - -variable "cpus" { - type = string - default = "2" -} - -variable "cpu_type" { - type = string - default = "Cascadelake-Server" -} - -variable "disk_size" { - type = string - default = "6144" -} - -variable "cloud_image_checksum" { - type = string - default = "d96622d77bcbab5526fd42e7d933ee851d239327946992a018b0bfc9fad777e7" -} - -variable "cloud_image_url" { - type = string - default = "https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img" -} - -variable "memory" { - type = string - default = "2048M" -} - -variable "qemu_binary" { - type = string - default = "qemu-system-x86_64" -} - -variable "ssh_password" { - type = string - default = "PeerP0d" -} - -variable "ssh_username" { - type = string - default = "peerpod" -} - -variable "ssh_timeout" { - type = string - default = "15m" -} - -variable "qemu_image_name" { - type = string - default = "peer-pod" -} - -variable "podvm_distro" { - type = string - default = env("PODVM_DISTRO") -} - -variable "cloud_provider" { - type = string - default = env("CLOUD_PROVIDER") -} - -variable "activation_key" { - type = string - default = env("ACTIVATION_KEY") -} - -variable "org_id" { - type = string - default = env("ORG_ID") -} - -variable "machine_type" { - type = string - default = "pc" -} - -variable "os_arch" { - type = string - default = "x86_64" -} - -variable "is_uefi" { - type = bool - default = false -} - -variable "uefi_firmware" { - type = string - default = "/usr/share/edk2/ovmf/OVMF_CODE.cc.fd" -} - -variable "boot_wait" { - type = string - default = "10s" -} - -variable "se_boot" { - type = string - default = env("SE_BOOT") -} - -variable "se_verify" { - type = string - default = env("SE_VERIFY") -} - -variable "output_directory" { - type = string - default = "output" -} diff --git a/src/cloud-api-adaptor/podvm/qcow2/selinux_relabel.sh b/src/cloud-api-adaptor/podvm/qcow2/selinux_relabel.sh deleted file mode 100755 index ed31afc997..0000000000 --- a/src/cloud-api-adaptor/podvm/qcow2/selinux_relabel.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -FOLDERS="/etc /usr/local/bin /pause_bundle" - -for entry in $FOLDERS -do - sudo restorecon -p -r $entry -done diff --git a/src/cloud-api-adaptor/podvm/qcow2/ubuntu/qemu-ubuntu.pkr.hcl b/src/cloud-api-adaptor/podvm/qcow2/ubuntu/qemu-ubuntu.pkr.hcl deleted file mode 100644 index 19d823e0c1..0000000000 --- a/src/cloud-api-adaptor/podvm/qcow2/ubuntu/qemu-ubuntu.pkr.hcl +++ /dev/null @@ -1,135 +0,0 @@ -locals { - machine_type = "${var.os_arch}" == "x86_64" && "${var.is_uefi}" ? "q35" : "${var.machine_type}" - use_pflash = "${var.os_arch}" == "x86_64" && "${var.is_uefi}" ? "true" : "false" - firmware = "${var.is_uefi}" ? "${var.uefi_firmware}" : "" - se_qemuargs = [ - ["-drive", "file=se-${var.qemu_image_name},if=none,cache=writeback,discard=ignore,format=qcow2,id=se-virtio-drive"], - ["-device", "virtio-blk,drive=se-virtio-drive,id=virtio-disk1"] - ] - qemuargs = "${var.os_arch}" == "x86_64" && "${var.is_uefi}" ? ( - [ - ["-m", "${var.memory}"], - ["-smp", "cpus=${var.cpus}"], - ["-cdrom", "${var.cloud_init_image}"], - ["-serial", "mon:stdio"] - ] - ) : ( - [ - ["-device", "virtio-blk,drive=virtio-drive,id=virtio-disk0,bootindex=1"], - ["-drive", "file=${var.output_directory}/${var.qemu_image_name},if=none,cache=writeback,discard=ignore,format=qcow2,id=virtio-drive"], - ["-device", "virtio-scsi"], - ["-drive", "file=${var.cloud_init_image},format=raw,if=none,id=c1"], - ["-device", "scsi-cd,drive=c1"], - ["-m", "${var.memory}"], - ["-smp", "cpus=${var.cpus}"], - ["-serial", "mon:stdio"] - ] - ) - efi_boot = "${var.is_uefi}" ? "true" : "false" - final_qemuargs = "${var.se_boot}" == "true" ? concat(local.qemuargs, local.se_qemuargs) : local.qemuargs -} - -source "qemu" "ubuntu" { - disable_vnc = true - disk_compression = true - disk_image = true - disk_size = "${var.disk_size}" - format = "qcow2" - headless = true - iso_checksum = "${var.cloud_image_checksum}" - iso_url = "${var.cloud_image_url}" - output_directory = "${var.output_directory}" - qemuargs = "${local.final_qemuargs}" - ssh_password = "${var.ssh_password}" - ssh_port = 22 - ssh_username = "${var.ssh_username}" - ssh_timeout = "${var.ssh_timeout}" - boot_wait = "${var.boot_wait}" - vm_name = "${var.qemu_image_name}" - shutdown_command = "sudo shutdown -h now" - qemu_binary = "${var.qemu_binary}" - machine_type = "${local.machine_type}" - use_pflash = "${local.use_pflash}" - firmware = "${local.firmware}" - efi_boot = "${local.efi_boot}" - efi_firmware_code = "${var.os_arch}" == "aarch64" ? "/usr/share/AAVMF/AAVMF_CODE.fd" : "" - efi_firmware_vars = "${var.os_arch}" == "aarch64" ? "/usr/share/AAVMF/AAVMF_VARS.fd" : "" - cpu_model = "${var.os_arch}" == "aarch64" ? "cortex-a57" : "" - -} - -build { - sources = ["source.qemu.ubuntu"] - - provisioner "shell-local" { - command = "tar cf toupload/files.tar files" - } - - provisioner "file" { - source = "./toupload" - destination = "/tmp/" - } - - provisioner "shell" { - inline = [ - "cd /tmp && tar xf toupload/files.tar", - "rm toupload/files.tar" - ] - } - - provisioner "file" { - source = "qcow2/copy-files.sh" - destination = "~/copy-files.sh" - } - - provisioner "shell" { - remote_folder = "~" - inline = [ - "sudo bash ~/copy-files.sh" - ] - } - - provisioner "file" { - source = "qcow2/misc-settings.sh" - destination = "~/misc-settings.sh" - } - - provisioner "shell" { - remote_folder = "~" - environment_vars = [ - "CLOUD_PROVIDER=${var.cloud_provider}", - "PODVM_DISTRO=${var.podvm_distro}", - "ARCH=${var.target_arch}" - ] - inline = [ - "sudo -E bash ~/misc-settings.sh" - ] - } - - provisioner "file" { - source = "qcow2/build-s390x-se-image.sh" - destination = "~/build-s390x-se-image.sh" - } - - provisioner "shell" { - remote_folder = "~" - environment_vars = [ - "SE_BOOT=${var.se_boot}", - "ARCH=${var.target_arch}" - ] - inline = [ - "sudo -E bash ~/build-s390x-se-image.sh" - ] - } - - post-processor "shell-local" { - name = "post-build-se-image" - script = "qcow2/build-s390x-se-image-post.sh" - environment_vars = [ - "SE_BOOT=${var.se_boot}", - "ARCH=${var.target_arch}", - "OUTPUT_DIRECTORY=${var.output_directory}", - "IMAGE_NAME=${var.qemu_image_name}" - ] - } -} diff --git a/src/cloud-api-adaptor/podvm/qcow2/ubuntu/variables.pkr.hcl b/src/cloud-api-adaptor/podvm/qcow2/ubuntu/variables.pkr.hcl deleted file mode 100644 index 06bc5054ff..0000000000 --- a/src/cloud-api-adaptor/podvm/qcow2/ubuntu/variables.pkr.hcl +++ /dev/null @@ -1,109 +0,0 @@ -// variables.pkr.hcl - -// For those variables that you don't provide a default for, you must -// set them from the command line, a var-file, or the environment. - -variable "cloud_init_image" { - type = string - default = "cloud-init.img" -} - -variable "cpus" { - type = string - default = "2" -} - -variable "disk_size" { - type = string - default = "6144" -} - -variable "cloud_image_checksum" { - type = string - default = "d96622d77bcbab5526fd42e7d933ee851d239327946992a018b0bfc9fad777e7" -} - -variable "cloud_image_url" { - type = string - default = "https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img" -} - -variable "memory" { - type = string - default = "2048M" -} - -variable "ssh_password" { - type = string - default = "PeerP0d" -} - -variable "ssh_username" { - type = string - default = "peerpod" -} - -variable "ssh_timeout" { - type = string - default = "15m" -} - -variable "qemu_image_name" { - type = string - default = "peer-pod" -} - -variable "qemu_binary" { - type = string - default = "qemu-system-x86_64" -} - -variable "machine_type" { - type = string - default = "pc" -} - -variable "boot_wait" { - type = string - default = "10s" -} - -variable "output_directory" { - type = string - default = "output" -} - -variable "podvm_distro" { - type = string - default = env("PODVM_DISTRO") -} - -variable "cloud_provider" { - type = string - default = env("CLOUD_PROVIDER") -} - -variable "se_boot" { - type = string - default = env("SE_BOOT") -} - -variable "target_arch" { - type = string - default = env("ARCH") -} - -variable "os_arch" { - type = string - default = "x86_64" -} - -variable "is_uefi" { - type = bool - default = false -} - -variable "uefi_firmware" { - type = string - default = "/usr/share/OVMF/OVMF_CODE.fd" -} diff --git a/src/cloud-api-adaptor/podvm/qcow2/userdata.cfg b/src/cloud-api-adaptor/podvm/qcow2/userdata.cfg deleted file mode 100644 index 1f650a5eac..0000000000 --- a/src/cloud-api-adaptor/podvm/qcow2/userdata.cfg +++ /dev/null @@ -1,18 +0,0 @@ -#cloud-config -ssh_pwauth: true -chpasswd: - expire: false - -locale: en_US.UTF-8 -locale_configfile: /etc/default/locale - -users: - - name: peerpod - # This is the hash of the default password: PeerP0d created using - # openssl passwd -1 PeerP0d - # This user/password will be locked post image creation - # If you want to change the user and password, ensure you change - # the ssh_username and ssh_password in the variables - passwd: $1$u4tXfju6$lvXwBnkFT4S0depRtcTjo/ - lock_passwd: false - sudo: ALL=(ALL) NOPASSWD:ALL diff --git a/src/cloud-api-adaptor/test/e2e/README.md b/src/cloud-api-adaptor/test/e2e/README.md index 31f0720d3c..c378a0e08e 100644 --- a/src/cloud-api-adaptor/test/e2e/README.md +++ b/src/cloud-api-adaptor/test/e2e/README.md @@ -10,7 +10,7 @@ As long as the cloud provider support is implemented on this framework, you can as shown below for *libvirt*: ``` -$ CLOUD_PROVIDER=libvirt make test-e2e +CLOUD_PROVIDER=libvirt make test-e2e ``` The above command run tests on an existing cluster. It will look for the kubeconf file exported on the @@ -19,7 +19,7 @@ The above command run tests on an existing cluster. It will look for the kubecon You can instruct the tool to provision a test environment though, as shown below: ``` -$ TEST_PROVISION=yes CLOUD_PROVIDER=libvirt make test-e2e +TEST_PROVISION=yes CLOUD_PROVIDER=libvirt make test-e2e ``` Each provider must have a provisioner implementation so that the framework is able to perform operations on the cluster. The provisioner will likely to need additional information (e.g. login credentials), and those are passed via a properties file with the following format: @@ -33,13 +33,13 @@ key2 = "value2" You should use the `TEST_PROVISION_FILE` variable to specify the properties file path, as shown below (for libvirt provider look at [libvirt/README.md](../../libvirt/README.md) setup): ``` -$ TEST_PROVISION=yes TEST_PROVISION_FILE=/path/to/libvirt.properties CLOUD_PROVIDER=libvirt make test-e2e +TEST_PROVISION=yes TEST_PROVISION_FILE=/path/to/libvirt.properties CLOUD_PROVIDER=libvirt make test-e2e ``` The `TEST_PODVM_IMAGE` is an optional variable which specifies the path to the podvm qcow2 image. If it is set then the image should be uploaded to the VPC storage. The following command, as an example, instructs the tool to upload `path/to/podvm-base.qcow2` after the provisioning of the test environment: ``` -$ TEST_PROVISION=yes TEST_PODVM_IMAGE="path/to/podvm-base.qcow2" CLOUD_PROVIDER=libvirt make test-e2e +TEST_PROVISION=yes TEST_PODVM_IMAGE="path/to/podvm-base.qcow2" CLOUD_PROVIDER=libvirt make test-e2e ``` By default it is given 20 minutes for the entire e2e execution to complete, otherwise the process is preempted. If you need to extend that timeout then export the `TEST_E2E_TIMEOUT` variable. For example, `TEST_E2E_TIMEOUT=30m` set the timeout to 30 minutes. See `-timeout` in [go test flags](https://pkg.go.dev/cmd/go#hdr-Testing_flags) for the values accepted. @@ -50,8 +50,9 @@ To use existing cluster which have already installed Cloud API Adaptor, you shou While in development and/or debugging it's common that you want to run just a sub-set of tests rather than the entire suite. To accomplish that you should export an unanchored regular expression in the `RUN_TESTS` variable that matches the tests names (see `-run` in [go test flags](https://pkg.go.dev/cmd/go#hdr-Testing_flags) for the regular expression format accepted). For example, to run only the simple creation pod test: + ``` -$ RUN_TESTS=CreateSimplePod TEST_PROVISION=yes TEST_PODVM_IMAGE="path/to/podvm-base.qcow2" CLOUD_PROVIDER=libvirt make test-e2e +RUN_TESTS=CreateSimplePod TEST_PROVISION=yes TEST_PODVM_IMAGE="path/to/podvm-base.qcow2" CLOUD_PROVIDER=libvirt make test-e2e ``` ## Attestation and KBS specific @@ -62,20 +63,22 @@ To prepare trustee, execute the following helper script: ```sh ${cloud-api-adaptor-repo-dir}/src/cloud-api-adaptor/test/utils/checkout_kbs.sh ``` + > [!NOTE] > This script requires [oras](https://oras.land/docs/installation/) to be installed to pull down and verify the cached kbs-client. - -We need build and use the PodVM image: +We need to build and use the PodVM image: ```sh -pushd ${cloud-api-adaptor} -make podvm-builder podvm-binaries podvm-image +pushd ${cloud-api-adaptor}/podvm +make # builds builder, binaries, and OS image +# Convert to QCOW2 format if needed for your provider +qemu-img convert -f raw -O qcow2 build/system.raw build/system.qcow2 popd ``` -Then extract the PodVM image and use it following [extracting-the-qcow2-image](../../podvm/README.md#extracting-the-qcow2-image) +The resulting image will be at `${cloud-api-adaptor}/podvm/build/system.raw` (or `system.qcow2` after conversion). See [podvm/README.md](../../podvm/README.md) for detailed build instructions. To deploy the KBS service and test attestation related cases, export the following variable: @@ -86,6 +89,7 @@ export DEPLOY_KBS=yes ## Other end-to-end test customizations Other options are provided via environment variables if you need to further customize the e2e test cases: + - `TEST_CAA_NAMESPACE` - This option is available, primarily for running the e2e tests on a downstream version of confidential containers, where the cloud-api-adaptor pod is deployed to a different namespace than the default `confidential-containers-system`. @@ -151,11 +155,12 @@ Use the properties on the table below for AWS: |vxlan_port|VXLAN port number|| >Notes: - * The AWS credentials are obtained from the CLI [configuration files](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html). **Important**: the access key and secret are recorded in plain-text in [install/charts/peerpods/providers/aws-secrets.yaml](../../install/charts/peerpods/providers/aws-secrets.yaml) - * The subnet is created with CIDR IPv4 block 10.0.0.0/25. In case of deploying an EKS cluster, + +- The AWS credentials are obtained from the CLI [configuration files](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html). **Important**: the access key and secret are recorded in plain-text in [install/charts/peerpods/providers/aws-secrets.yaml](../../install/charts/peerpods/providers/aws-secrets.yaml) +- The subnet is created with CIDR IPv4 block 10.0.0.0/25. In case of deploying an EKS cluster, a secondary (private) subnet is created with CIDR IPv4 block 10.0.0.128/25 - * The cluster type **onprem** assumes Kubernetes is already provisioned and its kubeconfig file path can be found at the `KUBECONFIG` environment variable or in the `~/.kube/config` file. Whereas **eks** type instructs to create an [AWS EKS](https://aws.amazon.com/eks/) cluster on the VPC - * You must have `qemu-img` installed in your workstation or CI runner because it is used to convert an qcow2 disk to raw. +- The cluster type **onprem** assumes Kubernetes is already provisioned and its kubeconfig file path can be found at the `KUBECONFIG` environment variable or in the `~/.kube/config` file. Whereas **eks** type instructs to create an [AWS EKS](https://aws.amazon.com/eks/) cluster on the VPC +- You must have `qemu-img` installed in your workstation or CI runner because it is used to convert an qcow2 disk to raw. ### Libvirt provision properties @@ -178,6 +183,7 @@ Use the properties on the table below for Libvirt: ## Running tests for PodVM with Authenticated Registry For running e2e test cases specifically for checking PodVM with Image from Authenticated Registry, we need to export following two variables + - `AUTHENTICATED_REGISTRY_IMAGE` - Name of the image along with the tag from authenticated registry (example: quay.io/kata-containers/confidential-containers-auth:test) - `REGISTRY_CREDENTIAL_ENCODED` - Credentials of registry encrypted as BASE64ENCODED(USERNAME:PASSWORD). If you're using quay registry, we can get the encrypted credentials from Account Settings >> Generate Encrypted Password >> Docker Configuration @@ -216,7 +222,9 @@ RUN_TESTS=TestAzureImageDecryption ``` ### IBM Cloud + Take region `jp-tok` for example. + ``` cd ../.. # go to project root cat < skip-provisioning.properties @@ -241,7 +249,7 @@ EOF - For `INSTANCE_PROFILE_NAME`, if it's not secure execution, the value is started with "bz2". If it's secure execution, the value is started with 'bz2e'. More values can be found through ibmcloud command `ibmcloud is instance-profiles`. - For `PODVM_IMAGE_ID`, the vpc image id uploaded to ibmcloud. -- For `CAA_IMAGE_TAG`, the commit id of project. The commit id can be found here: https://github.com/confidential-containers/cloud-api-adaptor/commits/main/ +- For `CAA_IMAGE_TAG`, the commit id of project. The commit id can be found here: # Adding support for a new cloud provider diff --git a/src/cloud-api-adaptor/test/e2e/csi_test.go b/src/cloud-api-adaptor/test/e2e/csi_test.go new file mode 100644 index 0000000000..11b416a5f2 --- /dev/null +++ b/src/cloud-api-adaptor/test/e2e/csi_test.go @@ -0,0 +1,381 @@ +// (C) Copyright Confidential Containers Contributors +// SPDX-License-Identifier: Apache-2.0 + +package e2e + +import ( + "context" + "fmt" + "os" + "strings" + "testing" + "time" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" +) + +const ( + csiDriverName = "caa-csi-block-driver" + csiStorageClassName = "caa-csi-block" + csiTestNamespace = "csi-e2e-test" + podReadyTimeout = 5 * time.Minute + podDeleteTimeout = 3 * time.Minute + pollInterval = 5 * time.Second +) + +func getClient(t *testing.T) *kubernetes.Clientset { + t.Helper() + kubeconfig := os.Getenv("KUBECONFIG") + if kubeconfig == "" { + kubeconfig = os.ExpandEnv("$HOME/.kube/config") + } + config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) + if err != nil { + t.Fatalf("Failed to build kubeconfig: %v", err) + } + cs, err := kubernetes.NewForConfig(config) + if err != nil { + t.Fatalf("Failed to create k8s client: %v", err) + } + return cs +} + +func skipIfNoCluster(t *testing.T) { + t.Helper() + if os.Getenv("CSI_E2E_ENABLED") != "true" { + t.Skip("CSI E2E tests disabled — set CSI_E2E_ENABLED=true to run") + } +} + +func ensureNamespace(t *testing.T, cs *kubernetes.Clientset) { + t.Helper() + ctx := context.Background() + _, err := cs.CoreV1().Namespaces().Get(ctx, csiTestNamespace, metav1.GetOptions{}) + if err != nil { + _, err = cs.CoreV1().Namespaces().Create(ctx, &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{Name: csiTestNamespace}, + }, metav1.CreateOptions{}) + if err != nil { + t.Fatalf("Failed to create test namespace: %v", err) + } + } +} + +func waitForPodCompletion(t *testing.T, cs *kubernetes.Clientset, ns, name string, timeout time.Duration) string { + t.Helper() + ctx := context.Background() + deadline := time.Now().Add(timeout) + for time.Now().Before(deadline) { + pod, err := cs.CoreV1().Pods(ns).Get(ctx, name, metav1.GetOptions{}) + if err == nil && (pod.Status.Phase == corev1.PodSucceeded || pod.Status.Phase == corev1.PodFailed) { + if pod.Status.Phase == corev1.PodFailed { + t.Fatalf("Pod %s failed", name) + } + logs, _ := cs.CoreV1().Pods(ns).GetLogs(name, &corev1.PodLogOptions{}).Do(ctx).Raw() + return string(logs) + } + time.Sleep(pollInterval) + } + t.Fatalf("Pod %s did not complete within %v", name, timeout) + return "" +} + +func deletePodAndWait(t *testing.T, cs *kubernetes.Clientset, ns, name string) { + t.Helper() + ctx := context.Background() + cs.CoreV1().Pods(ns).Delete(ctx, name, metav1.DeleteOptions{}) //nolint:errcheck + deadline := time.Now().Add(podDeleteTimeout) + for time.Now().Before(deadline) { + _, err := cs.CoreV1().Pods(ns).Get(ctx, name, metav1.GetOptions{}) + if err != nil { + return + } + time.Sleep(pollInterval) + } +} + +func newCSIPVC(namespace, name, size string) *corev1.PersistentVolumeClaim { + storageClass := csiStorageClassName + return &corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce}, + StorageClassName: &storageClass, + Resources: corev1.VolumeResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: resource.MustParse(size), + }, + }, + }, + } +} + +func newCSIWriterPod(namespace, podName, pvcName, mountPath, writeData string) *corev1.Pod { + runtimeClassName := "kata-remote" + return &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: podName, + Namespace: namespace, + Labels: map[string]string{"app": "csi-writer"}, + }, + Spec: corev1.PodSpec{ + RuntimeClassName: &runtimeClassName, + Containers: []corev1.Container{{ + Name: "writer", + Image: "busybox:1.36", + Command: []string{"/bin/sh", "-c", fmt.Sprintf("echo '%s' > %s/data.txt && sync && cat %s/data.txt", writeData, mountPath, mountPath)}, + VolumeMounts: []corev1.VolumeMount{ + {Name: "data", MountPath: mountPath}, + }, + }}, + Volumes: []corev1.Volume{{ + Name: "data", + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: pvcName}, + }, + }}, + RestartPolicy: corev1.RestartPolicyNever, + }, + } +} + +func newCSIReaderPod(namespace, podName, pvcName, mountPath string) *corev1.Pod { + runtimeClassName := "kata-remote" + return &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: podName, + Namespace: namespace, + Labels: map[string]string{"app": "csi-reader"}, + }, + Spec: corev1.PodSpec{ + RuntimeClassName: &runtimeClassName, + Containers: []corev1.Container{{ + Name: "reader", + Image: "busybox:1.36", + Command: []string{"/bin/sh", "-c", fmt.Sprintf("cat %s/data.txt", mountPath)}, + VolumeMounts: []corev1.VolumeMount{ + {Name: "data", MountPath: mountPath}, + }, + }}, + Volumes: []corev1.Volume{{ + Name: "data", + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: pvcName}, + }, + }}, + RestartPolicy: corev1.RestartPolicyNever, + }, + } +} + +func TestCSISingleDiskPersistence(t *testing.T) { + skipIfNoCluster(t) + cs := getClient(t) + ensureNamespace(t, cs) + ctx := context.Background() + + pvcName := "e2e-single-pvc" + testData := "hello-single-disk-e2e" + + t.Log("Creating PVC") + cs.CoreV1().PersistentVolumeClaims(csiTestNamespace).Delete(ctx, pvcName, metav1.DeleteOptions{}) //nolint:errcheck + _, err := cs.CoreV1().PersistentVolumeClaims(csiTestNamespace).Create(ctx, newCSIPVC(csiTestNamespace, pvcName, "1Gi"), metav1.CreateOptions{}) + if err != nil { + t.Fatalf("Failed to create PVC: %v", err) + } + defer cs.CoreV1().PersistentVolumeClaims(csiTestNamespace).Delete(ctx, pvcName, metav1.DeleteOptions{}) //nolint:errcheck + + t.Log("Creating writer pod") + writerPod := newCSIWriterPod(csiTestNamespace, "e2e-writer", pvcName, "/mnt/data", testData) + cs.CoreV1().Pods(csiTestNamespace).Delete(ctx, "e2e-writer", metav1.DeleteOptions{}) //nolint:errcheck + time.Sleep(5 * time.Second) + _, err = cs.CoreV1().Pods(csiTestNamespace).Create(ctx, writerPod, metav1.CreateOptions{}) + if err != nil { + t.Fatalf("Failed to create writer pod: %v", err) + } + + logs := waitForPodCompletion(t, cs, csiTestNamespace, "e2e-writer", podReadyTimeout) + t.Logf("Writer logs: %s", logs) + if !strings.Contains(logs, testData) { + t.Fatalf("Writer output does not contain test data %q", testData) + } + + t.Log("Deleting writer pod") + deletePodAndWait(t, cs, csiTestNamespace, "e2e-writer") + + t.Log("Creating reader pod") + readerPod := newCSIReaderPod(csiTestNamespace, "e2e-reader", pvcName, "/mnt/data") + _, err = cs.CoreV1().Pods(csiTestNamespace).Create(ctx, readerPod, metav1.CreateOptions{}) + if err != nil { + t.Fatalf("Failed to create reader pod: %v", err) + } + defer deletePodAndWait(t, cs, csiTestNamespace, "e2e-reader") + + logs = waitForPodCompletion(t, cs, csiTestNamespace, "e2e-reader", podReadyTimeout) + t.Logf("Reader logs: %s", logs) + if !strings.Contains(logs, testData) { + t.Fatalf("PERSISTENCE FAILED: reader output %q does not contain %q", strings.TrimSpace(logs), testData) + } + + t.Log("PASSED: Single disk persistence verified") +} + +func TestCSIMultiDiskPersistence(t *testing.T) { + skipIfNoCluster(t) + cs := getClient(t) + ensureNamespace(t, cs) + ctx := context.Background() + + pvcNames := []string{"e2e-multi-pvc-0", "e2e-multi-pvc-1"} + testData := []string{"data-on-disk-0", "data-on-disk-1"} + + for _, pvc := range pvcNames { + cs.CoreV1().PersistentVolumeClaims(csiTestNamespace).Delete(ctx, pvc, metav1.DeleteOptions{}) //nolint:errcheck + _, err := cs.CoreV1().PersistentVolumeClaims(csiTestNamespace).Create(ctx, newCSIPVC(csiTestNamespace, pvc, "1Gi"), metav1.CreateOptions{}) + if err != nil { + t.Fatalf("Failed to create PVC %s: %v", pvc, err) + } + defer cs.CoreV1().PersistentVolumeClaims(csiTestNamespace).Delete(ctx, pvc, metav1.DeleteOptions{}) //nolint:errcheck + } + + t.Log("Creating multi-disk writer pod") + runtimeClassName := "kata-remote" + cmd := "" + var mounts []corev1.VolumeMount + var vols []corev1.Volume + for i, pvc := range pvcNames { + mp := fmt.Sprintf("/mnt/%s", pvc) + mounts = append(mounts, corev1.VolumeMount{Name: pvc, MountPath: mp}) + vols = append(vols, corev1.Volume{Name: pvc, VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: pvc}, + }}) + cmd += fmt.Sprintf("echo '%s' > %s/data.txt && ", testData[i], mp) + } + cmd += "sync" + for _, pvc := range pvcNames { + cmd += fmt.Sprintf(" && cat /mnt/%s/data.txt", pvc) + } + + writerPod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{Name: "e2e-multi-writer", Namespace: csiTestNamespace}, + Spec: corev1.PodSpec{ + RuntimeClassName: &runtimeClassName, + Containers: []corev1.Container{{Name: "writer", Image: "busybox:1.36", Command: []string{"/bin/sh", "-c", cmd}, VolumeMounts: mounts}}, + Volumes: vols, + RestartPolicy: corev1.RestartPolicyNever, + }, + } + + cs.CoreV1().Pods(csiTestNamespace).Delete(ctx, "e2e-multi-writer", metav1.DeleteOptions{}) //nolint:errcheck + time.Sleep(5 * time.Second) + _, err := cs.CoreV1().Pods(csiTestNamespace).Create(ctx, writerPod, metav1.CreateOptions{}) + if err != nil { + t.Fatalf("Failed to create multi-writer pod: %v", err) + } + + logs := waitForPodCompletion(t, cs, csiTestNamespace, "e2e-multi-writer", podReadyTimeout) + t.Logf("Multi-writer logs: %s", logs) + for _, d := range testData { + if !strings.Contains(logs, d) { + t.Fatalf("Multi-writer output missing %q", d) + } + } + + t.Log("Deleting multi-writer pod") + deletePodAndWait(t, cs, csiTestNamespace, "e2e-multi-writer") + + t.Log("Creating multi-disk reader pod") + readCmd := "" + for _, pvc := range pvcNames { + readCmd += fmt.Sprintf("cat /mnt/%s/data.txt && ", pvc) + } + readCmd += "true" + + readerPod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{Name: "e2e-multi-reader", Namespace: csiTestNamespace}, + Spec: corev1.PodSpec{ + RuntimeClassName: &runtimeClassName, + Containers: []corev1.Container{{Name: "reader", Image: "busybox:1.36", Command: []string{"/bin/sh", "-c", readCmd}, VolumeMounts: mounts}}, + Volumes: vols, + RestartPolicy: corev1.RestartPolicyNever, + }, + } + + _, err = cs.CoreV1().Pods(csiTestNamespace).Create(ctx, readerPod, metav1.CreateOptions{}) + if err != nil { + t.Fatalf("Failed to create multi-reader pod: %v", err) + } + defer deletePodAndWait(t, cs, csiTestNamespace, "e2e-multi-reader") + + logs = waitForPodCompletion(t, cs, csiTestNamespace, "e2e-multi-reader", podReadyTimeout) + t.Logf("Multi-reader logs: %s", logs) + for _, d := range testData { + if !strings.Contains(logs, d) { + t.Fatalf("PERSISTENCE FAILED: multi-reader output missing %q", d) + } + } + + t.Log("PASSED: Multi-disk persistence verified") +} + +func TestCSIVolumeExpansion(t *testing.T) { + skipIfNoCluster(t) + cs := getClient(t) + ensureNamespace(t, cs) + ctx := context.Background() + + pvcName := "e2e-expand-pvc" + + cs.CoreV1().PersistentVolumeClaims(csiTestNamespace).Delete(ctx, pvcName, metav1.DeleteOptions{}) //nolint:errcheck + _, err := cs.CoreV1().PersistentVolumeClaims(csiTestNamespace).Create(ctx, newCSIPVC(csiTestNamespace, pvcName, "1Gi"), metav1.CreateOptions{}) + if err != nil { + t.Fatalf("Failed to create PVC: %v", err) + } + defer cs.CoreV1().PersistentVolumeClaims(csiTestNamespace).Delete(ctx, pvcName, metav1.DeleteOptions{}) //nolint:errcheck + + t.Log("Writing data to volume") + writerPod := newCSIWriterPod(csiTestNamespace, "e2e-expand-writer", pvcName, "/mnt/data", "expand-test") + cs.CoreV1().Pods(csiTestNamespace).Delete(ctx, "e2e-expand-writer", metav1.DeleteOptions{}) //nolint:errcheck + time.Sleep(5 * time.Second) + _, err = cs.CoreV1().Pods(csiTestNamespace).Create(ctx, writerPod, metav1.CreateOptions{}) + if err != nil { + t.Fatalf("Failed to create writer pod: %v", err) + } + waitForPodCompletion(t, cs, csiTestNamespace, "e2e-expand-writer", podReadyTimeout) + deletePodAndWait(t, cs, csiTestNamespace, "e2e-expand-writer") + + t.Log("Expanding PVC to 2Gi") + pvc, err := cs.CoreV1().PersistentVolumeClaims(csiTestNamespace).Get(ctx, pvcName, metav1.GetOptions{}) + if err != nil { + t.Fatalf("Failed to get PVC: %v", err) + } + pvc.Spec.Resources.Requests[corev1.ResourceStorage] = resource.MustParse("2Gi") + _, err = cs.CoreV1().PersistentVolumeClaims(csiTestNamespace).Update(ctx, pvc, metav1.UpdateOptions{}) + if err != nil { + t.Fatalf("Failed to expand PVC: %v", err) + } + + time.Sleep(30 * time.Second) + + t.Log("Verifying data persists after expansion") + readerPod := newCSIReaderPod(csiTestNamespace, "e2e-expand-reader", pvcName, "/mnt/data") + _, err = cs.CoreV1().Pods(csiTestNamespace).Create(ctx, readerPod, metav1.CreateOptions{}) + if err != nil { + t.Fatalf("Failed to create reader pod: %v", err) + } + defer deletePodAndWait(t, cs, csiTestNamespace, "e2e-expand-reader") + + logs := waitForPodCompletion(t, cs, csiTestNamespace, "e2e-expand-reader", podReadyTimeout) + if !strings.Contains(logs, "expand-test") { + t.Fatalf("EXPANSION FAILED: data lost after resize — got: %q", strings.TrimSpace(logs)) + } + + t.Log("PASSED: Volume expansion with data persistence verified") +} diff --git a/src/cloud-api-adaptor/test/e2e/docker_common.go b/src/cloud-api-adaptor/test/e2e/docker_common.go deleted file mode 100644 index c0dc1e6084..0000000000 --- a/src/cloud-api-adaptor/test/e2e/docker_common.go +++ /dev/null @@ -1,53 +0,0 @@ -//go:build docker - -// (C) Copyright Confidential Containers Contributors -// SPDX-License-Identifier: Apache-2.0 - -package e2e - -import ( - "context" - "strings" - "testing" - "time" - - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/client" -) - -// DockerAssert implements the CloudAssert interface for Docker. -type DockerAssert struct { - // TODO: create the connection once on the initializer. - //conn client.Connect -} - -func (c DockerAssert) DefaultTimeout() time.Duration { - return 1 * time.Minute -} - -func (l DockerAssert) HasPodVM(t *testing.T, podvmName string) { - conn, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) - if err != nil { - t.Fatal(err) - } - - // Check if the container is running - containers, err := conn.ContainerList(context.Background(), container.ListOptions{}) - if err != nil { - t.Fatal(err) - } - - for _, container := range containers { - if strings.Contains(container.Names[0], podvmName) { - return - } - } - - // It didn't find the PodVM if it reached here. - t.Error("PodVM was not created") -} - -func (l DockerAssert) GetInstanceType(t *testing.T, podName string) (string, error) { - // Get Instance Type of PodVM - return "", nil -} diff --git a/src/cloud-api-adaptor/test/e2e/docker_test.go b/src/cloud-api-adaptor/test/e2e/docker_test.go deleted file mode 100644 index 4f1d5c19c4..0000000000 --- a/src/cloud-api-adaptor/test/e2e/docker_test.go +++ /dev/null @@ -1,181 +0,0 @@ -//go:build docker - -// (C) Copyright Confidential Containers Contributors -// SPDX-License-Identifier: Apache-2.0 - -package e2e - -import ( - "os" - "testing" - - _ "github.com/confidential-containers/cloud-api-adaptor/src/cloud-api-adaptor/test/provisioner/docker" - "sigs.k8s.io/e2e-framework/pkg/envconf" -) - -func TestDockerCreateSimplePod(t *testing.T) { - assert := DockerAssert{} - DoTestCreateSimplePod(t, testEnv, assert) -} - -func TestDockerCreatePodWithConfigMap(t *testing.T) { - SkipTestOnCI(t) - assert := DockerAssert{} - DoTestCreatePodWithConfigMap(t, testEnv, assert) -} - -func TestDockerCreatePodWithSecret(t *testing.T) { - assert := DockerAssert{} - DoTestCreatePodWithSecret(t, testEnv, assert) -} - -func TestDockerCreatePeerPodContainerWithExternalIPAccess(t *testing.T) { - SkipTestOnCI(t) - assert := DockerAssert{} - DoTestCreatePeerPodContainerWithExternalIPAccess(t, testEnv, assert) - -} - -func TestDockerCreatePeerPodWithJob(t *testing.T) { - assert := DockerAssert{} - DoTestCreatePeerPodWithJob(t, testEnv, assert) -} - -func TestDockerCreatePeerPodAndCheckUserLogs(t *testing.T) { - assert := DockerAssert{} - DoTestCreatePeerPodAndCheckUserLogs(t, testEnv, assert) -} - -func TestDockerCreatePeerPodAndCheckWorkDirLogs(t *testing.T) { - assert := DockerAssert{} - DoTestCreatePeerPodAndCheckWorkDirLogs(t, testEnv, assert) -} - -func TestDockerCreatePeerPodAndCheckEnvVariableLogsWithImageOnly(t *testing.T) { - // This test is causing issues on CI with instability, so skip until we can resolve this. - // See https://github.com/confidential-containers/cloud-api-adaptor/issues/1831 - SkipTestOnCI(t) - assert := DockerAssert{} - DoTestCreatePeerPodAndCheckEnvVariableLogsWithImageOnly(t, testEnv, assert) -} - -func TestDockerCreatePeerPodAndCheckEnvVariableLogsWithDeploymentOnly(t *testing.T) { - assert := DockerAssert{} - DoTestCreatePeerPodAndCheckEnvVariableLogsWithDeploymentOnly(t, testEnv, assert) -} - -func TestDockerCreatePeerPodAndCheckEnvVariableLogsWithImageAndDeployment(t *testing.T) { - // This test is causing issues on CI with instability, so skip until we can resolve this. - // See https://github.com/confidential-containers/cloud-api-adaptor/issues/1831 - assert := DockerAssert{} - DoTestCreatePeerPodAndCheckEnvVariableLogsWithImageAndDeployment(t, testEnv, assert) -} - -func TestDockerCreateNginxDeployment(t *testing.T) { - assert := DockerAssert{} - DoTestNginxDeployment(t, testEnv, assert) -} - -/* -Failing due to issues will pulling image (ErrImagePull) -func TestDockerCreatePeerPodWithLargeImage(t *testing.T) { - assert := DockerAssert{} - DoTestCreatePeerPodWithLargeImage(t, testEnv, assert) -} -*/ - -func TestDockerDeletePod(t *testing.T) { - assert := DockerAssert{} - DoTestDeleteSimplePod(t, testEnv, assert) -} - -func TestDockerPodToServiceCommunication(t *testing.T) { - assert := DockerAssert{} - DoTestPodToServiceCommunication(t, testEnv, assert) -} - -func TestDockerPodsMTLSCommunication(t *testing.T) { - assert := DockerAssert{} - DoTestPodsMTLSCommunication(t, testEnv, assert) -} - -func TestDockerKbsKeyRelease(t *testing.T) { - if !isTestWithKbs() { - t.Skip("Skipping kbs related test as kbs is not deployed") - } - testSecret := envconf.RandomName("coco-pp-e2e-secret", 25) - resourcePath := "caa/workload_key/test_key.bin" - err := keyBrokerService.SetSecret(resourcePath, []byte(testSecret)) - if err != nil { - t.Fatalf("SetSecret failed with: %v", err) - } - err = keyBrokerService.EnableKbsCustomizedResourcePolicy("deny_all.rego") - if err != nil { - t.Fatalf("EnableKbsCustomizedResourcePolicy failed with: %v", err) - } - kbsEndpoint, err := keyBrokerService.GetCachedKbsEndpoint() - if err != nil { - t.Fatalf("GetCachedKbsEndpoint failed with: %v", err) - } - assert := DockerAssert{} - t.Parallel() - DoTestKbsKeyReleaseForFailure(t, testEnv, assert, kbsEndpoint, resourcePath, testSecret) - err = keyBrokerService.EnableKbsCustomizedResourcePolicy("allow_all.rego") - if err != nil { - t.Fatalf("EnableKbsCustomizedResourcePolicy failed with: %v", err) - } - DoTestKbsKeyRelease(t, testEnv, assert, kbsEndpoint, resourcePath, testSecret) -} - -func TestDockerCreatePeerPodWithAuthenticatedImageWithoutCredentials(t *testing.T) { - assert := DockerAssert{} - if os.Getenv("AUTHENTICATED_REGISTRY_IMAGE") != "" { - DoTestCreatePeerPodWithAuthenticatedImageWithoutCredentials(t, testEnv, assert) - } else { - t.Skip("Authenticated Image Name not exported") - } -} - -func TestDockerCreatePeerPodWithAuthenticatedImageWithImagePullSecretInServiceAccount(t *testing.T) { - assert := DockerAssert{} - if os.Getenv("REGISTRY_CREDENTIAL_ENCODED") != "" && os.Getenv("AUTHENTICATED_REGISTRY_IMAGE") != "" { - DoTestCreatePeerPodWithAuthenticatedImageWithImagePullSecretInServiceAccount(t, testEnv, assert) - } else { - t.Skip("Registry Credentials, or authenticated image name not exported") - } -} - -func TestDockerCreatePeerPodWithAuthenticatedImageWithImagePullSecretOnPod(t *testing.T) { - assert := DockerAssert{} - if os.Getenv("REGISTRY_CREDENTIAL_ENCODED") != "" && os.Getenv("AUTHENTICATED_REGISTRY_IMAGE") != "" { - DoTestCreatePeerPodWithAuthenticatedImageWithImagePullSecretOnPod(t, testEnv, assert) - } else { - t.Skip("Registry Credentials, or authenticated image name not exported") - } -} - -func TestDockerCreateWithCpuLimit(t *testing.T) { - // This test is covered as part of unit test and hence skipping to optimise CI time - SkipTestOnCI(t) - assert := DockerAssert{} - DoTestPodWithCPUMemLimitsAndRequests(t, testEnv, assert, "", "", "200m", "") -} - -func TestDockerCreateWithMemLimit(t *testing.T) { - // This test is covered as part of unit test and hence skipping to optimise CI time - SkipTestOnCI(t) - assert := DockerAssert{} - DoTestPodWithCPUMemLimitsAndRequests(t, testEnv, assert, "", "", "", "200Mi") -} - -func TestDockerCreateWithCpuAndMemLimit(t *testing.T) { - // This test is covered as part of unit test and hence skipping to optimise CI time - SkipTestOnCI(t) - assert := DockerAssert{} - DoTestPodWithCPUMemLimitsAndRequests(t, testEnv, assert, "", "", "200m", "200Mi") -} - -func TestDockerCreateWithCpuAndMemRequestLimit(t *testing.T) { - assert := DockerAssert{} - DoTestPodWithCPUMemLimitsAndRequests(t, testEnv, assert, "100m", "100Mi", "200m", "200Mi") -} diff --git a/src/cloud-api-adaptor/test/provisioner/aws/provision_common.go b/src/cloud-api-adaptor/test/provisioner/aws/provision_common.go index d821e63eb0..11bb1c91e8 100644 --- a/src/cloud-api-adaptor/test/provisioner/aws/provision_common.go +++ b/src/cloud-api-adaptor/test/provisioner/aws/provision_common.go @@ -1075,6 +1075,7 @@ func (i *AMIImage) registerImage(imageName string) error { RootDeviceName: aws.String(i.RootDeviceName), TpmSupport: ec2types.TpmSupportValuesV20, VirtualizationType: aws.String("hvm"), + ImdsSupport: ec2types.ImdsSupportValuesV20, TagSpecifications: defaultTagSpecifications(i.BaseName+"-img", ec2types.ResourceTypeImage), }) if err != nil { diff --git a/src/cloud-api-adaptor/test/provisioner/azure/provision_common.go b/src/cloud-api-adaptor/test/provisioner/azure/provision_common.go index 43431d9642..0e66b0bfb1 100644 --- a/src/cloud-api-adaptor/test/provisioner/azure/provision_common.go +++ b/src/cloud-api-adaptor/test/provisioner/azure/provision_common.go @@ -372,7 +372,7 @@ func (p *AzureCloudProvisioner) GetProperties(ctx context.Context, cfg *envconf. func (p *AzureCloudProvisioner) UploadPodvm(imagePath string, ctx context.Context, cfg *envconf.Config) error { log.Trace("UploadPodvm()") - log.Trace("Image is uploaded via packer in case of azure") + log.Trace("Image is uploaded via mkosi in case of azure") return nil } diff --git a/src/cloud-api-adaptor/test/provisioner/byom/provision_byom.properties b/src/cloud-api-adaptor/test/provisioner/byom/provision_byom.properties index 9e6f485b67..b9651ef9f5 100644 --- a/src/cloud-api-adaptor/test/provisioner/byom/provision_byom.properties +++ b/src/cloud-api-adaptor/test/provisioner/byom/provision_byom.properties @@ -6,7 +6,6 @@ # Running with Docker DOCKER_HOST="unix:///var/run/docker.sock" DOCKER_NETWORK_NAME="kind" -DOCKER_API_VERSION="1.48" BYOM_PODVM_IMAGE="quay.io/confidential-containers/podvm-byom-e2e-image-amd64:latest" diff --git a/src/cloud-api-adaptor/test/provisioner/byom/provision_common.go b/src/cloud-api-adaptor/test/provisioner/byom/provision_common.go index 3e31bbe770..2438cc4524 100644 --- a/src/cloud-api-adaptor/test/provisioner/byom/provision_common.go +++ b/src/cloud-api-adaptor/test/provisioner/byom/provision_common.go @@ -14,14 +14,12 @@ import ( "time" pv "github.com/confidential-containers/cloud-api-adaptor/src/cloud-api-adaptor/test/provisioner" - "github.com/confidential-containers/cloud-api-adaptor/src/cloud-api-adaptor/test/provisioner/docker" log "github.com/sirupsen/logrus" "sigs.k8s.io/e2e-framework/pkg/envconf" ) -// ByomProvisioner uses DockerProvisioner for BYOM-specific functionality +// ByomProvisioner implements BYOM e2e provisioning type ByomProvisioner struct { - *docker.DockerProvisioner provisionerCreatedVMs []string // Track VMs created by this provisioner instance } @@ -96,24 +94,12 @@ func NewByomProvisioner(properties map[string]string) (pv.CloudProvisioner, erro if err := initByomProperties(properties); err != nil { return nil, err } - dockerProps := map[string]string{ - "DOCKER_HOST": ByomProps.DockerHost, - "DOCKER_NETWORK_NAME": ByomProps.DockerNetworkName, - "BYOM_PODVM_IMAGE": ByomProps.ByomPodvmImage, - "CLUSTER_NAME": ByomProps.ClusterName, - "CONTAINER_RUNTIME": ByomProps.ContainerRuntime, - "CAA_IMAGE": ByomProps.CaaImage, - "CAA_IMAGE_TAG": ByomProps.CaaImageTag, - } - dockerProvisioner, err := docker.NewDockerProvisioner(dockerProps) - if err != nil { - return nil, err + if ByomProps.DockerHost != "" { + os.Setenv("DOCKER_HOST", ByomProps.DockerHost) } - return &ByomProvisioner{ - DockerProvisioner: dockerProvisioner.(*docker.DockerProvisioner), - }, nil + return &ByomProvisioner{}, nil } func (b *ByomProvisioner) CreateCluster(ctx context.Context, cfg *envconf.Config) error { @@ -125,10 +111,24 @@ func (b *ByomProvisioner) CreateCluster(ctx context.Context, cfg *envconf.Config log.Infof("Using BYOM kind config from: %s", kindConfigPath) os.Setenv("KIND_CONFIG_FILE", kindConfigPath) - if err := b.DockerProvisioner.CreateCluster(ctx, cfg); err != nil { + workingDir := filepath.Dir(kindConfigPath) + + if err := b.createKindCluster(workingDir); err != nil { + log.Errorf("Error creating Kind cluster: %v", err) return err } + home, err := os.UserHomeDir() + if err != nil { + return fmt.Errorf("failed to get user home directory: %w", err) + } + cfg.WithKubeconfigFile(filepath.Join(home, ".kube/config")) + + if err := pv.AddNodeRoleWorkerLabel(context.Background(), ByomProps.ClusterName, cfg); err != nil { + + return fmt.Errorf("failed to label nodes: %w", err) + } + // Update containerd configuration to not discard unpacked layers log.Info("Configuring containerd on worker node to keep unpacked layers...") @@ -197,8 +197,23 @@ func (b *ByomProvisioner) CreatePodVMInstance(ctx context.Context, cfg *envconf. return nil } +func (b *ByomProvisioner) CreateVPC(ctx context.Context, cfg *envconf.Config) error { + // BYOM e2e provisioning relies on local docker/kind resources + return nil +} + func (b *ByomProvisioner) DeleteCluster(ctx context.Context, cfg *envconf.Config) error { - return b.DockerProvisioner.DeleteCluster(ctx, cfg) + kindConfigPath, err := filepath.Abs(ByomProps.KindConfigFile) + if err != nil { + return fmt.Errorf("error getting absolute path of kind config file: %w", err) + } + + return b.deleteKindCluster(filepath.Dir(kindConfigPath)) +} + +func (b *ByomProvisioner) DeleteVPC(ctx context.Context, cfg *envconf.Config) error { + // BYOM e2e provisioning relies on local docker/kind resources + return nil } func (b *ByomProvisioner) DeletePodVMInstance(ctx context.Context, cfg *envconf.Config) error { @@ -223,6 +238,12 @@ func (b *ByomProvisioner) DeletePodVMInstance(ctx context.Context, cfg *envconf. return nil } +func (b *ByomProvisioner) UploadPodvm(imagePath string, ctx context.Context, cfg *envconf.Config) error { + // For BYOM e2e, the podvm artifact is represented by the container image configured in + // BYOM_PODVM_IMAGE, so there is nothing to upload here. + return nil +} + func (b *ByomProvisioner) GetProperties(ctx context.Context, cfg *envconf.Config) map[string]string { return map[string]string{ "VM_POOL_IPS": ByomProps.VMPoolIPs, @@ -455,3 +476,37 @@ func (b *ByomProvisioner) destroyContainer(containerName string) error { log.Infof("Container %s destroyed successfully", containerName) return nil } + +func (b *ByomProvisioner) createKindCluster(workingDir string) error { + // Create kind cluster by executing the script on the node + cmd := exec.Command("/bin/bash", "-c", "./kind_cluster.sh create") + cmd.Dir = workingDir + cmd.Stdout = os.Stdout + // TODO: better handle stderr. Messages getting out of order. + cmd.Stderr = os.Stderr + cmd.Env = os.Environ() + // Set CLUSTER_NAME and CONTAINER_RUNTIME if available. Also unset KUBECONFIG so that the default path is used. + cmd.Env = append(cmd.Env, "CLUSTER_NAME="+ByomProps.ClusterName, "KUBECONFIG=", "CONTAINER_RUNTIME="+ByomProps.ContainerRuntime) + err := cmd.Run() + if err != nil { + log.Errorf("Error creating Kind cluster: %v", err) + return err + } + + return nil +} + +func (b *ByomProvisioner) deleteKindCluster(workingDir string) error { + // Delete kind cluster by executing the script on the node + cmd := exec.Command("/bin/bash", "-c", "./kind_cluster.sh delete") + cmd.Dir = workingDir + cmd.Stdout = os.Stdout + // TODO: better handle stderr. Messages getting out of order. + cmd.Stderr = os.Stderr + err := cmd.Run() + if err != nil { + log.Errorf("Error deleting Kind cluster: %v", err) + return err + } + return nil +} diff --git a/src/cloud-api-adaptor/test/provisioner/docker/provision.go b/src/cloud-api-adaptor/test/provisioner/docker/provision.go deleted file mode 100644 index ede60a336f..0000000000 --- a/src/cloud-api-adaptor/test/provisioner/docker/provision.go +++ /dev/null @@ -1,15 +0,0 @@ -//go:build docker - -// (C) Copyright Confidential Containers Contributors -// SPDX-License-Identifier: Apache-2.0 - -package docker - -import ( - pv "github.com/confidential-containers/cloud-api-adaptor/src/cloud-api-adaptor/test/provisioner" -) - -func init() { - pv.NewProvisionerFunctions["docker"] = NewDockerProvisioner - pv.NewInstallChartFunctions["docker"] = NewDockerInstallChart -} diff --git a/src/cloud-api-adaptor/test/provisioner/docker/provision_common.go b/src/cloud-api-adaptor/test/provisioner/docker/provision_common.go deleted file mode 100644 index 5da52f0357..0000000000 --- a/src/cloud-api-adaptor/test/provisioner/docker/provision_common.go +++ /dev/null @@ -1,260 +0,0 @@ -// (C) Copyright Confidential Containers Contributors -// SPDX-License-Identifier: Apache-2.0 - -package docker - -import ( - "context" - "fmt" - - "os" - "os/exec" - "path/filepath" - - pv "github.com/confidential-containers/cloud-api-adaptor/src/cloud-api-adaptor/test/provisioner" - "github.com/docker/docker/client" - log "github.com/sirupsen/logrus" - - "sigs.k8s.io/e2e-framework/pkg/envconf" -) - -// DockerProvisioner implements the CloudProvisioner interface for Docker. -type DockerProvisioner struct { - conn *client.Client - wd string // docker's directory path on this repository -} - -// DockerInstallChart implements the InstallChart interface -type DockerInstallChart struct { - Helm *pv.Helm -} - -type DockerProperties struct { - DockerHost string - APIVer string - ClusterName string - NetworkName string - PodvmImage string - CaaImage string - CaaImageTag string - ContainerRuntime string - TunnelType string - VxlanPort string -} - -var DockerProps = &DockerProperties{} - -func initDockerProperties(properties map[string]string) error { - - DockerProps = &DockerProperties{ - DockerHost: properties["DOCKER_HOST"], - APIVer: properties["DOCKER_API_VERSION"], - ClusterName: properties["CLUSTER_NAME"], - NetworkName: properties["DOCKER_NETWORK_NAME"], - PodvmImage: properties["DOCKER_PODVM_IMAGE"], - CaaImage: properties["CAA_IMAGE"], - CaaImageTag: properties["CAA_IMAGE_TAG"], - ContainerRuntime: properties["CONTAINER_RUNTIME"], - TunnelType: properties["TUNNEL_TYPE"], - VxlanPort: properties["VXLAN_PORT"], - } - return nil -} - -func NewDockerProvisioner(properties map[string]string) (pv.CloudProvisioner, error) { - wd, err := filepath.Abs(filepath.Join("..", "..", "docker")) - if err != nil { - log.Errorf("Error getting the absolute path of the docker directory: %v", err) - return nil, err - } - - if err := initDockerProperties(properties); err != nil { - return nil, err - } - - // set environment variables - os.Setenv("DOCKER_HOST", DockerProps.DockerHost) - if DockerProps.APIVer != "" { - os.Setenv("DOCKER_API_VERSION", DockerProps.APIVer) - } - - conn, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) - if err != nil { - log.Errorf("Error creating the Docker client: %v", err) - return nil, err - } - - return &DockerProvisioner{ - conn: conn, - wd: wd, - }, nil -} - -func (l *DockerProvisioner) CreateCluster(ctx context.Context, cfg *envconf.Config) error { - - // Create kind cluster - if err := createKindCluster(l.wd); err != nil { - log.Errorf("Error creating Kind cluster: %v", err) - return err - } - - home, _ := os.UserHomeDir() - kubeconfig := filepath.Join(home, ".kube/config") - cfg.WithKubeconfigFile(kubeconfig) - - if err := pv.AddNodeRoleWorkerLabel(ctx, DockerProps.ClusterName, cfg); err != nil { - - return fmt.Errorf("labeling nodes: %w", err) - } - - return nil -} - -func (l *DockerProvisioner) DeleteCluster(ctx context.Context, cfg *envconf.Config) error { - - if err := deleteKindCluster(l.wd); err != nil { - log.Errorf("Error deleting Kind cluster: %v", err) - return err - } - return nil -} - -func (l *DockerProvisioner) CreateVPC(ctx context.Context, cfg *envconf.Config) error { - // TODO: delete the resources created on CreateVPC() that currently only checks - // the Docker's storage and network exist. - return nil -} - -func (l *DockerProvisioner) DeleteVPC(ctx context.Context, cfg *envconf.Config) error { - // TODO: delete the resources created on CreateVPC() that currently only checks - // the Docker's storage and network exist. - return nil -} - -func (l *DockerProvisioner) GetProperties(ctx context.Context, cfg *envconf.Config) map[string]string { - allProps := map[string]string{ - "DOCKER_HOST": DockerProps.DockerHost, - "DOCKER_API_VERSION": DockerProps.APIVer, - "CLUSTER_NAME": DockerProps.ClusterName, - "DOCKER_NETWORK_NAME": DockerProps.NetworkName, - "DOCKER_PODVM_IMAGE": DockerProps.PodvmImage, - "CAA_IMAGE": DockerProps.CaaImage, - "CAA_IMAGE_TAG": DockerProps.CaaImageTag, - "CONTAINER_RUNTIME": DockerProps.ContainerRuntime, - "TUNNEL_TYPE": DockerProps.TunnelType, - "VXLAN_PORT": DockerProps.VxlanPort, - } - - // Filter out empty values to avoid overriding defaults - props := make(map[string]string) - for k, v := range allProps { - if v != "" { - props[k] = v - } - } - - return props -} - -func (l *DockerProvisioner) UploadPodvm(imagePath string, ctx context.Context, cfg *envconf.Config) error { - - // Download the podvm image from the registry by using docker pull - cmd := exec.Command("/bin/bash", "-c", "docker pull "+DockerProps.PodvmImage) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err := cmd.Run() - if err != nil { - log.Errorf("Error pulling the podvm image: %v", err) - return err - } - - return nil -} - -func createKindCluster(workingDir string) error { - // Create kind cluster by executing the script on the node - cmd := exec.Command("/bin/bash", "-c", "./kind_cluster.sh create") - cmd.Dir = workingDir - cmd.Stdout = os.Stdout - // TODO: better handle stderr. Messages getting out of order. - cmd.Stderr = os.Stderr - cmd.Env = os.Environ() - // Set CLUSTER_NAME and CONTAINER_RUNTIME if available. Also unset KUBECONFIG so that the default path is used. - cmd.Env = append(cmd.Env, "CLUSTER_NAME="+DockerProps.ClusterName, "KUBECONFIG=", "CONTAINER_RUNTIME="+DockerProps.ContainerRuntime) - err := cmd.Run() - if err != nil { - log.Errorf("Error creating Kind cluster: %v", err) - return err - } - return nil -} - -func deleteKindCluster(workingDir string) error { - // Delete kind cluster by executing the script on the node - cmd := exec.Command("/bin/bash", "-c", "./kind_cluster.sh delete") - cmd.Dir = workingDir - cmd.Stdout = os.Stdout - // TODO: better handle stderr. Messages getting out of order. - cmd.Stderr = os.Stderr - err := cmd.Run() - if err != nil { - log.Errorf("Error deleting Kind cluster: %v", err) - return err - } - return nil -} - -func NewDockerInstallChart(installDir, provider string) (pv.InstallChart, error) { - chartPath := filepath.Join(installDir, "charts", "peerpods") - namespace := pv.GetCAANamespace() - releaseName := "peerpods" - debug := false - - helm, err := pv.NewHelm(chartPath, namespace, releaseName, provider, debug) - if err != nil { - return nil, err - } - - return &DockerInstallChart{ - Helm: helm, - }, nil -} - -func (d *DockerInstallChart) GetHelm() *pv.Helm { return d.Helm } - -func (d *DockerInstallChart) Install(ctx context.Context, cfg *envconf.Config) error { - return d.Helm.Install(ctx, cfg) -} - -func (d *DockerInstallChart) Uninstall(ctx context.Context, cfg *envconf.Config) error { - return d.Helm.Uninstall(ctx, cfg) -} - -func (d *DockerInstallChart) Configure(ctx context.Context, cfg *envconf.Config, properties map[string]string) error { - // Handle CAA image - already split into CAA_IMAGE and CAA_IMAGE_TAG - if properties["CAA_IMAGE"] != "" { - d.Helm.OverrideValues["image.name"] = properties["CAA_IMAGE"] - } - if properties["CAA_IMAGE_TAG"] != "" { - d.Helm.OverrideValues["image.tag"] = properties["CAA_IMAGE_TAG"] - } - - // Mapping the internal properties to Helm chart values. - mapProps := map[string]string{ - "DOCKER_HOST": "DOCKER_HOST", - "DOCKER_API_VERSION": "DOCKER_API_VERSION", - "DOCKER_PODVM_IMAGE": "DOCKER_PODVM_IMAGE", - "DOCKER_NETWORK_NAME": "DOCKER_NETWORK_NAME", - "TUNNEL_TYPE": "TUNNEL_TYPE", - "VXLAN_PORT": "VXLAN_PORT", - "INITDATA": "INITDATA", - } - - for k, v := range mapProps { - if properties[k] != "" { - d.Helm.OverrideProviderValues[v] = properties[k] - } - } - - return nil -} diff --git a/src/cloud-api-adaptor/test/provisioner/docker/provision_docker.properties b/src/cloud-api-adaptor/test/provisioner/docker/provision_docker.properties deleted file mode 100644 index 1fbf9d6026..0000000000 --- a/src/cloud-api-adaptor/test/provisioner/docker/provision_docker.properties +++ /dev/null @@ -1,11 +0,0 @@ -# Docker configs -CLUSTER_NAME="peer-pods" -DOCKER_HOST="unix:///var/run/docker.sock" -DOCKER_PODVM_IMAGE="quay.io/confidential-containers/podvm-docker-image" -DOCKER_NETWORK_NAME="kind" -DOCKER_API_VERSION="1.44" -CAA_IMAGE="" -CAA_IMAGE_TAG="" - -# either "containerd" or "crio" -CONTAINER_RUNTIME="containerd" diff --git a/src/cloud-api-adaptor/versions.schema.json b/src/cloud-api-adaptor/versions.schema.json index 4f56f017fb..da9b65522c 100644 --- a/src/cloud-api-adaptor/versions.schema.json +++ b/src/cloud-api-adaptor/versions.schema.json @@ -4,7 +4,11 @@ "description": "Schema for src/cloud-api-adaptor/versions.yaml", "type": "object", "additionalProperties": false, - "required": ["tools", "git", "oci"], + "required": [ + "tools", + "git", + "oci" + ], "properties": { "cloudimg": { "type": "object", @@ -31,21 +35,37 @@ "kcli", "mkosi", "protoc", - "packer", "oras", "helm" ], "properties": { - "cert-manager": { "$ref": "#/$defs/tool" }, - "bats": { "$ref": "#/$defs/tool" }, - "iptables-wrapper": { "$ref": "#/$defs/tool" }, - "golang": { "$ref": "#/$defs/tool" }, - "kcli": { "$ref": "#/$defs/tool" }, - "mkosi": { "$ref": "#/$defs/tool" }, - "protoc": { "$ref": "#/$defs/tool" }, - "packer": { "$ref": "#/$defs/tool" }, - "oras": { "$ref": "#/$defs/tool" }, - "helm": { "$ref": "#/$defs/tool" } + "cert-manager": { + "$ref": "#/$defs/tool" + }, + "bats": { + "$ref": "#/$defs/tool" + }, + "iptables-wrapper": { + "$ref": "#/$defs/tool" + }, + "golang": { + "$ref": "#/$defs/tool" + }, + "kcli": { + "$ref": "#/$defs/tool" + }, + "mkosi": { + "$ref": "#/$defs/tool" + }, + "protoc": { + "$ref": "#/$defs/tool" + }, + "oras": { + "$ref": "#/$defs/tool" + }, + "helm": { + "$ref": "#/$defs/tool" + } } }, "git": { @@ -54,10 +74,18 @@ "additionalProperties": { "type": "object", "additionalProperties": false, - "required": ["url", "reference"], + "required": [ + "url", + "reference" + ], "properties": { - "url": { "type": "string", "format": "uri" }, - "reference": { "$ref": "#/$defs/nonEmptyString" } + "url": { + "type": "string", + "format": "uri" + }, + "reference": { + "$ref": "#/$defs/nonEmptyString" + } } } }, @@ -67,15 +95,31 @@ "additionalProperties": { "type": "object", "additionalProperties": false, - "required": ["registry"], + "required": [ + "registry" + ], "properties": { - "registry": { "$ref": "#/$defs/nonEmptyString" }, - "tag": { "$ref": "#/$defs/nonEmptyString" }, - "reference": { "$ref": "#/$defs/nonEmptyString" } + "registry": { + "$ref": "#/$defs/nonEmptyString" + }, + "tag": { + "$ref": "#/$defs/nonEmptyString" + }, + "reference": { + "$ref": "#/$defs/nonEmptyString" + } }, "oneOf": [ - { "required": ["tag"] }, - { "required": ["reference"] } + { + "required": [ + "tag" + ] + }, + { + "required": [ + "reference" + ] + } ] } } @@ -91,14 +135,23 @@ }, "tool": { "oneOf": [ - { "$ref": "#/$defs/nonEmptyString" }, + { + "$ref": "#/$defs/nonEmptyString" + }, { "type": "object", "additionalProperties": false, - "required": ["version", "sha256"], + "required": [ + "version", + "sha256" + ], "properties": { - "version": { "$ref": "#/$defs/nonEmptyString" }, - "sha256": { "$ref": "#/$defs/sha256Hex" } + "version": { + "$ref": "#/$defs/nonEmptyString" + }, + "sha256": { + "$ref": "#/$defs/sha256Hex" + } } } ] @@ -106,9 +159,15 @@ "imageSource": { "type": "object", "additionalProperties": false, - "required": ["url", "checksum"], + "required": [ + "url", + "checksum" + ], "properties": { - "url": { "type": "string", "format": "uri" }, + "url": { + "type": "string", + "format": "uri" + }, "checksum": { "type": "string", "description": "Algorithm-prefixed digest, e.g. sha256:<64 hex> or sha512:<128 hex>.", diff --git a/src/cloud-api-adaptor/versions.yaml b/src/cloud-api-adaptor/versions.yaml index bd24db8177..b630244690 100644 --- a/src/cloud-api-adaptor/versions.yaml +++ b/src/cloud-api-adaptor/versions.yaml @@ -30,11 +30,10 @@ tools: cert-manager: v1.15.3 bats: 1.10.0 iptables-wrapper: bfef9e5087a198b50a4124bb9ce9d2c7c99025dd - golang: 1.25.10 + golang: 1.25.11 kcli: 99.0.202507200957 mkosi: v26 protoc: 3.16.0 - packer: v1.9.4 oras: 1.2.0 helm: version: v4.0.4 @@ -49,7 +48,7 @@ git: reference: v1.5.0 kbs: url: https://github.com/confidential-containers/trustee - reference: 22788122660d6e9be3e4bf52704282de5fcc0a2a + reference: d4e317620c4039c89779b725f74974d8f005da66 # If a tag is given it will attempt to pull the oci image by tag. if a # reference is specified the corresponding tag will be constructed using # the reference and suffixes like architecture or tee. @@ -59,10 +58,7 @@ oci: tag: "3.9" kata-containers: registry: ghcr.io/kata-containers/cached-artefacts - reference: 8dccf4cf37aeea4b6c2caacf3e61510d6eef2f71 # v3.29.0 + reference: bef049d07e34d467b3a0910fdc65f30aa4647a0a # v3.31.0 + tagging fix guest-components: registry: ghcr.io/confidential-containers/guest-components - reference: de3f6ff62aa736619b80d99dfca5bc3d2c9a799d - kata-agent-ctl: - registry: ghcr.io/kata-containers/cached-artefacts - reference: 3.29.0 + reference: f1561038b9a58d309a3366cc8e25d8e6162424a0 diff --git a/src/cloud-providers/Makefile b/src/cloud-providers/Makefile index 4f8a3cf4d2..b074535028 100644 --- a/src/cloud-providers/Makefile +++ b/src/cloud-providers/Makefile @@ -13,9 +13,9 @@ define gen-provider-values "", \ "provider: \(.provider)", \ "", \ - (if .provider == "libvirt" or .provider == "docker" then \ + (if .provider == "libvirt" then \ "# Dev image required for this provider (includes CGO bindings)", \ - "# Update to dev- at release time", \ + "# Update to -dev at release time", \ "image:", \ " tag: \"latest\"", \ "" \ diff --git a/src/cloud-providers/aws/ec2.go b/src/cloud-providers/aws/ec2.go index ea1f7913ee..8c13242f56 100644 --- a/src/cloud-providers/aws/ec2.go +++ b/src/cloud-providers/aws/ec2.go @@ -13,28 +13,31 @@ import ( "github.com/aws/aws-sdk-go-v2/service/ec2" ) -// TODO: Use IAM role func NewEC2Client(cloudCfg Config) (*ec2.Client, error) { var cfg aws.Config var err error if cloudCfg.AccessKeyID != "" && cloudCfg.SecretKey != "" { + logger.Printf("using static credentials") cfg, err = config.LoadDefaultConfig(context.TODO(), - config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(cloudCfg.AccessKeyID, cloudCfg.SecretKey, cloudCfg.SessionToken)), config.WithRegion(cloudCfg.Region)) - if err != nil { - return nil, fmt.Errorf("configuration error when using creds: %s", err) - } - - } else { - + config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(cloudCfg.AccessKeyID, cloudCfg.SecretKey, cloudCfg.SessionToken)), + config.WithRegion(cloudCfg.Region)) + } else if cloudCfg.LoginProfile != "" { + logger.Printf("using shared profile: %s", cloudCfg.LoginProfile) cfg, err = config.LoadDefaultConfig(context.TODO(), config.WithRegion(cloudCfg.Region), config.WithSharedConfigProfile(cloudCfg.LoginProfile)) - if err != nil { - return nil, fmt.Errorf("configuration error when using shared profile: %s", err) - } + } else { + logger.Printf("using default credential chain (supports IRSA)") + cfg, err = config.LoadDefaultConfig(context.TODO(), + config.WithRegion(cloudCfg.Region)) + } + + if err != nil { + return nil, fmt.Errorf("failed to load AWS configuration: %w", err) } + client := ec2.NewFromConfig(cfg) return client, nil } diff --git a/src/cloud-providers/aws/ec2_test.go b/src/cloud-providers/aws/ec2_test.go new file mode 100644 index 0000000000..a0f672e4bd --- /dev/null +++ b/src/cloud-providers/aws/ec2_test.go @@ -0,0 +1,85 @@ +// (C) Copyright Confidential Containers Contributors +// SPDX-License-Identifier: Apache-2.0 + +package aws + +import ( + "os" + "testing" +) + +// TestNewEC2Client_StaticCredentials verifies that NewEC2Client uses static credentials when provided +func TestNewEC2Client_StaticCredentials(t *testing.T) { + // Clear any environment variables that might interfere + os.Unsetenv("AWS_WEB_IDENTITY_TOKEN_FILE") + os.Unsetenv("AWS_ROLE_ARN") + os.Unsetenv("AWS_ACCESS_KEY_ID") + os.Unsetenv("AWS_SECRET_ACCESS_KEY") + + cfg := Config{ + AccessKeyID: "AKIAIOSFODNN7EXAMPLE", + SecretKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + Region: "us-east-1", + } + + // This should not panic or return error for invalid credentials (just test config creation) + // We can't actually test AWS connectivity without real credentials + client, err := NewEC2Client(cfg) + if err != nil { + t.Errorf("NewEC2Client with static credentials failed: %v", err) + } + if client == nil { + t.Error("Expected non-nil client") + } +} + +// TestNewEC2Client_DefaultCredentialChain verifies that NewEC2Client uses default credential chain when no static credentials +func TestNewEC2Client_DefaultCredentialChain(t *testing.T) { + // Clear any environment variables + os.Unsetenv("AWS_WEB_IDENTITY_TOKEN_FILE") + os.Unsetenv("AWS_ROLE_ARN") + os.Unsetenv("AWS_ACCESS_KEY_ID") + os.Unsetenv("AWS_SECRET_ACCESS_KEY") + + cfg := Config{ + AccessKeyID: "", + SecretKey: "", + Region: "us-east-1", + } + + // This should use default credential chain + // It will fail to find credentials in test environment, but the SDK initialization should work + client, err := NewEC2Client(cfg) + if err != nil { + t.Errorf("NewEC2Client with default credential chain failed: %v", err) + } + if client == nil { + t.Error("Expected non-nil client") + } +} + +// TestNewEC2Client_MissingSecretKey verifies that having only AccessKeyID without SecretKey still works +// (it will be ignored and fall through to default credential chain) +func TestNewEC2Client_PartialStaticCredentials(t *testing.T) { + // Clear environment + os.Unsetenv("AWS_WEB_IDENTITY_TOKEN_FILE") + os.Unsetenv("AWS_ROLE_ARN") + os.Unsetenv("AWS_ACCESS_KEY_ID") + os.Unsetenv("AWS_SECRET_ACCESS_KEY") + + // Only AccessKeyID, no SecretKey + cfg := Config{ + AccessKeyID: "AKIAIOSFODNN7EXAMPLE", + SecretKey: "", // Missing + Region: "us-east-1", + } + + // Should fall through to default credential chain since both are required for static credentials + client, err := NewEC2Client(cfg) + if err != nil { + t.Errorf("NewEC2Client with partial credentials failed: %v", err) + } + if client == nil { + t.Error("Expected non-nil client") + } +} diff --git a/src/cloud-providers/aws/provider.go b/src/cloud-providers/aws/provider.go index 7c6e57b957..f90a235bc9 100644 --- a/src/cloud-providers/aws/provider.go +++ b/src/cloud-providers/aws/provider.go @@ -10,11 +10,14 @@ import ( "fmt" "log" "net/netip" + "regexp" + "strings" "time" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/ec2" "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "golang.org/x/sync/errgroup" provider "github.com/confidential-containers/cloud-api-adaptor/src/cloud-providers" "github.com/confidential-containers/cloud-api-adaptor/src/cloud-providers/util" @@ -86,6 +89,12 @@ type ec2Client interface { ModifyNetworkInterfaceAttribute(ctx context.Context, params *ec2.ModifyNetworkInterfaceAttributeInput, optFns ...func(*ec2.Options)) (*ec2.ModifyNetworkInterfaceAttributeOutput, error) + AttachVolume(ctx context.Context, + params *ec2.AttachVolumeInput, + optFns ...func(*ec2.Options)) (*ec2.AttachVolumeOutput, error) + DescribeVolumes(ctx context.Context, + params *ec2.DescribeVolumesInput, + optFns ...func(*ec2.Options)) (*ec2.DescribeVolumesOutput, error) } // Make instanceRunningWaiter as an interface @@ -305,6 +314,12 @@ func (p *awsProvider) CreateInstance(ctx context.Context, podName, sandboxID str } } + if len(spec.Volumes) > 0 { + if err := p.validateVolumes(ctx, spec.Volumes); err != nil { + return nil, err + } + } + logger.Printf("Creating instance %s for sandbox %s", instanceName, sandboxID) result, err := p.ec2Client.RunInstances(ctx, input) @@ -339,6 +354,12 @@ func (p *awsProvider) CreateInstance(ctx context.Context, podName, sandboxID str ips[0] = publicIPAddr } + if len(spec.Volumes) > 0 { + if err := p.attachEBSVolumes(ctx, instanceID, spec.Volumes); err != nil { + return instance, fmt.Errorf("attaching EBS volumes to %s: %w", instanceID, err) + } + } + if spec.MultiNic { nIfaceID, err := p.createAddonNICforInstance(ctx, instanceID) if err != nil { @@ -358,6 +379,144 @@ func (p *awsProvider) CreateInstance(ctx context.Context, podName, sandboxID str return instance, nil } +const ( + ebsAttachTimeout = 60 * time.Second + ebsAttachPollRate = 3 * time.Second +) + +// ebsDeviceName generates /dev/xvdb../dev/xvdz for indices 0-24, then +// /dev/xvdba../dev/xvdbz for 25-50, etc. This avoids the 25-disk +// limit of the previous single-letter scheme. +func ebsDeviceName(index int) string { + if index < 25 { + return fmt.Sprintf("/dev/xvd%c", 'b'+rune(index)) + } + first := 'b' + rune((index-25)/26) + second := 'a' + rune((index-25)%26) + return fmt.Sprintf("/dev/xvd%c%c", first, second) +} + +var ebsVolumeIDRe = regexp.MustCompile(`^vol-[0-9a-f]{8,}$`) + +func isValidEBSVolumeID(id string) bool { + return ebsVolumeIDRe.MatchString(id) +} + +// validateVolumes checks that all EBS volumes exist and are in the "available" +// state before creating the PodVM instance. +func (p *awsProvider) validateVolumes(ctx context.Context, volumes []provider.CloudVolume) error { + var volumeIDs []string + for _, vol := range volumes { + if !isValidEBSVolumeID(vol.DiskID) { + return fmt.Errorf("invalid EBS volume ID format: %q", vol.DiskID) + } + volumeIDs = append(volumeIDs, vol.DiskID) + } + + result, err := p.ec2Client.DescribeVolumes(ctx, &ec2.DescribeVolumesInput{ + VolumeIds: volumeIDs, + }) + if err != nil { + return fmt.Errorf("validating EBS volumes: %w", err) + } + + var errs []string + for _, v := range result.Volumes { + if v.State != types.VolumeStateAvailable { + attached := "" + for _, att := range v.Attachments { + if att.InstanceId != nil { + attached = fmt.Sprintf(" (attached to %s)", *att.InstanceId) + } + } + errs = append(errs, fmt.Sprintf("volume %s is in state %q%s", + *v.VolumeId, v.State, attached)) + } + } + + if len(errs) > 0 { + return fmt.Errorf("EBS volume pre-validation failed: %s", strings.Join(errs, "; ")) + } + return nil +} + +// attachEBSVolumes attaches the given EBS volumes to the instance. +// IMPORTANT: EBS volumes can only be attached to instances in the same +// Availability Zone. Ensure the PodVM instance and volumes are in the same AZ +// (e.g. via subnet placement or StorageClass allowedTopologies). +func (p *awsProvider) attachEBSVolumes(ctx context.Context, instanceID string, volumes []provider.CloudVolume) error { + if len(volumes) == 0 { + return nil + } + + describeInput := &ec2.DescribeInstancesInput{ + InstanceIds: []string{instanceID}, + } + if err := p.waiter.Wait(ctx, describeInput, maxWaitTime); err != nil { + return fmt.Errorf("waiting for instance %s to be running: %w", instanceID, err) + } + + g, gctx := errgroup.WithContext(ctx) + + for i, vol := range volumes { + idx := i + diskID := vol.DiskID + deviceName := ebsDeviceName(idx) + + g.Go(func() error { + logger.Printf("Attaching EBS volume %s to instance %s as %s (index %d)", diskID, instanceID, deviceName, idx) + + attachOutput, err := p.ec2Client.AttachVolume(gctx, &ec2.AttachVolumeInput{ + Device: aws.String(deviceName), + InstanceId: aws.String(instanceID), + VolumeId: aws.String(diskID), + }) + if err != nil { + return fmt.Errorf("attaching EBS volume %s: %w", diskID, err) + } + + logger.Printf("AttachVolume request accepted for %s (state: %s)", diskID, attachOutput.State) + + if err := p.waitForVolumeAttached(gctx, diskID); err != nil { + return fmt.Errorf("waiting for EBS volume %s to attach: %w", diskID, err) + } + + logger.Printf("EBS volume %s attached successfully to %s as %s", diskID, instanceID, deviceName) + return nil + }) + } + + return g.Wait() +} + +func (p *awsProvider) waitForVolumeAttached(ctx context.Context, volumeID string) error { + deadline := time.Now().Add(ebsAttachTimeout) + for time.Now().Before(deadline) { + result, err := p.ec2Client.DescribeVolumes(ctx, &ec2.DescribeVolumesInput{ + VolumeIds: []string{volumeID}, + }) + if err != nil { + return fmt.Errorf("describing volume %s: %w", volumeID, err) + } + if len(result.Volumes) > 0 && len(result.Volumes[0].Attachments) > 0 { + state := result.Volumes[0].Attachments[0].State + if state == types.VolumeAttachmentStateAttached { + return nil + } + logger.Printf("Volume %s attachment state: %s, waiting...", volumeID, state) + } + select { + case <-ctx.Done(): + return ctx.Err() + case <-time.After(ebsAttachPollRate): + } + } + return fmt.Errorf("volume %s did not reach attached state within %v", volumeID, ebsAttachTimeout) +} + +// DeleteInstance terminates the EC2 instance. EBS volumes attached after +// launch (with DeleteOnTermination=false, the default) are automatically +// detached by AWS when the instance terminates. func (p *awsProvider) DeleteInstance(ctx context.Context, instanceID string) error { err := p.deleteElasticIPforInstance(ctx, instanceID) diff --git a/src/cloud-providers/aws/provider_test.go b/src/cloud-providers/aws/provider_test.go index 1b529ffade..92e000e6db 100644 --- a/src/cloud-providers/aws/provider_test.go +++ b/src/cloud-providers/aws/provider_test.go @@ -232,6 +232,34 @@ func (m mockEC2Client) ModifyNetworkInterfaceAttribute(ctx context.Context, return nil, nil } +func (m mockEC2Client) AttachVolume(ctx context.Context, + params *ec2.AttachVolumeInput, + optFns ...func(*ec2.Options)) (*ec2.AttachVolumeOutput, error) { + + return &ec2.AttachVolumeOutput{ + State: types.VolumeAttachmentStateAttaching, + }, nil +} + +func (m mockEC2Client) DescribeVolumes(ctx context.Context, + params *ec2.DescribeVolumesInput, + optFns ...func(*ec2.Options)) (*ec2.DescribeVolumesOutput, error) { + + var vols []types.Volume + for _, id := range params.VolumeIds { + vols = append(vols, types.Volume{ + VolumeId: aws.String(id), + State: types.VolumeStateAvailable, + Attachments: []types.VolumeAttachment{ + { + State: types.VolumeAttachmentStateAttached, + }, + }, + }) + } + return &ec2.DescribeVolumesOutput{Volumes: vols}, nil +} + // Mock instanceRunningWaiter type MockAWSInstanceWaiter struct{} @@ -681,6 +709,100 @@ func TestGetInstanceTypeInformation(t *testing.T) { } } +func TestEBSDeviceName(t *testing.T) { + tests := []struct { + index int + expected string + }{ + {0, "/dev/xvdb"}, + {1, "/dev/xvdc"}, + {24, "/dev/xvdz"}, + {25, "/dev/xvdba"}, + {26, "/dev/xvdbb"}, + {50, "/dev/xvdbz"}, + {51, "/dev/xvdca"}, + } + for _, tt := range tests { + got := ebsDeviceName(tt.index) + if got != tt.expected { + t.Errorf("ebsDeviceName(%d) = %q, want %q", tt.index, got, tt.expected) + } + } +} + +func TestAttachEBSVolumes(t *testing.T) { + t.Run("attaches single volume", func(t *testing.T) { + p := &awsProvider{ + ec2Client: newMockEC2Client(), + waiter: newMockAWSInstanceWaiter(), + serviceConfig: serviceConfig, + } + vols := []provider.CloudVolume{ + {DiskID: "vol-0abc1234def56789"}, + } + err := p.attachEBSVolumes(context.Background(), "i-1234567890abcdef0", vols) + if err != nil { + t.Errorf("attachEBSVolumes single volume: unexpected error: %v", err) + } + }) + + t.Run("attaches multiple volumes", func(t *testing.T) { + p := &awsProvider{ + ec2Client: newMockEC2Client(), + waiter: newMockAWSInstanceWaiter(), + serviceConfig: serviceConfig, + } + vols := []provider.CloudVolume{ + {DiskID: "vol-0abc1234def56789"}, + {DiskID: "vol-0def4567abc89012"}, + {DiskID: "vol-0fedcba987654321"}, + } + err := p.attachEBSVolumes(context.Background(), "i-1234567890abcdef0", vols) + if err != nil { + t.Errorf("attachEBSVolumes multiple volumes: unexpected error: %v", err) + } + }) + + t.Run("attaches no volumes", func(t *testing.T) { + p := &awsProvider{ + ec2Client: newMockEC2Client(), + waiter: newMockAWSInstanceWaiter(), + serviceConfig: serviceConfig, + } + err := p.attachEBSVolumes(context.Background(), "i-1234567890abcdef0", nil) + if err != nil { + t.Errorf("attachEBSVolumes empty volumes: unexpected error: %v", err) + } + }) +} + +func TestCreateInstanceWithVolumes(t *testing.T) { + p := &awsProvider{ + ec2Client: newMockEC2Client(), + waiter: newMockAWSInstanceWaiter(), + serviceConfig: serviceConfig, + } + + spec := provider.InstanceTypeSpec{ + InstanceType: "t2.small", + Volumes: []provider.CloudVolume{ + {DiskID: "vol-0abc1234def56789"}, + {DiskID: "vol-0def4567abc89012"}, + }, + } + + instance, err := p.CreateInstance(context.Background(), "pod-with-vols", "sandbox-1", &mockCloudConfig{}, spec) + if err != nil { + t.Fatalf("CreateInstance with volumes: unexpected error: %v", err) + } + if instance == nil { + t.Fatal("CreateInstance with volumes: expected non-nil instance") + } + if instance.ID != "i-1234567890abcdef0" { + t.Errorf("CreateInstance with volumes: got ID %s, want i-1234567890abcdef0", instance.ID) + } +} + func TestConfigVerifier(t *testing.T) { type fields struct { serviceConfig *Config diff --git a/src/cloud-providers/azure/provider.go b/src/cloud-providers/azure/provider.go index 3141e97e66..864854902d 100644 --- a/src/cloud-providers/azure/provider.go +++ b/src/cloud-providers/azure/provider.go @@ -31,6 +31,8 @@ var logger = log.New(log.Writer(), "[adaptor/cloud/azure] ", log.LstdFlags|log.L var errNotReady = errors.New("address not ready") var errNotFound = errors.New("VM name not found") +var diskResourceIDRe = regexp.MustCompile(`resourceGroups/([^/]+)/providers/Microsoft\.Compute/disks/(.+)$`) + const ( maxInstanceNameLen = 63 ) @@ -283,7 +285,16 @@ func (p *azureProvider) CreateInstance(ctx context.Context, podName, sandboxID s imageID = spec.Image } - vmParameters, err := p.getVMParameters(instanceSize, diskName, cloudConfigData, sshBytes, instanceName, nicName, imageID) + if len(spec.Volumes) > 0 { + if err := p.validateDiskCount(ctx, instanceSize, len(spec.Volumes)); err != nil { + return nil, err + } + if err := p.validateDisks(ctx, spec.Volumes); err != nil { + return nil, err + } + } + + vmParameters, err := p.getVMParameters(instanceSize, diskName, cloudConfigData, sshBytes, instanceName, nicName, imageID, spec.Volumes) if err != nil { return nil, err } @@ -412,6 +423,108 @@ func (p *azureProvider) updateInstanceSizeSpecList() error { return nil } +// validateDiskCount checks that the requested number of data disks does not +// exceed the maximum supported by the target VM size. +func (p *azureProvider) validateDiskCount(ctx context.Context, instanceSize string, diskCount int) error { + vmSizesClient, err := armcompute.NewVirtualMachineSizesClient(p.serviceConfig.SubscriptionID, p.azureClient, nil) + if err != nil { + logger.Printf("WARNING: could not create VM sizes client for disk count validation: %v", err) + return nil + } + + pager := vmSizesClient.NewListPager(p.serviceConfig.Region, nil) + for pager.More() { + page, err := pager.NextPage(ctx) + if err != nil { + logger.Printf("WARNING: could not list VM sizes for disk count validation: %v", err) + return nil + } + for _, vmSize := range page.Value { + if vmSize.Name != nil && *vmSize.Name == instanceSize && vmSize.MaxDataDiskCount != nil { + maxDisks := int(*vmSize.MaxDataDiskCount) + if diskCount > maxDisks { + return fmt.Errorf("requested %d data disks but VM size %s supports at most %d", + diskCount, instanceSize, maxDisks) + } + logger.Printf("Disk count validation: %d/%d data disks for VM size %s", diskCount, maxDisks, instanceSize) + return nil + } + } + } + + logger.Printf("WARNING: could not find max disk count for VM size %s, skipping validation", instanceSize) + return nil +} + +// validateDisks checks that all CSI volumes exist, are not already attached, +// and are in the same region as the target VM. +func (p *azureProvider) validateDisks(ctx context.Context, volumes []provider.CloudVolume) error { + disksClient, err := armcompute.NewDisksClient(p.serviceConfig.SubscriptionID, p.azureClient, nil) + if err != nil { + return fmt.Errorf("creating disks client for zone validation: %w", err) + } + + var errs []string + for _, vol := range volumes { + rg, diskName, parseErr := parseDiskResourceID(vol.DiskID) + if parseErr != nil { + logger.Printf("WARNING: could not parse resource group/disk name from disk ID %q, skipping validation", vol.DiskID) + continue + } + + disk, err := disksClient.Get(ctx, rg, diskName, nil) + if err != nil { + if strings.Contains(err.Error(), "ResourceNotFound") || strings.Contains(err.Error(), "NotFound") { + errs = append(errs, fmt.Sprintf("disk %q does not exist in resource group %q", diskName, rg)) + } else { + errs = append(errs, fmt.Sprintf("disk %q is inaccessible: %v", vol.DiskID, err)) + } + continue + } + + if disk.Properties != nil && disk.Properties.DiskState != nil { + state := *disk.Properties.DiskState + if state == armcompute.DiskStateAttached || state == armcompute.DiskStateReserved { + managedBy := "" + if disk.ManagedBy != nil { + managedBy = *disk.ManagedBy + } + errs = append(errs, fmt.Sprintf("disk %q is in state %q (attached to %s) — cannot attach to new PodVM", + vol.DiskID, state, managedBy)) + continue + } + } + + diskLocation := strings.ToLower(strings.ReplaceAll(*disk.Location, " ", "")) + vmLocation := strings.ToLower(strings.ReplaceAll(p.serviceConfig.Region, " ", "")) + + if diskLocation != vmLocation { + errs = append(errs, fmt.Sprintf("disk %q is in region %q but PodVM targets region %q", + vol.DiskID, *disk.Location, p.serviceConfig.Region)) + continue + } + + if len(disk.Zones) > 0 { + logger.Printf("Disk %q is in zone(s) %v — ensure PodVM targets a compatible zone", vol.DiskID, disk.Zones) + } + } + + if len(errs) > 0 { + return fmt.Errorf("disk pre-validation failed:\n - %s", strings.Join(errs, "\n - ")) + } + return nil +} + +// parseDiskResourceID extracts resource group and disk name from an Azure +// resource ID like /subscriptions/.../resourceGroups/RG/providers/Microsoft.Compute/disks/NAME. +func parseDiskResourceID(diskID string) (resourceGroup, diskName string, err error) { + match := diskResourceIDRe.FindStringSubmatch(diskID) + if len(match) < 3 { + return "", "", fmt.Errorf("cannot parse Azure disk resource ID: %q", diskID) + } + return match[1], match[2], nil +} + func (p *azureProvider) getResourceTags() map[string]*string { tags := map[string]*string{} @@ -422,7 +535,7 @@ func (p *azureProvider) getResourceTags() map[string]*string { return tags } -func (p *azureProvider) getVMParameters(instanceSize, diskName, cloudConfig string, sshBytes []byte, instanceName, nicName string, imageID string) (*armcompute.VirtualMachine, error) { +func (p *azureProvider) getVMParameters(instanceSize, diskName, cloudConfig string, sshBytes []byte, instanceName, nicName string, imageID string, csiVolumes []provider.CloudVolume) (*armcompute.VirtualMachine, error) { userDataB64 := base64.StdEncoding.EncodeToString([]byte(cloudConfig)) // Azure limits the base64 encrypted userData to 64KB. @@ -482,6 +595,20 @@ func (p *azureProvider) getVMParameters(instanceSize, diskName, cloudConfig stri logger.Printf("Setting root volume size to %d GB", p.serviceConfig.RootVolumeSize) } + // Attach CSI volumes as data disks + var dataDisks []*armcompute.DataDisk + for i, vol := range csiVolumes { + logger.Printf("Attaching data disk: LUN %d, ID: %s", i, vol.DiskID) + dataDisks = append(dataDisks, &armcompute.DataDisk{ + Lun: to.Ptr(int32(i)), //nolint:gosec // bounded above + CreateOption: to.Ptr(armcompute.DiskCreateOptionTypesAttach), + DeleteOption: to.Ptr(armcompute.DiskDeleteOptionTypesDetach), + ManagedDisk: &armcompute.ManagedDiskParameters{ + ID: to.Ptr(vol.DiskID), + }, + }) + } + vmParameters := armcompute.VirtualMachine{ Location: to.Ptr(p.serviceConfig.Region), Properties: &armcompute.VirtualMachineProperties{ @@ -491,6 +618,7 @@ func (p *azureProvider) getVMParameters(instanceSize, diskName, cloudConfig stri StorageProfile: &armcompute.StorageProfile{ ImageReference: imgRef, OSDisk: osDisk, + DataDisks: dataDisks, }, OSProfile: &armcompute.OSProfile{ AdminUsername: to.Ptr(p.serviceConfig.SSHUserName), diff --git a/src/cloud-providers/azure/provider_test.go b/src/cloud-providers/azure/provider_test.go new file mode 100644 index 0000000000..b90e949dc8 --- /dev/null +++ b/src/cloud-providers/azure/provider_test.go @@ -0,0 +1,91 @@ +// (C) Copyright Confidential Containers Contributors +// SPDX-License-Identifier: Apache-2.0 + +package azure + +import ( + "math" + "testing" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + armcompute "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4" + provider "github.com/confidential-containers/cloud-api-adaptor/src/cloud-providers" +) + +func buildDataDisks(volumes []provider.CloudVolume) ([]*armcompute.DataDisk, error) { + var dataDisks []*armcompute.DataDisk + for i, vol := range volumes { + if i > math.MaxInt32 { + return nil, nil + } + dataDisks = append(dataDisks, &armcompute.DataDisk{ + Lun: to.Ptr(int32(i)), + CreateOption: to.Ptr(armcompute.DiskCreateOptionTypesAttach), + DeleteOption: to.Ptr(armcompute.DiskDeleteOptionTypesDetach), + ManagedDisk: &armcompute.ManagedDiskParameters{ + ID: to.Ptr(vol.DiskID), + }, + }) + } + return dataDisks, nil +} + +func TestBuildDataDisks(t *testing.T) { + t.Run("empty volumes produces nil slice", func(t *testing.T) { + disks, err := buildDataDisks(nil) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if len(disks) != 0 { + t.Errorf("expected 0 disks, got %d", len(disks)) + } + }) + + t.Run("single volume produces one data disk at LUN 0", func(t *testing.T) { + vols := []provider.CloudVolume{ + {DiskID: "/subscriptions/sub-1/resourceGroups/rg/providers/Microsoft.Compute/disks/disk-1"}, + } + disks, err := buildDataDisks(vols) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if len(disks) != 1 { + t.Fatalf("expected 1 disk, got %d", len(disks)) + } + if *disks[0].Lun != 0 { + t.Errorf("expected LUN 0, got %d", *disks[0].Lun) + } + if *disks[0].ManagedDisk.ID != vols[0].DiskID { + t.Errorf("expected disk ID %q, got %q", vols[0].DiskID, *disks[0].ManagedDisk.ID) + } + if *disks[0].CreateOption != armcompute.DiskCreateOptionTypesAttach { + t.Errorf("expected Attach create option") + } + if *disks[0].DeleteOption != armcompute.DiskDeleteOptionTypesDetach { + t.Errorf("expected Detach delete option") + } + }) + + t.Run("multiple volumes get sequential LUNs", func(t *testing.T) { + vols := []provider.CloudVolume{ + {DiskID: "/subscriptions/sub-1/resourceGroups/rg/providers/Microsoft.Compute/disks/disk-1"}, + {DiskID: "/subscriptions/sub-1/resourceGroups/rg/providers/Microsoft.Compute/disks/disk-2"}, + {DiskID: "/subscriptions/sub-1/resourceGroups/rg/providers/Microsoft.Compute/disks/disk-3"}, + } + disks, err := buildDataDisks(vols) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if len(disks) != 3 { + t.Fatalf("expected 3 disks, got %d", len(disks)) + } + for i, d := range disks { + if *d.Lun != int32(i) { + t.Errorf("disk %d: expected LUN %d, got %d", i, i, *d.Lun) + } + if *d.ManagedDisk.ID != vols[i].DiskID { + t.Errorf("disk %d: expected ID %q, got %q", i, vols[i].DiskID, *d.ManagedDisk.ID) + } + } + }) +} diff --git a/src/cloud-providers/docker/docker.go b/src/cloud-providers/docker/docker.go deleted file mode 100644 index c1600b64fc..0000000000 --- a/src/cloud-providers/docker/docker.go +++ /dev/null @@ -1,98 +0,0 @@ -// (C) Copyright Confidential Containers Contributors -// SPDX-License-Identifier: Apache-2.0 - -package docker - -import ( - "context" - - // Ensure you explicitly get the specific docker module version - // to avoid incompatibility with the opentelemetry packages that - // is included implicitly. - // Refer to docker module specific vendor.mod for the versions - // eg. - https://github.com/moby/moby/blob/v25.0.5/vendor.mod - - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/network" - "github.com/docker/go-connections/nat" - v1 "github.com/opencontainers/image-spec/specs-go/v1" -) - -// The default podvm docker image to use -const defaultPodVMDockerImage = "quay.io/confidential-containers/podvm-docker-image" - -// Default docker network name to connect to -const defaultDockerNetworkName = "bridge" - -// dockerClient defines the interface for Docker operations -type dockerClient interface { - ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *v1.Platform, containerName string) (container.CreateResponse, error) - ContainerStart(ctx context.Context, containerID string, options container.StartOptions) error - ContainerInspect(ctx context.Context, containerID string) (container.InspectResponse, error) - ContainerRemove(ctx context.Context, containerID string, options container.RemoveOptions) error - Close() error -} - -// Method to create and start a container -// Returns the container ID and the IP address of the container -func createContainer(ctx context.Context, client dockerClient, - instanceName string, volumeBinding []string, - podvmImage string, networkName string) (string, string, error) { - - // No need to bind the port to the host - portBinding := nat.PortMap{} - - // Create a privileged container as it's required due to systemd - resp, err := client.ContainerCreate( - ctx, - &container.Config{ - Image: podvmImage, - ExposedPorts: nat.PortSet{ - "15150/tcp": struct{}{}, - }, - }, - &container.HostConfig{ - PortBindings: portBinding, - Binds: volumeBinding, - Privileged: true, // This line is added to create a privileged container - }, - // Connect to specific network name - &network.NetworkingConfig{ - EndpointsConfig: map[string]*network.EndpointSettings{ - networkName: {}, - }, - }, - - nil, instanceName, - ) - if err != nil { - return "", "", err - } - - // Start the container - - if err := client.ContainerStart(ctx, resp.ID, container.StartOptions{}); err != nil { - return "", "", err - } - - // Get the IP address of the container - - inspect, err := client.ContainerInspect(ctx, resp.ID) - if err != nil { - return "", "", err - } - - // Get the IP address of the container from the network settings - // networks: map[network-name: {IPAddress: ip-address}] - // The network name is the key in the networks map - - return resp.ID, inspect.NetworkSettings.Networks[networkName].IPAddress, nil - -} - -// Method to delete container given container id -func deleteContainer(ctx context.Context, client dockerClient, containerID string) error { - return client.ContainerRemove(ctx, containerID, container.RemoveOptions{ - Force: true, - }) -} diff --git a/src/cloud-providers/docker/manager.go b/src/cloud-providers/docker/manager.go deleted file mode 100644 index 996980d6a8..0000000000 --- a/src/cloud-providers/docker/manager.go +++ /dev/null @@ -1,47 +0,0 @@ -// (C) Copyright Confidential Containers Contributors -// SPDX-License-Identifier: Apache-2.0 - -package docker - -import ( - "flag" - - provider "github.com/confidential-containers/cloud-api-adaptor/src/cloud-providers" -) - -var dockerCfg Config - -type Manager struct{} - -const defaultDataDir = "/var/lib/docker/peerpods" - -func init() { - provider.AddCloudProvider("docker", &Manager{}) -} - -func (*Manager) ParseCmd(flags *flag.FlagSet) { - reg := provider.NewFlagRegistrar(flags) - - // Flags with environment variable support - reg.StringWithEnv(&dockerCfg.DockerHost, "docker-host", "unix:///var/run/docker.sock", "DOCKER_HOST", "Docker host") - reg.StringWithEnv(&dockerCfg.DockerAPIVersion, "docker-api-version", "1.44", "DOCKER_API_VERSION", "Docker API version") - reg.StringWithEnv(&dockerCfg.DockerCertPath, "docker-cert-path", "", "DOCKER_CERT_PATH", "Path to directory with Docker TLS certificates") - reg.BoolWithEnv(&dockerCfg.DockerTLSVerify, "docker-tls-verify", false, "DOCKER_TLS_VERIFY", "Use TLS and verify the remote server certificate") - reg.StringWithEnv(&dockerCfg.PodVMDockerImage, "podvm-docker-image", defaultPodVMDockerImage, "DOCKER_PODVM_IMAGE", "Docker image to use for podvm") - reg.StringWithEnv(&dockerCfg.NetworkName, "docker-network-name", defaultDockerNetworkName, "DOCKER_NETWORK_NAME", "Docker network name to connect to") - - // Flags without environment variable support (pass empty string for envVarName) - reg.StringWithEnv(&dockerCfg.DataDir, "data-dir", defaultDataDir, "", "docker storage dir") -} - -func (*Manager) LoadEnv() { - // No longer needed - environment variables are handled in ParseCmd -} - -func (*Manager) NewProvider() (provider.Provider, error) { - return NewProvider(&dockerCfg) -} - -func (*Manager) GetConfig() (config *Config) { - return &dockerCfg -} diff --git a/src/cloud-providers/docker/provider.go b/src/cloud-providers/docker/provider.go deleted file mode 100644 index bfcca57224..0000000000 --- a/src/cloud-providers/docker/provider.go +++ /dev/null @@ -1,154 +0,0 @@ -// (C) Copyright Confidential Containers Contributors -// SPDX-License-Identifier: Apache-2.0 - -package docker - -import ( - "context" - "fmt" - "log" - "net/netip" - "os" - "path/filepath" - - provider "github.com/confidential-containers/cloud-api-adaptor/src/cloud-providers" - putil "github.com/confidential-containers/cloud-api-adaptor/src/cloud-providers/util" - "github.com/confidential-containers/cloud-api-adaptor/src/cloud-providers/util/cloudinit" - "github.com/docker/docker/client" -) - -var logger = log.New(log.Writer(), "[adaptor/cloud/docker] ", log.LstdFlags|log.Lmsgprefix) - -type dockerProvider struct { - Client dockerClient - DataDir string - PodVMDockerImage string - NetworkName string -} - -const maxInstanceNameLen = 63 - -func NewProvider(config *Config) (*dockerProvider, error) { - - logger.Printf("docker config: %#v", config) - - cli, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion(config.DockerAPIVersion)) - if err != nil { - return nil, err - } - - // Log Docker client and server versions for debugging - ctx := context.Background() - serverVersion, err := cli.ServerVersion(ctx) - if err != nil { - logger.Printf("Warning: failed to get Docker server version: %v", err) - } else { - logger.Printf("Docker server version: %s, API version: %s", serverVersion.Version, serverVersion.APIVersion) - } - logger.Printf("Docker client API version: %s", cli.ClientVersion()) - - // Create the data directory if it doesn't exist - err = os.MkdirAll(config.DataDir, 0755) - if err != nil { - return nil, err - } - - return &dockerProvider{ - Client: cli, - DataDir: config.DataDir, - PodVMDockerImage: config.PodVMDockerImage, - NetworkName: config.NetworkName, - }, nil -} - -func (p *dockerProvider) CreateInstance(ctx context.Context, podName, sandboxID string, - cloudConfig cloudinit.CloudConfigGenerator, spec provider.InstanceTypeSpec) (*provider.Instance, error) { - - instanceName := putil.GenerateInstanceName(podName, sandboxID, maxInstanceNameLen) - - logger.Printf("CreateInstance: name: %q", instanceName) - - userData, err := cloudConfig.Generate() - if err != nil { - return nil, err - } - // Write userdata to a file named after the instance name in the data directory - // File name: $data-dir/instanceName-userdata - // File content: userdata - instanceUserdataFile, err := provider.WriteUserData(instanceName, userData, p.DataDir) - if err != nil { - return nil, err - } - - // Create volume binding for the container - - // mount userdata to /media/cidata/user-data - // This file will be read by process-user-data and apf.json will be written to - // /run/peerpods/apf.json at runtime - volumeBinding := []string{ - // note: we are not importing that path from the CAA package to avoid circular dependencies - fmt.Sprintf("%s:%s", instanceUserdataFile, "/media/cidata/user-data"), - } - - // Add host bind mount for /run/kata-containers and /run/image to avoid - // overlay on overlay issue - // (host)kata-containers dir -> (container) /run/kata-containers - volumeBinding = append(volumeBinding, fmt.Sprintf("%s:%s", - filepath.Join(p.DataDir, "kata-containers"), "/run/kata-containers")) - - // Add host bind mounts required by iptables - volumeBinding = append(volumeBinding, fmt.Sprintf("%s:%s", "/lib/modules", "/lib/modules")) - volumeBinding = append(volumeBinding, fmt.Sprintf("%s:%s", "/run/xtables.lock", "/run/xtables.lock")) - - if spec.Image != "" { - logger.Printf("Choosing %s from annotation as the docker image for the PodVM image", spec.Image) - p.PodVMDockerImage = spec.Image - } - - // (host)image dir -> (container) /image - // There is a podvm systemd service in pod which bind mounts /run/image to /image - volumeBinding = append(volumeBinding, fmt.Sprintf("%s:%s", - filepath.Join(p.DataDir, "image"), "/image")) - - instanceID, ip, err := createContainer(ctx, p.Client, instanceName, volumeBinding, - p.PodVMDockerImage, p.NetworkName) - if err != nil { - return nil, err - } - - logger.Printf("CreateInstance: instanceID: %q, ip: %q", instanceID, ip) - - // Convert ip to []netip.Addr - ipAddr, err := netip.ParseAddr(ip) - if err != nil { - return nil, err - } - - return &provider.Instance{ - ID: instanceID, - Name: instanceName, - IPs: []netip.Addr{ipAddr}, // Convert ipAddr to a slice of netip.Addr - }, nil - -} - -func (p *dockerProvider) DeleteInstance(ctx context.Context, instanceID string) error { - - logger.Printf("DeleteInstance: instanceID: %q", instanceID) - - // Delete the container - err := deleteContainer(ctx, p.Client, instanceID) - if err != nil { - return err - } - - return nil -} - -func (p *dockerProvider) Teardown() error { - return nil -} - -func (p *dockerProvider) ConfigVerifier() error { - return nil -} diff --git a/src/cloud-providers/docker/provider_test.go b/src/cloud-providers/docker/provider_test.go deleted file mode 100644 index 51260c474b..0000000000 --- a/src/cloud-providers/docker/provider_test.go +++ /dev/null @@ -1,159 +0,0 @@ -// (C) Copyright Confidential Containers Contributors -// SPDX-License-Identifier: Apache-2.0 - -package docker - -import ( - "context" - "net/netip" - "reflect" - "testing" - - provider "github.com/confidential-containers/cloud-api-adaptor/src/cloud-providers" - - "github.com/confidential-containers/cloud-api-adaptor/src/cloud-providers/util/cloudinit" - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/network" - v1 "github.com/opencontainers/image-spec/specs-go/v1" -) - -// Mock Docker client for testing -type mockDockerClient struct{} - -// Return a new mock Docker client -func newMockDockerClient() *mockDockerClient { - return &mockDockerClient{} -} - -// Create a mock Docker ContainerCreate method -func (m mockDockerClient) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *v1.Platform, containerName string) (container.CreateResponse, error) { - return container.CreateResponse{ - ID: "mock-container-id-12345", - }, nil -} - -// Create a mock Docker ContainerStart method -func (m mockDockerClient) ContainerStart(ctx context.Context, containerID string, options container.StartOptions) error { - return nil -} - -// Create a mock Docker ContainerInspect method -func (m mockDockerClient) ContainerInspect(ctx context.Context, containerID string) (container.InspectResponse, error) { - return container.InspectResponse{ - ContainerJSONBase: &container.ContainerJSONBase{ - ID: containerID, - }, - NetworkSettings: &container.NetworkSettings{ - Networks: map[string]*network.EndpointSettings{ - "bridge": { - IPAddress: "172.17.0.2", - }, - }, - }, - }, nil -} - -// Create a mock Docker ContainerRemove method -func (m mockDockerClient) ContainerRemove(ctx context.Context, containerID string, options container.RemoveOptions) error { - return nil -} - -// Create a mock Docker Close method -func (m mockDockerClient) Close() error { - return nil -} - -func Test_dockerProvider_CreateInstance(t *testing.T) { - type fields struct { - Client *mockDockerClient - } - - testAPFConfigJSON := `{ - "pod-network": { - "podip": "10.244.0.19/24", - "pod-hw-addr": "0e:8f:62:f3:81:ad", - "interface": "eth0", - "worker-node-ip": "10.224.0.4/16", - "tunnel-type": "vxlan", - "routes": [ - { - "Dst": "", - "GW": "10.244.0.1", - "Dev": "eth0" - } - ], - "mtu": 1500, - "index": 1, - "vxlan-port": 8472, - "vxlan-id": 555001, - "dedicated": false - }, - "pod-namespace": "default", - "pod-name": "nginx-866fdb5bfb-b98nw", - "tls-server-key": "-----BEGIN PRIVATE KEY-----\n....\n-----END PRIVATE KEY-----\n", - "tls-server-cert": "-----BEGIN CERTIFICATE-----\n....\n-----END CERTIFICATE-----\n", - "tls-client-ca": "-----BEGIN CERTIFICATE-----\n....\n-----END CERTIFICATE-----\n" - }` - - // Write tempAPFConfigJSON to cloud-init config file - // Create a CloudConfig struct - cloudConfig := &cloudinit.CloudConfig{ - WriteFiles: []cloudinit.WriteFile{ - {Path: "/run/peerpod/apf.json", Content: string(testAPFConfigJSON)}, - }, - } - - type args struct { - ctx context.Context - podName string - sandboxID string - spec provider.InstanceTypeSpec - } - tests := []struct { - name string - fields fields - args args - want *provider.Instance - wantErr bool - }{ - // TODO: Add test cases. - { - name: "Test CreateInstance", - fields: fields{ - Client: newMockDockerClient(), - }, - args: args{ - ctx: context.Background(), - podName: "test", - sandboxID: "test", - spec: provider.InstanceTypeSpec{}, - }, - want: &provider.Instance{ - ID: "mock-container-id-12345", - Name: "podvm-test-test", - IPs: []netip.Addr{netip.MustParseAddr("172.17.0.2")}, - }, - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tempDir := t.TempDir() - - p := &dockerProvider{ - Client: tt.fields.Client, - DataDir: tempDir, - PodVMDockerImage: "quay.io/confidential-containers/podvm-docker-image", - NetworkName: "bridge", - } - got, err := p.CreateInstance(tt.args.ctx, tt.args.podName, tt.args.sandboxID, cloudConfig, tt.args.spec) - if (err != nil) != tt.wantErr { - t.Errorf("dockerProvider.CreateInstance() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("dockerProvider.CreateInstance() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/src/cloud-providers/docker/types.go b/src/cloud-providers/docker/types.go deleted file mode 100644 index b06cf3dbee..0000000000 --- a/src/cloud-providers/docker/types.go +++ /dev/null @@ -1,14 +0,0 @@ -// (C) Copyright Confidential Containers Contributors -// SPDX-License-Identifier: Apache-2.0 - -package docker - -type Config struct { - DockerHost string - DockerAPIVersion string - DockerCertPath string - DockerTLSVerify bool - DataDir string - PodVMDockerImage string - NetworkName string -} diff --git a/src/cloud-providers/gcp/provider.go b/src/cloud-providers/gcp/provider.go index c517f33b0a..71b669181f 100644 --- a/src/cloud-providers/gcp/provider.go +++ b/src/cloud-providers/gcp/provider.go @@ -61,6 +61,11 @@ func NewProvider(config *Config) (provider.Provider, error) { return nil, fmt.Errorf("NewInstancesRESTClient error: %s", err) } } + + if err := provider.updateInstanceSizeSpecList(); err != nil { + return nil, err + } + return provider, nil } @@ -206,6 +211,45 @@ func (p *gcpProvider) selectMachineType(ctx context.Context, spec provider.Insta return provider.SelectInstanceTypeToUse(spec, p.serviceConfig.MachineTypeSpecList, p.serviceConfig.MachineTypes, p.serviceConfig.MachineType) } +func (p *gcpProvider) updateInstanceSizeSpecList() error { + machineTypeClient, err := compute.NewMachineTypesRESTClient(context.TODO()) + if err != nil { + return err + } + + machineTypes := p.serviceConfig.MachineTypes + + if len(machineTypes) == 0 { + machineTypes = append(machineTypes, p.serviceConfig.MachineType) + } + + var instanceTypeSpecList []provider.InstanceTypeSpec + + for _, name := range machineTypes { + machineType, err := machineTypeClient.Get( + context.TODO(), + &computepb.GetMachineTypeRequest{ + Project: p.serviceConfig.ProjectID, + Zone: p.serviceConfig.Zone, + MachineType: name, + }, + ) + if err != nil { + return fmt.Errorf("getting machine type(%s): %w", name, err) + } + + instanceTypeSpecList = append(instanceTypeSpecList, provider.InstanceTypeSpec{ + InstanceType: name, + VCPUs: int64(*machineType.GuestCpus), + Memory: int64(*machineType.MemoryMb), + }) + } + p.serviceConfig.MachineTypeSpecList = instanceTypeSpecList + logger.Printf("instanceSizeSpecList (%v)", p.serviceConfig.MachineTypeSpecList) + + return nil +} + func (p *gcpProvider) CreateInstance(ctx context.Context, podName, sandboxID string, cloudConfig cloudinit.CloudConfigGenerator, spec provider.InstanceTypeSpec) (instance *provider.Instance, err error) { instanceName := util.GenerateInstanceName(podName, sandboxID, maxInstanceNameLen) diff --git a/src/cloud-providers/go.mod b/src/cloud-providers/go.mod index fe21c6ca2e..41da30bc78 100644 --- a/src/cloud-providers/go.mod +++ b/src/cloud-providers/go.mod @@ -1,6 +1,6 @@ module github.com/confidential-containers/cloud-api-adaptor/src/cloud-providers -go 1.25.10 +go 1.25.11 require ( cloud.google.com/go/compute v1.61.0 @@ -18,23 +18,21 @@ require ( github.com/alibabacloud-go/vpc-20160428/v6 v6.12.5 github.com/aliyun/credentials-go v1.3.10 github.com/avast/retry-go/v4 v4.6.1 - github.com/aws/aws-sdk-go-v2 v1.41.7 - github.com/aws/aws-sdk-go-v2/config v1.32.17 - github.com/aws/aws-sdk-go-v2/credentials v1.19.16 - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.23 - github.com/aws/aws-sdk-go-v2/service/ec2 v1.299.1 - github.com/docker/docker v28.5.2+incompatible - github.com/docker/go-connections v0.5.0 + github.com/aws/aws-sdk-go-v2 v1.41.12 + github.com/aws/aws-sdk-go-v2/config v1.32.23 + github.com/aws/aws-sdk-go-v2/credentials v1.19.22 + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.28 + github.com/aws/aws-sdk-go-v2/service/ec2 v1.305.2 github.com/kdomanski/iso9660 v0.4.0 github.com/stretchr/testify v1.11.1 - golang.org/x/crypto v0.50.0 + golang.org/x/crypto v0.52.0 google.golang.org/api v0.274.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v2 v2.4.0 k8s.io/apimachinery v0.35.2 k8s.io/client-go v0.35.2 - libvirt.org/go/libvirt v1.11010.0 - libvirt.org/go/libvirtxml v1.11010.0 + libvirt.org/go/libvirt v1.12003.0 + libvirt.org/go/libvirtxml v1.12002.0 ) require ( @@ -45,20 +43,16 @@ require ( github.com/alibabacloud-go/openapi-util v0.1.1 // indirect github.com/alibabacloud-go/tea-utils/v2 v2.0.7 // indirect github.com/alibabacloud-go/tea-xml v1.1.3 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.24 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9 // indirect - github.com/aws/aws-sdk-go-v2/service/signin v1.0.11 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.29 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.1.4 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.5 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/clbanning/mxj/v2 v2.7.0 // indirect - github.com/containerd/errdefs v1.0.0 // indirect - github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/google/gnostic-models v0.7.0 // indirect github.com/kr/fs v0.1.0 // indirect - github.com/moby/docker-image-spec v1.3.1 // indirect - github.com/moby/sys/atomicwriter v0.1.0 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect @@ -81,18 +75,14 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect - github.com/Microsoft/go-winio v0.5.2 // indirect github.com/alibabacloud-go/ecs-20140526/v4 v4.26.10 - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.23 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.23 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.23 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.30.17 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.42.1 // indirect - github.com/aws/smithy-go v1.25.1 // indirect - github.com/containerd/log v0.1.0 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.28 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.28 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.28 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.31.2 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.43.2 // indirect + github.com/aws/smithy-go v1.27.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/distribution/reference v0.6.0 // indirect - github.com/docker/go-units v0.5.0 // indirect github.com/emicklei/go-restful/v3 v3.12.2 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/gabriel-vasile/mimetype v1.4.11 // indirect @@ -123,17 +113,12 @@ require ( github.com/kylelemons/godebug v1.1.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mailru/easyjson v0.9.0 // indirect - github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect - github.com/morikuni/aec v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/oklog/ulid v1.3.1 // indirect - github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0 github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pkg/sftp v1.13.9 github.com/pmezard/go-difflib v1.0.0 // indirect go.mongodb.org/mongo-driver v1.17.6 // indirect @@ -141,14 +126,13 @@ require ( go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect go.opentelemetry.io/otel v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 // indirect go.opentelemetry.io/otel/metric v1.43.0 // indirect go.opentelemetry.io/otel/trace v1.43.0 // indirect - golang.org/x/net v0.53.0 // indirect - golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.43.0 // indirect - golang.org/x/term v0.42.0 // indirect - golang.org/x/text v0.36.0 // indirect + golang.org/x/net v0.55.0 // indirect + golang.org/x/sync v0.20.0 + golang.org/x/sys v0.45.0 // indirect + golang.org/x/term v0.43.0 // indirect + golang.org/x/text v0.37.0 // indirect golang.org/x/time v0.15.0 // indirect google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect @@ -156,7 +140,6 @@ require ( google.golang.org/grpc v1.80.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - gotest.tools/v3 v3.5.1 // indirect k8s.io/api v0.35.2 k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect diff --git a/src/cloud-providers/go.sum b/src/cloud-providers/go.sum index 9c06432d08..dc5dec4b6f 100644 --- a/src/cloud-providers/go.sum +++ b/src/cloud-providers/go.sum @@ -33,8 +33,6 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.2 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.2.1/go.mod h1:Bzf34hhAE9NSxailk8xVeLEZbUjOXcC+GnU1mMKdhLw= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 h1:Dd+RhdJn0OTtVGaeDLZpcumkIVCtA/3/Fo42+eoYvVM= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0/go.mod h1:5kakwfW5CjC9KK+Q4wjXAg+ShuIm2mBMua0ZFj2C8PE= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs= @@ -50,8 +48,6 @@ github.com/IBM/vpc-go-sdk v0.83.2 h1:RLMha7+buktU9hrhvAMF2QRHIvxaAsuU14W6akybJqs github.com/IBM/vpc-go-sdk v0.83.2/go.mod h1:85bJ/0FS7vYAifHdZvlnXypf8pQSmuf9kxReDDI5ZdY= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= -github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= -github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6 h1:eIf+iGJxdU4U9ypaUfbtOWCsZSbTb8AUHvyPrxu6mAA= github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6/go.mod h1:4EUIoxs/do24zMOGGqYVWgw0s9NtiylnJglOeEB5UJo= github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc= @@ -107,38 +103,36 @@ github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4t github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= github.com/avast/retry-go/v4 v4.6.1 h1:VkOLRubHdisGrHnTu89g08aQEWEgRU7LVEop3GbIcMk= github.com/avast/retry-go/v4 v4.6.1/go.mod h1:V6oF8njAwxJ5gRo1Q7Cxab24xs5NCWZBeaHHBklR8mA= -github.com/aws/aws-sdk-go-v2 v1.41.7 h1:DWpAJt66FmnnaRIOT/8ASTucrvuDPZASqhhLey6tLY8= -github.com/aws/aws-sdk-go-v2 v1.41.7/go.mod h1:4LAfZOPHNVNQEckOACQx60Y8pSRjIkNZQz1w92xpMJc= -github.com/aws/aws-sdk-go-v2/config v1.32.17 h1:FpL4/758/diKwqbytU0prpuiu60fgXKUWCpDJtApclU= -github.com/aws/aws-sdk-go-v2/config v1.32.17/go.mod h1:OXqUMzgXytfoF9JaKkhrOYsyh72t9G+MJH8mMRaexOE= -github.com/aws/aws-sdk-go-v2/credentials v1.19.16 h1:r3RJBuU7X9ibt8RHbMjWE6y60QbKBiII6wSrXnapxSU= -github.com/aws/aws-sdk-go-v2/credentials v1.19.16/go.mod h1:6cx7zqDENJDbBIIWX6P8s0h6hqHC8Avbjh9Dseo27ug= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.23 h1:UuSfcORqNSz/ey3VPRS8TcVH2Ikf0/sC+Hdj400QI6U= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.23/go.mod h1:+G/OSGiOFnSOkYloKj/9M35s74LgVAdJBSD5lsFfqKg= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.23 h1:GpT/TrnBYuE5gan2cZbTtvP+JlHsutdmlV2YfEyNde0= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.23/go.mod h1:xYWD6BS9ywC5bS3sz9Xh04whO/hzK2plt2Zkyrp4JuA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.23 h1:bpd8vxhlQi2r1hiueOw02f/duEPTMK59Q4QMAoTTtTo= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.23/go.mod h1:15DfR2nw+CRHIk0tqNyifu3G1YdAOy68RftkhMDDwYk= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.24 h1:OQqn11BtaYv1WLUowvcA30MpzIu8Ti4pcLPIIyoKZrA= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.24/go.mod h1:X5ZJyfwVrWA96GzPmUCWFQaEARPR7gCrpq2E92PJwAE= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.299.1 h1:gQ9fSyFk3Y9Vm2fVbphBeJfXJlkJvEvC35TszBVjprg= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.299.1/go.mod h1:Y95W0Hm6FYLPa6o0hbnJ+sWgmdc4ifcLFjGkdobWVhY= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9 h1:FLudkZLt5ci0ozzgkVo8BJGwvqNaZbTWb3UcucAateA= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9/go.mod h1:w7wZ/s9qK7c8g4al+UyoF1Sp/Z45UwMGcqIzLWVQHWk= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.23 h1:pbrxO/kuIwgEsOPLkaHu0O+m4fNgLU8B3vxQ+72jTPw= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.23/go.mod h1:/CMNUqoj46HpS3MNRDEDIwcgEnrtZlKRaHNaHxIFpNA= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.11 h1:TdJ+HdzOBhU8+iVAOGUTU63VXopcumCOF1paFulHWZc= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.11/go.mod h1:R82ZRExE/nheo0N+T8zHPcLRTcH8MGsnR3BiVGX0TwI= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.17 h1:7byT8HUWrgoRp6sXjxtZwgOKfhss5fW6SkLBtqzgRoE= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.17/go.mod h1:xNWknVi4Ezm1vg1QsB/5EWpAJURq22uqd38U8qKvOJc= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21 h1:+1Kl1zx6bWi4X7cKi3VYh29h8BvsCoHQEQ6ST9X8w7w= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21/go.mod h1:4vIRDq+CJB2xFAXZ+YgGUTiEft7oAQlhIs71xcSeuVg= -github.com/aws/aws-sdk-go-v2/service/sts v1.42.1 h1:F/M5Y9I3nwr2IEpshZgh1GeHpOItExNM9L1euNuh/fk= -github.com/aws/aws-sdk-go-v2/service/sts v1.42.1/go.mod h1:mTNxImtovCOEEuD65mKW7DCsL+2gjEH+RPEAexAzAio= -github.com/aws/smithy-go v1.25.1 h1:J8ERsGSU7d+aCmdQur5Txg6bVoYelvQJgtZehD12GkI= -github.com/aws/smithy-go v1.25.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= -github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= -github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/aws/aws-sdk-go-v2 v1.41.12 h1:DIKX2c31ekm9RA2D9FBj1EWXx++9AdAqRw+e78Tq2Ck= +github.com/aws/aws-sdk-go-v2 v1.41.12/go.mod h1:27+ACypSLljLAEKsCYOmrjKh83vuTRkuAe9Uv/3A4bg= +github.com/aws/aws-sdk-go-v2/config v1.32.23 h1:PYDobtcsJXK6bQe9I8RQk6s19Bz3xa3xRU08Hy1Em3Y= +github.com/aws/aws-sdk-go-v2/config v1.32.23/go.mod h1:QID4dqUQVgEOYPKsPWd1sNWCCR2c5g7o3jeEtIXPOZU= +github.com/aws/aws-sdk-go-v2/credentials v1.19.22 h1:SHfH6wyPsEgG7fVsi5rQxWEt7tuIcN2PGhb1mTFv6tE= +github.com/aws/aws-sdk-go-v2/credentials v1.19.22/go.mod h1:54nO8lKD4aQPOntM/VTWjnR+DYzTwx0YkSMZMhAgewQ= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.28 h1:b+kcDejJrXc30zU/w8Tc9klISwaO5wh+6T0sMBdDoHM= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.28/go.mod h1:LnI62O9GnSv6GcuLXxOYqlq0C8EmxMcgnF6m7LdYuOY= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.28 h1:Xf2j7NdVcUKomlZ4iihOP4AZ3Fzlr8h4yKpXeP+OFPg= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.28/go.mod h1:O8cDo1dW63jU7ki//kRe1z+tLGcpnD1jrouitsQddDw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.28 h1:KqIfN9kpkKkcBqBbNpNGTIrXO6ExTUvFKvXkC+YAzVo= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.28/go.mod h1:uxtQiKvLtNS4iXVsH2McVD/ls8FKN/uUhe1hGxPjrw0= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.29 h1:VkE9FuzTQVjBBrnj4+oCdxCLFIz7aqLYKUCjtvxVcOs= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.29/go.mod h1:H32Z2Qth9b+9LqjyBsCnozMQ8H2N7YBUDVXwbs0iggg= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.305.2 h1:xcHUdx4FNgUZmLuI6vqB5QDsVnWx1MVbPSoaXtLxUB8= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.305.2/go.mod h1:SUtka5kgdr5Wx2BdRrH/IGkCKHW63IVQoyBkP0P1gdo= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12 h1:ZD2+BSw9vFsNlKYIasSNt3uDbjqqXIBcM13UJv/Lx2k= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12/go.mod h1:Ms4zlcVBbXbiP7EVLhl+lgjvA/a7YphqQ3Ih3174EmI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.28 h1:axj4mEDletwKmTm/9jR+DkIMmCfcn5vE4jBMAAN+3Vg= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.28/go.mod h1:3Aaz69M0jqfSHLKqxgolgUBFT4hpwSNc7DzC95orEi8= +github.com/aws/aws-sdk-go-v2/service/signin v1.1.4 h1:YcpVyIPLCbiypN6KSphijN5fC7DDjX114SqA7prnnxg= +github.com/aws/aws-sdk-go-v2/service/signin v1.1.4/go.mod h1:5ZICS++oFTRPfa1GsBqFDWX/8WamZ/QQOcCzIuU/zLw= +github.com/aws/aws-sdk-go-v2/service/sso v1.31.2 h1:ySNWu7TPmj5fKFIa1GYvX+Ddxd5ccruqC20aMNuyWDM= +github.com/aws/aws-sdk-go-v2/service/sso v1.31.2/go.mod h1:A+U9luAOwFeB1kseyWCITVg7/NntoPebCFR9pQ4ch9A= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.5 h1:KSzGGqfk39O+WU3OEyYbx6F7sLDQCqxlOJ+2IksfK6U= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.5/go.mod h1:ATs88lXDeQB6CZOgQ5BIl9JbYS+EsCWUSDyff6L/oVo= +github.com/aws/aws-sdk-go-v2/service/sts v1.43.2 h1:RTO7mmGyedgnNmcPh3yQizNfc6GKoV5iqfdJavuf9vw= +github.com/aws/aws-sdk-go-v2/service/sts v1.43.2/go.mod h1:fBhUZXDin9YYqhcpOMjIcpdik25rVwWyxLdPH1RZd9s= +github.com/aws/smithy-go v1.27.1 h1:4T340VFndXtADGF52gYa1POyL7s9E4Z1OeZ1hCscIw8= +github.com/aws/smithy-go v1.27.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -149,23 +143,9 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w= github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI= -github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= -github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= -github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= -github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= -github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= -github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= -github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= -github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= -github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -262,8 +242,6 @@ github.com/googleapis/gax-go/v2 v2.21.0 h1:h45NjjzEO3faG9Lg/cFrBh2PgegVVgzqKzuZl github.com/googleapis/gax-go/v2 v2.21.0/go.mod h1:But/NJU6TnZsrLai/xBAQLLz+Hc7fHZJt/hsCz3Fih4= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= @@ -298,14 +276,6 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= -github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= -github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= -github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= -github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= -github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= -github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= -github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -314,8 +284,6 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= @@ -329,16 +297,10 @@ github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= -github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.9 h1:4NGkvGudBL7GteO3m6qnaQ4pC0Kvf0onSVc9gR3EWBw= github.com/pkg/sftp v1.13.9/go.mod h1:OBN7bVXdstkFFN/gdnHPUb5TE8eb8G1Rp9wCItqjkkA= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= @@ -348,9 +310,6 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= @@ -361,7 +320,6 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -386,10 +344,6 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:Oyrsyzu go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg= go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak= go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= @@ -398,8 +352,6 @@ go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfC go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= -go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= -go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= @@ -419,8 +371,8 @@ golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOM golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= -golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -431,8 +383,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= -golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= +golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= +golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -454,8 +406,8 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= -golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= +golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= +golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= @@ -475,12 +427,10 @@ golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -495,8 +445,8 @@ golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= -golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -510,8 +460,8 @@ golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= -golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY= -golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -523,8 +473,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= -golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -539,8 +489,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= -golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= +golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= +golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -592,8 +542,6 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= -gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.35.2 h1:tW7mWc2RpxW7HS4CoRXhtYHSzme1PN1UjGHJ1bdrtdw= @@ -608,10 +556,10 @@ k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZ k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -libvirt.org/go/libvirt v1.11010.0 h1:1EIh2x6qcRoIBBOvrgN62vq5FIpgUBrmGadprQ/4M0Y= -libvirt.org/go/libvirt v1.11010.0/go.mod h1:1WiFE8EjZfq+FCVog+rvr1yatKbKZ9FaFMZgEqxEJqQ= -libvirt.org/go/libvirtxml v1.11010.0 h1:lGUv6OQ4gz5Hm7F40G+swxmK/kcrMZGQ3M8/S+UyhME= -libvirt.org/go/libvirtxml v1.11010.0/go.mod h1:7Oq2BLDstLr/XtoQD8Fr3mfDNrzlI3utYKySXF2xkng= +libvirt.org/go/libvirt v1.12003.0 h1:3ek4ObakscdShZRloa9s8/mGhK7xVduqNmAkb15ZEDQ= +libvirt.org/go/libvirt v1.12003.0/go.mod h1:1WiFE8EjZfq+FCVog+rvr1yatKbKZ9FaFMZgEqxEJqQ= +libvirt.org/go/libvirtxml v1.12002.0 h1:NbEHw+R3IZE0vZF1deCQt+6tA+6Io4pAw9RjS7tM4fs= +libvirt.org/go/libvirtxml v1.12002.0/go.mod h1:7Oq2BLDstLr/XtoQD8Fr3mfDNrzlI3utYKySXF2xkng= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= diff --git a/src/cloud-providers/libvirt/cloudinit_test.go b/src/cloud-providers/libvirt/cloudinit_test.go index 7153e90cf1..d4b628932a 100644 --- a/src/cloud-providers/libvirt/cloudinit_test.go +++ b/src/cloud-providers/libvirt/cloudinit_test.go @@ -5,7 +5,6 @@ package libvirt import ( CR "crypto/rand" - "fmt" "io" "math/rand" "os" @@ -19,48 +18,35 @@ import ( ) func TestCloudInit(t *testing.T) { + tests := []struct { + name string + userDataContent []byte + metaDataContent []byte + expectedFiles map[string][]byte + }{ + { + name: "basic cloud-init", + userDataContent: []byte("userdata"), + metaDataContent: []byte("metadata"), + expectedFiles: map[string][]byte{ + userDataFilename: []byte("userdata"), + metaDataFilename: []byte("metadata"), + }, + }, + } - file, err := os.CreateTemp("", "CloudInit-*.iso") - require.NoError(t, err) - defer os.Remove(file.Name()) - - fmt.Printf("temp file: %s", file.Name()) - - userDataContent := []byte("userdata") - metaDataContent := []byte("metadata") - - isoData, err := createCloudInit(userDataContent, metaDataContent) - require.NoError(t, err) - - err = os.WriteFile(file.Name(), isoData, os.ModePerm) - require.NoError(t, err) - - isoFile, err := os.Open(file.Name()) - require.NoError(t, err) - - isoImg, err := iso9660.OpenImage(isoFile) - require.NoError(t, err) - - rootFile, err := isoImg.RootDir() - require.NoError(t, err) - - children, err := rootFile.GetChildren() - require.NoError(t, err) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + isoData, err := createCloudInit(tt.userDataContent, tt.metaDataContent) + require.NoError(t, err) - files := make(map[string][]byte) - for _, child := range children { - key := child.Name() - data, err := io.ReadAll(child.Reader()) - require.NoError(t, err) + files := verifyISOContents(t, isoData) - files[key] = data + for filename, expectedContent := range tt.expectedFiles { + assert.Equal(t, expectedContent, files[filename]) + } + }) } - - assert.Equal(t, userDataContent, files[userDataFilename]) - assert.Equal(t, metaDataContent, files[metaDataFilename]) - - err = isoFile.Close() - require.NoError(t, err) } func TestInMemoryCopier(t *testing.T) { @@ -87,3 +73,106 @@ func TestInMemoryCopier(t *testing.T) { assert.Equal(t, buf, otherBuf) } + +func TestCreateCloudInitVariations(t *testing.T) { + largeData := make([]byte, 10000) + for i := range largeData { + largeData[i] = byte('A' + (i % 26)) + } + + tests := []struct { + name string + userDataContent []byte + metaDataContent []byte + expectError bool + verifyFileCount bool + expectedFileCount int + verifyVendorData bool + }{ + { + name: "empty data", + userDataContent: []byte(""), + metaDataContent: []byte(""), + expectError: false, + }, + { + name: "large data", + userDataContent: largeData, + metaDataContent: []byte("instance-id: test-instance\nlocal-hostname: test-host"), + expectError: false, + }, + { + name: "special characters", + userDataContent: []byte("#cloud-config\nusers:\n - name: test\n ssh-authorized-keys:\n - ssh-rsa AAAAB3..."), + metaDataContent: []byte("instance-id: test-123\nlocal-hostname: test-host-456"), + expectError: false, + verifyFileCount: true, + expectedFileCount: 3, + }, + { + name: "verify vendor data", + userDataContent: []byte("userdata"), + metaDataContent: []byte("metadata"), + expectError: false, + verifyVendorData: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + isoData, err := createCloudInit(tt.userDataContent, tt.metaDataContent) + + if tt.expectError { + assert.Error(t, err) + return + } + + require.NoError(t, err) + assert.NotEmpty(t, isoData) + + files := verifyISOContents(t, isoData) + + if tt.verifyFileCount { + assert.Equal(t, tt.expectedFileCount, len(files)) + } + + if tt.verifyVendorData { + assert.Contains(t, files, vendorDataFilename) + assert.Equal(t, []byte{}, files[vendorDataFilename]) + } + }) + } +} + +func verifyISOContents(t *testing.T, isoData []byte) map[string][]byte { + t.Helper() + + file, err := os.CreateTemp("", "CloudInit-*.iso") + require.NoError(t, err) + defer os.Remove(file.Name()) + + err = os.WriteFile(file.Name(), isoData, os.ModePerm) + require.NoError(t, err) + + isoFile, err := os.Open(file.Name()) + require.NoError(t, err) + defer isoFile.Close() + + isoImg, err := iso9660.OpenImage(isoFile) + require.NoError(t, err) + + rootFile, err := isoImg.RootDir() + require.NoError(t, err) + + children, err := rootFile.GetChildren() + require.NoError(t, err) + + files := make(map[string][]byte) + for _, child := range children { + data, err := io.ReadAll(child.Reader()) + require.NoError(t, err) + files[child.Name()] = data + } + + return files +} diff --git a/src/cloud-providers/libvirt/image_test.go b/src/cloud-providers/libvirt/image_test.go new file mode 100644 index 0000000000..da0d7186ff --- /dev/null +++ b/src/cloud-providers/libvirt/image_test.go @@ -0,0 +1,118 @@ +//go:build cgo + +// (C) Copyright Confidential Containers Contributors +// SPDX-License-Identifier: Apache-2.0 + +package libvirt + +import ( + "fmt" + "io" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + libvirtxml "libvirt.org/go/libvirtxml" +) + +const ( + testVolumeName = "test-volume" +) + +// TestInMemoryImage tests the core functionality of inMemoryImage +func TestInMemoryImage(t *testing.T) { + tests := []struct { + name string + data []byte + }{ + { + name: "text data", + data: []byte("test image data"), + }, + { + name: "empty data", + data: []byte{}, + }, + { + name: "binary data", + data: []byte{0x00, 0x01, 0x02, 0xFF, 0xFE, 0xFD}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Test creation + img, err := newImageFromBytes(tt.data) + require.NoError(t, err) + assert.NotNil(t, img) + + // Test size() + size, err := img.size() + require.NoError(t, err) + assert.Equal(t, uint64(len(tt.data)), size) + + // Test string() + str := img.string() + expectedStr := fmt.Sprintf("plain bytes of size [%d]", len(tt.data)) + assert.Equal(t, expectedStr, str) + }) + } +} + +// TestInMemoryImageImport tests the importImage functionality +func TestInMemoryImageImport(t *testing.T) { + tests := []struct { + name string + data []byte + expectError bool + copierFunc func(*[]byte) func(io.Reader) error + validate func(*testing.T, []byte, []byte) + }{ + { + name: "successful import", + data: []byte("test data"), + copierFunc: func(captured *[]byte) func(io.Reader) error { + return func(rdr io.Reader) error { + data, err := io.ReadAll(rdr) + *captured = data + return err + } + }, + validate: func(t *testing.T, expected, actual []byte) { + assert.Equal(t, expected, actual) + }, + }, + { + name: "copier error", + data: []byte("test data"), + copierFunc: func(captured *[]byte) func(io.Reader) error { + return func(rdr io.Reader) error { + return assert.AnError + } + }, + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + img, err := newImageFromBytes(tt.data) + require.NoError(t, err) + + var capturedData []byte + copier := tt.copierFunc(&capturedData) + + volumeDef := libvirtxml.StorageVolume{Name: testVolumeName} + err = img.importImage(copier, volumeDef) + + if tt.expectError { + assert.Error(t, err) + } else { + require.NoError(t, err) + if tt.validate != nil { + tt.validate(t, tt.data, capturedData) + } + } + }) + } +} diff --git a/src/cloud-providers/libvirt/libvirt.go b/src/cloud-providers/libvirt/libvirt.go index 9a0c9b005b..bf7903fe81 100644 --- a/src/cloud-providers/libvirt/libvirt.go +++ b/src/cloud-providers/libvirt/libvirt.go @@ -31,6 +31,14 @@ const ( GetDomainIPsRetries = 20 // The sleep time between retries to get the domain IP addresses GetDomainIPsSleep = time.Second * 3 + + // cpuModeHostModel asks libvirt to select the closest named CPU model that + // the host supports and expose its full feature set to the guest. + cpuModeHostModel = "host-model" + + // cpuModeHostPassthrough exposes the exact host CPU to the guest with no + // model abstraction. + cpuModeHostPassthrough = "host-passthrough" ) // validateCPUSet validates the CPUSet format. @@ -256,6 +264,7 @@ func createDomainXMLs390x(client *libvirtClient, cfg *domainConfig, vm *vmConfig Value: cfg.cpu, CPUSet: vm.cpuset, }, + CPU: &libvirtxml.DomainCPU{Mode: cpuModeHostModel}, Clock: &libvirtxml.DomainClock{ Offset: "utc", }, @@ -266,7 +275,10 @@ func createDomainXMLs390x(client *libvirtClient, cfg *domainConfig, vm *vmConfig }, Emulator: guest.Arch.Emulator, MemBalloon: &libvirtxml.DomainMemBalloon{ - Model: "none", + Model: "virtio", + Driver: &libvirtxml.DomainMemBalloonDriver{ + IOMMU: "on", + }, }, RNGs: []libvirtxml.DomainRNG{ { @@ -323,7 +335,7 @@ func createDomainXMLx86_64(client *libvirtClient, cfg *domainConfig, vm *vmConfi APIC: &libvirtxml.DomainFeatureAPIC{}, VMPort: &libvirtxml.DomainFeatureState{State: "off"}, }, - CPU: &libvirtxml.DomainCPU{Mode: "host-model"}, + CPU: &libvirtxml.DomainCPU{Mode: cpuModeHostModel}, OnReboot: "restart", Devices: &libvirtxml.DomainDeviceList{ // Disks. @@ -464,7 +476,7 @@ func createDomainXMLaarch64(client *libvirtClient, cfg *domainConfig, vm *vmConf }, Memory: &libvirtxml.DomainMemory{Value: cfg.mem, Unit: "MiB"}, VCPU: &libvirtxml.DomainVCPU{Value: cfg.cpu, CPUSet: vm.cpuset}, - CPU: &libvirtxml.DomainCPU{Mode: "host-passthrough"}, + CPU: &libvirtxml.DomainCPU{Mode: cpuModeHostPassthrough}, Devices: &libvirtxml.DomainDeviceList{ Disks: []libvirtxml.DomainDisk{ bootDisk, diff --git a/src/cloud-providers/libvirt/libvirt_test.go b/src/cloud-providers/libvirt/libvirt_test.go index 3c35f0e56e..d8156e4c4c 100644 --- a/src/cloud-providers/libvirt/libvirt_test.go +++ b/src/cloud-providers/libvirt/libvirt_test.go @@ -9,9 +9,18 @@ import ( provider "github.com/confidential-containers/cloud-api-adaptor/src/cloud-providers" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + libvirt "libvirt.org/go/libvirt" libvirtxml "libvirt.org/go/libvirtxml" ) +const ( + testBootDisk = "/var/lib/libvirt/images/root.qcow2" + testCiDataISO = "/var/lib/libvirt/images/cidata.iso" + testCloudInitISO = "/var/lib/libvirt/images/cloudinit.iso" + testNetworkName = "default" +) + var testCfg Config func init() { @@ -31,28 +40,22 @@ func TestLibvirtConnection(t *testing.T) { checkConfig(t) client, err := NewLibvirtClient(testCfg) - if err != nil { - t.Error(err) - } + require.NoError(t, err) defer client.connection.Close() - assert.NotNil(t, client.nodeInfo) - assert.NotNil(t, client.caps) + assert.NotEmpty(t, client.nodeInfo) + assert.NotEmpty(t, client.caps) } func TestGetArchitecture(t *testing.T) { checkConfig(t) client, err := NewLibvirtClient(testCfg) - if err != nil { - t.Error(err) - } + require.NoError(t, err) defer client.connection.Close() node, err := client.connection.GetNodeInfo() - if err != nil { - t.Error(err) - } + require.NoError(t, err) arch := node.Model if arch == "" { @@ -80,76 +83,75 @@ func verifyDomainXML(domXML *libvirtxml.Domain) error { return nil } -func TestCreateDomainXMLs390x(t *testing.T) { - checkConfig(t) - - client, err := NewLibvirtClient(testCfg) - if err != nil { - t.Error(err) - } - defer client.connection.Close() - - vm := vmConfig{} - - domainCfg := domainConfig{ - name: "TestCreateDomainS390x", - cpu: 2, - mem: 2, - networkName: client.networkName, - bootDisk: "/var/lib/libvirt/images/root.qcow2", - cidataDisk: "/var/lib/libvirt/images/cidata.iso", - } - - domCfg, err := createDomainXML(client, &domainCfg, &vm) - if err != nil { - t.Error(err) - } - - arch := domCfg.OS.Type.Arch - if domCfg.OS.Type.Arch != archS390x { - t.Skipf("Skipping because architecture is [%s] and not [%s].", arch, archS390x) - } - - // verify the config - err = verifyDomainXML(domCfg) - if err != nil { - t.Error(err) +func createTestDomainConfig(name string, cpu, mem uint, networkName, cidataDisk string) *domainConfig { + return &domainConfig{ + name: name, + cpu: cpu, + mem: mem, + networkName: networkName, + bootDisk: testBootDisk, + cidataDisk: cidataDisk, } } -func TestCreateDomainXMLaarch64(t *testing.T) { +func TestCreateDomainXMLArchitectures(t *testing.T) { checkConfig(t) client, err := NewLibvirtClient(testCfg) - if err != nil { - t.Error(err) - } + require.NoError(t, err) defer client.connection.Close() - vm := vmConfig{} - - domainCfg := domainConfig{ - name: "TestCreateDomainAArch64", - cpu: 2, - mem: 4, - networkName: client.networkName, - bootDisk: "/var/lib/libvirt/images/root.qcow2", - cidataDisk: "/var/lib/libvirt/images/cloudinit.iso", + tests := []struct { + name string + domainName string + cpu uint + mem uint + cidataDisk string + expectedArch string + }{ + { + name: "s390x architecture", + domainName: "TestCreateDomainS390x", + cpu: 2, + mem: 2, + cidataDisk: testCiDataISO, + expectedArch: archS390x, + }, + { + name: "aarch64 architecture", + domainName: "TestCreateDomainAArch64", + cpu: 2, + mem: 4, + cidataDisk: testCloudInitISO, + expectedArch: archAArch64, + }, } - domCfg, err := createDomainXML(client, &domainCfg, &vm) - if err != nil { - t.Error(err) - } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + vm := vmConfig{} - arch := domCfg.OS.Type.Arch - if domCfg.OS.Type.Arch != archAArch64 { - t.Skipf("Skipping because architecture is [%s] and not [%s].", arch, archAArch64) - } + domainCfg := domainConfig{ + name: tt.domainName, + cpu: tt.cpu, + mem: tt.mem, + networkName: client.networkName, + bootDisk: testBootDisk, + cidataDisk: tt.cidataDisk, + } - err = verifyDomainXML(domCfg) - if err != nil { - t.Error(err) + domCfg, err := createDomainXML(client, &domainCfg, &vm) + assert.NoError(t, err) + + arch := domCfg.OS.Type.Arch + if domCfg.OS.Type.Arch != tt.expectedArch { + t.Skipf("Skipping because architecture is [%s] and not [%s].", arch, tt.expectedArch) + } + + // verify the config + err = verifyDomainXML(domCfg) + assert.NoError(t, err) + }) } } @@ -160,18 +162,36 @@ func TestGetDeletableDiskPaths(t *testing.T) { expected []string }{ { - name: "returns file-backed disks only", + name: "extract file-backed disks only", domain: &libvirtxml.Domain{ Devices: &libvirtxml.DomainDeviceList{ Disks: []libvirtxml.DomainDisk{ { Source: &libvirtxml.DomainDiskSource{ - File: &libvirtxml.DomainDiskSourceFile{File: "/var/lib/libvirt/images/root.qcow2"}, + File: &libvirtxml.DomainDiskSourceFile{File: testBootDisk}, }, }, { Source: &libvirtxml.DomainDiskSource{ - File: &libvirtxml.DomainDiskSourceFile{File: "/var/lib/libvirt/images/cloudinit.iso"}, + File: &libvirtxml.DomainDiskSourceFile{File: testCloudInitISO}, + }, + }, + }, + }, + }, + expected: []string{ + testBootDisk, + testCloudInitISO, + }, + }, + { + name: "skip disks without file source", + domain: &libvirtxml.Domain{ + Devices: &libvirtxml.DomainDeviceList{ + Disks: []libvirtxml.DomainDisk{ + { + Source: &libvirtxml.DomainDiskSource{ + File: &libvirtxml.DomainDiskSourceFile{File: testBootDisk}, }, }, { @@ -182,8 +202,7 @@ func TestGetDeletableDiskPaths(t *testing.T) { }, }, expected: []string{ - "/var/lib/libvirt/images/root.qcow2", - "/var/lib/libvirt/images/cloudinit.iso", + testBootDisk, }, }, { @@ -194,16 +213,299 @@ func TestGetDeletableDiskPaths(t *testing.T) { { name: "empty disk list returns empty slice", domain: &libvirtxml.Domain{ - Devices: &libvirtxml.DomainDeviceList{}, + Devices: &libvirtxml.DomainDeviceList{ + Disks: []libvirtxml.DomainDisk{}, + }, + }, + expected: []string{}, + }, + { + name: "skip disks with empty file path", + domain: &libvirtxml.Domain{ + Devices: &libvirtxml.DomainDeviceList{ + Disks: []libvirtxml.DomainDisk{ + { + Source: &libvirtxml.DomainDiskSource{ + File: &libvirtxml.DomainDiskSourceFile{File: ""}, + }, + }, + }, + }, }, expected: []string{}, }, } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - paths := getDeletableDiskPaths(tc.domain) - assert.Equal(t, tc.expected, paths) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + paths := getDeletableDiskPaths(tt.domain) + assert.Equal(t, tt.expected, paths) + }) + } +} + +func TestGetGuestForArchType(t *testing.T) { + tests := []struct { + name string + caps *libvirtxml.Caps + arch string + ostype string + expectError bool + expectArch string + }{ + { + name: "find x86_64 guest", + caps: createMockCaps("x86_64", ""), + arch: "x86_64", + ostype: "hvm", + expectError: false, + expectArch: "x86_64", + }, + { + name: "find s390x guest", + caps: createMockCaps("s390x", ""), + arch: "s390x", + ostype: "hvm", + expectError: false, + expectArch: "s390x", + }, + { + name: "find aarch64 guest", + caps: createMockCaps("aarch64", ""), + arch: "aarch64", + ostype: "hvm", + expectError: false, + expectArch: "aarch64", + }, + { + name: "architecture not found", + caps: createMockCaps("x86_64", ""), + arch: "invalid-arch", + ostype: "hvm", + expectError: true, + }, + { + name: "ostype mismatch", + caps: &libvirtxml.Caps{ + Guests: []libvirtxml.CapsGuest{ + { + OSType: "xen", + Arch: libvirtxml.CapsGuestArch{ + Name: "x86_64", + }, + }, + }, + }, + arch: "x86_64", + ostype: "hvm", + expectError: true, + }, + { + name: "empty capabilities", + caps: &libvirtxml.Caps{ + Guests: []libvirtxml.CapsGuest{}, + }, + arch: "x86_64", + ostype: "hvm", + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + guest, err := getGuestForArchType(tt.caps, tt.arch, tt.ostype) + if tt.expectError { + assert.Error(t, err) + assert.Nil(t, guest) + assert.ErrorContains(t, err, "could not find any guests") + } else { + assert.NoError(t, err) + require.NotNil(t, guest) + assert.Equal(t, tt.expectArch, guest.Arch.Name) + assert.Equal(t, tt.ostype, guest.OSType) + } + }) + } +} + +func TestLookupMachine(t *testing.T) { + tests := []struct { + name string + machines []libvirtxml.CapsGuestMachine + targetMachine string + expectedResult string + }{ + { + name: "find machine with canonical name", + machines: []libvirtxml.CapsGuestMachine{ + {Name: "pc", Canonical: "pc-i440fx-2.12"}, + {Name: "q35", Canonical: "pc-q35-2.12"}, + }, + targetMachine: "pc", + expectedResult: "pc-i440fx-2.12", + }, + { + name: "find machine without canonical name", + machines: []libvirtxml.CapsGuestMachine{ + {Name: "virt"}, + {Name: "pc"}, + }, + targetMachine: "virt", + expectedResult: "virt", + }, + { + name: "machine not found returns empty string", + machines: []libvirtxml.CapsGuestMachine{ + {Name: "pc"}, + {Name: "q35"}, + }, + targetMachine: "nonexistent", + expectedResult: "", + }, + { + name: "s390x machine with canonical", + machines: []libvirtxml.CapsGuestMachine{ + {Name: "s390-ccw-virtio", Canonical: "s390-ccw-virtio-rhel9.0.0"}, + }, + targetMachine: "s390-ccw-virtio", + expectedResult: "s390-ccw-virtio-rhel9.0.0", + }, + { + name: "empty machine list", + machines: []libvirtxml.CapsGuestMachine{}, + targetMachine: "pc", + expectedResult: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := lookupMachine(tt.machines, tt.targetMachine) + assert.Equal(t, tt.expectedResult, result) + }) + } +} + +func TestGetCanonicalMachineName(t *testing.T) { + tests := []struct { + name string + caps *libvirtxml.Caps + arch string + virttype string + targetMachine string + expectedResult string + expectError bool + }{ + { + name: "find canonical machine in arch machines", + caps: createMockCaps("x86_64", "", + libvirtxml.CapsGuestMachine{Name: "pc", Canonical: "pc-i440fx-2.12"}), + arch: "x86_64", + virttype: "hvm", + targetMachine: "pc", + expectedResult: "pc-i440fx-2.12", + expectError: false, + }, + { + name: "find canonical machine in domain machines", + caps: &libvirtxml.Caps{ + Guests: []libvirtxml.CapsGuest{ + { + OSType: "hvm", + Arch: libvirtxml.CapsGuestArch{ + Name: "x86_64", + Domains: []libvirtxml.CapsGuestDomain{ + { + Machines: []libvirtxml.CapsGuestMachine{ + {Name: "q35", Canonical: "pc-q35-2.12"}, + }, + }, + }, + }, + }, + }, + }, + arch: "x86_64", + virttype: "hvm", + targetMachine: "q35", + expectedResult: "pc-q35-2.12", + expectError: false, + }, + { + name: "machine not found returns error", + caps: createMockCaps("x86_64", "", + libvirtxml.CapsGuestMachine{Name: "pc"}), + arch: "x86_64", + virttype: "hvm", + targetMachine: "nonexistent", + expectError: true, + }, + { + name: "architecture not found returns error", + caps: createMockCaps("x86_64", ""), + arch: "invalid-arch", + virttype: "hvm", + targetMachine: "pc", + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := getCanonicalMachineName(tt.caps, tt.arch, tt.virttype, tt.targetMachine) + if tt.expectError { + assert.Error(t, err) + assert.Empty(t, result) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expectedResult, result) + } + }) + } +} + +func TestCreateCloudInitISO(t *testing.T) { + tests := []struct { + name string + vmConfig *vmConfig + expectError bool + }{ + { + name: "valid cloud-init data", + vmConfig: &vmConfig{ + name: "test-vm", + userData: "#cloud-config\nruncmd:\n - echo 'Hello World'\n", + }, + expectError: false, + }, + { + name: "empty user data", + vmConfig: &vmConfig{ + name: "test-vm", + userData: "", + }, + expectError: false, + }, + { + name: "large user data", + vmConfig: &vmConfig{ + name: "test-vm", + userData: "#cloud-config\n" + string(make([]byte, 10000)), + }, + expectError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + isoData, err := createCloudInitISO(tt.vmConfig) + if tt.expectError { + assert.Error(t, err) + assert.Nil(t, isoData) + } else { + assert.NoError(t, err) + assert.NotEmpty(t, isoData) + } }) } } @@ -213,7 +515,7 @@ func TestConfigVerifier(t *testing.T) { cfg := &Config{ URI: "qemu:///system", PoolName: "default", - NetworkName: "default", + NetworkName: testNetworkName, VolName: "podvm-base.qcow2", CPU: 1, Memory: 512, @@ -516,3 +818,366 @@ func TestCPUSetXMLMarshaling(t *testing.T) { }) } } + +func TestGetLaunchSecurityTypeInvalidURI(t *testing.T) { + _, err := GetLaunchSecurityType("invalid://uri") + assert.Error(t, err) +} + +func createMockCaps(arch, emulator string, machines ...libvirtxml.CapsGuestMachine) *libvirtxml.Caps { + return &libvirtxml.Caps{ + Guests: []libvirtxml.CapsGuest{ + { + OSType: "hvm", + Arch: libvirtxml.CapsGuestArch{ + Name: arch, + Emulator: emulator, + Machines: machines, + }, + }, + }, + } +} + +// TestCreateDomainXMLArchitecturesWithMocks tests domain XML generation for s390x and aarch64 architectures using mocks +func TestCreateDomainXMLArchitecturesWithMocks(t *testing.T) { + tests := []struct { + name string + arch string + vmName string + cpu uint + mem uint + cidataDisk string + emulator string + machineName string + machineCanonical string + createFunc func(*libvirtClient, *domainConfig, *vmConfig) (*libvirtxml.Domain, error) + expectedFirmware string + expectSCSIController bool + expectedCPUMode string + }{ + { + name: "s390x architecture", + arch: "s390x", + vmName: "test-s390x-vm", + cpu: 2, + mem: 2048, + cidataDisk: testCiDataISO, + emulator: "/usr/bin/qemu-system-s390x", + machineName: "s390-ccw-virtio", + machineCanonical: "s390-ccw-virtio-rhel9.0.0", + createFunc: createDomainXMLs390x, + expectedCPUMode: "host-model", + }, + { + name: "aarch64 architecture", + arch: "aarch64", + vmName: "test-aarch64-vm", + cpu: 4, + mem: 4096, + cidataDisk: testCloudInitISO, + emulator: "/usr/bin/qemu-system-aarch64", + machineName: "virt", + machineCanonical: "virt-4.2", + createFunc: createDomainXMLaarch64, + expectedFirmware: "efi", + expectSCSIController: true, + expectedCPUMode: "host-passthrough", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create mock capabilities + mockCaps := createMockCaps(tt.arch, tt.emulator, libvirtxml.CapsGuestMachine{ + Name: tt.machineName, + Canonical: tt.machineCanonical, + }) + + mockClient := &libvirtClient{ + caps: mockCaps, + networkName: testNetworkName, + } + + cfg := createTestDomainConfig(tt.vmName, tt.cpu, tt.mem, testNetworkName, tt.cidataDisk) + + vm := &vmConfig{} + + domain, err := tt.createFunc(mockClient, cfg, vm) + assert.NoError(t, err) + require.NotNil(t, domain) + + // Verify domain properties + assert.Equal(t, "kvm", domain.Type) + assert.Equal(t, tt.vmName, domain.Name) + assert.Equal(t, tt.cpu, domain.VCPU.Value) + assert.Equal(t, tt.mem, domain.Memory.Value) + assert.Equal(t, tt.arch, domain.OS.Type.Arch) + assert.Equal(t, tt.machineCanonical, domain.OS.Type.Machine) + + if tt.expectedFirmware != "" { + assert.Equal(t, tt.expectedFirmware, domain.OS.Firmware) + } + + // Verify disks + assert.Len(t, domain.Devices.Disks, 2) + assert.Equal(t, testBootDisk, domain.Devices.Disks[0].Source.File.File) + assert.Equal(t, "on", domain.Devices.Disks[0].Driver.IOMMU) + assert.Equal(t, tt.cidataDisk, domain.Devices.Disks[1].Source.File.File) + + // Verify network interface has IOMMU + assert.Len(t, domain.Devices.Interfaces, 1) + assert.Equal(t, "on", domain.Devices.Interfaces[0].Driver.IOMMU) + + // Verify SCSI controller if expected + if tt.expectSCSIController { + assert.Len(t, domain.Devices.Controllers, 1) + assert.Equal(t, "scsi", domain.Devices.Controllers[0].Type) + } + + // Verify CPU mode is set as expected for the architecture + if tt.expectedCPUMode != "" { + require.NotNil(t, domain.CPU, "CPU configuration should be set") + assert.Equal(t, tt.expectedCPUMode, domain.CPU.Mode) + } + }) + } +} + +// TestCreateDomainXMLx86_64 tests x86_64 domain XML generation +func TestCreateDomainXMLx86_64(t *testing.T) { + mockCaps := createMockCaps("x86_64", "/usr/bin/qemu-system-x86_64") + + mockClient := &libvirtClient{ + caps: mockCaps, + networkName: testNetworkName, + } + + tests := []struct { + name string + cfg *domainConfig + vm *vmConfig + expectError bool + checkFirmware bool + expectedFW string + }{ + { + name: "basic x86_64 domain", + cfg: createTestDomainConfig("test-x86-vm", 2, 2048, testNetworkName, testCiDataISO), + vm: &vmConfig{ + launchSecurityType: NoLaunchSecurity, + }, + expectError: false, + }, + { + name: "x86_64 with firmware", + cfg: createTestDomainConfig("test-x86-fw-vm", 4, 4096, testNetworkName, testCiDataISO), + vm: &vmConfig{ + launchSecurityType: NoLaunchSecurity, + firmware: "/usr/share/OVMF/OVMF_CODE.fd", + }, + expectError: false, + checkFirmware: true, + expectedFW: "efi", + }, + { + name: "unsupported security type", + cfg: createTestDomainConfig("test-x86-sec-vm", 2, 2048, testNetworkName, testCiDataISO), + vm: &vmConfig{ + launchSecurityType: S390PV, // Not supported on x86_64 + }, + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + domain, err := createDomainXMLx86_64(mockClient, tt.cfg, tt.vm) + + if tt.expectError { + assert.Error(t, err) + assert.Nil(t, domain) + return + } + + assert.NoError(t, err) + require.NotNil(t, domain) + + // Verify basic properties + assert.Equal(t, "kvm", domain.Type) + assert.Equal(t, tt.cfg.name, domain.Name) + assert.Equal(t, tt.cfg.cpu, domain.VCPU.Value) + assert.Equal(t, tt.cfg.mem, domain.Memory.Value) + assert.Equal(t, "x86_64", domain.OS.Type.Arch) + + // Verify disks + assert.Len(t, domain.Devices.Disks, 2) + assert.Equal(t, tt.cfg.bootDisk, domain.Devices.Disks[0].Source.File.File) + assert.Equal(t, tt.cfg.cidataDisk, domain.Devices.Disks[1].Source.File.File) + + // Check firmware if specified + if tt.checkFirmware { + assert.NotEmpty(t, domain.OS.Loader) + assert.Equal(t, tt.vm.firmware, domain.OS.Loader.Path) + assert.Equal(t, tt.expectedFW, domain.OS.Firmware) + } + }) + } +} + +// TestCreateDomainXML tests the architecture-based domain XML dispatcher +func TestCreateDomainXML(t *testing.T) { + tests := []struct { + name string + arch string + expectedArch string + }{ + { + name: "s390x architecture", + arch: "s390x", + expectedArch: "s390x", + }, + { + name: "aarch64 architecture", + arch: "aarch64", + expectedArch: "aarch64", + }, + { + name: "x86_64 architecture (default)", + arch: "x86_64", + expectedArch: "x86_64", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create appropriate mock capabilities using helper function + var mockCaps *libvirtxml.Caps + switch tt.arch { + case "s390x": + mockCaps = createMockCaps("s390x", "/usr/bin/qemu-system-s390x", + libvirtxml.CapsGuestMachine{Name: "s390-ccw-virtio", Canonical: "s390-ccw-virtio-rhel9.0.0"}) + case "aarch64": + mockCaps = createMockCaps("aarch64", "/usr/bin/qemu-system-aarch64", + libvirtxml.CapsGuestMachine{Name: "virt", Canonical: "virt-4.2"}) + default: + mockCaps = createMockCaps("x86_64", "/usr/bin/qemu-system-x86_64") + } + + mockClient := &libvirtClient{ + caps: mockCaps, + networkName: testNetworkName, + nodeInfo: &libvirt.NodeInfo{Model: tt.arch}, + } + + cfg := createTestDomainConfig("test-vm", 2, 2048, testNetworkName, testCiDataISO) + + vm := &vmConfig{ + launchSecurityType: NoLaunchSecurity, + } + + domain, err := createDomainXML(mockClient, cfg, vm) + assert.NoError(t, err) + require.NotNil(t, domain) + assert.Equal(t, tt.expectedArch, domain.OS.Type.Arch) + }) + } +} + +// TestVerifyDomainXMLIOMMU tests IOMMU verification for s390x and aarch64 +func TestVerifyDomainXMLIOMMU(t *testing.T) { + tests := []struct { + name string + domain *libvirtxml.Domain + expectError bool + errorMsg string + }{ + { + name: "s390x with proper IOMMU on disks and interfaces", + domain: &libvirtxml.Domain{ + OS: &libvirtxml.DomainOS{ + Type: &libvirtxml.DomainOSType{Arch: "s390x"}, + }, + Devices: &libvirtxml.DomainDeviceList{ + Disks: []libvirtxml.DomainDisk{ + { + Target: &libvirtxml.DomainDiskTarget{Bus: "virtio"}, + Driver: &libvirtxml.DomainDiskDriver{IOMMU: "on"}, + }, + }, + Interfaces: []libvirtxml.DomainInterface{ + { + Model: &libvirtxml.DomainInterfaceModel{Type: "virtio"}, + Driver: &libvirtxml.DomainInterfaceDriver{IOMMU: "on"}, + }, + }, + }, + }, + expectError: false, + }, + { + name: "s390x missing IOMMU on disk", + domain: &libvirtxml.Domain{ + OS: &libvirtxml.DomainOS{ + Type: &libvirtxml.DomainOSType{Arch: "s390x"}, + }, + Devices: &libvirtxml.DomainDeviceList{ + Disks: []libvirtxml.DomainDisk{ + { + Target: &libvirtxml.DomainDiskTarget{Bus: "virtio"}, + Driver: &libvirtxml.DomainDiskDriver{}, // Missing IOMMU + }, + }, + }, + }, + expectError: true, + errorMsg: "does not have IOMMU assigned", + }, + { + name: "aarch64 missing IOMMU on interface", + domain: &libvirtxml.Domain{ + OS: &libvirtxml.DomainOS{ + Type: &libvirtxml.DomainOSType{Arch: "aarch64"}, + }, + Devices: &libvirtxml.DomainDeviceList{ + Interfaces: []libvirtxml.DomainInterface{ + { + Model: &libvirtxml.DomainInterfaceModel{Type: "virtio"}, + Driver: &libvirtxml.DomainInterfaceDriver{}, // Missing IOMMU + }, + }, + }, + }, + expectError: true, + errorMsg: "does not have IOMMU assigned", + }, + { + name: "x86_64 does not require IOMMU", + domain: &libvirtxml.Domain{ + OS: &libvirtxml.DomainOS{ + Type: &libvirtxml.DomainOSType{Arch: "x86_64"}, + }, + Devices: &libvirtxml.DomainDeviceList{ + Disks: []libvirtxml.DomainDisk{ + { + Target: &libvirtxml.DomainDiskTarget{Bus: "virtio"}, + Driver: &libvirtxml.DomainDiskDriver{}, // No IOMMU required + }, + }, + }, + }, + expectError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := verifyDomainXML(tt.domain) + if tt.expectError { + assert.ErrorContains(t, err, tt.errorMsg) + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/src/cloud-providers/libvirt/manager_test.go b/src/cloud-providers/libvirt/manager_test.go new file mode 100644 index 0000000000..660f3e15a6 --- /dev/null +++ b/src/cloud-providers/libvirt/manager_test.go @@ -0,0 +1,324 @@ +// (C) Copyright Confidential Containers Contributors +// SPDX-License-Identifier: Apache-2.0 + +package libvirt + +import ( + "flag" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func resetLibvirtConfig() { + libvirtcfg = Config{ + URI: defaultURI, + PoolName: defaultPoolName, + NetworkName: defaultNetworkName, + DataDir: defaultDataDir, + VolName: defaultVolName, + LaunchSecurity: defaultLaunchSecurity, + Firmware: defaultFirmware, + CPU: 2, + Memory: 8192, + DisableCVM: true, + } +} + +// setupManagerTest creates a fresh Manager with flags for testing +func setupManagerTest(t *testing.T) (*Manager, *flag.FlagSet) { + t.Helper() + resetLibvirtConfig() + manager := &Manager{} + flags := flag.NewFlagSet("test", flag.ContinueOnError) + manager.ParseCmd(flags) + return manager, flags +} + +func TestManagerParseCmdWithEnvVars(t *testing.T) { + // Set environment variables + t.Setenv("LIBVIRT_URI", "qemu+ssh://testhost/system") + t.Setenv("LIBVIRT_POOL", "env-pool") + t.Setenv("LIBVIRT_NET", "env-network") + t.Setenv("LIBVIRT_VOL_NAME", "env-vol.qcow2") + t.Setenv("LIBVIRT_LAUNCH_SECURITY", "sev") + t.Setenv("LIBVIRT_EFI_FIRMWARE", "/env/firmware.fd") + t.Setenv("LIBVIRT_CPU", "8") + t.Setenv("LIBVIRT_MEMORY", "16384") + t.Setenv("DISABLECVM", "false") + + _, _ = setupManagerTest(t) + + // Verify environment variables were applied + assert.Equal(t, "qemu+ssh://testhost/system", libvirtcfg.URI) + assert.Equal(t, "env-pool", libvirtcfg.PoolName) + assert.Equal(t, "env-network", libvirtcfg.NetworkName) + assert.Equal(t, "env-vol.qcow2", libvirtcfg.VolName) + assert.Equal(t, "sev", libvirtcfg.LaunchSecurity) + assert.Equal(t, "/env/firmware.fd", libvirtcfg.Firmware) + assert.Equal(t, uint(8), libvirtcfg.CPU) + assert.Equal(t, uint(16384), libvirtcfg.Memory) + assert.False(t, libvirtcfg.DisableCVM) +} + +func TestManagerParseCmdFlagOverridesEnv(t *testing.T) { + // Set environment variable + t.Setenv("LIBVIRT_URI", "qemu+ssh://envhost/system") + t.Setenv("LIBVIRT_CPU", "8") + + _, flags := setupManagerTest(t) + + // Verify env var was applied initially + assert.Equal(t, "qemu+ssh://envhost/system", libvirtcfg.URI) + assert.Equal(t, uint(8), libvirtcfg.CPU) + + // Override with flag + err := flags.Set("uri", "qemu:///system") + require.NoError(t, err) + err = flags.Set("cpu", "4") + require.NoError(t, err) + + // Verify flag overrides env var + assert.Equal(t, "qemu:///system", libvirtcfg.URI) + assert.Equal(t, uint(4), libvirtcfg.CPU) +} + +func TestManagerParseCmdInvalidValues(t *testing.T) { + tests := []struct { + name string + flagName string + invalidValue string + description string + }{ + { + name: "invalid CPU value", + flagName: "cpu", + invalidValue: "invalid", + description: "non-numeric CPU value should be rejected", + }, + { + name: "invalid memory value", + flagName: "memory", + invalidValue: "not-a-number", + description: "non-numeric memory value should be rejected", + }, + { + name: "invalid bool value", + flagName: "disable-cvm", + invalidValue: "maybe", + description: "invalid boolean value should be rejected", + }, + { + name: "negative CPU value", + flagName: "cpu", + invalidValue: "-1", + description: "negative CPU value should be rejected", + }, + { + name: "negative memory value", + flagName: "memory", + invalidValue: "-1", + description: "negative memory value should be rejected", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + _, flags := setupManagerTest(t) + + err := flags.Set(tc.flagName, tc.invalidValue) + assert.Error(t, err, tc.description) + }) + } +} + +func TestManagerParseCmdEmptyStringValues(t *testing.T) { + _, flags := setupManagerTest(t) + + // Set empty string values (should be allowed) + err := flags.Set("uri", "") + require.NoError(t, err) + err = flags.Set("pool-name", "") + require.NoError(t, err) + err = flags.Set("launch-security", "") + require.NoError(t, err) + + // Verify empty strings are set + assert.Equal(t, "", libvirtcfg.URI) + assert.Equal(t, "", libvirtcfg.PoolName) + assert.Equal(t, "", libvirtcfg.LaunchSecurity) +} + +func TestManagerLoadEnv(t *testing.T) { + manager, _ := setupManagerTest(t) + // LoadEnv should do nothing (it's a no-op) + manager.LoadEnv() + // No assertion needed, just verify it doesn't panic +} + +func TestManagerGetConfig(t *testing.T) { + manager, _ := setupManagerTest(t) + config := manager.GetConfig() + assert.NotNil(t, config) + assert.Equal(t, &libvirtcfg, config) +} + +func TestManagerNewProvider(t *testing.T) { + tests := []struct { + name string + setupConfig func(*testing.T, *flag.FlagSet) + expectError bool + description string + }{ + { + name: "valid config", + setupConfig: func(t *testing.T, flags *flag.FlagSet) { + resetLibvirtConfig() + checkConfig(t) + require.NoError(t, flags.Set("uri", testCfg.URI)) + require.NoError(t, flags.Set("pool-name", testCfg.PoolName)) + require.NoError(t, flags.Set("network-name", testCfg.NetworkName)) + require.NoError(t, flags.Set("vol-name", testCfg.VolName)) + }, + expectError: false, + description: "should succeed with valid config", + }, + { + name: "invalid URI", + setupConfig: func(t *testing.T, flags *flag.FlagSet) { + require.NoError(t, flags.Set("uri", "invalid://uri")) + }, + expectError: true, + description: "should fail with invalid URI", + }, + { + name: "empty URI", + setupConfig: func(t *testing.T, flags *flag.FlagSet) { + require.NoError(t, flags.Set("uri", "")) + }, + expectError: true, + description: "should fail with empty URI", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + manager, flags := setupManagerTest(t) + tc.setupConfig(t, flags) + + provider, err := manager.NewProvider() + if tc.expectError { + assert.Error(t, err, tc.description) + } else { + require.NoError(t, err, tc.description) + assert.NotNil(t, provider) + } + }) + } +} + +func TestDefaultConstants(t *testing.T) { + // Test all constants in a single assertion block + assert.Equal(t, "qemu+ssh://root@192.168.122.1/system?no_verify=1", defaultURI) + assert.Equal(t, "default", defaultPoolName) + assert.Equal(t, "default", defaultNetworkName) + assert.Equal(t, "/var/lib/libvirt/images", defaultDataDir) + assert.Equal(t, "podvm-base.qcow2", defaultVolName) + assert.Equal(t, "", defaultLaunchSecurity) + assert.Equal(t, "/usr/share/OVMF/OVMF_CODE_4M.fd", defaultFirmware) + assert.Equal(t, "2", defaultCPU) + assert.Equal(t, "8192", defaultMemory) +} + +func TestManagerParseCmdFlags(t *testing.T) { + tests := []struct { + name string + flagName string + flagValue string + expectedValue interface{} + getActual func() interface{} + }{ + { + name: "uri flag", + flagName: "uri", + flagValue: "qemu:///system", + expectedValue: "qemu:///system", + getActual: func() interface{} { return libvirtcfg.URI }, + }, + { + name: "pool-name flag", + flagName: "pool-name", + flagValue: "test-pool", + expectedValue: "test-pool", + getActual: func() interface{} { return libvirtcfg.PoolName }, + }, + { + name: "network-name flag", + flagName: "network-name", + flagValue: "test-network", + expectedValue: "test-network", + getActual: func() interface{} { return libvirtcfg.NetworkName }, + }, + { + name: "vol-name flag", + flagName: "vol-name", + flagValue: "test-vol.qcow2", + expectedValue: "test-vol.qcow2", + getActual: func() interface{} { return libvirtcfg.VolName }, + }, + { + name: "launch-security flag", + flagName: "launch-security", + flagValue: "s390-pv", + expectedValue: "s390-pv", + getActual: func() interface{} { return libvirtcfg.LaunchSecurity }, + }, + { + name: "firmware flag", + flagName: "firmware", + flagValue: "/custom/path/to/firmware.fd", + expectedValue: "/custom/path/to/firmware.fd", + getActual: func() interface{} { return libvirtcfg.Firmware }, + }, + { + name: "data-dir flag", + flagName: "data-dir", + flagValue: "/custom/data/dir", + expectedValue: "/custom/data/dir", + getActual: func() interface{} { return libvirtcfg.DataDir }, + }, + { + name: "cpu flag", + flagName: "cpu", + flagValue: "4", + expectedValue: uint(4), + getActual: func() interface{} { return libvirtcfg.CPU }, + }, + { + name: "memory flag", + flagName: "memory", + flagValue: "4096", + expectedValue: uint(4096), + getActual: func() interface{} { return libvirtcfg.Memory }, + }, + { + name: "disable-cvm flag", + flagName: "disable-cvm", + flagValue: "false", + expectedValue: false, + getActual: func() interface{} { return libvirtcfg.DisableCVM }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + _, flags := setupManagerTest(t) + + err := flags.Set(tc.flagName, tc.flagValue) + require.NoError(t, err) + + assert.Equal(t, tc.expectedValue, tc.getActual()) + }) + } +} diff --git a/src/cloud-providers/libvirt/provider_test.go b/src/cloud-providers/libvirt/provider_test.go new file mode 100644 index 0000000000..1f4f8fdaef --- /dev/null +++ b/src/cloud-providers/libvirt/provider_test.go @@ -0,0 +1,263 @@ +//go:build cgo + +// (C) Copyright Confidential Containers Contributors +// SPDX-License-Identifier: Apache-2.0 + +package libvirt + +import ( + "context" + "net/netip" + "testing" + + provider "github.com/confidential-containers/cloud-api-adaptor/src/cloud-providers" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const ( + testPodName = "test-pod" + testSandboxID = "test-sandbox" + testVMName = "test-vm" + testDefaultCPU = 2 + testDefaultMemory = 2048 + testQemuURI = "qemu:///system" + testDefaultPool = "default" + testDefaultNetwork = "default" + testVolName = "test.qcow2" + testInvalidURI = "invalid://uri" +) + +// MockCloudConfigGenerator is a mock implementation for testing +type MockCloudConfigGenerator struct { + userData string + err error +} + +func (m *MockCloudConfigGenerator) Generate() (string, error) { + if m.err != nil { + return "", m.err + } + return m.userData, nil +} + +// newTestConfig creates a config for testing with optional customizations. +// Uses hardcoded test constants for unit tests. +func newTestConfig(opts ...func(*Config)) *Config { + cfg := &Config{ + URI: testQemuURI, + PoolName: testDefaultPool, + NetworkName: testDefaultNetwork, + VolName: testVolName, + CPU: testDefaultCPU, + Memory: testDefaultMemory, + } + for _, opt := range opts { + opt(cfg) + } + return cfg +} + +func withURI(uri string) func(*Config) { + return func(c *Config) { c.URI = uri } +} + +func withPoolName(pool string) func(*Config) { + return func(c *Config) { c.PoolName = pool } +} + +func withNetworkName(network string) func(*Config) { + return func(c *Config) { c.NetworkName = network } +} + +func withVolName(vol string) func(*Config) { + return func(c *Config) { c.VolName = vol } +} + +func withCPU(cpu uint) func(*Config) { + return func(c *Config) { c.CPU = cpu } +} + +func withMemory(memory uint) func(*Config) { + return func(c *Config) { c.Memory = memory } +} + +func withDisableCVM(disabled bool) func(*Config) { + return func(c *Config) { c.DisableCVM = disabled } +} + +func withLaunchSecurity(security string) func(*Config) { + return func(c *Config) { c.LaunchSecurity = security } +} + +func TestNewProvider(t *testing.T) { + t.Run("invalid URI", func(t *testing.T) { + config := newTestConfig(withURI(testInvalidURI)) + _, err := NewProvider(config) + assert.Error(t, err) + }) +} + +func TestGetIPs(t *testing.T) { + tests := []struct { + name string + ips []netip.Addr + expectEmpty bool + expectNil bool + }{ + { + name: "multiple IPs", + ips: []netip.Addr{ + netip.MustParseAddr("192.168.122.10"), + netip.MustParseAddr("10.0.0.5"), + netip.MustParseAddr("2001:db8::1"), + }, + }, + { + name: "empty IPs", + ips: []netip.Addr{}, + expectEmpty: true, + }, + { + name: "nil IPs", + ips: nil, + expectNil: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + vm := &vmConfig{ + name: testVMName, + ips: tt.ips, + } + + ips, err := getIPs(vm) + require.NoError(t, err) + + if tt.expectEmpty || tt.expectNil { + assert.Empty(t, ips) + } else { + assert.Equal(t, tt.ips, ips) + assert.Len(t, ips, len(tt.ips)) + } + }) + } +} + +func TestTeardown(t *testing.T) { + p := &libvirtProvider{} + err := p.Teardown() + assert.NoError(t, err) +} + +func TestDeleteInstanceEmptyID(t *testing.T) { + p := &libvirtProvider{ + serviceConfig: newTestConfig(), + } + + err := p.DeleteInstance(context.Background(), "") + assert.Error(t, err) + assert.Contains(t, err.Error(), "empty instanceID") +} + +func TestCreateInstanceCloudConfigError(t *testing.T) { + // Test that doesn't require libvirt - tests early validation + // Create provider without checkConfig to test cloud config error path + p := &libvirtProvider{ + serviceConfig: newTestConfig(), + } + + mockGen := &MockCloudConfigGenerator{ + err: assert.AnError, + } + + spec := provider.InstanceTypeSpec{ + InstanceType: "test", + } + + _, err := p.CreateInstance(context.Background(), testPodName, testSandboxID, mockGen, spec) + assert.Error(t, err) + assert.Contains(t, err.Error(), "assert.AnError") +} + +// TestCreateInstanceLaunchSecurity tests launch security validation logic. +// This is a unit test that verifies the validation happens correctly without requiring libvirt. +func TestProviderConfigVerifier(t *testing.T) { + t.Run("valid configurations", func(t *testing.T) { + tests := []struct { + name string + config *Config + }{ + { + name: "all fields set", + config: newTestConfig(), + }, + { + name: "with optional fields", + config: newTestConfig( + withLaunchSecurity("s390-pv"), + withDisableCVM(false), + ), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &libvirtProvider{serviceConfig: tt.config} + err := p.ConfigVerifier() + assert.NoError(t, err) + }) + } + }) + + t.Run("invalid configurations", func(t *testing.T) { + tests := []struct { + name string + configOpts []func(*Config) + errorContains string + }{ + { + name: "empty URI", + configOpts: []func(*Config){withURI("")}, + errorContains: "URI is empty", + }, + { + name: "empty PoolName", + configOpts: []func(*Config){withPoolName("")}, + errorContains: "PoolName is empty", + }, + { + name: "empty NetworkName", + configOpts: []func(*Config){withNetworkName("")}, + errorContains: "NetworkName is empty", + }, + { + name: "empty VolName", + configOpts: []func(*Config){withVolName("")}, + errorContains: "VolName is empty", + }, + { + name: "zero CPU", + configOpts: []func(*Config){withCPU(0)}, + errorContains: "CPU must be greater than zero", + }, + { + name: "zero Memory", + configOpts: []func(*Config){withMemory(0)}, + errorContains: "Memory must be greater than zero", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + config := newTestConfig(tt.configOpts...) + p := &libvirtProvider{serviceConfig: config} + + err := p.ConfigVerifier() + require.Error(t, err) + assert.Contains(t, err.Error(), tt.errorContains) + }) + } + }) +} diff --git a/src/cloud-providers/types.go b/src/cloud-providers/types.go index b4698cf161..9f8a996324 100644 --- a/src/cloud-providers/types.go +++ b/src/cloud-providers/types.go @@ -68,4 +68,9 @@ type InstanceTypeSpec struct { GPUs int64 Image string MultiNic bool + Volumes []CloudVolume +} + +type CloudVolume struct { + DiskID string } diff --git a/src/csi-wrapper/Dockerfile.csi_wrappers b/src/csi-wrapper/Dockerfile.csi_wrappers deleted file mode 100644 index 4bed454f8f..0000000000 --- a/src/csi-wrapper/Dockerfile.csi_wrappers +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright Confidential Containers Contributors -# -# SPDX-License-Identifier: Apache-2.0 -# -# Builds csi wrappers images inside container - -ARG SOURCE_FROM=remote - -##### Builder Dev Image ##### -# golang:1.25.10 -FROM --platform=${BUILDPLATFORM} golang@sha256:3965b9511a9bed199f0b4eb146f99c31971a5c0adb3240d9f25e233cbd9e01c8 AS builder-local -WORKDIR /src -COPY csi-wrapper ./cloud-api-adaptor/src/csi-wrapper/ -COPY cloud-api-adaptor ./cloud-api-adaptor/src/cloud-api-adaptor - -##### Builder Release Image ##### -# golang:1.25.10 -FROM --platform=${BUILDPLATFORM} golang@sha256:3965b9511a9bed199f0b4eb146f99c31971a5c0adb3240d9f25e233cbd9e01c8 AS builder-remote -ARG BINARY -ARG CAA_SRC="https://github.com/confidential-containers/cloud-api-adaptor" -ARG CAA_SRC_REF="main" - -WORKDIR /src - -RUN echo $CAA_SRC -RUN echo $CAA_SRC_REF -RUN git clone ${CAA_SRC} -b ${CAA_SRC_REF} cloud-api-adaptor - -FROM builder-${SOURCE_FROM} AS builder -ARG TARGETARCH -ARG BINARY - -RUN cd cloud-api-adaptor/src/csi-wrapper/ && make ARCH=${TARGETARCH} ${BINARY} - -# ubuntu:24.04 -FROM ubuntu@sha256:0d39fcc8335d6d74d5502f6df2d30119ff4790ebbb60b364818d5112d9e3e932 -ARG BINARY -ENV BINARY=${BINARY} -WORKDIR / -COPY --from=builder /src/cloud-api-adaptor/src/csi-wrapper/build/${BINARY}/${BINARY} /usr/bin/${BINARY} -COPY --from=builder /src/cloud-api-adaptor/src/csi-wrapper/entrypoint.sh /usr/bin/entrypoint.sh - -ENTRYPOINT ["/usr/bin/entrypoint.sh"] diff --git a/src/csi-wrapper/Makefile b/src/csi-wrapper/Makefile deleted file mode 100644 index 9731917cbf..0000000000 --- a/src/csi-wrapper/Makefile +++ /dev/null @@ -1,66 +0,0 @@ -BINARIES := csi-controller-wrapper csi-node-wrapper csi-podvm-wrapper -SOURCEDIRS := ./cmd ./pkg -PACKAGES := $(shell go list $(addsuffix /...,$(SOURCEDIRS))) -SOURCES := $(shell find $(SOURCEDIRS) -name '*.go' -print) - -ARCH ?= $(subst x86_64,amd64,$(shell uname -m)) -GOOPTIONS ?= GOOS=linux GOARCH=$(ARCH) CGO_ENABLED=0 -BUILD_ROOT=${PWD} - -all: build -build: $(BINARIES) - -$(BINARIES): $(SOURCES) - $(GOOPTIONS) go build -o "$(BUILD_ROOT)/build/$@/$@" "cmd/$@/main.go" - -test: - go test -cover $(PACKAGES) - -check: fmt vet - -fmt: - find $(SOURCEDIRS) -name '*.go' -print0 | xargs -0 gofmt -l -d - -vet: - go vet $(PACKAGES) - -clean: - rm -fr $(BUILD_ROOT)/build/* - -.PHONY: csi-node-wrapper-docker -csi-node-wrapper-docker: - cd ../ && docker buildx build --platform "linux/$(ARCH)" \ - --build-arg BINARY=csi-node-wrapper \ - --build-arg SOURCE_FROM=local \ - -t csi-node-wrapper:local \ - -f csi-wrapper/Dockerfile.csi_wrappers --load . - -.PHONY: csi-controller-wrapper-docker -csi-controller-wrapper-docker: - cd ../ && docker buildx build --platform "linux/$(ARCH)" \ - --build-arg BINARY=csi-controller-wrapper \ - --build-arg SOURCE_FROM=local \ - -t csi-controller-wrapper:local \ - -f csi-wrapper/Dockerfile.csi_wrappers --load . - -.PHONY: csi-podvm-wrapper-docker -csi-podvm-wrapper-docker: - cd ../ && docker buildx build --platform "linux/$(ARCH)" \ - --build-arg BINARY=csi-podvm-wrapper \ - --build-arg SOURCE_FROM=local \ - -t csi-podvm-wrapper:local \ - -f csi-wrapper/Dockerfile.csi_wrappers --load . - -.PHONY: import-csi-node-wrapper-docker -import-csi-node-wrapper-docker: csi-node-wrapper-docker - docker save "csi-node-wrapper:local" | ctr -n=k8s.io images import - - -.PHONY: import-csi-controller-wrapper-docker -import-csi-controller-wrapper-docker: csi-controller-wrapper-docker - docker save "csi-controller-wrapper:local" | ctr -n=k8s.io images import - - -.PHONY: import-csi-podvm-wrapper-docker -import-csi-podvm-wrapper-docker: csi-podvm-wrapper-docker - docker save "csi-podvm-wrapper:local" | ctr -n=k8s.io images import - - -.PHONY: all build check fmt vet clean diff --git a/src/csi-wrapper/README.md b/src/csi-wrapper/README.md deleted file mode 100644 index 7d1b50ea32..0000000000 --- a/src/csi-wrapper/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# csi-wrapper -CSI Wrapper for Peer Pod Storage - -## High Level Design - -![design](./images/csi-wrapper.png) - -> **Note** Edited via https://excalidraw.com/ - -## Cloud provider examples - -* [Azure](examples/azure/README.md) -* [IBM Cloud](examples/ibm/README.md) -* [AWS](examples/aws/README.md) diff --git a/src/csi-wrapper/cmd/csi-controller-wrapper/main.go b/src/csi-wrapper/cmd/csi-controller-wrapper/main.go deleted file mode 100644 index 2001a9c02a..0000000000 --- a/src/csi-wrapper/cmd/csi-controller-wrapper/main.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright Confidential Containers Contributors -// SPDX-License-Identifier: Apache-2.0 - -package main - -import ( - "context" - "flag" - - "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/config" - peerpodvolumeV1alpha1 "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned" - "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/peerpodvolume" - "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/wrapper" - "github.com/golang/glog" - "k8s.io/client-go/tools/clientcmd" -) - -func init() { - _ = flag.Set("logtostderr", "true") // TODO: error check -} - -func main() { - cfg := config.Endpoints{} - - flag.StringVar(&cfg.Endpoint, "endpoint", "/csi/csi-controller-wrapper.sock", "Wrapper CSI Controller service endpoint path") - flag.StringVar(&cfg.Namespace, "namespace", "default", "The namespace where the peer pod volume crd object will be created") - flag.StringVar(&cfg.TargetEndpoint, "target-endpoint", "/csi/csi.sock", "Target CSI Controller service endpoint path") - - flag.Parse() - - if cfg.Endpoint == "" { - glog.Fatalf("No wrapper csi endpoint provided") - } - - if cfg.TargetEndpoint == "" { - glog.Fatalf("No target csi endpoint provided") - } - - glog.Infof("Endpoint: %s ", cfg.Endpoint) - glog.Infof("TargetEndpoint: %s", cfg.TargetEndpoint) - - k8sconfig, err := clientcmd.BuildConfigFromFlags("", "") - if err != nil { - glog.Fatalf("Build kubeconfig failed: %v", err) - } - peerPodVolumeClient := peerpodvolumeV1alpha1.NewForConfigOrDie(k8sconfig) - - identityService := wrapper.NewIdentityService(cfg.TargetEndpoint) - controllerService := wrapper.NewControllerService(cfg.TargetEndpoint, cfg.Namespace, peerPodVolumeClient) - - podVolumeMonitor, err := peerpodvolume.NewPodVolumeMonitor( - peerPodVolumeClient, - cfg.Namespace, - controllerService.SyncHandler, - controllerService.DeleteFunction, - ) - if err != nil { - glog.Fatalf("Initialize peer pod Volume Controller monitor failed: %v", err) - } - go func() { - if err := podVolumeMonitor.Start(context.Background()); err != nil { - glog.Fatalf("Running peer pod Volume Controller monitor failed: %v", err) - } - }() - - if err := wrapper.Run(cfg.Endpoint, identityService, controllerService, nil); err != nil { - glog.Fatalf("Failed to run csi controller plugin wrapper: %s", err.Error()) - } -} diff --git a/src/csi-wrapper/cmd/csi-node-wrapper/main.go b/src/csi-wrapper/cmd/csi-node-wrapper/main.go deleted file mode 100644 index 6935317c59..0000000000 --- a/src/csi-wrapper/cmd/csi-node-wrapper/main.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright Confidential Containers Contributors -// SPDX-License-Identifier: Apache-2.0 - -package main - -import ( - "context" - "flag" - - "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/config" - peerpodvolumeV1alpha1 "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned" - "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/peerpodvolume" - "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/wrapper" - "github.com/golang/glog" - "k8s.io/client-go/tools/clientcmd" -) - -func init() { - _ = flag.Set("logtostderr", "true") // TODO: error check -} - -func main() { - cfg := config.Endpoints{} - - flag.StringVar(&cfg.Endpoint, "endpoint", "/csi/csi-node-wrapper.sock", "Wrapper CSI Node service endpoint path") - flag.StringVar(&cfg.Namespace, "namespace", "default", "The namespace where the peer pod volume crd object will be created") - flag.StringVar(&cfg.TargetEndpoint, "target-endpoint", "/csi/csi.sock", "Target CSI Node service endpoint path") - flag.StringVar(&cfg.VMIDInformationEndpoint, "vm-id-information-endpoint", "/run/peerpod/hypervisor.sock", "Unix domain socket path of VM ID information service") - - flag.Parse() - - glog.Infof("Endpoint: %v ", cfg.Endpoint) - glog.Infof("TargetEndpoint: %s", cfg.TargetEndpoint) - glog.Infof("VMIDInformationEndpoint: %s", cfg.VMIDInformationEndpoint) - - k8sconfig, err := clientcmd.BuildConfigFromFlags("", "") - if err != nil { - glog.Fatalf("Build kubeconfig failed: %v", err) - } - peerPodVolumeClient := peerpodvolumeV1alpha1.NewForConfigOrDie(k8sconfig) - - identityService := wrapper.NewIdentityService(cfg.TargetEndpoint) - nodeService := wrapper.NewNodeService(cfg.TargetEndpoint, cfg.Namespace, peerPodVolumeClient, cfg.VMIDInformationEndpoint) - - podVolumeMonitor, err := peerpodvolume.NewPodVolumeMonitor( - peerPodVolumeClient, - cfg.Namespace, - nodeService.SyncHandler, - nodeService.DeleteFunction, - ) - if err != nil { - glog.Fatalf("Initialize peer pod Volume Node monitor failed: %v", err) - } - go func() { - if err := podVolumeMonitor.Start(context.Background()); err != nil { - glog.Fatalf("Running peer pod Volume Node monitor failed: %v", err) - } - }() - - if err := wrapper.Run(cfg.Endpoint, identityService, nil, nodeService); err != nil { - glog.Fatalf("Failed to run csi node plugin wrapper: %s", err.Error()) - } -} diff --git a/src/csi-wrapper/cmd/csi-podvm-wrapper/main.go b/src/csi-wrapper/cmd/csi-podvm-wrapper/main.go deleted file mode 100644 index e30b2c93d3..0000000000 --- a/src/csi-wrapper/cmd/csi-podvm-wrapper/main.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright Confidential Containers Contributors -// SPDX-License-Identifier: Apache-2.0 - -package main - -import ( - "context" - "flag" - "os" - - "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/apis/peerpodvolume/v1alpha1" - "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/config" - peerpodvolumeV1alpha1 "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned" - "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/peerpodvolume" - "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/wrapper" - "github.com/golang/glog" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/clientcmd" -) - -func init() { - _ = flag.Set("logtostderr", "true") // TODO: error check -} - -func main() { - cfg := config.Endpoints{} - - flag.StringVar(&cfg.Endpoint, "endpoint", "/csi/csi-podvm-wrapper.sock", "Wrapper CSI Node service endpoint path") - flag.StringVar(&cfg.Namespace, "namespace", "default", "The namespace where the peer pod volume crd object will be created") - flag.StringVar(&cfg.TargetEndpoint, "target-endpoint", "/csi/csi.sock", "Target CSI Node service endpoint path") - - flag.Parse() - - glog.Infof("Endpoint: %v ", cfg.Endpoint) - glog.Infof("TargetEndpoint: %s", cfg.TargetEndpoint) - podName := os.Getenv("POD_NAME") - podNamespace := os.Getenv("POD_NAME_SPACE") - podUID := os.Getenv("POD_UID") - podNodeName := os.Getenv("POD_NODE_NAME") - - glog.Infof("POD_NAME: %v ", podName) - glog.Infof("POD_NAME_SPACE: %s", podNamespace) - glog.Infof("POD_UID: %v ", podUID) - glog.Infof("POD_NODE_NAME: %v ", podNodeName) - - k8sconfig, err := clientcmd.BuildConfigFromFlags("", "") - if err != nil { - glog.Fatalf("Build kubeconfig failed: %v", err) - } - peerPodVolumeClient := peerpodvolumeV1alpha1.NewForConfigOrDie(k8sconfig) - - identityService := wrapper.NewIdentityService(cfg.TargetEndpoint) - podvmService := wrapper.NewPodVMNodeService(cfg.TargetEndpoint, cfg.Namespace, peerPodVolumeClient) - - podVolumeMonitor, err := peerpodvolume.NewPodVolumeMonitor( - peerPodVolumeClient, - cfg.Namespace, - podvmService.SyncHandler, - podvmService.DeleteFunction, - ) - if err != nil { - glog.Fatalf("Initialize peer pod Volume Node monitor failed: %v", err) - } - go func() { - if err := podVolumeMonitor.Start(context.Background()); err != nil { - glog.Fatalf("Running peer pod Volume Node monitor failed: %v", err) - } - }() - - labelSelector := labels.SelectorFromSet(map[string]string{"podUid": string(podUID)}) - options := metav1.ListOptions{ - LabelSelector: labelSelector.String(), - } - peerpodVolumes, err := peerPodVolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(cfg.Namespace).List(context.Background(), options) - if err != nil { - glog.Fatalf("Failed to get peerpodVolume crd object by podUid: %v, err: %v", podUID, err) - } - glog.Infof("peerpodVolume crd object number is: %v ", len(peerpodVolumes.Items)) - for idx, savedPeerpodvolume := range peerpodVolumes.Items { - glog.Infof("Index of peerpodVolumes.Items: %v ", idx) - glog.Infof("peerpodVolumes detail: %v ", savedPeerpodvolume) - savedPeerpodvolume.Spec.PodName = podName - savedPeerpodvolume.Spec.PodNamespace = podNamespace - savedPeerpodvolume.Spec.NodeName = podNodeName - savedPeerpodvolume.Labels["podName"] = podName - savedPeerpodvolume.Labels["podNamespace"] = podNamespace - savedPeerpodvolume.Labels["podNodeName"] = podNodeName - updatedPeerpodvolume, err := peerPodVolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(cfg.Namespace).Update(context.Background(), &savedPeerpodvolume, metav1.UpdateOptions{}) - if err != nil { - glog.Fatalf("Error happens while Update podName and podNamespace to PeerpodVolume, err: %v", err.Error()) - } - updatedPeerpodvolume.Status = v1alpha1.PeerpodVolumeStatus{ - State: v1alpha1.PeerPodVSIRunning, - } - _, err = peerPodVolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(cfg.Namespace).UpdateStatus(context.Background(), updatedPeerpodvolume, metav1.UpdateOptions{}) - if err != nil { - glog.Fatalf("Error happens while Update PeerpodVolume status to PeerPodVSIRunning, err: %v", err.Error()) - } - } - if err := wrapper.Run(cfg.Endpoint, identityService, nil, podvmService); err != nil { - glog.Fatalf("Failed to run csi podvm plugin wrapper: %s", err.Error()) - } -} diff --git a/src/csi-wrapper/crd/peerpodvolume-object-test.yaml b/src/csi-wrapper/crd/peerpodvolume-object-test.yaml deleted file mode 100644 index 78238d7c59..0000000000 --- a/src/csi-wrapper/crd/peerpodvolume-object-test.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: "confidentialcontainers.org/v1alpha1" -kind: PeerpodVolume -metadata: - name: test - namespace: kube-system - labels: - podUid: "6c12175c-469c-4ad8-90b3-cd94448ab71d" -spec: - nodeID: nodeID - volumeID: volumeID - vmID: vmID - vmName: vmName - wrapperControllerPublishVolumeReq: "{k1:v1}" - wrapperControllerPublishVolumeRes: "{k2:v2}" - wrapperNodeStageVolumeReq: "{k3:v3}" - wrapperNodePublishVolumeReq: "{k4:v4}" diff --git a/src/csi-wrapper/crd/peerpodvolume.yaml b/src/csi-wrapper/crd/peerpodvolume.yaml deleted file mode 100644 index c80528a60c..0000000000 --- a/src/csi-wrapper/crd/peerpodvolume.yaml +++ /dev/null @@ -1,67 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: peerpodvolumes.confidentialcontainers.org -spec: - group: confidentialcontainers.org - names: - kind: PeerpodVolume - singular: peerpodvolume - plural: peerpodvolumes - scope: Namespaced - versions: - - name: v1alpha1 - served: true - storage: true - schema: - openAPIV3Schema: - type: object - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - type: object - properties: - podName: - type: string - podNamespace: - type: string - podUid: - type: string - nodeID: - type: string - nodeName: - type: string - volumeID: - type: string - volumeName: - type: string - vmID: - type: string - vmName: - type: string - devicePath: - type: string - stagingTargetPath: - type: string - targetPath: - type: string - wrapperControllerPublishVolumeReq: - type: string - wrapperControllerPublishVolumeRes: - type: string - wrapperNodeStageVolumeReq: - type: string - wrapperNodePublishVolumeReq: - type: string - status: - type: object - properties: - state: - type: string - subresources: - status: {} diff --git a/src/csi-wrapper/entrypoint.sh b/src/csi-wrapper/entrypoint.sh deleted file mode 100755 index 5a36b81c1f..0000000000 --- a/src/csi-wrapper/entrypoint.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -/usr/bin/${BINARY} "$@" diff --git a/src/csi-wrapper/examples/aws/README.md b/src/csi-wrapper/examples/aws/README.md deleted file mode 100644 index c8adb70d62..0000000000 --- a/src/csi-wrapper/examples/aws/README.md +++ /dev/null @@ -1,76 +0,0 @@ -# AWS EBS CSI Wrapper for Peer Pod Storage - -## Prerequisites - -* Running Kubernetes cluster (Version >= 1.20) on AWS - -* Peer-Pods is [deployed](../../../cloud-api-adaptor/aws/README.md) - -## AWS EBS CSI Driver Installation - -**NOTE:** the following is just a basic example, follow official [installation instructions](https://github.com/kubernetes-sigs/aws-ebs-csi-driver/blob/master/docs/install.md) for advanced configuration. - -1. Create IAM Policy -``` -aws iam create-policy \ - --policy-name EBS_Policy \ - --policy-document file://example-iam-policy.json -``` -2. Grant the driver IAM permissions: -``` -kubectl create secret generic aws-secret \ - --namespace kube-system \ - --from-literal "key_id=${AWS_ACCESS_KEY_ID}" \ - --from-literal "access_key=${AWS_SECRET_ACCESS_KEY}" -``` -3. Deploy the driver: -``` -kubectl apply -k "github.com/kubernetes-sigs/aws-ebs-csi-driver/deploy/kubernetes/overlays/stable/?ref=release-1.37" -``` -4. Verify the pods are running: -``` -kubectl get pods -n kube-system -l app.kubernetes.io/name=aws-ebs-csi-driver -``` - -## Apply the PeerPods CSI wrapper - -1. Create the PeerpodVolume CRD object -``` -kubectl apply -f ../../crd/peerpodvolume.yaml -``` -2. Apply RBAC roles to permit the wrapper to execute the required operations -``` -kubectl apply -f rbac-ebs-csi-wrapper-runner.yaml -kubectl apply -f rbac-ebs-csi-wrapper-podvm.yaml -``` -3. Patch the EBS CSI Driver: -``` -kubectl patch deploy ebs-csi-controller -n kube-system --patch-file patch-controller.yaml -kubectl -n kube-system delete replicaset -l app=ebs-csi-controller -kubectl patch ds ebs-csi-node -n kube-system --patch-file patch-node.yaml -``` -4. Verify the pods are running (each pod should contain an additional container): -``` -kubectl get pods -n kube-system -l app.kubernetes.io/name=aws-ebs-csi-driver -``` - -## Example Workload With Provisioned Volume - -This is based on the Dynamic Volume Provisioning [example](https://github.com/kubernetes-sigs/aws-ebs-csi-driver/tree/master/examples/kubernetes/dynamic-provisioning) - -1. Deploy example pod on your cluster along with the StorageClass and PersistentVolumeClaim: -``` - kubectl apply -f dynamic-provisioning/ -``` -2. Validate the PersistentVolumeClaim is bound to your PersistentVolume: -``` -kubectl get pvc ebs-claim -``` -3. Once the pod is running you can validate some date (timestamps) has been written to the dynamically provisioned volume: -``` -kubectl exec app -- cat /data/out.txt -``` -4. Cleanup resources: -``` -kubectl delete -f dynamic-provisioning/ -``` diff --git a/src/csi-wrapper/examples/aws/dynamic-provisioning/claim.yaml b/src/csi-wrapper/examples/aws/dynamic-provisioning/claim.yaml deleted file mode 100644 index 5e6eede717..0000000000 --- a/src/csi-wrapper/examples/aws/dynamic-provisioning/claim.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: ebs-claim -spec: - accessModes: - - ReadWriteOnce - storageClassName: ebs-sc - resources: - requests: - storage: 4Gi \ No newline at end of file diff --git a/src/csi-wrapper/examples/aws/dynamic-provisioning/pod.yaml b/src/csi-wrapper/examples/aws/dynamic-provisioning/pod.yaml deleted file mode 100644 index 9c4faa0fc4..0000000000 --- a/src/csi-wrapper/examples/aws/dynamic-provisioning/pod.yaml +++ /dev/null @@ -1,96 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: app -spec: - runtimeClassName: kata-remote - serviceAccountName: csi-ebs-podvm-sa - volumes: - - name: persistent-storage - persistentVolumeClaim: - claimName: ebs-claim - - name: kubelet-data-dir - hostPath: - path: /var/lib/kubelet - type: Directory - - emptyDir: {} - name: plugin-dir - - hostPath: - path: /dev - type: Directory - name: device-dir - - containers: - - name: app - image: quay.io/centos/centos:latest - command: ["/bin/sh"] - args: ["-c", "while true; do echo $(date -u) >> /data/out.txt; sleep 5; done"] - volumeMounts: - - name: persistent-storage - mountPath: /data - mountPropagation: HostToContainer - - - name: csi-podvm-node-driver - env: - - name: KUBE_NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - - name: CSI_NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - # set when IMDS isn't accessible from the podvm - # - name: AWS_REGION - # value: "us-east-1" - - image: public.ecr.aws/ebs-csi-driver/aws-ebs-csi-driver:v1.37.0 - imagePullPolicy: Always - securityContext: - privileged: true - runAsNonRoot: false - runAsUser: 0 - ports: - - containerPort: 9808 - name: healthz - protocol: TCP - volumeMounts: - - name: kubelet-data-dir - mountPath: /var/lib/kubelet - mountPropagation: Bidirectional - - mountPath: /tmp - name: plugin-dir - - mountPath: /dev - name: device-dir - - - name: csi-podvm-wrapper - env: - - name: BINARY - value: "csi-podvm-wrapper" - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAME_SPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: POD_UID - valueFrom: - fieldRef: - fieldPath: metadata.uid - - name: POD_NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - image: quay.io/confidential-containers/csi-podvm-wrapper:latest - imagePullPolicy: Always - command: ["/usr/bin/csi-podvm-wrapper"] # TODO: using default entrypoint seems to fail with peer-pods - args: - - --v=2 - - --endpoint=/tmp/csi-podvm-wrapper.sock - - --target-endpoint=/tmp/csi.sock - - --namespace=kube-system - volumeMounts: - - mountPath: /tmp - name: plugin-dir diff --git a/src/csi-wrapper/examples/aws/dynamic-provisioning/storageclass.yaml b/src/csi-wrapper/examples/aws/dynamic-provisioning/storageclass.yaml deleted file mode 100644 index ceea3a3887..0000000000 --- a/src/csi-wrapper/examples/aws/dynamic-provisioning/storageclass.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: storage.k8s.io/v1 -kind: StorageClass -metadata: - name: ebs-sc -provisioner: ebs.csi.aws.com -volumeBindingMode: WaitForFirstConsumer -parameters: - peerpod: 'true' diff --git a/src/csi-wrapper/examples/aws/example-iam-policy.json b/src/csi-wrapper/examples/aws/example-iam-policy.json deleted file mode 100644 index 57ae140b47..0000000000 --- a/src/csi-wrapper/examples/aws/example-iam-policy.json +++ /dev/null @@ -1,133 +0,0 @@ -{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "ec2:CreateSnapshot", - "ec2:AttachVolume", - "ec2:DetachVolume", - "ec2:ModifyVolume", - "ec2:DescribeAvailabilityZones", - "ec2:DescribeInstances", - "ec2:DescribeSnapshots", - "ec2:DescribeTags", - "ec2:DescribeVolumes", - "ec2:DescribeVolumesModifications" - ], - "Resource": "*" - }, - { - "Effect": "Allow", - "Action": [ - "ec2:CreateTags" - ], - "Resource": [ - "arn:aws:ec2:*:*:volume/*", - "arn:aws:ec2:*:*:snapshot/*" - ], - "Condition": { - "StringEquals": { - "ec2:CreateAction": [ - "CreateVolume", - "CreateSnapshot" - ] - } - } - }, - { - "Effect": "Allow", - "Action": [ - "ec2:DeleteTags" - ], - "Resource": [ - "arn:aws:ec2:*:*:volume/*", - "arn:aws:ec2:*:*:snapshot/*" - ] - }, - { - "Effect": "Allow", - "Action": [ - "ec2:CreateVolume" - ], - "Resource": "*", - "Condition": { - "StringLike": { - "aws:RequestTag/ebs.csi.aws.com/cluster": "true" - } - } - }, - { - "Effect": "Allow", - "Action": [ - "ec2:CreateVolume" - ], - "Resource": "*", - "Condition": { - "StringLike": { - "aws:RequestTag/CSIVolumeName": "*" - } - } - }, - { - "Effect": "Allow", - "Action": [ - "ec2:DeleteVolume" - ], - "Resource": "*", - "Condition": { - "StringLike": { - "ec2:ResourceTag/ebs.csi.aws.com/cluster": "true" - } - } - }, - { - "Effect": "Allow", - "Action": [ - "ec2:DeleteVolume" - ], - "Resource": "*", - "Condition": { - "StringLike": { - "ec2:ResourceTag/CSIVolumeName": "*" - } - } - }, - { - "Effect": "Allow", - "Action": [ - "ec2:DeleteVolume" - ], - "Resource": "*", - "Condition": { - "StringLike": { - "ec2:ResourceTag/kubernetes.io/created-for/pvc/name": "*" - } - } - }, - { - "Effect": "Allow", - "Action": [ - "ec2:DeleteSnapshot" - ], - "Resource": "*", - "Condition": { - "StringLike": { - "ec2:ResourceTag/CSIVolumeSnapshotName": "*" - } - } - }, - { - "Effect": "Allow", - "Action": [ - "ec2:DeleteSnapshot" - ], - "Resource": "*", - "Condition": { - "StringLike": { - "ec2:ResourceTag/ebs.csi.aws.com/cluster": "true" - } - } - } - ] -} diff --git a/src/csi-wrapper/examples/aws/patch-controller.yaml b/src/csi-wrapper/examples/aws/patch-controller.yaml deleted file mode 100644 index 63bfca5f9e..0000000000 --- a/src/csi-wrapper/examples/aws/patch-controller.yaml +++ /dev/null @@ -1,32 +0,0 @@ -spec: - replicas: 1 - template: - spec: - containers: - - name: csi-controller-wrapper - args: - - -v=5 - - --endpoint=/var/lib/csi/sockets/pluginproxy/csi-controller-wrapper.sock - - --target-endpoint=/var/lib/csi/sockets/pluginproxy/csi.sock - - --namespace=kube-system - image: quay.io/confidential-containers/csi-controller-wrapper:latest - imagePullPolicy: IfNotPresent - volumeMounts: - - mountPath: /var/lib/csi/sockets/pluginproxy/ - name: socket-dir - - - name: csi-attacher - args: - - -v=2 - - --csi-address=/var/lib/csi/sockets/pluginproxy/csi-controller-wrapper.sock - - --timeout=900s - - - name: csi-provisioner - env: - - name: ADDRESS - value: /var/lib/csi/sockets/pluginproxy/csi-controller-wrapper.sock - - - name: csi-resizer - env: - - name: ADDRESS - value: /var/lib/csi/sockets/pluginproxy/csi-controller-wrapper.sock diff --git a/src/csi-wrapper/examples/aws/patch-node.yaml b/src/csi-wrapper/examples/aws/patch-node.yaml deleted file mode 100644 index 3df829cf8b..0000000000 --- a/src/csi-wrapper/examples/aws/patch-node.yaml +++ /dev/null @@ -1,41 +0,0 @@ -spec: - template: - spec: - containers: - - name: csi-node-wrapper - args: - - --v=5 - - --endpoint=/csi/csi-node-wrapper.sock - - --target-endpoint=/csi/csi.sock - - --namespace=kube-system - env: - - name: POD_NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - image: quay.io/confidential-containers/csi-node-wrapper:latest - imagePullPolicy: IfNotPresent - volumeMounts: - - mountPath: /csi - name: plugin-dir - - mountPath: /run/peerpod/ - name: podvminfo-dir - - mountPath: /run/kata-containers/shared/direct-volumes - name: kata-direct-volumes-dir - - - name: node-driver-registrar - env: - - name: ADDRESS - value: /csi/csi-node-wrapper.sock - - name: DRIVER_REG_SOCK_PATH - value: /var/lib/kubelet/plugins/ebs.csi.aws.com/csi-node-wrapper.sock - - volumes: - - name: podvminfo-dir - hostPath: - path: /run/peerpod/ - type: Directory - - name: kata-direct-volumes-dir - hostPath: - path: /run/kata-containers/shared/direct-volumes - type: DirectoryOrCreate diff --git a/src/csi-wrapper/examples/aws/rbac-ebs-csi-wrapper-podvm.yaml b/src/csi-wrapper/examples/aws/rbac-ebs-csi-wrapper-podvm.yaml deleted file mode 100644 index 92af8a57ee..0000000000 --- a/src/csi-wrapper/examples/aws/rbac-ebs-csi-wrapper-podvm.yaml +++ /dev/null @@ -1,59 +0,0 @@ -apiVersion: v1 -kind: ServiceAccount -metadata: - name: csi-ebs-podvm-sa ---- -kind: ClusterRole -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: ebs-csi-wrapper-podvm -rules: - - apiGroups: ["confidentialcontainers.org"] - resources: ["peerpodvolumes"] - verbs: ["get", "list", "watch", "create", "delete", "update", "patch"] - - apiGroups: ["confidentialcontainers.org"] - resources: ["peerpodvolumes/status"] - verbs: ["update"] - # required by the ebs-csi-driver - https://github.com/kubernetes-sigs/aws-ebs-csi-driver/blob/master/deploy/kubernetes/base/clusterrole-csi-node.yaml - - apiGroups: [""] - resources: ["nodes"] - verbs: ["get", "watch"] - - apiGroups: ["storage.k8s.io"] - resources: ["csinodes"] - verbs: ["get", "list", "watch"] ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: ebs-csi-wrapper-podvm-binding -subjects: - - kind: ServiceAccount - name: csi-ebs-podvm-sa - namespace: default -roleRef: - kind: ClusterRole - name: ebs-csi-wrapper-podvm - apiGroup: rbac.authorization.k8s.io ---- -kind: Role -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: ebs-csi-wrapper-podvm - namespace: default -rules: - - apiGroups: [""] - resources: ["secrets"] - verbs: ["get", "list", "watch"] ---- -kind: RoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: ebs-csi-wrapper-podvm-binding -subjects: - - kind: ServiceAccount - name: csi-ebs-podvm-sa - namespace: default -roleRef: - kind: Role - name: ebs-csi-wrapper-podvm - apiGroup: rbac.authorization.k8s.io diff --git a/src/csi-wrapper/examples/aws/rbac-ebs-csi-wrapper-runner.yaml b/src/csi-wrapper/examples/aws/rbac-ebs-csi-wrapper-runner.yaml deleted file mode 100644 index b033a75fde..0000000000 --- a/src/csi-wrapper/examples/aws/rbac-ebs-csi-wrapper-runner.yaml +++ /dev/null @@ -1,76 +0,0 @@ ---- -kind: ClusterRole -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: ebs-csi-wrapper-runner -rules: - - apiGroups: [""] - resources: ["secrets"] - verbs: ["get", "list", "watch"] - - apiGroups: [""] - resources: ["persistentvolumes"] - verbs: ["get", "list", "watch", "create", "delete"] - - apiGroups: [""] - resources: ["persistentvolumeclaims"] - verbs: ["get", "list", "watch", "update"] - - apiGroups: ["storage.k8s.io"] - resources: ["storageclasses"] - verbs: ["get", "list", "watch"] - - apiGroups: [""] - resources: ["events"] - verbs: ["list", "watch", "create", "update", "patch"] - - apiGroups: ["snapshot.storage.k8s.io"] - resources: ["volumesnapshots"] - verbs: ["get", "list"] - - apiGroups: ["snapshot.storage.k8s.io"] - resources: ["volumesnapshotcontents"] - verbs: ["get", "list"] - - apiGroups: ["storage.k8s.io"] - resources: ["csinodes"] - verbs: ["get", "list", "watch"] - - apiGroups: [""] - resources: ["nodes"] - verbs: ["get", "list", "watch"] - - apiGroups: ["storage.k8s.io"] - resources: ["volumeattachments"] - verbs: ["get", "list", "watch"] - - apiGroups: [""] - resources: ["configmaps"] - verbs: ["get", "list"] - - apiGroups: ["confidentialcontainers.org"] - resources: ["peerpodvolumes"] - verbs: ["get", "list", "watch", "create", "delete", "update", "patch"] - - apiGroups: ["confidentialcontainers.org"] - resources: ["peerpodvolumes/status"] - verbs: ["update"] - - apiGroups: [""] - resources: ["pods"] - verbs: ["get", "list"] - ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: ebs-csi-wrapper-controller-binding -subjects: - - kind: ServiceAccount - name: ebs-csi-controller-sa - namespace: kube-system -roleRef: - kind: ClusterRole - name: ebs-csi-wrapper-runner - apiGroup: rbac.authorization.k8s.io - ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: ebs-csi-wrapper-node-binding -subjects: - - kind: ServiceAccount - name: ebs-csi-node-sa - namespace: kube-system -roleRef: - kind: ClusterRole - name: ebs-csi-wrapper-runner - apiGroup: rbac.authorization.k8s.io diff --git a/src/csi-wrapper/examples/azure/README.md b/src/csi-wrapper/examples/azure/README.md deleted file mode 100644 index b42898586f..0000000000 --- a/src/csi-wrapper/examples/azure/README.md +++ /dev/null @@ -1,311 +0,0 @@ -# Azure CSI Wrapper for Peer Pod Storage - -## Set up a demo environment on your development machine - -1. Follow the [README.md](../../../cloud-api-adaptor/azure/README.md) to setup a x86_64 based demo environment on AKS. - -2. To prevent our changes to be rolled back, disable the built-in AKS azurefile and azuredisk drivers: - - ```bash - az aks update -g ${AZURE_RESOURCE_GROUP} --name ${CLUSTER_NAME} --disable-file-driver --disable-disk-driver - ``` - -3. Create the PeerpodVolume CRD object - - ```bash - kubectl apply -f src/csi-wrapper/crd/peerpodvolume.yaml - ``` - - The output looks like: - - ```bash - customresourcedefinition.apiextensions.k8s.io/peerpodvolumes.confidentialcontainers.org created - ``` - -## Build custom csi-wrapper images (for development) - -Follow this if you have made changes to the CSI wrapper code and want to deploy those changes. - -1. Build csi-wrapper images: - - ```bash - pushd src/csi-wrapper/ - make csi-controller-wrapper-docker - make csi-node-wrapper-docker - make csi-podvm-wrapper-docker - popd - ``` - -2. Export custom registry - - ```bash - export REGISTRY="my-registry" # e.g. "quay.io/my-registry" - ``` - -3. Tag and push images - - ```bash - docker tag csi-controller-wrapper:local ${REGISTRY}/csi-controller-wrapper:latest - docker tag csi-node-wrapper:local ${REGISTRY}/csi-node-wrapper:latest - docker tag csi-podvm-wrapper:local ${REGISTRY}/csi-podvm-wrapper:latest - - docker push ${REGISTRY}/csi-controller-wrapper:latest - docker push ${REGISTRY}/csi-node-wrapper:latest - docker push ${REGISTRY}/csi-podvm-wrapper:latest - ``` - -4. Change image in CSI wrapper k8s resources - - ```bash - sed -i "s#quay.io/confidential-containers#${REGISTRY}#g" src/csi-wrapper/examples/azure/disk/*.yaml - sed -i "s#quay.io/confidential-containers#${REGISTRY}#g" src/csi-wrapper/examples/azure/file/*.yaml - ``` - -## Peer Pod example using CSI Wrapper with azurefile-csi-driver - -Prerequisite: Assign the `Storage Account Contributor` role to the AKS agent pool application so it can create storage accounts: - -```bash -OBJECT_ID="$(az ad sp list --display-name "${CLUSTER_NAME}-agentpool" --query '[].id' --output tsv)" -az role assignment create \ - --role "Storage Account Contributor" \ - --assignee-object-id ${OBJECT_ID} \ - --assignee-principal-type ServicePrincipal \ - --scope "/subscriptions/${AZURE_SUBSCRIPTION_ID}/resourceGroups/${AZURE_RESOURCE_GROUP}-aks" -``` - -### Deploy azurefile-csi-driver on the cluster - -Note: All the steps can be performed anywhere with cluster access - -1. Clone the azurefile-csi-driver source: - - ```bash - git clone --depth 1 --branch v1.28.0 https://github.com/kubernetes-sigs/azurefile-csi-driver - pushd azurefile-csi-driver - ``` - -2. Enable `attachRequired` in the CSI Driver: - - ```bash - sed -i 's/attachRequired: false/attachRequired: true/g' deploy/csi-azurefile-driver.yaml - ``` - -3. Run the script: - - ```bash - bash ./deploy/install-driver.sh master local - popd - ``` - -### Deploy csi-wrapper to patch azurefile-csi-driver - -1. Configure RBAC so that the wrapper has access to the required operations - - ```bash - kubectl apply -f src/csi-wrapper/examples/azure/file/azure-files-csi-wrapper-runner.yaml - kubectl apply -f src/csi-wrapper/examples/azure/file/azure-files-csi-wrapper-podvm.yaml - ``` - -2. Patch csi-azurefile-driver: - - ```bash - kubectl patch deploy csi-azurefile-controller -n kube-system --patch-file src/csi-wrapper/examples/azure/file/patch-controller.yaml - kubectl -n kube-system delete replicaset -l app=csi-azurefile-controller - kubectl patch ds csi-azurefile-node -n kube-system --patch-file src/csi-wrapper/examples/azure/file/patch-node.yaml - ``` - -3. Create a peerpod enabled StorageClass: - - ```bash - kubectl apply -f src/csi-wrapper/examples/azure/file/azure-file-StorageClass-for-peerpod.yaml - ``` - -### Create a Pod with a PVC using the azurefile-csi-driver - -1. Create a PVC that use `azurefile-csi-driver` - - ```bash - kubectl apply -f src/csi-wrapper/examples/azure/file/my-pvc.yaml - ``` - -2. Wait for the PVC status to become `bound` - - ```bash - $ kubectl get pvc - NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE - pvc-azurefile Bound pvc-3edc7a93-4531-4034-8818-1b1608907494 1Gi RWO azure-file-storage 3m11s - ``` - -3. Create the nginx peer-pod demo with with `podvm-wrapper` and `azurefile-csi-driver` containers - - ```bash - kubectl apply -f src/csi-wrapper/examples/azure/file/nginx-kata-with-my-pvc-and-csi-wrapper.yaml - ``` - -4. Exec into the container and check the mount - - ```bash - kubectl exec nginx-pv -c nginx -i -t -- sh - # mount | grep mount-path - //fffffffffffffffffffffff.file.core.windows.net/pvc-ff587660-73ed-4bd0-8850-285be480f490 on /mount-path type cifs (rw,relatime,vers=3.1.1,cache=strict,username=fffffffffffffffffffffff,uid=0,noforceuid,gid=0,noforcegid,addr=x.x.x.x,file_mode=0777,dir_mode=0777,soft,persistenthandles,nounix,serverino,mapposix,mfsymlinks,rsize=1048576,wsize=1048576,bsize=1048576,echo_interval=60,actimeo=30,closetimeo=1) - ``` - - **Note:** We can see there's a CIFS mount to `/mount-path` as expected - -## Peer Pod example using CSI Wrapper with azuredisk-csi-driver - -Prerequisite: The service principal of the cluster requires the `Contributor` role for the CAA resource group so it can manage disks. -Additionally, the CSI driver needs to be configured to create disks in the CAA instead of the AKS resource group. - -1. Start by assigning the `Contributor` role to the AKS agent pool - - ```bash - OBJECT_ID="$(az ad sp list --display-name "${CLUSTER_NAME}-agentpool" --query '[].id' --output tsv)" - az role assignment create \ - --role "Contributor" \ - --assignee-object-id ${OBJECT_ID} \ - --assignee-principal-type ServicePrincipal \ - --scope "/subscriptions/${AZURE_SUBSCRIPTION_ID}/resourceGroups/${AZURE_RESOURCE_GROUP}" - ``` - -2. Get the user assigned identity ID of the identity assigned to the cluster - - ```bash - USER_ASSIGNED_CLIENT_ID=$(az aks show \ - --resource-group $AZURE_RESOURCE_GROUP \ - --name $CLUSTER_NAME \ - --query identityProfile.kubeletidentity.clientId \ - -o tsv) - ``` - -3. Update `cloud-config.yaml` with the cloud config for the user assigned identity: - - ```bash - cloud_config=$(cat < 36s - ``` - -### Create a Pod with a PVC using the azuredisk-csi-driver - -1. Create the nginx peer-pod demo with with `podvm-wrapper` and `azuredisk-csi-driver` containers - - ```bash - kubectl apply -f src/csi-wrapper/examples/azure/disk/nginx-kata-with-my-pvc-and-csi-wrapper.yaml - ``` - -2. Exec into the container and check the mount - - ```bash - kubectl exec nginx-pv-disk -c nginx -i -t -- sh - # mount | grep mount-path - /dev/sdb on /mount-path type ext4 (rw,relatime) - ``` - - **Note:** We can see there's a ext4 mount to `/mount-path` as expected diff --git a/src/csi-wrapper/examples/azure/disk/azure-disk-csi-wrapper-podvm.yaml b/src/csi-wrapper/examples/azure/disk/azure-disk-csi-wrapper-podvm.yaml deleted file mode 100644 index df3c8b7a3f..0000000000 --- a/src/csi-wrapper/examples/azure/disk/azure-disk-csi-wrapper-podvm.yaml +++ /dev/null @@ -1,52 +0,0 @@ -apiVersion: v1 -kind: ServiceAccount -metadata: - name: csi-azuredisk-podvm-sa ---- -kind: ClusterRole -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: azure-disk-csi-wrapper-podvm -rules: - - apiGroups: ['confidentialcontainers.org'] - resources: ['peerpodvolumes'] - verbs: ['get', 'list', 'watch', 'create', 'delete', 'update', 'patch'] - - apiGroups: ['confidentialcontainers.org'] - resources: ['peerpodvolumes/status'] - verbs: ['update'] ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: azure-disk-csi-wrapper-podvm-binding -subjects: - - kind: ServiceAccount - name: csi-azuredisk-podvm-sa - namespace: default -roleRef: - kind: ClusterRole - name: azure-disk-csi-wrapper-podvm - apiGroup: rbac.authorization.k8s.io ---- -kind: Role -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: azure-disk-csi-wrapper-podvm - namespace: default -rules: - - apiGroups: [''] - resources: ['secrets'] - verbs: ['get', 'list', 'watch'] ---- -kind: RoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: azure-disk-csi-wrapper-podvm-binding -subjects: - - kind: ServiceAccount - name: csi-azuredisk-podvm-sa - namespace: default -roleRef: - kind: Role - name: azure-disk-csi-wrapper-podvm - apiGroup: rbac.authorization.k8s.io diff --git a/src/csi-wrapper/examples/azure/disk/azure-disk-csi-wrapper-runner.yaml b/src/csi-wrapper/examples/azure/disk/azure-disk-csi-wrapper-runner.yaml deleted file mode 100644 index b521c64a0a..0000000000 --- a/src/csi-wrapper/examples/azure/disk/azure-disk-csi-wrapper-runner.yaml +++ /dev/null @@ -1,76 +0,0 @@ ---- -kind: ClusterRole -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: azure-disk-csi-wrapper-runner -rules: - - apiGroups: [''] - resources: ['secrets'] - verbs: ['get', 'list', 'watch'] - - apiGroups: [''] - resources: ['persistentvolumes'] - verbs: ['get', 'list', 'watch', 'create', 'delete'] - - apiGroups: [''] - resources: ['persistentvolumeclaims'] - verbs: ['get', 'list', 'watch', 'update'] - - apiGroups: ['storage.k8s.io'] - resources: ['storageclasses'] - verbs: ['get', 'list', 'watch'] - - apiGroups: [''] - resources: ['events'] - verbs: ['list', 'watch', 'create', 'update', 'patch'] - - apiGroups: ['snapshot.storage.k8s.io'] - resources: ['volumesnapshots'] - verbs: ['get', 'list'] - - apiGroups: ['snapshot.storage.k8s.io'] - resources: ['volumesnapshotcontents'] - verbs: ['get', 'list'] - - apiGroups: ['storage.k8s.io'] - resources: ['csinodes'] - verbs: ['get', 'list', 'watch'] - - apiGroups: [''] - resources: ['nodes'] - verbs: ['get', 'list', 'watch'] - - apiGroups: ['storage.k8s.io'] - resources: ['volumeattachments'] - verbs: ['get', 'list', 'watch'] - - apiGroups: [''] - resources: ['configmaps'] - verbs: ['get', 'list'] - - apiGroups: ['confidentialcontainers.org'] - resources: ['peerpodvolumes'] - verbs: ['get', 'list', 'watch', 'create', 'delete', 'update', 'patch'] - - apiGroups: ['confidentialcontainers.org'] - resources: ['peerpodvolumes/status'] - verbs: ['update'] - - apiGroups: [''] - resources: ['pods'] - verbs: ['get', 'list'] - ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: azure-disk-csi-wrapper-controller-binding -subjects: - - kind: ServiceAccount - name: csi-azuredisk-controller-sa - namespace: kube-system -roleRef: - kind: ClusterRole - name: azure-disk-csi-wrapper-runner - apiGroup: rbac.authorization.k8s.io - ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: azure-disk-csi-wrapper-node-binding -subjects: - - kind: ServiceAccount - name: csi-azuredisk-node-sa - namespace: kube-system -roleRef: - kind: ClusterRole - name: azure-disk-csi-wrapper-runner - apiGroup: rbac.authorization.k8s.io diff --git a/src/csi-wrapper/examples/azure/disk/azure-disk-storageclass-for-peerpod.yaml b/src/csi-wrapper/examples/azure/disk/azure-disk-storageclass-for-peerpod.yaml deleted file mode 100644 index e5f56a857b..0000000000 --- a/src/csi-wrapper/examples/azure/disk/azure-disk-storageclass-for-peerpod.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: storage.k8s.io/v1 -kind: StorageClass -metadata: - name: azure-disk-storage -provisioner: disk.csi.azure.com -parameters: - skuName: StandardSSD_LRS # available values: StandardSSD_LRS, StandardSSD_ZRS, Premium_LRS, Premium_ZRS, etc. - peerpod: 'true' -reclaimPolicy: Delete -allowVolumeExpansion: true -volumeBindingMode: Immediate diff --git a/src/csi-wrapper/examples/azure/disk/cloud-config.yaml b/src/csi-wrapper/examples/azure/disk/cloud-config.yaml deleted file mode 100644 index e9eb02635b..0000000000 --- a/src/csi-wrapper/examples/azure/disk/cloud-config.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: cloud-config - namespace: kube-system -type: Opaque -data: - cloud-config: "@@CLOUD_CONFIG_BASE64@@" diff --git a/src/csi-wrapper/examples/azure/disk/dynamic-pvc.yaml b/src/csi-wrapper/examples/azure/disk/dynamic-pvc.yaml deleted file mode 100644 index f43db147cc..0000000000 --- a/src/csi-wrapper/examples/azure/disk/dynamic-pvc.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: pvc-azuredisk -spec: - storageClassName: azure-disk-storage - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 10Gi diff --git a/src/csi-wrapper/examples/azure/disk/nginx-kata-with-my-pvc-and-csi-wrapper.yaml b/src/csi-wrapper/examples/azure/disk/nginx-kata-with-my-pvc-and-csi-wrapper.yaml deleted file mode 100755 index daee948d3d..0000000000 --- a/src/csi-wrapper/examples/azure/disk/nginx-kata-with-my-pvc-and-csi-wrapper.yaml +++ /dev/null @@ -1,84 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: nginx-pv-disk - labels: - app: nginx - namespace: default -spec: - runtimeClassName: kata-remote - containers: - - name: azure-disk-podvm-node-driver - env: - - name: KUBE_NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - command: ['/bin/sh'] - args: - - -c - - | - mount -t devtmpfs none /dev && \ - exec /azurediskplugin --v=4 --disable-update-cache - image: mcr.microsoft.com/oss/kubernetes-csi/azuredisk-csi:v1.31.0 - imagePullPolicy: Always - securityContext: - privileged: true - runAsNonRoot: false - runAsUser: 0 - ports: - - containerPort: 9808 - name: healthz - protocol: TCP - volumeMounts: - - name: kubelet-data-dir - mountPath: /var/lib/kubelet - mountPropagation: Bidirectional - - mountPath: /tmp - name: plugin-dir - - name: csi-podvm-wrapper - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAME_SPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: POD_UID - valueFrom: - fieldRef: - fieldPath: metadata.uid - - name: POD_NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - args: - - -v=2 - - --endpoint=/tmp/csi-podvm-wrapper.sock - - --target-endpoint=/tmp/csi.sock - - --namespace=kube-system - image: quay.io/confidential-containers/csi-podvm-wrapper:latest - imagePullPolicy: Always - volumeMounts: - - mountPath: /tmp - name: plugin-dir - - name: nginx - image: nginx:stable - imagePullPolicy: Always - volumeMounts: - - name: my-volume - mountPath: /mount-path - mountPropagation: HostToContainer - serviceAccountName: csi-azuredisk-podvm-sa - volumes: - - name: kubelet-data-dir - hostPath: - path: /var/lib/kubelet - type: Directory - - emptyDir: {} - name: plugin-dir - - name: my-volume - persistentVolumeClaim: - claimName: pvc-azuredisk diff --git a/src/csi-wrapper/examples/azure/disk/patch-controller.yaml b/src/csi-wrapper/examples/azure/disk/patch-controller.yaml deleted file mode 100755 index 890387ec93..0000000000 --- a/src/csi-wrapper/examples/azure/disk/patch-controller.yaml +++ /dev/null @@ -1,48 +0,0 @@ -spec: - replicas: 1 - template: - spec: - containers: - - name: csi-controller-wrapper - env: - - name: POD_NAME_SPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - args: - - -v=2 - - --endpoint=/csi/csi-controller-wrapper.sock - - --target-endpoint=/csi/csi.sock - - --namespace=$(POD_NAME_SPACE) - image: quay.io/confidential-containers/csi-controller-wrapper:latest - imagePullPolicy: IfNotPresent - volumeMounts: - - mountPath: /csi - name: socket-dir - - - name: azuredisk - args: - - "--v=5" - - "--endpoint=$(CSI_ENDPOINT)" - - "--metrics-address=0.0.0.0:29604" - - "--user-agent-suffix=OSS-kubectl" - - "--disable-avset-nodes=false" - - "--allow-empty-cloud-config=false" - - "--cloud-config-secret-name=cloud-config" - - "--cloud-config-secret-namespace=kube-system" - - - name: csi-attacher - args: - - -v=2 - - --csi-address=/csi/csi-controller-wrapper.sock - - --timeout=900s - - - name: csi-provisioner - env: - - name: ADDRESS - value: /csi/csi-controller-wrapper.sock - - - name: csi-resizer - env: - - name: ADDRESS - value: /csi/csi-controller-wrapper.sock diff --git a/src/csi-wrapper/examples/azure/disk/patch-node.yaml b/src/csi-wrapper/examples/azure/disk/patch-node.yaml deleted file mode 100755 index 8f4978a6e6..0000000000 --- a/src/csi-wrapper/examples/azure/disk/patch-node.yaml +++ /dev/null @@ -1,51 +0,0 @@ -spec: - template: - spec: - containers: - - name: csi-node-wrapper - args: - - --v=2 - - --endpoint=/csi/csi-node-wrapper.sock - - --target-endpoint=/csi/csi.sock - - --namespace=$(POD_NAME_SPACE) - env: - - name: POD_NAME_SPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: POD_NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - image: quay.io/confidential-containers/csi-node-wrapper:latest - imagePullPolicy: IfNotPresent - volumeMounts: - - mountPath: /csi - name: socket-dir - - mountPath: /run/peerpod/ - name: podvminfo-dir - - mountPath: /run/kata-containers/shared/direct-volumes - name: kata-direct-volumes-dir - - mountPath: /var/lib/kubelet - name: kubelet-data-dir - - - name: node-driver-registrar - env: - - name: ADDRESS - value: /csi/csi-node-wrapper.sock - - name: DRIVER_REG_SOCK_PATH - value: /var/lib/kubelet/plugins/disk.csi.azure.com/csi-node-wrapper.sock - - volumes: - - name: podvminfo-dir - hostPath: - path: /run/peerpod/ - type: Directory - - name: kata-direct-volumes-dir - hostPath: - path: /run/kata-containers/shared/direct-volumes - type: DirectoryOrCreate - - name: kubelet-data-dir - hostPath: - path: /var/lib/kubelet - type: Directory diff --git a/src/csi-wrapper/examples/azure/disk/static-pvc.yaml b/src/csi-wrapper/examples/azure/disk/static-pvc.yaml deleted file mode 100644 index 8570393876..0000000000 --- a/src/csi-wrapper/examples/azure/disk/static-pvc.yaml +++ /dev/null @@ -1,31 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: pvc-azuredisk -spec: - storageClassName: azure-disk-storage - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 10Gi - volumeName: pv-azuredisk ---- -apiVersion: v1 -kind: PersistentVolume -metadata: - name: pv-azuredisk -spec: - capacity: - storage: 10Gi - accessModes: - - ReadWriteOnce - persistentVolumeReclaimPolicy: Retain - storageClassName: azure-disk-storage - csi: - driver: disk.csi.azure.com - volumeHandle: '@@AZURE_DISK_ID@@' - volumeAttributes: - fsType: ext4 - peerpod: 'true' # Indicator for csi-wrapper that this is a volume should be used for a peerpod volume ---- diff --git a/src/csi-wrapper/examples/azure/file/azure-file-StorageClass-for-peerpod.yaml b/src/csi-wrapper/examples/azure/file/azure-file-StorageClass-for-peerpod.yaml deleted file mode 100755 index 88cfde991c..0000000000 --- a/src/csi-wrapper/examples/azure/file/azure-file-StorageClass-for-peerpod.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: storage.k8s.io/v1 -kind: StorageClass -metadata: - name: azure-file-storage -provisioner: file.csi.azure.com -parameters: - skuName: Standard_LRS - peerpod: "true" -reclaimPolicy: Delete -volumeBindingMode: Immediate -mountOptions: - - dir_mode=0777 - - file_mode=0777 - - mfsymlinks - - cache=strict # https://linux.die.net/man/8/mount.cifs - - nosharesock # reduce probability of reconnect race - - actimeo=30 diff --git a/src/csi-wrapper/examples/azure/file/azure-files-csi-wrapper-podvm.yaml b/src/csi-wrapper/examples/azure/file/azure-files-csi-wrapper-podvm.yaml deleted file mode 100644 index a3f48e1a2b..0000000000 --- a/src/csi-wrapper/examples/azure/file/azure-files-csi-wrapper-podvm.yaml +++ /dev/null @@ -1,52 +0,0 @@ -apiVersion: v1 -kind: ServiceAccount -metadata: - name: csi-azurefile-podvm-sa ---- -kind: ClusterRole -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: azure-files-csi-wrapper-podvm -rules: - - apiGroups: ["confidentialcontainers.org"] - resources: ["peerpodvolumes"] - verbs: ["get", "list", "watch", "create", "delete", "update", "patch"] - - apiGroups: ["confidentialcontainers.org"] - resources: ["peerpodvolumes/status"] - verbs: ["update"] ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: azure-files-csi-wrapper-podvm-binding -subjects: - - kind: ServiceAccount - name: csi-azurefile-podvm-sa - namespace: default -roleRef: - kind: ClusterRole - name: azure-files-csi-wrapper-podvm - apiGroup: rbac.authorization.k8s.io ---- -kind: Role -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: azure-files-csi-wrapper-podvm - namespace: default -rules: - - apiGroups: [""] - resources: ["secrets"] - verbs: ["get", "list", "watch"] ---- -kind: RoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: azure-files-csi-wrapper-podvm-binding -subjects: - - kind: ServiceAccount - name: csi-azurefile-podvm-sa - namespace: default -roleRef: - kind: Role - name: azure-files-csi-wrapper-podvm - apiGroup: rbac.authorization.k8s.io diff --git a/src/csi-wrapper/examples/azure/file/azure-files-csi-wrapper-runner.yaml b/src/csi-wrapper/examples/azure/file/azure-files-csi-wrapper-runner.yaml deleted file mode 100644 index 5f437a0630..0000000000 --- a/src/csi-wrapper/examples/azure/file/azure-files-csi-wrapper-runner.yaml +++ /dev/null @@ -1,76 +0,0 @@ ---- -kind: ClusterRole -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: azure-files-csi-wrapper-runner -rules: - - apiGroups: [""] - resources: ["secrets"] - verbs: ["get", "list", "watch"] - - apiGroups: [""] - resources: ["persistentvolumes"] - verbs: ["get", "list", "watch", "create", "delete"] - - apiGroups: [""] - resources: ["persistentvolumeclaims"] - verbs: ["get", "list", "watch", "update"] - - apiGroups: ["storage.k8s.io"] - resources: ["storageclasses"] - verbs: ["get", "list", "watch"] - - apiGroups: [""] - resources: ["events"] - verbs: ["list", "watch", "create", "update", "patch"] - - apiGroups: ["snapshot.storage.k8s.io"] - resources: ["volumesnapshots"] - verbs: ["get", "list"] - - apiGroups: ["snapshot.storage.k8s.io"] - resources: ["volumesnapshotcontents"] - verbs: ["get", "list"] - - apiGroups: ["storage.k8s.io"] - resources: ["csinodes"] - verbs: ["get", "list", "watch"] - - apiGroups: [""] - resources: ["nodes"] - verbs: ["get", "list", "watch"] - - apiGroups: ["storage.k8s.io"] - resources: ["volumeattachments"] - verbs: ["get", "list", "watch"] - - apiGroups: [""] - resources: ["configmaps"] - verbs: ["get", "list"] - - apiGroups: ["confidentialcontainers.org"] - resources: ["peerpodvolumes"] - verbs: ["get", "list", "watch", "create", "delete", "update", "patch"] - - apiGroups: ["confidentialcontainers.org"] - resources: ["peerpodvolumes/status"] - verbs: ["update"] - - apiGroups: [""] - resources: ["pods"] - verbs: ["get", "list"] - ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: azure-files-csi-wrapper-controller-binding -subjects: - - kind: ServiceAccount - name: csi-azurefile-controller-sa - namespace: kube-system -roleRef: - kind: ClusterRole - name: azure-files-csi-wrapper-runner - apiGroup: rbac.authorization.k8s.io - ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: azure-files-csi-wrapper-node-binding -subjects: - - kind: ServiceAccount - name: csi-azurefile-node-sa - namespace: kube-system -roleRef: - kind: ClusterRole - name: azure-files-csi-wrapper-runner - apiGroup: rbac.authorization.k8s.io diff --git a/src/csi-wrapper/examples/azure/file/my-pvc.yaml b/src/csi-wrapper/examples/azure/file/my-pvc.yaml deleted file mode 100755 index 8228f7e96f..0000000000 --- a/src/csi-wrapper/examples/azure/file/my-pvc.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: pvc-azurefile -spec: - storageClassName: azure-file-storage - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 1Gi diff --git a/src/csi-wrapper/examples/azure/file/nginx-kata-with-my-pvc-and-csi-wrapper.yaml b/src/csi-wrapper/examples/azure/file/nginx-kata-with-my-pvc-and-csi-wrapper.yaml deleted file mode 100755 index 3fce870805..0000000000 --- a/src/csi-wrapper/examples/azure/file/nginx-kata-with-my-pvc-and-csi-wrapper.yaml +++ /dev/null @@ -1,78 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: nginx-pv - labels: - app: nginx - namespace: default -spec: - runtimeClassName: kata-remote - containers: - - name: azure-file-podvm-node-driver - env: - - name: KUBE_NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - image: mcr.microsoft.com/oss/kubernetes-csi/azurefile-csi:v1.28.0 - imagePullPolicy: Always - securityContext: - privileged: true - runAsNonRoot: false - runAsUser: 0 - ports: - - containerPort: 9808 - name: healthz - protocol: TCP - volumeMounts: - - name: kubelet-data-dir - mountPath: /var/lib/kubelet - mountPropagation: Bidirectional - - mountPath: /tmp - name: plugin-dir - - name: csi-podvm-wrapper - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAME_SPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: POD_UID - valueFrom: - fieldRef: - fieldPath: metadata.uid - - name: POD_NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - args: - - -v=2 - - --endpoint=/tmp/csi-podvm-wrapper.sock - - --target-endpoint=/tmp/csi.sock - - --namespace=kube-system - image: quay.io/confidential-containers/csi-podvm-wrapper:latest - imagePullPolicy: Always - volumeMounts: - - mountPath: /tmp - name: plugin-dir - - name: nginx - image: nginx:stable - imagePullPolicy: Always - volumeMounts: - - name: my-volume - mountPath: /mount-path - mountPropagation: HostToContainer - serviceAccountName: csi-azurefile-podvm-sa - volumes: - - name: kubelet-data-dir - hostPath: - path: /var/lib/kubelet # OpenShift worker, for kubernetes /var/data/kubelet - type: Directory - - emptyDir: {} - name: plugin-dir - - name: my-volume - persistentVolumeClaim: - claimName: pvc-azurefile diff --git a/src/csi-wrapper/examples/azure/file/patch-controller.yaml b/src/csi-wrapper/examples/azure/file/patch-controller.yaml deleted file mode 100755 index b3644fafd6..0000000000 --- a/src/csi-wrapper/examples/azure/file/patch-controller.yaml +++ /dev/null @@ -1,32 +0,0 @@ -spec: - replicas: 1 - template: - spec: - containers: - - name: csi-controller-wrapper - args: - - -v=2 - - --endpoint=/csi/csi-controller-wrapper.sock - - --target-endpoint=/csi/csi.sock - - --namespace=kube-system - image: quay.io/confidential-containers/csi-controller-wrapper:latest - imagePullPolicy: IfNotPresent - volumeMounts: - - mountPath: /csi - name: socket-dir - - - name: csi-attacher - args: - - -v=2 - - --csi-address=/csi/csi-controller-wrapper.sock - - --timeout=900s - - - name: csi-provisioner - env: - - name: ADDRESS - value: /csi/csi-controller-wrapper.sock - - - name: csi-resizer - env: - - name: ADDRESS - value: /csi/csi-controller-wrapper.sock diff --git a/src/csi-wrapper/examples/azure/file/patch-node.yaml b/src/csi-wrapper/examples/azure/file/patch-node.yaml deleted file mode 100755 index e9591895d4..0000000000 --- a/src/csi-wrapper/examples/azure/file/patch-node.yaml +++ /dev/null @@ -1,41 +0,0 @@ -spec: - template: - spec: - containers: - - name: csi-node-wrapper - args: - - --v=2 - - --endpoint=/csi/csi-node-wrapper.sock - - --target-endpoint=/csi/csi.sock - - --namespace=kube-system - env: - - name: POD_NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - image: quay.io/confidential-containers/csi-node-wrapper:latest - imagePullPolicy: IfNotPresent - volumeMounts: - - mountPath: /csi - name: socket-dir - - mountPath: /run/peerpod/ - name: podvminfo-dir - - mountPath: /run/kata-containers/shared/direct-volumes - name: kata-direct-volumes-dir - - - name: node-driver-registrar - env: - - name: ADDRESS - value: /csi/csi-node-wrapper.sock - - name: DRIVER_REG_SOCK_PATH - value: /var/lib/kubelet/plugins/file.csi.azure.com/csi-node-wrapper.sock - - volumes: - - name: podvminfo-dir - hostPath: - path: /run/peerpod/ - type: Directory - - name: kata-direct-volumes-dir - hostPath: - path: /run/kata-containers/shared/direct-volumes - type: DirectoryOrCreate diff --git a/src/csi-wrapper/examples/ibm/README.md b/src/csi-wrapper/examples/ibm/README.md deleted file mode 100644 index 2d4dc429c2..0000000000 --- a/src/csi-wrapper/examples/ibm/README.md +++ /dev/null @@ -1,267 +0,0 @@ -# Test CSI Wrapper with ibm-vpc-block-csi-driver for peer pod demo on IBM Cloud VPC - -## Set up a demo environment on your development machine - -Follow the [README.md](../../../cloud-api-adaptor/ibmcloud/README.md) to setup a x86_64 based demo environment on IBM Cloud VPC. - -## Deploy `ibm-vpc-block-csi-driver` on the cluster - -1. Setup kubeconfig so that you can access the cluster using `kubectl`. - -2. Get the worker instance id -- Check the worker node name -```bash -kubectl get nodes -o wide -``` -- Check vsi instance id for the worker node -```bash -export IBMCLOUD_API_KEY= -/root/cloud-api-adaptor/src/cloud-api-adaptor/ibmcloud/image/login.sh - -``` -> **Note** You can export `IBMCLOUD_API_ENDPOINT` and `IBMCLOUD_VPC_REGION` to use other regions -- List instances -```bash -ibmcloud is ins |grep ${your-worker-node-name} -``` -The expected result should look like: -```bash -... -0797_162a604f-82da-4b8c-9144-1204d4c560db liudali-csi-amd64-node-1 running 10.242.64.19 141.125.156.56 bx2-2x8 ibm-ubuntu-22-04-5-minimal-amd64-1 se-image-e2e-test-eu-gb eu-gb-2 Default -``` -In the example `0797_162a604f-82da-4b8c-9144-1204d4c560db` is the instanceID, `liudali-csi-amd64-node-1` is the node-name, `eu-gb-2` is the zone, the region should be `eu-gb`. - -3. Set providerID with correct instanceID: -`kubectl patch node {the-worker-node-name} -p '{"spec":{"providerID":"//////${instanceID-of-the-worker-node}"}}'` -eg: -``` -kubectl patch node liudali-csi-amd64-node-1 -p '{"spec":{"providerID":"//////0797_162a604f-82da-4b8c-9144-1204d4c560db"}}' -node/liudali-csi-amd64-node-1 patched -``` -> **Note** -> - The `providerID` for node only can be set once, if you want to update it's value again you will get follow error: -> `The Node "liudali-csi-amd64-node-1" is invalid: spec.providerID: Forbidden: node updates may not change providerID except from "" to valid`. -> -> The only way is delete the node from your cluster and then run the `kubeadm join` command again. -> - On control panel: -> ``` -> kubectl delete node liudali-csi-amd64-node-1 -> kubeadm token create --print-join-command -> ``` -> - On the worker node, clean env and run the join command from above output: -> ``` -> kubeadm reset -> rm -rf /etc/cni/net.d -> kubeadm join 10.242.64.18:6443 --token twdudr.gkoyhki9915qh7a8 --discovery-token-ca-cert-hash sha256:445b3a713ede97dd4a0d62fafd4dee6bba0b2d42c3cb4db5fc0df215b3fd5542 -> reboot -> ``` -> After the worker node status changed to ready, please set the work role again: -> `kubectl label node liudali-csi-amd64-node-1 node.kubernetes.io/worker=` - -4. Add labels to worker node: -```bash -cd /root/cloud-api-adaptor/src/csi-wrapper/ -bash ./examples/ibm/apply-required-labels.sh -``` -The expected result looks like: -```bash -bash ./examples/ibm/apply-required-labels.sh liudali-csi-amd64-node-1 0797_162a604f-82da-4b8c-9144-1204d4c560db eu-gb eu-gb-2 -liudali-csi-amd64-node-1 Ready worker 2h v1.27.3 -node/liudali-csi-amd64-node-1 labeled -node/liudali-csi-amd64-node-1 labeled -node/liudali-csi-amd64-node-1 labeled -node/liudali-csi-amd64-node-1 labeled -node/liudali-csi-amd64-node-1 labeled -``` - -5. Create the [slclient_Gen2.toml](https://github.com/kubernetes-sigs/ibm-vpc-block-csi-driver/blob/v5.2.0/deploy/kubernetes/driver/kubernetes/slclient_Gen2.toml) for the cluster: -```bash -export IBMCLOUD_VPC_REGION= -export IBMCLOUD_RESOURCE_GROUP_ID= -export IBMCLOUD_API_KEY= -cat < slclient_Gen2.toml -[VPC] - iam_client_id = "bx" - iam_client_secret = "bx" - g2_token_exchange_endpoint_url = "https://iam.cloud.ibm.com" - g2_riaas_endpoint_url = "https://${IBMCLOUD_VPC_REGION}.iaas.cloud.ibm.com" - g2_resource_group_id = "${IBMCLOUD_RESOURCE_GROUP_ID}" - g2_api_key = "${IBMCLOUD_API_KEY}" - provider_type = "g2" -END -cat slclient_Gen2.toml -``` -> **Note** Please export `IBMCLOUD_VPC_REGION`, `IBMCLOUD_RESOURCE_GROUP_ID`, `IBMCLOUD_API_KEY` with correct values and check what the file was updated. - -6. Deploy original `ibm-vpc-block-csi-driver`: -```bash -encodeVal=$(base64 -w 0 slclient_Gen2.toml) -sed -i "s/REPLACE_ME/$encodeVal/g" ./examples/ibm/ibm-vpc-block-csi-driver-v5.2.0.yaml -kubectl create -f ./examples/ibm/ibm-vpc-block-csi-driver-v5.2.0.yaml -``` -Check `ibm-vpc-block-csi-driver related` pod status -```bash -kubectl get po -A -o wide | grep vpc -kube-system ibm-vpc-block-csi-controller-bdfdf4657-vksh2 6/6 Running 0 15s -kube-system ibm-vpc-block-csi-node-2w4jf 2/3 CrashLoopBackOff 1 (12s ago) 16s -kube-system ibm-vpc-block-csi-node-pf2j6 3/3 Running 0 16s -``` -> **Note** -> - The `CrashLoopBackOff` for node plugin `ibm-vpc-block-csi-node-2w4jf` on controller node is expected - -## Deploy csi-wrapper to patch on ibm-vpc-block-csi-driver - -1. Create the PeerpodVolume CRD object -```bash -kubectl create -f crd/peerpodvolume.yaml -``` -The output looks like: -```bash -customresourcedefinition.apiextensions.k8s.io/peerpodvolumes.confidentialcontainers.org created -``` -2. Create vpc-block-csi-wrapper-runner role bind to ibm-vpc-block-controller-sa account -```bash -kubectl create -f examples/ibm/vpc-block-csi-wrapper-runner.yaml -``` -The output looks like: -``` -clusterrole.rbac.authorization.k8s.io/vpc-block-csi-wrapper-runner created -clusterrolebinding.rbac.authorization.k8s.io/vpc-block-csi-wrapper-controller-binding created -clusterrolebinding.rbac.authorization.k8s.io/vpc-block-csi-wrapper-node-binding created -``` - -3. patch ibm-vpc-block-csi-driver: -```bash -kubectl patch Deployment ibm-vpc-block-csi-controller -n kube-system --patch-file examples/ibm/patch-controller.yaml -kubectl patch ds ibm-vpc-block-csi-node -n kube-system --patch-file examples/ibm/patch-node.yaml -``` - -4. Check pod status now: -```bash -kubectl get po -A -o wide | grep vpc -kube-system ibm-vpc-block-csi-controller-664ccf487d-q9zjh 7/7 Running 0 22s 172.20.3.14 liudali-csi-amd64-node-1 -kube-system ibm-vpc-block-csi-node-fc8lx 4/4 Running 0 21s 172.20.3.15 liudali-csi-amd64-node-1 -kube-system ibm-vpc-block-csi-node-qbfhc 0/4 ContainerCreating 0 21s liudali-csi-amd64-node-0 -``` -> **Note** The `ibm-vpc-block-csi-node-qbfhc` pod won't in `Running` status as it's on control node, just ignore it. - -5. Create **storage class** for Peerpod: -```bash -kubectl apply -f examples/ibm/ibm-vpc-block-5iopsTier-StorageClass-for-peerpod.yaml -``` -> **Note** -> - One parameter `peerpod: "true"` is added, without it, csi-wrapper won't create PeerpodVolume objects, csi-requests will be processed as normal csi-requests. - -# Run the `csi-wrapper for peerpod storage` demo - -1. Create one pvc that use the storage class for peerpod. -```bash -kubectl create -f examples/ibm/my-pvc-kube-system.yaml -``` - -2. Wait for the pvc status to become `bound` -```bash -kubectl -n kube-system get pvc -NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE -my-pvc Bound pvc-b0803078-551e-42bc-9a44-fc98d95b8010 10Gi RWO ibmc-vpc-block-5iops-tier 58s -``` - -3. Create nginx peer-pod demo with with `podvm-wrapper` and `ibm-vpc-block-csi-driver` containers -```bash -kubectl create -f examples/ibm/nginx-kata-with-my-pvc-and-csi-wrapper.yaml -``` - -4. Wait 2 minutes, check if the `nginx` pod is running: -```bash -kubectl get po -A | grep nginx -kube-system nginx 3/3 Running 0 101s -``` - -5. Check the `PeerpodVolume` crd object and the state is `nodePublishVolumeApplied` -- Getting `PeerpodVolume` objects: -```bash -kubectl get PeerpodVolume -A -NAMESPACE NAME AGE -kube-system r018-1f874ae2-abed-4f37-853e-b1748acd8ced 2m35s -``` -- Using name get the state of the PeerpodVolume: -```bash -kubectl -n kube-system get PeerpodVolume r018-1f874ae2-abed-4f37-853e-b1748acd8ced -o yaml |grep state - state: nodePublishVolumeApplied -``` - -6. Exec into nginx container check the mount: -```bash -kubectl exec nginx -n kube-system -c nginx -i -t -- sh -# lsblk -NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT -loop0 7:0 0 91.8M 1 loop -loop1 7:1 0 63.2M 1 loop -loop2 7:2 0 49.6M 1 loop -vda 252:0 0 100G 0 disk -|-vda1 252:1 0 99.9G 0 part /run/secrets/kubernetes.io/serviceaccount -|-vda14 252:14 0 4M 0 part -`-vda15 252:15 0 106M 0 part -vdb 252:16 0 372K 0 disk -vdc 252:32 0 44K 0 disk -vdd 252:48 0 10G 0 disk /mount-path -# ls /mount-path -lost+found -``` -> **Note** We can see, the device **vdd** is mounted to `/mount-path` in nginx container as expected. - -- Create a file under `/mount-path` -```bash -echo "from nignx container date:" $(date) > /mount-path/incontainer.log -``` -- On the development machine check the instanceID of created vsi for nginx pod: -```bash -ibmcloud is ins |grep nginx -0797_aa4084b3-253b-4720-8f90-152957b29410 podvm-nginx-b3593f0a running 10.242.64.4 - bx2-2x8 podvm-e2e-test-image-amd64 se-image-e2e-test-eu-gb eu-gb-2 Default -``` -- Create a floating ip for the nginx pod VSI -```bash -export vsi_name=podvm-nginx-b3593f0a -export vsi_id=0797_aa4084b3-253b-4720-8f90-152957b29410 -nic_id=$(ibmcloud is instance ${vsi_id} --output JSON | jq -r '.network_interfaces[].id') -floating_ip=$(ibmcloud is floating-ip-reserve ${vsi_name}-ip --nic-id ${nic_id} --output JSON | jq -r '.address') -echo $floating_ip -141.125.163.10 -``` -- Login to the created vsi -```bash -ssh root@141.125.163.10 -``` -- Check the mount point: -```bash -root@podvm-nginx-b3593f0a:~# lsblk -NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT -loop0 7:0 0 63.5M 1 loop /snap/core20/1891 -loop1 7:1 0 91.9M 1 loop /snap/lxd/24061 -loop2 7:2 0 53.3M 1 loop /snap/snapd/19361 -vda 252:0 0 10G 0 disk -├─vda1 252:1 0 9.9G 0 part / -├─vda14 252:14 0 4M 0 part -└─vda15 252:15 0 106M 0 part /boot/efi -vdb 252:16 0 374K 0 disk -vdc 252:32 0 44K 0 disk -vdd 252:48 0 10G 0 disk /var/lib/kubelet/pods/d57f98fe-b6b3-48bd-900f-d565d2823032/volumes/kubernetes.io~csi/pvc-b0803078-551e-42bc-9a44-fc98d95b8010/mount -root@podvm-nginx-b3593f0a:~# -``` -> **Note** We can see, the device **vdd** is mounted to `/var/lib/kubelet/pods/d57f98fe-b6b3-48bd-900f-d565d2823032/volumes/kubernetes.io~csi/pvc-b0803078-551e-42bc-9a44-fc98d95b8010/mount` in the vsi as expected. - -- Check the created file from container -``` -cat /var/lib/kubelet/pods/d57f98fe-b6b3-48bd-900f-d565d2823032/volumes/kubernetes.io~csi/pvc-b0803078-551e-42bc-9a44-fc98d95b8010/mount/incontainer.log -from nignx container date: Tue Jun 20 07:40:08 UTC 2023 -``` - -## Debug -- Monitor the csi-controller-wrapper log: -``` -kubectl logs -n kube-system ibm-vpc-block-csi-controller-664ccf487d-q9zjh -c csi-controller-wrapper -f -``` -- Monitor the cloud-api-adaptor-daemonset log: -``` -kubectl logs -n confidential-containers-system cloud-api-adaptor-daemonset-gx69f -f -``` diff --git a/src/csi-wrapper/examples/ibm/apply-required-labels.sh b/src/csi-wrapper/examples/ibm/apply-required-labels.sh deleted file mode 100644 index 79baea643e..0000000000 --- a/src/csi-wrapper/examples/ibm/apply-required-labels.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash - -function help() -{ - echo "You need to run this script like as follows ...." - echo "./apply-required-setup.sh " - exit 1 -} - -function apply_labels() -{ - kubectl label nodes $1 "ibm-cloud.kubernetes.io/worker-id"=$2 - kubectl label nodes $1 "failure-domain.beta.kubernetes.io/region"=$3 - kubectl label nodes $1 "failure-domain.beta.kubernetes.io/zone"=$4 - kubectl label nodes $1 "topology.kubernetes.io/region"=$3 - kubectl label nodes $1 "topology.kubernetes.io/zone"=$4 -} - -function verify_node() -{ - kubectl get nodes | grep $1 - if (( $? == 0 )) - then - return 0 - else - return 1 - fi -} - -if (( $# < 4 )) -then - help -fi - -node=$1 -instanceID=$2 -region=$3 -zone=$4 - -verify_node $node -if (( $? == 0 )) -then - apply_labels $node $instanceID $region $zone -else - echo "Node " \'$node\' " not found in the cluster, please check the node or passing correct parameters while executing script" - help -fi diff --git a/src/csi-wrapper/examples/ibm/cleanandreinstall.sh b/src/csi-wrapper/examples/ibm/cleanandreinstall.sh deleted file mode 100755 index 398ca2bd4a..0000000000 --- a/src/csi-wrapper/examples/ibm/cleanandreinstall.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -# this script is for develop test only, user must prepare value of slclient.toml via README.md -# make the `REPLACE_ME` is replaced in ibm-vpc-block-csi-driver-master.yaml -kubectl delete po nginx -kubectl delete pvc my-pvc -kubectl -n kube-system delete po nginx -kubectl -n kube-system delete pvc my-pvc -pv_name=$(kubectl get pv |grep ibmc-vpc-block-5iops-tier|awk '{print $1}') -kubectl delete pv $pv_name - -kubectl delete -f ./ibm-vpc-block-csi-driver-v5.2.0.yaml -kubectl delete -f ../../crd/peerpodvolume.yaml -kubectl delete -f ./vpc-block-csi-wrapper-runner.yaml -kubectl create -f ./ibm-vpc-block-csi-driver-v5.2.0.yaml -kubectl create -f ../../crd/peerpodvolume.yaml -kubectl create -f ./vpc-block-csi-wrapper-runner.yaml - -kubectl patch Deployment ibm-vpc-block-csi-controller -n kube-system --patch-file ./patch-controller.yaml -kubectl patch ds ibm-vpc-block-csi-node -n kube-system --patch-file ./patch-node.yaml diff --git a/src/csi-wrapper/examples/ibm/ibm-vpc-block-5iopsTier-StorageClass-for-peerpod.yaml b/src/csi-wrapper/examples/ibm/ibm-vpc-block-5iopsTier-StorageClass-for-peerpod.yaml deleted file mode 100644 index 281f8ef1c3..0000000000 --- a/src/csi-wrapper/examples/ibm/ibm-vpc-block-5iopsTier-StorageClass-for-peerpod.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: storage.k8s.io/v1 -kind: StorageClass -metadata: - name: ibmc-vpc-block-5iops-tier -provisioner: vpc.block.csi.ibm.io -parameters: - profile: "5iops-tier" # The VPC Storage profile used. https://cloud.ibm.com/docs/vpc?topic=vpc-block-storage-profiles&interface=ui#tiers-beta - csi.storage.k8s.io/fstype: "ext4" # ext4 is the default filesytem used. The user can override this default - billingType: "hourly" # The default billing policy used. The uer can override this default - encrypted: "false" # By default, all PVC using this class will only be provider managed encrypted. The user can override this default - encryptionKey: "" # If encrypted is true, then a user must specify the encryption key used associated KP instance - resourceGroup: "" # Use resource group if specified here. else use the one mentioned in storage-secrete-store - region: "" # (applicable only for dev/prestage/stage) By default, the storage vpc driver will select a region. The user can override this default - zone: "" # (applicable only for dev/prestage/stage) By default, the storage vpc driver will select a zone. The user can override this default - tags: "" # A list of tags "a, b, c" that will be created when the volume is created. This can be overidden by user - classVersion: "1" - peerpod: "true" -reclaimPolicy: "Delete" -allowVolumeExpansion: true diff --git a/src/csi-wrapper/examples/ibm/ibm-vpc-block-csi-driver-v5.2.0.yaml b/src/csi-wrapper/examples/ibm/ibm-vpc-block-csi-driver-v5.2.0.yaml deleted file mode 100644 index 836f7696b3..0000000000 --- a/src/csi-wrapper/examples/ibm/ibm-vpc-block-csi-driver-v5.2.0.yaml +++ /dev/null @@ -1,909 +0,0 @@ -apiVersion: v1 -imagePullSecrets: -- name: bluemix-default-secret -- name: bluemix-default-secret-regional -- name: bluemix-default-secret-international -- name: icr-io-secret -kind: ServiceAccount -metadata: - labels: - app: ibm-vpc-block-csi-driver - name: ibm-vpc-block-controller-sa - namespace: kube-system ---- -apiVersion: v1 -imagePullSecrets: -- name: bluemix-default-secret -- name: bluemix-default-secret-regional -- name: bluemix-default-secret-international -- name: icr-io-secret -kind: ServiceAccount -metadata: - labels: - addonmanager.kubernetes.io/mode: Reconcile - app: ibm-vpc-block-csi-driver - name: ibm-vpc-block-node-sa - namespace: kube-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - addonmanager.kubernetes.io/mode: Reconcile - app: ibm-vpc-block-csi-driver - name: vpc-block-driver-registrar-role -rules: -- apiGroups: - - "" - resources: - - events - verbs: - - get - - list - - watch - - create - - update - - patch -- apiGroups: - - "" - resources: - - nodes - verbs: - - get - - list - - watch -- apiGroups: - - "" - resources: - - persistentvolumes - verbs: - - get - - list - - watch - - create - - delete -- apiGroups: - - "" - resources: - - persistentvolumeclaims - verbs: - - get - - list - - watch - - update -- apiGroups: - - "" - resources: - - secrets - verbs: - - get - - list - - watch -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - addonmanager.kubernetes.io/mode: Reconcile - app: ibm-vpc-block-csi-driver - name: vpc-block-driver-snapshotter-role -rules: -- apiGroups: - - "" - resources: - - events - verbs: - - list - - watch - - create - - update - - patch -- apiGroups: - - snapshot.storage.k8s.io - resources: - - volumesnapshotclasses - verbs: - - get - - list - - watch -- apiGroups: - - "" - resources: - - secrets - verbs: - - get - - list -- apiGroups: - - snapshot.storage.k8s.io - resources: - - volumesnapshotcontents - verbs: - - create - - get - - list - - watch - - update - - delete - - patch -- apiGroups: - - snapshot.storage.k8s.io - resources: - - volumesnapshotcontents/status - verbs: - - update ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - addonmanager.kubernetes.io/mode: Reconcile - app: ibm-vpc-block-csi-driver - name: vpc-block-external-attacher-role -rules: -- apiGroups: - - "" - resources: - - persistentvolumes - verbs: - - get - - list - - watch - - update - - patch -- apiGroups: - - "" - resources: - - nodes - verbs: - - get - - list - - watch -- apiGroups: - - csi.storage.k8s.io - resources: - - csinodeinfos - verbs: - - get - - list - - watch -- apiGroups: - - storage.k8s.io - resources: - - volumeattachments - verbs: - - get - - list - - watch - - update - - patch -- apiGroups: - - storage.k8s.io - resources: - - volumeattachments/status - verbs: - - patch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - addonmanager.kubernetes.io/mode: Reconcile - app: ibm-vpc-block-csi-driver - name: vpc-block-external-resizer-role -rules: -- apiGroups: - - "" - resources: - - persistentvolumes - verbs: - - get - - list - - watch - - update - - patch -- apiGroups: - - "" - resources: - - persistentvolumeclaims - verbs: - - get - - list - - watch - - update - - patch -- apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - update - - patch -- apiGroups: - - "" - resources: - - persistentvolumeclaims/status - verbs: - - patch - - update -- apiGroups: - - "" - resources: - - events - verbs: - - list - - watch - - create - - update - - patch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - addonmanager.kubernetes.io/mode: Reconcile - app: ibm-vpc-block-csi-driver - name: vpc-block-provisioner-role -rules: -- apiGroups: - - "" - resources: - - secrets - verbs: - - get - - list - - watch -- apiGroups: - - "" - resources: - - persistentvolumes - verbs: - - get - - list - - watch - - create - - delete -- apiGroups: - - "" - resources: - - persistentvolumeclaims - verbs: - - get - - list - - watch - - update -- apiGroups: - - storage.k8s.io - resources: - - storageclasses - verbs: - - get - - list - - watch -- apiGroups: - - "" - resources: - - events - verbs: - - list - - watch - - create - - update - - patch -- apiGroups: - - snapshot.storage.k8s.io - resources: - - volumesnapshots - verbs: - - get - - list -- apiGroups: - - snapshot.storage.k8s.io - resources: - - volumesnapshotcontents - verbs: - - get - - list -- apiGroups: - - storage.k8s.io - resources: - - csinodes - verbs: - - get - - list - - watch -- apiGroups: - - "" - resources: - - nodes - verbs: - - get - - list - - watch -- apiGroups: - - storage.k8s.io - resources: - - volumeattachments - verbs: - - get - - list - - watch -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - addonmanager.kubernetes.io/mode: Reconcile - app: ibm-vpc-block-csi-driver - name: vpc-block-driver-registrar-binding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: vpc-block-driver-registrar-role -subjects: -- kind: ServiceAccount - name: ibm-vpc-block-node-sa - namespace: kube-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - addonmanager.kubernetes.io/mode: Reconcile - app: ibm-vpc-block-csi-driver - name: vpc-block-driver-snapshotter-binding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: vpc-block-driver-snapshotter-role -subjects: -- kind: ServiceAccount - name: ibm-vpc-block-controller-sa - namespace: kube-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - addonmanager.kubernetes.io/mode: Reconcile - app: ibm-vpc-block-csi-driver - name: vpc-block-external-attacher-binding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: vpc-block-external-attacher-role -subjects: -- kind: ServiceAccount - name: ibm-vpc-block-controller-sa - namespace: kube-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - addonmanager.kubernetes.io/mode: Reconcile - app: ibm-vpc-block-csi-driver - name: vpc-block-external-resizer-binding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: vpc-block-external-resizer-role -subjects: -- kind: ServiceAccount - name: ibm-vpc-block-controller-sa - namespace: kube-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - addonmanager.kubernetes.io/mode: Reconcile - app: ibm-vpc-block-csi-driver - name: vpc-block-provisioner-binding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: vpc-block-provisioner-role -subjects: -- kind: ServiceAccount - name: ibm-vpc-block-controller-sa - namespace: kube-system ---- -apiVersion: v1 -data: - AttachDetachMaxRetryAttempt: "46" - AttachDetachMinRetryAttempt: "3" - AttachDetachMinRetryGAP: "3" - AttacherKubeAPIBurst: "10" - AttacherKubeAPIQPS: "5.0" - AttacherWorkerThreads: "15" - BlockDriverCPULimit: 300m - BlockDriverCPURequest: 75m - BlockDriverMemoryLimit: 600Mi - BlockDriverMemoryRequest: 150Mi - CSIAttacherCPULimits: 60m - CSIAttacherCPURequest: 15m - CSIAttacherMemoryLimit: 120Mi - CSIAttacherMemoryRequest: 30Mi - CSIDriverRegistrarCPULimit: 40m - CSIDriverRegistrarCPURequest: 10m - CSIDriverRegistrarMemoryLimit: 80Mi - CSIDriverRegistrarMemoryRequest: 20Mi - CSIProvisionerCPULimit: 80m - CSIProvisionerCPURequest: 20m - CSIProvisionerMemoryLimit: 160Mi - CSIProvisionerMemoryRequest: 40Mi - CSIResizerCPULimit: 80m - CSIResizerCPURequest: 20m - CSIResizerMemoryLimit: 160Mi - CSIResizerMemoryRequest: 40Mi - CSISnapshotterCPULimit: 80m - CSISnapshotterCPURequest: 20m - CSISnapshotterMemoryLimit: 160Mi - CSISnapshotterMemoryRequest: 40Mi - IsStorageClassDefault: "true" - LivenessProbeCPULimit: 20m - LivenessProbeCPURequest: 5m - LivenessProbeMemoryLimit: 40Mi - LivenessProbeMemoryRequest: 10Mi - NodeDriverCPULimit: 120m - NodeDriverCPURequest: 75Mi - NodeDriverMemoryLimit: 300Mi - NodeDriverMemoryRequest: 30m - SecretSidecarCPULimit: 40m - SecretSidecarCPURequest: 10m - SecretSidecarMemoryLimit: 80Mi - SecretSidecarMemoryRequest: 20Mi -kind: ConfigMap -metadata: - labels: - addonmanager.kubernetes.io/mode: EnsureExists - app: ibm-vpc-block-csi-driver - name: addon-vpc-block-csi-driver-configmap - namespace: kube-system ---- -apiVersion: v1 -data: - CSI_ENDPOINT: unix:/csi/csi.sock - IKS_BLOCK_PROVIDER_NAME: iks-vpc-classic - SECRET_CONFIG_PATH: /etc/storage_ibmc - VPC_API_GENERATION: "1" - VPC_API_TIMEOUT: 90s - VPC_API_VERSION: "2019-07-02" - VPC_BLOCK_PROVIDER_NAME: vpc-classic - VPC_ENABLED: "True" - VPC_RETRY_ATTEMPT: "10" - VPC_RETRY_INTERVAL: "60" -kind: ConfigMap -metadata: - labels: - addonmanager.kubernetes.io/mode: Reconcile - app: ibm-vpc-block-csi-driver - name: ibm-vpc-block-csi-configmap - namespace: kube-system ---- -apiVersion: v1 -data: - slclient.toml: REPLACE_ME -kind: Secret -metadata: - labels: - addonmanager.kubernetes.io/mode: Reconcile - app: ibm-vpc-block-csi-driver - kubernetes.io/cluster-service: "true" - name: storage-secret-store - namespace: kube-system -type: Opaque ---- -apiVersion: apps/v1 -kind: DaemonSet -metadata: - labels: - addonmanager.kubernetes.io/mode: Reconcile - app: ibm-vpc-block-csi-driver - app.kubernetes.io/name: ibm-vpc-block-csi-driver - name: ibm-vpc-block-csi-node - namespace: kube-system -spec: - selector: - matchLabels: - app: ibm-vpc-block-csi-driver - app.kubernetes.io/name: ibm-vpc-block-csi-driver - template: - metadata: - annotations: - prometheus.io/path: /metrics - prometheus.io/port: "9080" - prometheus.io/scrape: "true" - labels: - app: ibm-vpc-block-csi-driver - app.kubernetes.io/name: ibm-vpc-block-csi-driver - spec: - containers: - - args: - - --v=5 - - --endpoint=unix:/csi/csi.sock - env: - - name: KUBE_NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - envFrom: - - configMapRef: - name: ibm-vpc-block-csi-configmap - image: gcr.io/k8s-staging-cloud-provider-ibm/ibm-vpc-block-csi-driver:v5.2.0 - imagePullPolicy: Always - livenessProbe: - failureThreshold: 5 - httpGet: - path: /healthz - port: healthz - initialDelaySeconds: 10 - periodSeconds: 10 - timeoutSeconds: 3 - name: iks-vpc-block-node-driver - ports: - - containerPort: 9808 - name: healthz - protocol: TCP - resources: - limits: - cpu: '120m' - memory: '300Mi' - requests: - cpu: '30m' - memory: '75Mi' - securityContext: - privileged: true - runAsNonRoot: false - runAsUser: 0 - volumeMounts: - - mountPath: /var/lib/kubelet - mountPropagation: Bidirectional - name: kubelet-data-dir - - mountPath: /csi - name: plugin-dir - - mountPath: /dev - name: device-dir - - mountPath: /etc/udev - name: etcudevpath - - mountPath: /run/udev - name: runudevpath - - mountPath: /lib/udev - name: libudevpath - - mountPath: /sys - name: syspath - - mountPath: /etc/storage_ibmc - name: customer-auth - readOnly: true - - args: - - --v=5 - - --csi-address=$(ADDRESS) - - --kubelet-registration-path=$(DRIVER_REGISTRATION_SOCK) - env: - - name: ADDRESS - value: /csi/csi.sock - - name: DRIVER_REGISTRATION_SOCK - value: /var/lib/kubelet/csi-plugins/vpc.block.csi.ibm.io/csi.sock - - name: KUBE_NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - image: registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.2.0 - imagePullPolicy: Always - name: csi-driver-registrar - resources: - limits: - cpu: '40m' - memory: '80Mi' - requests: - cpu: '10m' - memory: '20Mi' - securityContext: - privileged: false - runAsNonRoot: false - runAsUser: 0 - volumeMounts: - - mountPath: /csi - name: plugin-dir - - mountPath: /registration - name: registration-dir - - args: - - --csi-address=/csi/csi.sock - image: registry.k8s.io/sig-storage/livenessprobe:v2.3.0 - name: liveness-probe - resources: - limits: - cpu: '20m' - memory: '40Mi' - requests: - cpu: '5m' - memory: '10Mi' - securityContext: - privileged: false - runAsNonRoot: false - runAsUser: 0 - volumeMounts: - - mountPath: /csi - name: plugin-dir - priorityClassName: system-node-critical - serviceAccountName: ibm-vpc-block-node-sa - tolerations: - - operator: Exists - volumes: - - hostPath: - path: /var/lib/kubelet/plugins_registry/ - type: Directory - name: registration-dir - - hostPath: - path: /var/lib/kubelet - type: Directory - name: kubelet-data-dir - - hostPath: - path: /var/lib/kubelet/csi-plugins/vpc.block.csi.ibm.io/ - type: DirectoryOrCreate - name: plugin-dir - - hostPath: - path: /dev - type: Directory - name: device-dir - - hostPath: - path: /etc/udev - type: Directory - name: etcudevpath - - hostPath: - path: /run/udev - type: Directory - name: runudevpath - - hostPath: - path: /lib/udev - type: Directory - name: libudevpath - - hostPath: - path: /sys - type: Directory - name: syspath - - name: customer-auth - secret: - secretName: storage-secret-store ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - addonmanager.kubernetes.io/mode: Reconcile - app: ibm-vpc-block-csi-driver - app.kubernetes.io/name: ibm-vpc-block-csi-driver - name: ibm-vpc-block-csi-controller - namespace: kube-system -spec: - replicas: 1 - selector: - matchLabels: - app: ibm-vpc-block-csi-driver - app.kubernetes.io/name: ibm-vpc-block-csi-driver - template: - metadata: - annotations: - prometheus.io/path: /metrics - prometheus.io/port: "9080" - prometheus.io/scrape: "true" - labels: - app: ibm-vpc-block-csi-driver - app.kubernetes.io/name: ibm-vpc-block-csi-driver - spec: - containers: - - args: - - --v=5 - - --csi-address=$(ADDRESS) - - --timeout=600s - - --feature-gates=Topology=true - env: - - name: ADDRESS - value: /csi/csi.sock - image: registry.k8s.io/sig-storage/csi-provisioner:v3.2.1 - imagePullPolicy: Always - name: csi-provisioner - resources: - limits: - cpu: '80m' - memory: '160Mi' - requests: - cpu: '20m' - memory: '40Mi' - securityContext: - allowPrivilegeEscalation: false - privileged: false - volumeMounts: - - mountPath: /csi - name: socket-dir - - args: - - --v=5 - - --csi-address=/csi/csi.sock - - --timeout=900s - - --worker-threads=15 - - --kube-api-qps=5 - - --kube-api-burst=10 - image: registry.k8s.io/sig-storage/csi-attacher:v3.5.0 - name: csi-attacher - resources: - limits: - cpu: '60m' - memory: '120Mi' - requests: - cpu: '15m' - memory: '30Mi' - securityContext: - allowPrivilegeEscalation: false - privileged: false - volumeMounts: - - mountPath: /csi - name: socket-dir - - args: - - --csi-address=/csi/csi.sock - image: registry.k8s.io/sig-storage/livenessprobe:v2.6.0 - name: liveness-probe - resources: - limits: - cpu: '20m' - memory: '40Mi' - requests: - cpu: '5m' - memory: '10Mi' - securityContext: - allowPrivilegeEscalation: false - privileged: false - volumeMounts: - - mountPath: /csi - name: socket-dir - - args: - - --v=5 - - --endpoint=$(CSI_ENDPOINT) - - --lock_enabled=false - - --sidecarEndpoint=$(SIDECAREP) - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: KUBE_NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - - name: MAX_VPC_RETRY_ATTEMPT - value: '46' - - name: MIN_VPC_RETRY_INTERVAL - value: '3' - - name: MIN_VPC_RETRY_INTERVAL_ATTEMPT - value: '3' - envFrom: - - configMapRef: - name: ibm-vpc-block-csi-configmap - image: gcr.io/k8s-staging-cloud-provider-ibm/ibm-vpc-block-csi-driver:v5.2.0 - imagePullPolicy: Always - livenessProbe: - failureThreshold: 5 - httpGet: - path: /healthz - port: healthz - initialDelaySeconds: 10 - periodSeconds: 10 - timeoutSeconds: 3 - name: iks-vpc-block-driver - ports: - - containerPort: 9808 - name: healthz - protocol: TCP - resources: - limits: - cpu: '300m' - memory: '600Mi' - requests: - cpu: '75m' - memory: '150Mi' - securityContext: - allowPrivilegeEscalation: false - privileged: false - volumeMounts: - - mountPath: /csi - name: socket-dir - - mountPath: /etc/storage_ibmc - name: customer-auth - readOnly: true - - mountPath: /var/run/secrets/tokens - name: vault-token - - args: - - --v=5 - - --csi-address=$(ADDRESS) - - --timeout=600s - - --handle-volume-inuse-error=false - env: - - name: ADDRESS - value: /csi/csi.sock - image: registry.k8s.io/sig-storage/csi-resizer:v1.5.0 - imagePullPolicy: Always - name: csi-resizer - resources: - limits: - cpu: '80m' - memory: '160Mi' - requests: - cpu: '20m' - memory: '40Mi' - securityContext: - allowPrivilegeEscalation: false - privileged: false - volumeMounts: - - mountPath: /csi - name: socket-dir - - args: - - --v=5 - - --csi-address=/csi/csi.sock - - --timeout=900s - - --leader-election=false - image: registry.k8s.io/sig-storage/csi-snapshotter:v6.0.1 - imagePullPolicy: Always - name: csi-snapshotter - resources: - limits: - cpu: '80m' - memory: '160Mi' - requests: - cpu: '20m' - memory: '40Mi' - securityContext: - allowPrivilegeEscalation: false - privileged: false - volumeMounts: - - mountPath: /csi - name: socket-dir - priorityClassName: system-cluster-critical - securityContext: - runAsNonRoot: true - runAsUser: 2121 - serviceAccountName: ibm-vpc-block-controller-sa - volumes: - - name: vault-token - projected: - sources: - - serviceAccountToken: - expirationSeconds: 600 - path: vault-token - - emptyDir: {} - name: socket-dir - - name: customer-auth - secret: - secretName: storage-secret-store ---- -apiVersion: storage.k8s.io/v1 -kind: CSIDriver -metadata: - labels: - app: ibm-vpc-block-csi-driver - name: vpc.block.csi.ibm.io - namespace: kube-system -spec: - attachRequired: true - podInfoOnMount: true - volumeLifecycleModes: - - Persistent diff --git a/src/csi-wrapper/examples/ibm/ibm-vpc-block-csi-driver.patch b/src/csi-wrapper/examples/ibm/ibm-vpc-block-csi-driver.patch deleted file mode 100644 index d4ea36bbe7..0000000000 --- a/src/csi-wrapper/examples/ibm/ibm-vpc-block-csi-driver.patch +++ /dev/null @@ -1,82 +0,0 @@ -diff --git a/deploy/kubernetes/driver/kubernetes/manifests/controller-server.yaml b/deploy/kubernetes/driver/kubernetes/manifests/controller-server.yaml -index 8ce6e7e..551c3c3 100644 ---- a/deploy/kubernetes/driver/kubernetes/manifests/controller-server.yaml -+++ b/deploy/kubernetes/driver/kubernetes/manifests/controller-server.yaml -@@ -161,5 +161,5 @@ spec: - emptyDir: {} - - name: customer-auth - secret: -- secretName: storage-secret-store -+ secretName: vpc-storage-secret-store - volumeClaimTemplates: [] -diff --git a/deploy/kubernetes/driver/kubernetes/manifests/node-server.yaml b/deploy/kubernetes/driver/kubernetes/manifests/node-server.yaml -index eb16bcb..42a8602 100644 ---- a/deploy/kubernetes/driver/kubernetes/manifests/node-server.yaml -+++ b/deploy/kubernetes/driver/kubernetes/manifests/node-server.yaml -@@ -164,4 +164,4 @@ spec: - type: Directory - - name: customer-auth # altough its not required, This is just to finish lib configuration which is a common code in the driver - secret: -- secretName: storage-secret-store -+ secretName: vpc-storage-secret-store -diff --git a/deploy/kubernetes/driver/kubernetes/manifests/storage-secret-store.yaml b/deploy/kubernetes/driver/kubernetes/manifests/storage-secret-store.yaml -index 775fc23..e482c13 100644 ---- a/deploy/kubernetes/driver/kubernetes/manifests/storage-secret-store.yaml -+++ b/deploy/kubernetes/driver/kubernetes/manifests/storage-secret-store.yaml -@@ -7,6 +7,6 @@ metadata: - addonmanager.kubernetes.io/mode: Reconcile - app: ibm-vpc-block-csi-driver - kubernetes.io/cluster-service: "true" -- name: storage-secret-store -+ name: vpc-storage-secret-store - namespace: kube-system - type: Opaque -diff --git a/deploy/kubernetes/driver/kubernetes/overlays/stage/controller-server-images.yaml b/deploy/kubernetes/driver/kubernetes/overlays/stage/controller-server-images.yaml -index 1d9fd58..cc5a29e 100644 ---- a/deploy/kubernetes/driver/kubernetes/overlays/stage/controller-server-images.yaml -+++ b/deploy/kubernetes/driver/kubernetes/overlays/stage/controller-server-images.yaml -@@ -14,6 +14,6 @@ spec: - image: k8s.gcr.io/sig-storage/livenessprobe:v2.3.0 - - name: iks-vpc-block-driver - imagePullPolicy: Always -- image: gcr.io/k8s-staging-cloud-provider-ibm/ibm-vpc-block-csi-driver:master -+ image: gcr.io/k8s-staging-cloud-provider-ibm/ibm-vpc-block-csi-driver:v4.3.1 - - name: csi-resizer - image: k8s.gcr.io/sig-storage/csi-resizer:v1.2.0 -diff --git a/deploy/kubernetes/driver/kubernetes/overlays/stage/kustomization.yaml b/deploy/kubernetes/driver/kubernetes/overlays/stage/kustomization.yaml -index e4324e5..79543bb 100644 ---- a/deploy/kubernetes/driver/kubernetes/overlays/stage/kustomization.yaml -+++ b/deploy/kubernetes/driver/kubernetes/overlays/stage/kustomization.yaml -@@ -1,5 +1,5 @@ - bases: - - ../../manifests --patches: -+patchesStrategicMerge: - - controller-server-images.yaml - - node-server-images.yaml -diff --git a/deploy/kubernetes/driver/kubernetes/overlays/stage/node-server-images.yaml b/deploy/kubernetes/driver/kubernetes/overlays/stage/node-server-images.yaml -index 8900848..a29a012 100644 ---- a/deploy/kubernetes/driver/kubernetes/overlays/stage/node-server-images.yaml -+++ b/deploy/kubernetes/driver/kubernetes/overlays/stage/node-server-images.yaml -@@ -8,7 +8,7 @@ spec: - containers: - - name: iks-vpc-block-node-driver - imagePullPolicy: Always -- image: gcr.io/k8s-staging-cloud-provider-ibm/ibm-vpc-block-csi-driver:master -+ image: gcr.io/k8s-staging-cloud-provider-ibm/ibm-vpc-block-csi-driver:v4.3.1 - - name: csi-driver-registrar - image: k8s.gcr.io/sig-storage/csi-node-driver-registrar:v2.2.0 - - name: liveness-probe -diff --git a/scripts/apply-required-setup.sh b/scripts/apply-required-setup.sh -index 674739e..9e39f0c 100644 ---- a/scripts/apply-required-setup.sh -+++ b/scripts/apply-required-setup.sh -@@ -10,6 +10,7 @@ function help() - function apply_labels() - { - kubectl label nodes $1 "ibm-cloud.kubernetes.io/worker-id"=$2 -+ kubectl label nodes $1 "ibm-cloud.kubernetes.io/vpc-instance-id"=$2 - kubectl label nodes $1 "failure-domain.beta.kubernetes.io/region"=$3 - kubectl label nodes $1 "failure-domain.beta.kubernetes.io/zone"=$4 - kubectl label nodes $1 "topology.kubernetes.io/region"=$3 - diff --git a/src/csi-wrapper/examples/ibm/my-pvc-kube-system.yaml b/src/csi-wrapper/examples/ibm/my-pvc-kube-system.yaml deleted file mode 100644 index 648e247a0b..0000000000 --- a/src/csi-wrapper/examples/ibm/my-pvc-kube-system.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: my-pvc - namespace: kube-system -spec: - storageClassName: ibmc-vpc-block-5iops-tier - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 10Gi diff --git a/src/csi-wrapper/examples/ibm/nginx-kata-with-my-pvc-and-csi-wrapper.yaml b/src/csi-wrapper/examples/ibm/nginx-kata-with-my-pvc-and-csi-wrapper.yaml deleted file mode 100644 index 780e3a2508..0000000000 --- a/src/csi-wrapper/examples/ibm/nginx-kata-with-my-pvc-and-csi-wrapper.yaml +++ /dev/null @@ -1,118 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: nginx - labels: - app: nginx - namespace: kube-system -spec: - runtimeClassName: kata-remote - containers: - - name: ibm-vpc-block-podvm-node-driver - env: - - name: KUBE_NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - envFrom: - - configMapRef: - name: ibm-vpc-block-csi-configmap - image: gcr.io/k8s-staging-cloud-provider-ibm/ibm-vpc-block-csi-driver:v5.2.0 - imagePullPolicy: Always - securityContext: - privileged: true - runAsNonRoot: false - runAsUser: 0 - ports: - - containerPort: 9808 - name: healthz - protocol: TCP - volumeMounts: - - name: kubelet-data-dir - mountPath: /var/lib/kubelet - mountPropagation: Bidirectional - - mountPath: /tmp - name: plugin-dir - - mountPath: /dev - name: device-dir - - mountPath: /etc/udev - name: etcudevpath - - mountPath: /run/udev - name: runudevpath - - mountPath: /lib/udev - name: libudevpath - - mountPath: /sys - name: syspath - - mountPath: /etc/storage_ibmc - name: customer-auth - readOnly: true - - name: csi-podvm-wrapper - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAME_SPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: POD_UID - valueFrom: - fieldRef: - fieldPath: metadata.uid - - name: POD_NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - command: ["csi-podvm-wrapper"] - args: - - --v=5 - - --endpoint=/tmp/csi-podvm-wrapper.sock - - --target-endpoint=/tmp/csi.sock - - --namespace=kube-system - image: quay.io/confidential-containers/csi-podvm-wrapper:latest - imagePullPolicy: Always - volumeMounts: - - mountPath: /tmp - name: plugin-dir - - name: nginx - image: nginx:stable - imagePullPolicy: Always - volumeMounts: - - name: my-volume - mountPath: /mount-path - mountPropagation: HostToContainer - serviceAccountName: ibm-vpc-block-node-sa - volumes: - - name: kubelet-data-dir - hostPath: - path: /var/lib/kubelet # OpenShift worker, for kubernetes /var/data/kubelet - type: Directory - - emptyDir: {} - name: plugin-dir - - hostPath: - path: /dev - type: Directory - name: device-dir - - hostPath: - path: /etc/udev - type: Directory - name: etcudevpath - - hostPath: - path: /run/udev - type: Directory - name: runudevpath - - hostPath: - path: /lib/udev - type: Directory - name: libudevpath - - hostPath: - path: /sys - type: Directory - name: syspath - - name: customer-auth - secret: - secretName: storage-secret-store - - name: my-volume - persistentVolumeClaim: - claimName: my-pvc diff --git a/src/csi-wrapper/examples/ibm/patch-controller.yaml b/src/csi-wrapper/examples/ibm/patch-controller.yaml deleted file mode 100644 index b6ce9b7228..0000000000 --- a/src/csi-wrapper/examples/ibm/patch-controller.yaml +++ /dev/null @@ -1,34 +0,0 @@ -spec: - template: - spec: - containers: - - name: csi-controller-wrapper - args: - - --v=5 - - --endpoint=/csi/csi-controller-wrapper.sock - - --target-endpoint=/csi/csi.sock - - --namespace=kube-system - image: quay.io/confidential-containers/csi-controller-wrapper:latest - imagePullPolicy: IfNotPresent - volumeMounts: - - mountPath: /csi - name: socket-dir - - - name: csi-attacher - args: - - --v=5 - - --csi-address=/csi/csi-controller-wrapper.sock - - --timeout=900s - - --worker-threads=15 - - --kube-api-qps=5 - - --kube-api-burst=10 - - - name: csi-provisioner - env: - - name: ADDRESS - value: /csi/csi-controller-wrapper.sock - - - name: csi-resizer - env: - - name: ADDRESS - value: /csi/csi-controller-wrapper.sock diff --git a/src/csi-wrapper/examples/ibm/patch-node.yaml b/src/csi-wrapper/examples/ibm/patch-node.yaml deleted file mode 100644 index 9d0f2bbcc9..0000000000 --- a/src/csi-wrapper/examples/ibm/patch-node.yaml +++ /dev/null @@ -1,41 +0,0 @@ -spec: - template: - spec: - containers: - - name: csi-node-wrapper - args: - - --v=5 - - --endpoint=/csi/csi-node-wrapper.sock - - --target-endpoint=/csi/csi.sock - - --namespace=kube-system - env: - - name: POD_NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - image: quay.io/confidential-containers/csi-node-wrapper:latest - imagePullPolicy: IfNotPresent - volumeMounts: - - mountPath: /csi - name: plugin-dir - - mountPath: /run/peerpod/ - name: podvminfo-dir - - mountPath: /run/kata-containers/shared/direct-volumes - name: kata-direct-volumes-dir - - - name: csi-driver-registrar - env: - - name: ADDRESS - value: /csi/csi-node-wrapper.sock - - name: DRIVER_REGISTRATION_SOCK - value: /var/lib/kubelet/csi-plugins/vpc.block.csi.ibm.io/csi-node-wrapper.sock - - volumes: - - name: podvminfo-dir - hostPath: - path: /run/peerpod/ - type: Directory - - name: kata-direct-volumes-dir - hostPath: - path: /run/kata-containers/shared/direct-volumes - type: DirectoryOrCreate diff --git a/src/csi-wrapper/examples/ibm/vpc-block-csi-wrapper-runner.yaml b/src/csi-wrapper/examples/ibm/vpc-block-csi-wrapper-runner.yaml deleted file mode 100644 index 855e741910..0000000000 --- a/src/csi-wrapper/examples/ibm/vpc-block-csi-wrapper-runner.yaml +++ /dev/null @@ -1,76 +0,0 @@ ---- -kind: ClusterRole -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: vpc-block-csi-wrapper-runner -rules: - - apiGroups: [""] - resources: ["secrets"] - verbs: ["get", "list", "watch"] - - apiGroups: [""] - resources: ["persistentvolumes"] - verbs: ["get", "list", "watch", "create", "delete"] - - apiGroups: [""] - resources: ["persistentvolumeclaims"] - verbs: ["get", "list", "watch", "update"] - - apiGroups: ["storage.k8s.io"] - resources: ["storageclasses"] - verbs: ["get", "list", "watch"] - - apiGroups: [""] - resources: ["events"] - verbs: ["list", "watch", "create", "update", "patch"] - - apiGroups: ["snapshot.storage.k8s.io"] - resources: ["volumesnapshots"] - verbs: ["get", "list"] - - apiGroups: ["snapshot.storage.k8s.io"] - resources: ["volumesnapshotcontents"] - verbs: ["get", "list"] - - apiGroups: ["storage.k8s.io"] - resources: ["csinodes"] - verbs: ["get", "list", "watch"] - - apiGroups: [""] - resources: ["nodes"] - verbs: ["get", "list", "watch"] - - apiGroups: ["storage.k8s.io"] - resources: ["volumeattachments"] - verbs: ["get", "list", "watch"] - - apiGroups: [""] - resources: ["configmaps"] - verbs: ["get", "list"] - - apiGroups: ["confidentialcontainers.org"] - resources: ["peerpodvolumes"] - verbs: ["get", "list", "watch", "create", "delete", "update", "patch"] - - apiGroups: ["confidentialcontainers.org"] - resources: ["peerpodvolumes/status"] - verbs: ["update"] - - apiGroups: [""] - resources: ["pods"] - verbs: ["get", "list"] - ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: vpc-block-csi-wrapper-controller-binding -subjects: - - kind: ServiceAccount - name: ibm-vpc-block-controller-sa - namespace: kube-system -roleRef: - kind: ClusterRole - name: vpc-block-csi-wrapper-runner - apiGroup: rbac.authorization.k8s.io - ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: vpc-block-csi-wrapper-node-binding -subjects: - - kind: ServiceAccount - name: ibm-vpc-block-node-sa - namespace: kube-system -roleRef: - kind: ClusterRole - name: vpc-block-csi-wrapper-runner - apiGroup: rbac.authorization.k8s.io diff --git a/src/csi-wrapper/examples/tools.go b/src/csi-wrapper/examples/tools.go deleted file mode 100644 index e5a64a6eed..0000000000 --- a/src/csi-wrapper/examples/tools.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright Confidential Containers Contributors -// SPDX-License-Identifier: Apache-2.0 - -package hack - -// This statement forces `go mod` to recognize the packages required by update-codegen.sh -import _ "k8s.io/code-generator" - -// For VS Code users, adding the following setting in .vscode/settings.json will suppress -// the error of `build constraints exclude all Go files` -// "gopls": { -// "build.directoryFilters": ["-hack"] -// } diff --git a/src/csi-wrapper/examples/update-codegen.sh b/src/csi-wrapper/examples/update-codegen.sh deleted file mode 100755 index a8f3930f51..0000000000 --- a/src/csi-wrapper/examples/update-codegen.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash - -set -o errexit -o pipefail -o nounset -o errtrace - -package=github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper -group=peerpodvolume -version=v1alpha1 - -basedir=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." &>/dev/null && pwd -P) - -if [[ ! -d "$basedir/vendor/k8s.io/code-generator" ]]; then - echo "Run `go mod vendor` before running this script" - exit 1 -fi - -cleanup() { - trap - SIGINT SIGTERM ERR EXIT - rm -fr "$tmpdir" -} - -tmpdir=$(mktemp -d /tmp/gopath.XXXXX) -trap cleanup SIGINT SIGTERM ERR EXIT - -mkdir -p "$(dirname "$tmpdir/src/$package")" -ln -s "$basedir" "$tmpdir/src/$package" - -bash "$basedir/vendor/k8s.io/code-generator/generate-groups.sh" all \ - "$package/pkg/generated/peerpodvolume" \ - "$package/pkg/apis" \ - "$group:$version" \ - --output-base "$tmpdir/src" \ - --go-header-file /dev/null diff --git a/src/csi-wrapper/go.mod b/src/csi-wrapper/go.mod deleted file mode 100644 index 1a5315f39c..0000000000 --- a/src/csi-wrapper/go.mod +++ /dev/null @@ -1,68 +0,0 @@ -module github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper - -go 1.25.10 - -require ( - github.com/confidential-containers/cloud-api-adaptor/src/cloud-api-adaptor v0.0.0-00010101000000-000000000000 - github.com/container-storage-interface/spec v1.8.0 - github.com/containerd/ttrpc v1.2.7 - github.com/gofrs/uuid v4.4.0+incompatible - github.com/golang/glog v1.2.5 - github.com/golang/protobuf v1.5.4 - github.com/kata-containers/kata-containers/src/runtime v0.0.0-20260422184550-8dccf4cf37ae - google.golang.org/grpc v1.80.0 - k8s.io/apimachinery v0.35.2 - k8s.io/client-go v0.35.2 - k8s.io/code-generator v0.35.2 -) - -require ( - github.com/containerd/log v0.1.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/emicklei/go-restful/v3 v3.12.2 // indirect - github.com/fxamacker/cbor/v2 v2.9.0 // indirect - github.com/go-logr/logr v1.4.3 // indirect - github.com/go-openapi/jsonpointer v0.21.1 // indirect - github.com/go-openapi/jsonreference v0.21.0 // indirect - github.com/go-openapi/swag v0.23.1 // indirect - github.com/google/gnostic-models v0.7.0 // indirect - github.com/google/go-cmp v0.7.0 // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/josharian/intern v1.0.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/mailru/easyjson v0.9.0 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect - github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/sirupsen/logrus v1.9.3 // indirect - github.com/spf13/pflag v1.0.9 // indirect - github.com/x448/float16 v0.8.4 // indirect - go.yaml.in/yaml/v2 v2.4.3 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/mod v0.34.0 // indirect - golang.org/x/net v0.53.0 // indirect - golang.org/x/oauth2 v0.36.0 // indirect - golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.43.0 // indirect - golang.org/x/term v0.42.0 // indirect - golang.org/x/text v0.36.0 // indirect - golang.org/x/time v0.15.0 // indirect - golang.org/x/tools v0.43.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect - google.golang.org/protobuf v1.36.11 // indirect - gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect - gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.35.2 // indirect - k8s.io/gengo/v2 v2.0.0-20250922181213-ec3ebc5fd46b // indirect - k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect - k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect - sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect - sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482 // indirect - sigs.k8s.io/yaml v1.6.0 // indirect -) - -replace github.com/confidential-containers/cloud-api-adaptor/src/cloud-api-adaptor => ../cloud-api-adaptor diff --git a/src/csi-wrapper/go.sum b/src/csi-wrapper/go.sum deleted file mode 100644 index ca383eafa3..0000000000 --- a/src/csi-wrapper/go.sum +++ /dev/null @@ -1,172 +0,0 @@ -github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= -github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= -github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= -github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/container-storage-interface/spec v1.8.0 h1:D0vhF3PLIZwlwZEf2eNbpujGCNwspwTYf2idJRJx4xI= -github.com/container-storage-interface/spec v1.8.0/go.mod h1:ROLik+GhPslwwWRNFF1KasPzroNARibH2rfz1rkg4H0= -github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= -github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ= -github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= -github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= -github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= -github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= -github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic= -github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk= -github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= -github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= -github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= -github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= -github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= -github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= -github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/golang/glog v1.2.5 h1:DrW6hGnjIhtvhOIiAKT6Psh/Kd/ldepEa81DKeiRJ5I= -github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= -github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= -github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/kata-containers/kata-containers/src/runtime v0.0.0-20260422184550-8dccf4cf37ae h1:52j1vpTT7yfFrDEjVbvKnasG8I5Ca7+pYcNx4AN/1Tw= -github.com/kata-containers/kata-containers/src/runtime v0.0.0-20260422184550-8dccf4cf37ae/go.mod h1:Dnl25Qo2S1qiYQX4Ytk7U4oxT2+eRPFzP8K8idi8WA8= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= -github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= -github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= -github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= -github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= -github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= -github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= -github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= -github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= -go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= -go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= -go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= -go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= -go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= -go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= -go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= -go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= -go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= -go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= -go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= -go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= -golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= -golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= -golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= -golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= -golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= -golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY= -golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY= -golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= -golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= -golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= -golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= -golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= -golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= -golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= -golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= -golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= -golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8= -gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= -gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= -google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= -google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4= -google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= -google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= -gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= -gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.35.2 h1:tW7mWc2RpxW7HS4CoRXhtYHSzme1PN1UjGHJ1bdrtdw= -k8s.io/api v0.35.2/go.mod h1:7AJfqGoAZcwSFhOjcGM7WV05QxMMgUaChNfLTXDRE60= -k8s.io/apimachinery v0.35.2 h1:NqsM/mmZA7sHW02JZ9RTtk3wInRgbVxL8MPfzSANAK8= -k8s.io/apimachinery v0.35.2/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= -k8s.io/client-go v0.35.2 h1:YUfPefdGJA4aljDdayAXkc98DnPkIetMl4PrKX97W9o= -k8s.io/client-go v0.35.2/go.mod h1:4QqEwh4oQpeK8AaefZ0jwTFJw/9kIjdQi0jpKeYvz7g= -k8s.io/code-generator v0.35.2 h1:3874swbO2c26VWTf6lKD4NWGyHIfyBeTCk7caCG3TuU= -k8s.io/code-generator v0.35.2/go.mod h1:id4XLCm0yAQq5nlvyfAKibMOKnMjzlesAwGw6kM3Adc= -k8s.io/gengo/v2 v2.0.0-20250922181213-ec3ebc5fd46b h1:gMplByicHV/TJBizHd9aVEsTYoJBnnUAT5MHlTkbjhQ= -k8s.io/gengo/v2 v2.0.0-20250922181213-ec3ebc5fd46b/go.mod h1:CgujABENc3KuTrcsdpGmrrASjtQsWCT7R99mEV4U/fM= -k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= -k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= -k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= -k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= -k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= -sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= -sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= -sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= -sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482 h1:2WOzJpHUBVrrkDjU4KBT8n5LDcj824eX0I5UKcgeRUs= -sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= -sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= -sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/src/csi-wrapper/images/csi-wrapper.excalidraw b/src/csi-wrapper/images/csi-wrapper.excalidraw deleted file mode 100644 index aab3178cfe..0000000000 --- a/src/csi-wrapper/images/csi-wrapper.excalidraw +++ /dev/null @@ -1,3579 +0,0 @@ -{ - "type": "excalidraw", - "version": 2, - "source": "https://excalidraw.com", - "elements": [ - { - "type": "rectangle", - "version": 1106, - "versionNonce": 474649136, - "isDeleted": false, - "id": "O1I-IpAKwkwr17CxMeeWW", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 380.066650390625, - "y": 440.2292175292969, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 774.1245727539062, - "height": 426.15417480468744, - "seed": 460087749, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508241, - "link": null, - "locked": false - }, - { - "type": "rectangle", - "version": 789, - "versionNonce": 2107184688, - "isDeleted": false, - "id": "XtKoJWqbX9Um3mIGBLsS9", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 712.3602600097656, - "y": 706.5401916503906, - "strokeColor": "#000000", - "backgroundColor": "#228be6", - "width": 164, - "height": 32, - "seed": 774688011, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "Ai1NLaqytQBYNNMxW6HQf" - } - ], - "updated": 1689170510926, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 461, - "versionNonce": 597216304, - "isDeleted": false, - "id": "XRm6yPBAzWpFcOkt7tHh-", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 388.5823059082031, - "y": 886.4776611328125, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 157, - "height": 24, - "seed": 132258667, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508241, - "link": null, - "locked": false, - "fontSize": 19.48214892274844, - "fontFamily": 1, - "text": "k8s worker node", - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "k8s worker node", - "lineHeight": 1.2318969583471495, - "baseline": 17 - }, - { - "type": "rectangle", - "version": 311, - "versionNonce": 407719632, - "isDeleted": false, - "id": "r6VV1mXbDPcGEuTmJ55d5", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 391.6958923339844, - "y": 619.2171020507812, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 146, - "height": 60, - "seed": 1104538309, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "UWETWU28FaT1H3vkbwE_r" - } - ], - "updated": 1689170508241, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 274, - "versionNonce": 1494206000, - "isDeleted": false, - "id": "UWETWU28FaT1H3vkbwE_r", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 396.6958923339844, - "y": 624.2171020507812, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 136, - "height": 50, - "seed": 1946018347, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508241, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "containerd/ \ncrio", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "r6VV1mXbDPcGEuTmJ55d5", - "originalText": "containerd/ crio", - "lineHeight": 1.25, - "baseline": 43 - }, - { - "type": "line", - "version": 295, - "versionNonce": 1432024272, - "isDeleted": false, - "id": "FjRoV5uCwuWfF700pOkNp", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 461.1828918457031, - "y": 684.5984497070312, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0.75872802734375, - "height": 25.248687744140625, - "seed": 1719959813, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1689170508241, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0.75872802734375, - 25.248687744140625 - ] - ] - }, - { - "type": "rectangle", - "version": 379, - "versionNonce": 2039971888, - "isDeleted": false, - "id": "4t3FjwZHSUvfNiWkwXdAZ", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 441.3872375488281, - "y": 711.9903259277344, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 187, - "height": 60, - "seed": 890126245, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "1hmWN9BCWWF97csBOL6BI" - } - ], - "updated": 1689170508241, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 334, - "versionNonce": 767810256, - "isDeleted": false, - "id": "1hmWN9BCWWF97csBOL6BI", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 446.3872375488281, - "y": 716.9903259277344, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 177, - "height": 50, - "seed": 1553116933, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508241, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "containerd-shim-k\nata-v2", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "4t3FjwZHSUvfNiWkwXdAZ", - "originalText": "containerd-shim-kata-v2", - "lineHeight": 1.25, - "baseline": 43 - }, - { - "type": "line", - "version": 413, - "versionNonce": 1696070192, - "isDeleted": false, - "id": "qmWJmb6Ccte5TivfXWTtq", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 629.5439340408891, - "y": 751.1750731114298, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 45.06111890077591, - "height": 0.5427158157154963, - "seed": 1311839819, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1689170508241, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 45.06111890077591, - 0.5427158157154963 - ] - ] - }, - { - "type": "rectangle", - "version": 835, - "versionNonce": 1333519920, - "isDeleted": false, - "id": "L0vsRpgis0KkM6AgN_Dy4", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 716.0170593261719, - "y": 798.2314453125, - "strokeColor": "#000000", - "backgroundColor": "#fa5252", - "width": 164, - "height": 32, - "seed": 310943173, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "tsMjhpL3bdQ4uH2TgH29w" - } - ], - "updated": 1689170515725, - "link": null, - "locked": false - }, - { - "type": "rectangle", - "version": 625, - "versionNonce": 1048130608, - "isDeleted": false, - "id": "qQfOvENCASoz4UskT9zYk", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 712.2542419433594, - "y": 658.4229431152344, - "strokeColor": "#000000", - "backgroundColor": "#fab005", - "width": 164, - "height": 34, - "seed": 901870597, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "oWl78vinwQEMYLzSal0df" - } - ], - "updated": 1689170508241, - "link": null, - "locked": false - }, - { - "type": "rectangle", - "version": 667, - "versionNonce": 1745315536, - "isDeleted": false, - "id": "s61Ub_qW20xRqyNSZBnaY", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "dotted", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 464.9500427246094, - "y": 768.9439086914062, - "strokeColor": "#000000", - "backgroundColor": "#4c6ef5", - "width": 159, - "height": 60, - "seed": 740023845, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "LhM3ueQmtRA6oGDkQLcOe" - } - ], - "updated": 1689170508241, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 450, - "versionNonce": 195243568, - "isDeleted": false, - "id": "LhM3ueQmtRA6oGDkQLcOe", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 469.9500427246094, - "y": 778.9439086914062, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 149, - "height": 40, - "seed": 498467525, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508241, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "Remote Hypervisor \nSupport", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "s61Ub_qW20xRqyNSZBnaY", - "originalText": "Remote Hypervisor Support", - "lineHeight": 1.25, - "baseline": 35 - }, - { - "type": "text", - "version": 373, - "versionNonce": 1444023504, - "isDeleted": false, - "id": "oWl78vinwQEMYLzSal0df", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "dotted", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 717.2542419433594, - "y": 665.4229431152344, - "strokeColor": "#000000", - "backgroundColor": "#4c6ef5", - "width": 154, - "height": 20, - "seed": 1032174859, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508241, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "AWS", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "qQfOvENCASoz4UskT9zYk", - "originalText": "AWS", - "lineHeight": 1.25, - "baseline": 15 - }, - { - "type": "text", - "version": 493, - "versionNonce": 349086928, - "isDeleted": false, - "id": "Ai1NLaqytQBYNNMxW6HQf", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "dotted", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 717.3602600097656, - "y": 711.5401916503906, - "strokeColor": "#000000", - "backgroundColor": "#fab005", - "width": 154, - "height": 20, - "seed": 1784024613, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170510926, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "IBM Cloud", - "textAlign": "center", - "verticalAlign": "top", - "containerId": "XtKoJWqbX9Um3mIGBLsS9", - "originalText": "IBM Cloud", - "lineHeight": 1.25, - "baseline": 15 - }, - { - "type": "text", - "version": 426, - "versionNonce": 1665193168, - "isDeleted": false, - "id": "tsMjhpL3bdQ4uH2TgH29w", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "dotted", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 773.5170593261719, - "y": 804.2314453125, - "strokeColor": "#000000", - "backgroundColor": "#fa5252", - "width": 49, - "height": 20, - "seed": 1090195307, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170515725, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "Libvirt", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "L0vsRpgis0KkM6AgN_Dy4", - "originalText": "Libvirt", - "lineHeight": 1.25, - "baseline": 15 - }, - { - "type": "rectangle", - "version": 607, - "versionNonce": 1207001648, - "isDeleted": false, - "id": "l01K4RvsztEMEwlm0aoyJ", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 676.4830017089844, - "y": 601.9906311035156, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 244.60168457031244, - "height": 254.33761596679685, - "seed": 924679493, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508241, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 274, - "versionNonce": 1576369872, - "isDeleted": false, - "id": "lxwM3JjwsjnapH2o4MGiB", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 680.3016891479492, - "y": 601.6656799316406, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 172.0500030517578, - "height": 25, - "seed": 1036665483, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508241, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "cloud-api-adaptor", - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "cloud-api-adaptor", - "lineHeight": 1.25, - "baseline": 18 - }, - { - "type": "text", - "version": 412, - "versionNonce": 1161991728, - "isDeleted": false, - "id": "j8FF0eTx8WYcUUx3oomW-", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 705.9649353027344, - "y": 627.553722879161, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 204.60479736328116, - "height": 14.826434591542117, - "seed": 253198341, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508241, - "link": null, - "locked": false, - "fontSize": 11.861147673233695, - "fontFamily": 1, - "text": "(Remote Hypervisor Implementation)", - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "(Remote Hypervisor Implementation)", - "lineHeight": 1.2499999999999998, - "baseline": 10 - }, - { - "type": "rectangle", - "version": 383, - "versionNonce": 377300176, - "isDeleted": false, - "id": "8eTvhJAGoqnDHhU6H3P4f", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 990.49462890625, - "y": 715.4835205078125, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 121, - "height": 51, - "seed": 1833992299, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "fxyQwgnPQxiCWBBH3VWpd" - } - ], - "updated": 1689170508241, - "link": null, - "locked": false - }, - { - "type": "rectangle", - "version": 521, - "versionNonce": 1995460656, - "isDeleted": false, - "id": "FnbsIOG6um_CgCO3BX4D7", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1272.2000732421875, - "y": 449.9513843039357, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 513.146240234375, - "height": 414.4194958230174, - "seed": 1343733963, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508241, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 731, - "versionNonce": 1797262032, - "isDeleted": false, - "id": "0_fS6c-788yj821Cxj2EW", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1281.2103271484375, - "y": 881.5790405273438, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 450.3833312988281, - "height": 44, - "seed": 1965716549, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508241, - "link": null, - "locked": false, - "fontSize": 17.46259646532011, - "fontFamily": 1, - "text": "Pod VM (AWS EC2, Azure VM, IBM Cloud Instance, \nLibvirt VM, ...)", - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "Pod VM (AWS EC2, Azure VM, IBM Cloud Instance, \nLibvirt VM, ...)", - "lineHeight": 1.2598355601751983, - "baseline": 38 - }, - { - "type": "rectangle", - "version": 288, - "versionNonce": 1591134768, - "isDeleted": false, - "id": "waxPNqApsGE_vqLRKhkpR", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1290.063851182652, - "y": 496.76149915157487, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 103.204833984375, - "height": 38.347442626953125, - "seed": 95415941, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "k0jBjo1h_pCe2U8eSg902" - } - ], - "updated": 1689170508241, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 239, - "versionNonce": 913715408, - "isDeleted": false, - "id": "k0jBjo1h_pCe2U8eSg902", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1295.063851182652, - "y": 503.43522046505143, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 93, - "height": 25, - "seed": 1442694949, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508241, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "systemd", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "waxPNqApsGE_vqLRKhkpR", - "originalText": "systemd", - "lineHeight": 1.25, - "baseline": 18 - }, - { - "type": "line", - "version": 616, - "versionNonce": 937296944, - "isDeleted": false, - "id": "_nE4hxl5Chba2a68n__jL", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1342.3510826279644, - "y": 535.315484747278, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 3.514976965635924, - "height": 102.41099983371794, - "seed": 10883787, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1689170508241, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 3.514976965635924, - 102.41099983371794 - ] - ] - }, - { - "type": "line", - "version": 693, - "versionNonce": 1075287760, - "isDeleted": false, - "id": "JaIbnejFFgfZuK8s_xWhg", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1340.830941026402, - "y": 569.5900819152467, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 52.8579994680731, - "height": 0.4994034763798254, - "seed": 2012222827, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1689170508241, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 52.8579994680731, - 0.4994034763798254 - ] - ] - }, - { - "type": "rectangle", - "version": 1077, - "versionNonce": 535690800, - "isDeleted": false, - "id": "gwRyDoyKAAx2nYmOOyD40", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1397.6657798935894, - "y": 551.0102631896608, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 217, - "height": 36, - "seed": 1500282827, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "id": "Rc8aIDnlrNxpot4pIXKUS", - "type": "text" - } - ], - "updated": 1689170508241, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 1021, - "versionNonce": 480001232, - "isDeleted": false, - "id": "Rc8aIDnlrNxpot4pIXKUS", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1402.6657798935894, - "y": 559.0102631896608, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 207, - "height": 20, - "seed": 561280613, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508241, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "agent-protocol-forwarder", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "gwRyDoyKAAx2nYmOOyD40", - "originalText": "agent-protocol-forwarder", - "lineHeight": 1.25, - "baseline": 15 - }, - { - "type": "rectangle", - "version": 774, - "versionNonce": 938391600, - "isDeleted": false, - "id": "o9GU0q-_Gw5_k8uWar3O_", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1403.151985948277, - "y": 606.7679383605592, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 139, - "height": 31, - "seed": 1836429957, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "FeD5uzPBZgwc17RD5AE77" - } - ], - "updated": 1689170508241, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 733, - "versionNonce": 228336336, - "isDeleted": false, - "id": "FeD5uzPBZgwc17RD5AE77", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1408.151985948277, - "y": 612.2679383605592, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 129, - "height": 20, - "seed": 140219973, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508241, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "kata-agent", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "o9GU0q-_Gw5_k8uWar3O_", - "originalText": "kata-agent", - "lineHeight": 1.25, - "baseline": 15 - }, - { - "type": "line", - "version": 646, - "versionNonce": 1137193520, - "isDeleted": false, - "id": "Bdm-pSP-1TWr_Gn-IoHFw", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1344.875374620152, - "y": 617.3824708312624, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 58.038330078125, - "height": 0.4066162109375, - "seed": 850792197, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1689170508241, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 58.038330078125, - 0.4066162109375 - ] - ] - }, - { - "type": "arrow", - "version": 368, - "versionNonce": 1638877392, - "isDeleted": false, - "id": "LoREk8gIfugkPUBpuZvR3", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1602.4089439560894, - "y": 624.7090699523561, - "strokeColor": "#1864ab", - "backgroundColor": "transparent", - "width": 2.0252685546875, - "height": 38.928131103515625, - "seed": 697358437, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1689170508241, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 2.0252685546875, - -38.928131103515625 - ] - ] - }, - { - "type": "arrow", - "version": 342, - "versionNonce": 1247964208, - "isDeleted": false, - "id": "thwt-06Mc56oUsr65IElD", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1540.513802354527, - "y": 634.1251160949342, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0, - "height": 0.000030517578125, - "seed": 2067600427, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1689170508241, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 0, - 0.000030517578125 - ] - ] - }, - { - "type": "arrow", - "version": 439, - "versionNonce": 1416474320, - "isDeleted": false, - "id": "L3p0tt56qkUzqxRilIVz4", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0.002315951460715837, - "x": 1604.1635826279644, - "y": 624.4659058898561, - "strokeColor": "#1864ab", - "backgroundColor": "transparent", - "width": 67.2682147976011, - "height": 0.07689146557822824, - "seed": 1885267813, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1689170508242, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - -67.2682147976011, - 0.07689146557822824 - ] - ] - }, - { - "type": "rectangle", - "version": 566, - "versionNonce": 900032048, - "isDeleted": false, - "id": "N12foFduZg-GQFx33-0EL", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "dotted", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1346.8568115234375, - "y": 668.6194819888043, - "strokeColor": "#1864ab", - "backgroundColor": "transparent", - "width": 425.88671756707805, - "height": 183.99154096041443, - "seed": 1159700613, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 1 - }, - "boundElements": [], - "updated": 1689170508242, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 470, - "versionNonce": 1852789968, - "isDeleted": false, - "id": "2ssB-PZdXwtwXcK6GM3YL", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "dotted", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1360.0239135742188, - "y": 834.56884765625, - "strokeColor": "#1864ab", - "backgroundColor": "transparent", - "width": 139.05384521484368, - "height": 18.058940936992684, - "seed": 584645803, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1689170508242, - "link": null, - "locked": false, - "fontSize": 14.447152749594157, - "fontFamily": 1, - "text": "Network Namespace", - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "Network Namespace", - "lineHeight": 1.2499999999999991, - "baseline": 13 - }, - { - "type": "line", - "version": 275, - "versionNonce": 1920540720, - "isDeleted": false, - "id": "4f3ffXAWcYMIfu_zmXc-z", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "angle": 0, - "x": 1470.0722740342144, - "y": 638.0508057921999, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0.631162522919567, - "height": 22.542510939128647, - "seed": 270459211, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1689170508242, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - -0.631162522919567, - 22.542510939128647 - ] - ] - }, - { - "type": "rectangle", - "version": 532, - "versionNonce": 1699494608, - "isDeleted": false, - "id": "5wEmfq0ncZj5OY5WdB8gz", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "angle": 0, - "x": 1377.8899437761377, - "y": 706.9606448689674, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 121, - "height": 91, - "seed": 767557, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 1 - }, - "boundElements": [ - { - "type": "text", - "id": "EV3_MPnRM7LMLFEODRy3O" - } - ], - "updated": 1689170508242, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 521, - "versionNonce": 281385520, - "isDeleted": false, - "id": "EV3_MPnRM7LMLFEODRy3O", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "angle": 0, - "x": 1398.7019631242822, - "y": 733.4606448689674, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 79.37596130371094, - "height": 38, - "seed": 647714405, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1689170508242, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "app\ncontainers", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "5wEmfq0ncZj5OY5WdB8gz", - "originalText": "app\ncontainers", - "lineHeight": 1.1875, - "baseline": 33 - }, - { - "type": "line", - "version": 817, - "versionNonce": 2073845968, - "isDeleted": false, - "id": "YQsmtIgewspkXhf-tsD8T", - "fillStyle": "hachure", - "strokeWidth": 4, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "angle": 0, - "x": 1129.0079345703125, - "y": 835.036376953125, - "strokeColor": "#364fc7", - "backgroundColor": "transparent", - "width": 219.4881591796875, - "height": 0.02642822265625, - "seed": 1717057451, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1689170508242, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 219.4881591796875, - -0.02642822265625 - ] - ] - }, - { - "type": "text", - "version": 291, - "versionNonce": 1668469808, - "isDeleted": false, - "id": "yHNLwnr6yIlKmlZBedihE", - "fillStyle": "hachure", - "strokeWidth": 4, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "angle": 0, - "x": 1187.3507080078125, - "y": 816.8802490234375, - "strokeColor": "#364fc7", - "backgroundColor": "transparent", - "width": 51, - "height": 20, - "seed": 973188875, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1689170508242, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "Tunnel", - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "Tunnel", - "lineHeight": 1.25, - "baseline": 15 - }, - { - "type": "rectangle", - "version": 493, - "versionNonce": 1626851024, - "isDeleted": false, - "id": "a3uI5lRWd94Upg_ypikKy", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "dotted", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 975.2284545898438, - "y": 797.1257019042969, - "strokeColor": "#1864ab", - "backgroundColor": "transparent", - "width": 152.21569824218744, - "height": 61.37475585937502, - "seed": 1115521349, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 1 - }, - "boundElements": [], - "updated": 1689170508242, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 439, - "versionNonce": 543739440, - "isDeleted": false, - "id": "4pdH-JARAteztsbx1odxV", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "dotted", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 983.8025268554688, - "y": 840.4932956447849, - "strokeColor": "#1864ab", - "backgroundColor": "transparent", - "width": 127.38165283203125, - "height": 16.54307179636769, - "seed": 1545807435, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1689170508242, - "link": null, - "locked": false, - "fontSize": 13.23445743709417, - "fontFamily": 1, - "text": "Network Namespace", - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "Network Namespace", - "lineHeight": 1.2499999999999984, - "baseline": 12 - }, - { - "type": "text", - "version": 173, - "versionNonce": 301362384, - "isDeleted": false, - "id": "fxyQwgnPQxiCWBBH3VWpd", - "fillStyle": "hachure", - "strokeWidth": 4, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "angle": 0, - "x": 995.49462890625, - "y": 730.9835205078125, - "strokeColor": "#364fc7", - "backgroundColor": "transparent", - "width": 111, - "height": 20, - "seed": 531637387, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1689170508242, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "CNI network", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "8eTvhJAGoqnDHhU6H3P4f", - "originalText": "CNI network", - "lineHeight": 1.25, - "baseline": 15 - }, - { - "type": "line", - "version": 169, - "versionNonce": 9406512, - "isDeleted": false, - "id": "xtqAajah7G7T7eCUV9jhk", - "fillStyle": "hachure", - "strokeWidth": 4, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "angle": 0, - "x": 1047.827880859375, - "y": 767.1981811523438, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0.34490966796875, - "height": 29.10028076171875, - "seed": 273880683, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1689170508242, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - -0.34490966796875, - 29.10028076171875 - ] - ] - }, - { - "type": "line", - "version": 346, - "versionNonce": 942691024, - "isDeleted": false, - "id": "ZhSzZDzu400EDKlxFH40o", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 0, - "opacity": 100, - "angle": 0, - "x": 925.1881103515625, - "y": 759.111328125, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 63.52252197265625, - "height": 0.18603515625, - "seed": 852793291, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1689170508242, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 63.52252197265625, - 0.18603515625 - ] - ] - }, - { - "type": "text", - "version": 236, - "versionNonce": 1607402032, - "isDeleted": false, - "id": "4qc-c_2upK6fe_MSq5Na6", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 0, - "opacity": 100, - "angle": 0, - "x": 1577.8483970810894, - "y": 484.6459290832155, - "strokeColor": "#c92a2a", - "backgroundColor": "transparent", - "width": 70, - "height": 20, - "seed": 1990957451, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1689170508242, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "Kata VM", - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "Kata VM", - "lineHeight": 1.25, - "baseline": 15 - }, - { - "type": "rectangle", - "version": 1098, - "versionNonce": 171800784, - "isDeleted": false, - "id": "1BcAI64ufEgH7177GTYL6", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 731.1441955566406, - "y": 53.204986572265625, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 774.1245727539062, - "height": 281.1541748046875, - "seed": 73230474, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508242, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 547, - "versionNonce": 1561279536, - "isDeleted": false, - "id": "HQh5iJJ-l_JAUsfaZw5fh", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 745.7064819335938, - "y": 69.78207397460938, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 162, - "height": 25, - "seed": 841044490, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508242, - "link": null, - "locked": false, - "fontSize": 19.48214892274844, - "fontFamily": 1, - "text": "k8s control node", - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "k8s control node", - "lineHeight": 1.2832259982782808, - "baseline": 18 - }, - { - "type": "rectangle", - "version": 689, - "versionNonce": 733339344, - "isDeleted": false, - "id": "agm2TC_0rO69P1R_4MpSN", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 395.20648193359375, - "y": 459.7820739746094, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 142, - "height": 60, - "seed": 1840401750, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "id": "16cJK4ZxJe1SlxwY4h-eM", - "type": "text" - } - ], - "updated": 1689170508242, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 662, - "versionNonce": 2050504240, - "isDeleted": false, - "id": "16cJK4ZxJe1SlxwY4h-eM", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 400.20648193359375, - "y": 477.7820739746094, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 132, - "height": 24, - "seed": 929323786, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508242, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "kubelet", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "agm2TC_0rO69P1R_4MpSN", - "originalText": "kubelet", - "lineHeight": 1.2, - "baseline": 17 - }, - { - "type": "rectangle", - "version": 693, - "versionNonce": 221349072, - "isDeleted": false, - "id": "th7TN-d2VvlE-NQeGgQJr", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 673.9056396484375, - "y": 447.61326599121094, - "strokeColor": "#000000", - "backgroundColor": "#15aabf", - "width": 244.60168457031244, - "height": 114.33761596679685, - "seed": 346739222, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508242, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 428, - "versionNonce": 1009905712, - "isDeleted": false, - "id": "BDkV1hfViglOiR_0NpHk1", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 709.2937349654121, - "y": 445.01555386403913, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 106.8798828125, - "height": 26, - "seed": 1724515542, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508242, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "csi-wrapper", - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "csi-wrapper", - "lineHeight": 1.3, - "baseline": 18 - }, - { - "type": "rectangle", - "version": 468, - "versionNonce": 1279814352, - "isDeleted": false, - "id": "3KyO9Wh2UPlk4RouiLQt-", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 724.9641418457031, - "y": 521.0498046875, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 164, - "height": 34, - "seed": 1898674314, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "WF3Max-E75FQZOUTS8bD0" - } - ], - "updated": 1689170508242, - "link": null, - "locked": false - }, - { - "type": "rectangle", - "version": 845, - "versionNonce": 1256483376, - "isDeleted": false, - "id": "uA1pHlSbKG1p-Y3MfumCi", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 726.2064819335938, - "y": 473.7820739746094, - "strokeColor": "#000000", - "backgroundColor": "#fab005", - "width": 164, - "height": 34, - "seed": 1914379158, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "id": "zY-HdHKNFXbhmObDXqFN1", - "type": "text" - } - ], - "updated": 1689170508242, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 616, - "versionNonce": 328372432, - "isDeleted": false, - "id": "zY-HdHKNFXbhmObDXqFN1", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "dotted", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 733.670539855957, - "y": 481.2820739746094, - "strokeColor": "#000000", - "backgroundColor": "#4c6ef5", - "width": 149.07188415527344, - "height": 19, - "seed": 627478730, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508242, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "node plugin wrapper", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "uA1pHlSbKG1p-Y3MfumCi", - "originalText": "node plugin wrapper", - "lineHeight": 1.1875, - "baseline": 14 - }, - { - "type": "text", - "version": 149, - "versionNonce": 288859184, - "isDeleted": false, - "id": "WF3Max-E75FQZOUTS8bD0", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 729.9641418457031, - "y": 528.5498046875, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 154, - "height": 19, - "seed": 650908182, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508242, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "origin node plugin", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "3KyO9Wh2UPlk4RouiLQt-", - "originalText": "origin node plugin", - "lineHeight": 1.1875, - "baseline": 14 - }, - { - "type": "rectangle", - "version": 744, - "versionNonce": 2057828048, - "isDeleted": false, - "id": "7DRWtLQnntCbRkKolq-yF", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 851.2064819335938, - "y": 254.78207397460938, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 590, - "height": 60, - "seed": 248998730, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "id": "iB7z1ded4Mc8oNPfZF5RH", - "type": "text" - } - ], - "updated": 1689170508242, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 739, - "versionNonce": 1561566768, - "isDeleted": false, - "id": "iB7z1ded4Mc8oNPfZF5RH", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 856.2064819335938, - "y": 272.7820739746094, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 580, - "height": 24, - "seed": 2099201622, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508242, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "api server", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "7DRWtLQnntCbRkKolq-yF", - "originalText": "api server", - "lineHeight": 1.2, - "baseline": 17 - }, - { - "type": "rectangle", - "version": 1005, - "versionNonce": 1713413328, - "isDeleted": false, - "id": "Fhzofbzp2AppDPJT9NqBO", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1083.9056396484375, - "y": 58.61326599121094, - "strokeColor": "#000000", - "backgroundColor": "#15aabf", - "width": 316.60168457031256, - "height": 183.33761596679685, - "seed": 1386467286, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508242, - "link": null, - "locked": false - }, - { - "type": "rectangle", - "version": 640, - "versionNonce": 2137020464, - "isDeleted": false, - "id": "mt4s5Ckixx22B016UHAQD", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1134.9641418457031, - "y": 87.0498046875, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 220, - "height": 38, - "seed": 1981956234, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "id": "kFZqzR-q_10PUVZIVjDWm", - "type": "text" - } - ], - "updated": 1689170508242, - "link": null, - "locked": false - }, - { - "type": "rectangle", - "version": 907, - "versionNonce": 1737786064, - "isDeleted": false, - "id": "P_FityWK_2Se-osTjHf9O", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1135.2064819335938, - "y": 137.78207397460938, - "strokeColor": "#000000", - "backgroundColor": "#fab005", - "width": 217, - "height": 35, - "seed": 612853014, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "id": "qS8M1DHk93iwqFopCNdSS", - "type": "text" - } - ], - "updated": 1689170508242, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 685, - "versionNonce": 1812320816, - "isDeleted": false, - "id": "qS8M1DHk93iwqFopCNdSS", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "dotted", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1140.2064819335938, - "y": 145.78207397460938, - "strokeColor": "#000000", - "backgroundColor": "#4c6ef5", - "width": 207, - "height": 19, - "seed": 1440587594, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508242, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "controller plugin wrapper", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "P_FityWK_2Se-osTjHf9O", - "originalText": "controller plugin wrapper", - "lineHeight": 1.1875, - "baseline": 14 - }, - { - "type": "text", - "version": 332, - "versionNonce": 496740560, - "isDeleted": false, - "id": "kFZqzR-q_10PUVZIVjDWm", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1139.9641418457031, - "y": 96.5498046875, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 210, - "height": 19, - "seed": 935744086, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508242, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "origin controller plugin", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "mt4s5Ckixx22B016UHAQD", - "originalText": "origin controller plugin", - "lineHeight": 1.1875, - "baseline": 14 - }, - { - "type": "text", - "version": 586, - "versionNonce": 1508641840, - "isDeleted": false, - "id": "lxZr50h6uAhZbwZ0XNlRX", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1143.753242480912, - "y": 58.453157277640656, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 106.8798828125, - "height": 26, - "seed": 1509098506, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508242, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "csi-wrapper", - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "csi-wrapper", - "lineHeight": 1.3, - "baseline": 18 - }, - { - "type": "rectangle", - "version": 646, - "versionNonce": 332271312, - "isDeleted": false, - "id": "nQ7LS793-gYD9G8eU10wA", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1137.2064819335938, - "y": 191.78207397460938, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 220, - "height": 38, - "seed": 1268316502, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "id": "DED_zYm4luHzzCtbnKGl5", - "type": "text" - } - ], - "updated": 1689170508242, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 358, - "versionNonce": 1660618288, - "isDeleted": false, - "id": "DED_zYm4luHzzCtbnKGl5", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1142.2064819335938, - "y": 201.28207397460938, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 210, - "height": 19, - "seed": 294547210, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508243, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "k8s csi sidecars", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "nQ7LS793-gYD9G8eU10wA", - "originalText": "k8s csi sidecars", - "lineHeight": 1.1875, - "baseline": 14 - }, - { - "type": "line", - "version": 439, - "versionNonce": 583251152, - "isDeleted": false, - "id": "aOy-W0ygd4zOLdzmbj_up", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 450.8271179199219, - "y": 525.1577301025391, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 1.9070116288959866, - "height": 91.04641827270393, - "seed": 649439754, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508243, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 1.9070116288959866, - 91.04641827270393 - ] - ] - }, - { - "type": "line", - "version": 611, - "versionNonce": 605732912, - "isDeleted": false, - "id": "N1hA25mKpw9qypZ-ZCr6A", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 1.608927453554327, - "x": 603.2919209015463, - "y": 425.9187964439161, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 2.882265951780596, - "height": 126.0878553625858, - "seed": 1723392726, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1689170508243, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 2.882265951780596, - 126.0878553625858 - ] - ] - }, - { - "type": "line", - "version": 326, - "versionNonce": 1343527632, - "isDeleted": false, - "id": "8yIqZQqeO2G25ctfvs4GZ", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 795.8271179199219, - "y": 567.1577301025391, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0.75872802734375, - "height": 25.248687744140625, - "seed": 751297098, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1689170508243, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0.75872802734375, - 25.248687744140625 - ] - ] - }, - { - "type": "rectangle", - "version": 835, - "versionNonce": 1563638320, - "isDeleted": false, - "id": "8xAHyGcc9oJA1lMCY6I1U", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1318.2064819335938, - "y": 257.7820739746094, - "strokeColor": "#000000", - "backgroundColor": "#15aabf", - "width": 60, - "height": 34, - "seed": 319427786, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508243, - "link": null, - "locked": false - }, - { - "type": "rectangle", - "version": 984, - "versionNonce": 777568464, - "isDeleted": false, - "id": "gAkAHA_mI9Z_5_cZDMz3O", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1324.2064819335938, - "y": 262.7820739746094, - "strokeColor": "#000000", - "backgroundColor": "#15aabf", - "width": 60, - "height": 34, - "seed": 516786710, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508243, - "link": null, - "locked": false - }, - { - "type": "rectangle", - "version": 990, - "versionNonce": 288126000, - "isDeleted": false, - "id": "vvFF0vxL2mUbgHPMLsNFE", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1329.7064819335938, - "y": 267.0320739746094, - "strokeColor": "#000000", - "backgroundColor": "#15aabf", - "width": 60, - "height": 34, - "seed": 1570003402, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "-6PQgoFci8sakpzHFDq02" - } - ], - "updated": 1689170508243, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 121, - "versionNonce": 1026041552, - "isDeleted": false, - "id": "-6PQgoFci8sakpzHFDq02", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1334.7064819335938, - "y": 273.5320739746094, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 50, - "height": 19, - "seed": 973019978, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508243, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "CRDs", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "vvFF0vxL2mUbgHPMLsNFE", - "originalText": "CRDs", - "lineHeight": 1.1875, - "baseline": 14 - }, - { - "type": "line", - "version": 809, - "versionNonce": 636382768, - "isDeleted": false, - "id": "jMKLG_G7TykTxQ0Thwlks", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 1.5981392791865865, - "x": 789.2690022923765, - "y": 75.77003615483304, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 29.605245986336058, - "height": 638.809812796761, - "seed": 409475414, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1689170508243, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 29.605245986336058, - 638.809812796761 - ] - ] - }, - { - "type": "line", - "version": 712, - "versionNonce": 1226493136, - "isDeleted": false, - "id": "rSgF-KKjzkcFGxvWHZAZm", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 484.25297611914567, - "y": 404.2588648382575, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 2.502629616484114, - "height": 45.24061203449969, - "seed": 1357589002, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508243, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - -2.502629616484114, - 45.24061203449969 - ] - ] - }, - { - "type": "line", - "version": 452, - "versionNonce": 73476144, - "isDeleted": false, - "id": "OEVuguWY-m3o9mVQaceTh", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1125.2529761191458, - "y": 315.2588648382574, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0.9954661540687084, - "height": 74.33999620266263, - "seed": 1774500182, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508243, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0.9954661540687084, - 74.33999620266263 - ] - ] - }, - { - "type": "line", - "version": 1162, - "versionNonce": 1271444176, - "isDeleted": false, - "id": "W70iAkFpTxOtMKCTNufzM", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1683.7844973598967, - "y": 394.2780873167716, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0.3492896497800757, - "height": 267.9459197278386, - "seed": 1932990922, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508243, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - -0.3492896497800757, - 267.9459197278386 - ] - ] - }, - { - "type": "line", - "version": 861, - "versionNonce": 946643504, - "isDeleted": false, - "id": "1ScXlAxWnLrkkioNrp-lM", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 1.5981392791865865, - "x": 1399.2818403101505, - "y": 113.5106400098036, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 9.719870011589137, - "height": 556.4279568395435, - "seed": 820271574, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1689170508243, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 9.719870011589137, - 556.4279568395435 - ] - ] - }, - { - "type": "line", - "version": 535, - "versionNonce": 1271519440, - "isDeleted": false, - "id": "d_jBKOkrHRqnUS85uhbHF", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1244.7087488565594, - "y": 240.61207587327806, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0.7092684643387202, - "height": 11.195244553312705, - "seed": 1428217942, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508243, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - -0.7092684643387202, - 11.195244553312705 - ] - ] - }, - { - "type": "rectangle", - "version": 709, - "versionNonce": 284744752, - "isDeleted": false, - "id": "EubH__kZZJ1swMeTBnyxo", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 854.2064819335938, - "y": 127.78207397460938, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 142, - "height": 60, - "seed": 1296026262, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "id": "ZufBFE03jygvJ1wEd6LhS", - "type": "text" - } - ], - "updated": 1689170508243, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 700, - "versionNonce": 1018576592, - "isDeleted": false, - "id": "ZufBFE03jygvJ1wEd6LhS", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 859.2064819335938, - "y": 133.28207397460938, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 132, - "height": 48, - "seed": 2137543114, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508244, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "Controller \nManager", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "EubH__kZZJ1swMeTBnyxo", - "originalText": "Controller Manager", - "lineHeight": 1.2, - "baseline": 41 - }, - { - "type": "line", - "version": 438, - "versionNonce": 1072551472, - "isDeleted": false, - "id": "q0Wa96ZSXoXzrsKw2CCgh", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 919.2529761191457, - "y": 190.25886483825752, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 1.4786984536796126, - "height": 59.94113740585749, - "seed": 1296376266, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508244, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 1.4786984536796126, - 59.94113740585749 - ] - ] - }, - { - "type": "rectangle", - "version": 910, - "versionNonce": 1596318928, - "isDeleted": false, - "id": "ZhsH-Gg1t4f6D_tmFYxmn", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 874.2064819335938, - "y": 258.7820739746094, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 75, - "height": 48, - "seed": 1010418902, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508244, - "link": null, - "locked": false - }, - { - "type": "rectangle", - "version": 1020, - "versionNonce": 2050199600, - "isDeleted": false, - "id": "SM4yThFFNO7eXb-4YXUO5", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 880.7064819335938, - "y": 260.7820739746094, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 75, - "height": 48, - "seed": 518653270, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "id": "xB_tz94yeheCyXvcp_ZN_", - "type": "text" - } - ], - "updated": 1689170508244, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 902, - "versionNonce": 548457168, - "isDeleted": false, - "id": "xB_tz94yeheCyXvcp_ZN_", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 885.7064819335938, - "y": 265.7820739746094, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 65, - "height": 38, - "seed": 547627786, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508244, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "CSI\nObjects", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "SM4yThFFNO7eXb-4YXUO5", - "originalText": "CSI\nObjects", - "lineHeight": 1.1875, - "baseline": 33 - }, - { - "type": "line", - "version": 571, - "versionNonce": 397543984, - "isDeleted": false, - "id": "N8HgfdLMshBBtiViuGIst", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 795.5611161657632, - "y": 508.184451697953, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0.7092684643387202, - "height": 11.195244553312705, - "seed": 976517974, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508244, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - -0.7092684643387202, - 11.195244553312705 - ] - ] - }, - { - "type": "line", - "version": 601, - "versionNonce": 2079256784, - "isDeleted": false, - "id": "fR5s1saWgPJMB_SBwTHxr", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1238.5611161657632, - "y": 177.18445169795302, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0.7092684643387202, - "height": 11.195244553312705, - "seed": 567684246, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508244, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - -0.7092684643387202, - 11.195244553312705 - ] - ] - }, - { - "type": "line", - "version": 596, - "versionNonce": 860963888, - "isDeleted": false, - "id": "lPqiIrA-ZOgGgFGrx0ZtV", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1234.561116165763, - "y": 124.18445169795302, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0.7092684643387202, - "height": 11.195244553312705, - "seed": 748634262, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508244, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - -0.7092684643387202, - 11.195244553312705 - ] - ] - }, - { - "type": "rectangle", - "version": 1233, - "versionNonce": 1484281552, - "isDeleted": false, - "id": "4eYlpnDQzUhti9voa-STf", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1527.9891705605003, - "y": 704.9115602070187, - "strokeColor": "#000000", - "backgroundColor": "#15aabf", - "width": 228.33364253104892, - "height": 114.33761596679685, - "seed": 41663894, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508244, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 790, - "versionNonce": 1257848368, - "isDeleted": false, - "id": "7Ew_521_UYkVvhVygdL5q", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1540.4419934942512, - "y": 704.807392228488, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 106.8798828125, - "height": 26, - "seed": 1989909206, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508244, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "csi-wrapper", - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "csi-wrapper", - "lineHeight": 1.3, - "baseline": 18 - }, - { - "type": "rectangle", - "version": 846, - "versionNonce": 1793391824, - "isDeleted": false, - "id": "BrAQMPrr5yNQsO6CHbjzG", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1556.1124003745422, - "y": 781.841643051949, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 187, - "height": 33, - "seed": 760074262, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "3WzQ1A83Wcqhww2MxgK08" - } - ], - "updated": 1689170508244, - "link": null, - "locked": false - }, - { - "type": "rectangle", - "version": 1243, - "versionNonce": 1603401776, - "isDeleted": false, - "id": "HG_iVn6RXtvvGuUV9CPMV", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1557.3547404624328, - "y": 733.5739123390583, - "strokeColor": "#000000", - "backgroundColor": "#fab005", - "width": 190, - "height": 36, - "seed": 1384602966, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "id": "yCcHP11ArzaqOOxZaVVsI", - "type": "text" - } - ], - "updated": 1689170508244, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 1051, - "versionNonce": 348971728, - "isDeleted": false, - "id": "yCcHP11ArzaqOOxZaVVsI", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "dotted", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1572.7388118125305, - "y": 742.0739123390583, - "strokeColor": "#000000", - "backgroundColor": "#4c6ef5", - "width": 159.2318572998047, - "height": 19, - "seed": 1268597398, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508244, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "podvm plugin wrapper", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "HG_iVn6RXtvvGuUV9CPMV", - "originalText": "podvm plugin wrapper", - "lineHeight": 1.1875, - "baseline": 14 - }, - { - "type": "text", - "version": 528, - "versionNonce": 352547376, - "isDeleted": false, - "id": "3WzQ1A83Wcqhww2MxgK08", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1585.3084376670226, - "y": 788.841643051949, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 128.60792541503906, - "height": 19, - "seed": 189038550, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508244, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "origin node plugin", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "BrAQMPrr5yNQsO6CHbjzG", - "originalText": "origin node plugin", - "lineHeight": 1.1875, - "baseline": 14 - }, - { - "type": "line", - "version": 933, - "versionNonce": 1153960144, - "isDeleted": false, - "id": "jAnkG1Pm63-YUhjyMdf0b", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1626.7093746946023, - "y": 767.9762900624019, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0.7092684643387202, - "height": 11.195244553312705, - "seed": 1704072470, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170508244, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - -0.7092684643387202, - 11.195244553312705 - ] - ] - }, - { - "type": "rectangle", - "version": 685, - "versionNonce": 640531664, - "isDeleted": false, - "id": "nujzsBs_XgbOHkz-5TG5j", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 714.7064819335938, - "y": 750.7660369873047, - "strokeColor": "#000000", - "backgroundColor": "#a5d8ff", - "width": 164, - "height": 34, - "seed": 1769310256, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "hT6UpetlDuomP9HRi7MGK" - } - ], - "updated": 1689170513134, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 438, - "versionNonce": 1781872688, - "isDeleted": false, - "id": "hT6UpetlDuomP9HRi7MGK", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "dotted", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 774.5231494903564, - "y": 757.7660369873047, - "strokeColor": "#000000", - "backgroundColor": "#4c6ef5", - "width": 44.36666488647461, - "height": 20, - "seed": 1005912624, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1689170513134, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "Azure", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "nujzsBs_XgbOHkz-5TG5j", - "originalText": "Azure", - "lineHeight": 1.25, - "baseline": 15 - }, - { - "id": "eLzw-Qz8ERlNz_THegLgF", - "type": "text", - "x": 783.7064819335938, - "y": 826.7660369873047, - "width": 16.450000762939453, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#a5d8ff", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1805600816, - "version": 66, - "versionNonce": 1628481072, - "isDeleted": false, - "boundElements": null, - "updated": 1689170508244, - "link": null, - "locked": false, - "text": "...", - "fontSize": 20, - "fontFamily": 1, - "textAlign": "left", - "verticalAlign": "top", - "baseline": 18, - "containerId": null, - "originalText": "...", - "lineHeight": 1.25, - "isFrameName": false - } - ], - "appState": { - "gridSize": null, - "viewBackgroundColor": "#ffffff" - }, - "files": {} -} \ No newline at end of file diff --git a/src/csi-wrapper/images/csi-wrapper.png b/src/csi-wrapper/images/csi-wrapper.png deleted file mode 100644 index 99c7f0270c..0000000000 Binary files a/src/csi-wrapper/images/csi-wrapper.png and /dev/null differ diff --git a/src/csi-wrapper/pkg/apis/peerpodvolume/register.go b/src/csi-wrapper/pkg/apis/peerpodvolume/register.go deleted file mode 100644 index 2b81d00e17..0000000000 --- a/src/csi-wrapper/pkg/apis/peerpodvolume/register.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright Confidential Containers Contributors -// SPDX-License-Identifier: Apache-2.0 - -package peerpodvolume - -const ( - // GroupName is the group name of the CRD - GroupName = "confidentialcontainers.org" - // Version is the version of the CRD - Version = "v1alpha1" -) diff --git a/src/csi-wrapper/pkg/apis/peerpodvolume/v1alpha1/doc.go b/src/csi-wrapper/pkg/apis/peerpodvolume/v1alpha1/doc.go deleted file mode 100644 index 585fd47b6b..0000000000 --- a/src/csi-wrapper/pkg/apis/peerpodvolume/v1alpha1/doc.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright Confidential Containers Contributors -// SPDX-License-Identifier: Apache-2.0 - -// +k8s:deepcopy-gen=package -// +groupName=confidentialcontainers.org - -package v1alpha1 diff --git a/src/csi-wrapper/pkg/apis/peerpodvolume/v1alpha1/register.go b/src/csi-wrapper/pkg/apis/peerpodvolume/v1alpha1/register.go deleted file mode 100644 index 40136ccadf..0000000000 --- a/src/csi-wrapper/pkg/apis/peerpodvolume/v1alpha1/register.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright Confidential Containers Contributors -// SPDX-License-Identifier: Apache-2.0 - -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - - "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/apis/peerpodvolume" -) - -// SchemeGroupVersion is group version used to register these objects -var SchemeGroupVersion = schema.GroupVersion{Group: peerpodvolume.GroupName, Version: peerpodvolume.Version} - -// Kind takes an unqualified kind and returns back a Group qualified GroupKind -func Kind(kind string) schema.GroupKind { - return SchemeGroupVersion.WithKind(kind).GroupKind() -} - -// Resource takes an unqualified resource and returns a Group qualified GroupResource -func Resource(resource string) schema.GroupResource { - return SchemeGroupVersion.WithResource(resource).GroupResource() -} - -var ( - // SchemeBuilder initializes a scheme builder - SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) - // AddToScheme is a global function that registers this API group & version to a scheme - AddToScheme = SchemeBuilder.AddToScheme -) - -// Adds the list of known types to Scheme. -func addKnownTypes(scheme *runtime.Scheme) error { - scheme.AddKnownTypes(SchemeGroupVersion, - &PeerpodVolume{}, - &PeerpodVolumeList{}, - ) - metav1.AddToGroupVersion(scheme, SchemeGroupVersion) - return nil -} diff --git a/src/csi-wrapper/pkg/apis/peerpodvolume/v1alpha1/types.go b/src/csi-wrapper/pkg/apis/peerpodvolume/v1alpha1/types.go deleted file mode 100644 index 61cdf21c24..0000000000 --- a/src/csi-wrapper/pkg/apis/peerpodvolume/v1alpha1/types.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright Confidential Containers Contributors -// SPDX-License-Identifier: Apache-2.0 - -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// +genclient -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// PeerpodVolume is a specification for a PeerpodVolume resource -type PeerpodVolume struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec PeerpodVolumeSpec `json:"spec"` - Status PeerpodVolumeStatus `json:"status,omitempty"` -} - -// PeerpodVolumeSpec is the spec for a PeerpodVolume resource -type PeerpodVolumeSpec struct { - PodName string `json:"podName"` - PodNamespace string `json:"podNamespace"` - PodUID string `json:"podUid"` - NodeID string `json:"nodeID"` - NodeName string `json:"nodeName"` - VolumeID string `json:"volumeID"` - VolumeName string `json:"volumeName"` - VMID string `json:"vmID"` - VMName string `json:"vmName"` - DevicePath string `json:"devicePath"` - StagingTargetPath string `json:"stagingTargetPath"` - TargetPath string `json:"targetPath"` - WrapperControllerPublishVolumeReq string `json:"wrapperControllerPublishVolumeReq"` - WrapperControllerPublishVolumeRes string `json:"wrapperControllerPublishVolumeRes"` - WrapperNodeStageVolumeReq string `json:"wrapperNodeStageVolumeReq"` - WrapperNodePublishVolumeReq string `json:"wrapperNodePublishVolumeReq"` - WrapperNodeUnpublishVolumeReq string `json:"wrapperNodeUnpublishVolumeReq"` - WrapperNodeUnstageVolumeReq string `json:"wrapperNodeUnstageVolumeReq"` -} - -type PeerpodVolumeState string - -const ( - // CSI-requests be cached to crd - ControllerPublishVolumeCached PeerpodVolumeState = "controllerPublishVolumeCached" - NodeStageVolumeCached PeerpodVolumeState = "nodeStageVolumeCached" - NodePublishVolumeCached PeerpodVolumeState = "nodePublishVolumeCached" - NodeUnpublishVolumeCached PeerpodVolumeState = "nodeUnpublishVolumeCached" - NodeUnstageVolumeCached PeerpodVolumeState = "nodeUnstageVolumeCached" - // The VSI instance id MUST be set when update the status to `peerPodVSIIDReady` - PeerPodVSIIDReady PeerpodVolumeState = "peerPodVSIIDReady" - // We can get the VSI instance from cloud-api-adaptor podVMInfoService when update the status to `peerPodVSIRunning` - PeerPodVSIRunning PeerpodVolumeState = "peerPodVSIRunning" - // The cached ControllerPublishVolume, NodeStageVolume, NodePublishVolume will be reproduced with replace peer-pod instance-id - // before peer-pod container is created - ControllerPublishVolumeApplied PeerpodVolumeState = "controllerPublishVolumeApplied" - NodeStageVolumeApplied PeerpodVolumeState = "nodeStageVolumeApplied" - NodePublishVolumeApplied PeerpodVolumeState = "nodePublishVolumeApplied" - // csi-wrapper plugins will call original csi-driver to release volumes when peer-pod be deleted - NodeUnpublishVolumeApplied PeerpodVolumeState = "nodeUnpublishVolumeApplied" - NodeUnstageVolumeApplied PeerpodVolumeState = "nodeUnstageVolumeApplied" - ControllerUnpublishVolumeApplied PeerpodVolumeState = "controllerUnpublishVolumeApplied" -) - -// PeerpodVolumeStatus is the status for a PeerpodVolume resource -type PeerpodVolumeStatus struct { - State PeerpodVolumeState `json:"state"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// PeerpodVolumeList is a list of PeerpodVolume resources -type PeerpodVolumeList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - - Items []PeerpodVolume `json:"items"` -} diff --git a/src/csi-wrapper/pkg/apis/peerpodvolume/v1alpha1/zz_generated.deepcopy.go b/src/csi-wrapper/pkg/apis/peerpodvolume/v1alpha1/zz_generated.deepcopy.go deleted file mode 100644 index 226f617e41..0000000000 --- a/src/csi-wrapper/pkg/apis/peerpodvolume/v1alpha1/zz_generated.deepcopy.go +++ /dev/null @@ -1,103 +0,0 @@ -//go:build !ignore_autogenerated -// +build !ignore_autogenerated - -// Code generated by deepcopy-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - runtime "k8s.io/apimachinery/pkg/runtime" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PeerpodVolume) DeepCopyInto(out *PeerpodVolume) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - out.Status = in.Status - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PeerpodVolume. -func (in *PeerpodVolume) DeepCopy() *PeerpodVolume { - if in == nil { - return nil - } - out := new(PeerpodVolume) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *PeerpodVolume) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PeerpodVolumeList) DeepCopyInto(out *PeerpodVolumeList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]PeerpodVolume, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PeerpodVolumeList. -func (in *PeerpodVolumeList) DeepCopy() *PeerpodVolumeList { - if in == nil { - return nil - } - out := new(PeerpodVolumeList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *PeerpodVolumeList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PeerpodVolumeSpec) DeepCopyInto(out *PeerpodVolumeSpec) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PeerpodVolumeSpec. -func (in *PeerpodVolumeSpec) DeepCopy() *PeerpodVolumeSpec { - if in == nil { - return nil - } - out := new(PeerpodVolumeSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PeerpodVolumeStatus) DeepCopyInto(out *PeerpodVolumeStatus) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PeerpodVolumeStatus. -func (in *PeerpodVolumeStatus) DeepCopy() *PeerpodVolumeStatus { - if in == nil { - return nil - } - out := new(PeerpodVolumeStatus) - in.DeepCopyInto(out) - return out -} diff --git a/src/csi-wrapper/pkg/config/config.go b/src/csi-wrapper/pkg/config/config.go deleted file mode 100644 index 94ad079528..0000000000 --- a/src/csi-wrapper/pkg/config/config.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright Confidential Containers Contributors -// SPDX-License-Identifier: Apache-2.0 - -package config - -type Endpoints struct { - Endpoint string - TargetEndpoint string - Namespace string - VMIDInformationEndpoint string -} diff --git a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/clientset.go b/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/clientset.go deleted file mode 100644 index 69e126cb57..0000000000 --- a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/clientset.go +++ /dev/null @@ -1,104 +0,0 @@ -// Code generated by client-gen. DO NOT EDIT. - -package versioned - -import ( - "fmt" - "net/http" - - confidentialcontainersv1alpha1 "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/typed/peerpodvolume/v1alpha1" - discovery "k8s.io/client-go/discovery" - rest "k8s.io/client-go/rest" - flowcontrol "k8s.io/client-go/util/flowcontrol" -) - -type Interface interface { - Discovery() discovery.DiscoveryInterface - ConfidentialcontainersV1alpha1() confidentialcontainersv1alpha1.ConfidentialcontainersV1alpha1Interface -} - -// Clientset contains the clients for groups. -type Clientset struct { - *discovery.DiscoveryClient - confidentialcontainersV1alpha1 *confidentialcontainersv1alpha1.ConfidentialcontainersV1alpha1Client -} - -// ConfidentialcontainersV1alpha1 retrieves the ConfidentialcontainersV1alpha1Client -func (c *Clientset) ConfidentialcontainersV1alpha1() confidentialcontainersv1alpha1.ConfidentialcontainersV1alpha1Interface { - return c.confidentialcontainersV1alpha1 -} - -// Discovery retrieves the DiscoveryClient -func (c *Clientset) Discovery() discovery.DiscoveryInterface { - if c == nil { - return nil - } - return c.DiscoveryClient -} - -// NewForConfig creates a new Clientset for the given config. -// If config's RateLimiter is not set and QPS and Burst are acceptable, -// NewForConfig will generate a rate-limiter in configShallowCopy. -// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), -// where httpClient was generated with rest.HTTPClientFor(c). -func NewForConfig(c *rest.Config) (*Clientset, error) { - configShallowCopy := *c - - if configShallowCopy.UserAgent == "" { - configShallowCopy.UserAgent = rest.DefaultKubernetesUserAgent() - } - - // share the transport between all clients - httpClient, err := rest.HTTPClientFor(&configShallowCopy) - if err != nil { - return nil, err - } - - return NewForConfigAndClient(&configShallowCopy, httpClient) -} - -// NewForConfigAndClient creates a new Clientset for the given config and http client. -// Note the http client provided takes precedence over the configured transport values. -// If config's RateLimiter is not set and QPS and Burst are acceptable, -// NewForConfigAndClient will generate a rate-limiter in configShallowCopy. -func NewForConfigAndClient(c *rest.Config, httpClient *http.Client) (*Clientset, error) { - configShallowCopy := *c - if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { - if configShallowCopy.Burst <= 0 { - return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0") - } - configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) - } - - var cs Clientset - var err error - cs.confidentialcontainersV1alpha1, err = confidentialcontainersv1alpha1.NewForConfigAndClient(&configShallowCopy, httpClient) - if err != nil { - return nil, err - } - - cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfigAndClient(&configShallowCopy, httpClient) - if err != nil { - return nil, err - } - return &cs, nil -} - -// NewForConfigOrDie creates a new Clientset for the given config and -// panics if there is an error in the config. -func NewForConfigOrDie(c *rest.Config) *Clientset { - cs, err := NewForConfig(c) - if err != nil { - panic(err) - } - return cs -} - -// New creates a new Clientset for the given RESTClient. -func New(c rest.Interface) *Clientset { - var cs Clientset - cs.confidentialcontainersV1alpha1 = confidentialcontainersv1alpha1.New(c) - - cs.DiscoveryClient = discovery.NewDiscoveryClient(c) - return &cs -} diff --git a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/doc.go b/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/doc.go deleted file mode 100644 index 0e0c2a8900..0000000000 --- a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/doc.go +++ /dev/null @@ -1,4 +0,0 @@ -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated clientset. -package versioned diff --git a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/fake/clientset_generated.go b/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/fake/clientset_generated.go deleted file mode 100644 index 9a779aa9db..0000000000 --- a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/fake/clientset_generated.go +++ /dev/null @@ -1,69 +0,0 @@ -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - clientset "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned" - confidentialcontainersv1alpha1 "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/typed/peerpodvolume/v1alpha1" - fakeconfidentialcontainersv1alpha1 "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/typed/peerpodvolume/v1alpha1/fake" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/discovery" - fakediscovery "k8s.io/client-go/discovery/fake" - "k8s.io/client-go/testing" -) - -// NewSimpleClientset returns a clientset that will respond with the provided objects. -// It's backed by a very simple object tracker that processes creates, updates and deletions as-is, -// without applying any validations and/or defaults. It shouldn't be considered a replacement -// for a real clientset and is mostly useful in simple unit tests. -func NewSimpleClientset(objects ...runtime.Object) *Clientset { - o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) - for _, obj := range objects { - if err := o.Add(obj); err != nil { - panic(err) - } - } - - cs := &Clientset{tracker: o} - cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} - cs.AddReactor("*", "*", testing.ObjectReaction(o)) - cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { - gvr := action.GetResource() - ns := action.GetNamespace() - watch, err := o.Watch(gvr, ns) - if err != nil { - return false, nil, err - } - return true, watch, nil - }) - - return cs -} - -// Clientset implements clientset.Interface. Meant to be embedded into a -// struct to get a default implementation. This makes faking out just the method -// you want to test easier. -type Clientset struct { - testing.Fake - discovery *fakediscovery.FakeDiscovery - tracker testing.ObjectTracker -} - -func (c *Clientset) Discovery() discovery.DiscoveryInterface { - return c.discovery -} - -func (c *Clientset) Tracker() testing.ObjectTracker { - return c.tracker -} - -var ( - _ clientset.Interface = &Clientset{} - _ testing.FakeClient = &Clientset{} -) - -// ConfidentialcontainersV1alpha1 retrieves the ConfidentialcontainersV1alpha1Client -func (c *Clientset) ConfidentialcontainersV1alpha1() confidentialcontainersv1alpha1.ConfidentialcontainersV1alpha1Interface { - return &fakeconfidentialcontainersv1alpha1.FakeConfidentialcontainersV1alpha1{Fake: &c.Fake} -} diff --git a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/fake/doc.go b/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/fake/doc.go deleted file mode 100644 index 3630ed1cd1..0000000000 --- a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/fake/doc.go +++ /dev/null @@ -1,4 +0,0 @@ -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated fake clientset. -package fake diff --git a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/fake/register.go b/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/fake/register.go deleted file mode 100644 index aa439c3339..0000000000 --- a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/fake/register.go +++ /dev/null @@ -1,40 +0,0 @@ -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - confidentialcontainersv1alpha1 "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/apis/peerpodvolume/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - schema "k8s.io/apimachinery/pkg/runtime/schema" - serializer "k8s.io/apimachinery/pkg/runtime/serializer" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" -) - -var scheme = runtime.NewScheme() -var codecs = serializer.NewCodecFactory(scheme) - -var localSchemeBuilder = runtime.SchemeBuilder{ - confidentialcontainersv1alpha1.AddToScheme, -} - -// AddToScheme adds all types of this clientset into the given scheme. This allows composition -// of clientsets, like in: -// -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) -// -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) -// -// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types -// correctly. -var AddToScheme = localSchemeBuilder.AddToScheme - -func init() { - v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) - utilruntime.Must(AddToScheme(scheme)) -} diff --git a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/scheme/doc.go b/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/scheme/doc.go deleted file mode 100644 index 14db57a58f..0000000000 --- a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/scheme/doc.go +++ /dev/null @@ -1,4 +0,0 @@ -// Code generated by client-gen. DO NOT EDIT. - -// This package contains the scheme of the automatically generated clientset. -package scheme diff --git a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/scheme/register.go b/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/scheme/register.go deleted file mode 100644 index 2f63191012..0000000000 --- a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/scheme/register.go +++ /dev/null @@ -1,40 +0,0 @@ -// Code generated by client-gen. DO NOT EDIT. - -package scheme - -import ( - confidentialcontainersv1alpha1 "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/apis/peerpodvolume/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - schema "k8s.io/apimachinery/pkg/runtime/schema" - serializer "k8s.io/apimachinery/pkg/runtime/serializer" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" -) - -var Scheme = runtime.NewScheme() -var Codecs = serializer.NewCodecFactory(Scheme) -var ParameterCodec = runtime.NewParameterCodec(Scheme) -var localSchemeBuilder = runtime.SchemeBuilder{ - confidentialcontainersv1alpha1.AddToScheme, -} - -// AddToScheme adds all types of this clientset into the given scheme. This allows composition -// of clientsets, like in: -// -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) -// -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) -// -// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types -// correctly. -var AddToScheme = localSchemeBuilder.AddToScheme - -func init() { - v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) - utilruntime.Must(AddToScheme(Scheme)) -} diff --git a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/typed/peerpodvolume/v1alpha1/doc.go b/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/typed/peerpodvolume/v1alpha1/doc.go deleted file mode 100644 index 93a7ca4e0e..0000000000 --- a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/typed/peerpodvolume/v1alpha1/doc.go +++ /dev/null @@ -1,4 +0,0 @@ -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated typed clients. -package v1alpha1 diff --git a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/typed/peerpodvolume/v1alpha1/fake/doc.go b/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/typed/peerpodvolume/v1alpha1/fake/doc.go deleted file mode 100644 index 2b5ba4c8e4..0000000000 --- a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/typed/peerpodvolume/v1alpha1/fake/doc.go +++ /dev/null @@ -1,4 +0,0 @@ -// Code generated by client-gen. DO NOT EDIT. - -// Package fake has the automatically generated clients. -package fake diff --git a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/typed/peerpodvolume/v1alpha1/fake/fake_peerpodvolume.go b/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/typed/peerpodvolume/v1alpha1/fake/fake_peerpodvolume.go deleted file mode 100644 index a58132c4e1..0000000000 --- a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/typed/peerpodvolume/v1alpha1/fake/fake_peerpodvolume.go +++ /dev/null @@ -1,126 +0,0 @@ -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - "context" - - v1alpha1 "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/apis/peerpodvolume/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakePeerpodVolumes implements PeerpodVolumeInterface -type FakePeerpodVolumes struct { - Fake *FakeConfidentialcontainersV1alpha1 - ns string -} - -var peerpodvolumesResource = schema.GroupVersionResource{Group: "confidentialcontainers.org", Version: "v1alpha1", Resource: "peerpodvolumes"} - -var peerpodvolumesKind = schema.GroupVersionKind{Group: "confidentialcontainers.org", Version: "v1alpha1", Kind: "PeerpodVolume"} - -// Get takes name of the peerpodVolume, and returns the corresponding peerpodVolume object, and an error if there is any. -func (c *FakePeerpodVolumes) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.PeerpodVolume, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(peerpodvolumesResource, c.ns, name), &v1alpha1.PeerpodVolume{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.PeerpodVolume), err -} - -// List takes label and field selectors, and returns the list of PeerpodVolumes that match those selectors. -func (c *FakePeerpodVolumes) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.PeerpodVolumeList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(peerpodvolumesResource, peerpodvolumesKind, c.ns, opts), &v1alpha1.PeerpodVolumeList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.PeerpodVolumeList{ListMeta: obj.(*v1alpha1.PeerpodVolumeList).ListMeta} - for _, item := range obj.(*v1alpha1.PeerpodVolumeList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested peerpodVolumes. -func (c *FakePeerpodVolumes) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(peerpodvolumesResource, c.ns, opts)) - -} - -// Create takes the representation of a peerpodVolume and creates it. Returns the server's representation of the peerpodVolume, and an error, if there is any. -func (c *FakePeerpodVolumes) Create(ctx context.Context, peerpodVolume *v1alpha1.PeerpodVolume, opts v1.CreateOptions) (result *v1alpha1.PeerpodVolume, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(peerpodvolumesResource, c.ns, peerpodVolume), &v1alpha1.PeerpodVolume{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.PeerpodVolume), err -} - -// Update takes the representation of a peerpodVolume and updates it. Returns the server's representation of the peerpodVolume, and an error, if there is any. -func (c *FakePeerpodVolumes) Update(ctx context.Context, peerpodVolume *v1alpha1.PeerpodVolume, opts v1.UpdateOptions) (result *v1alpha1.PeerpodVolume, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(peerpodvolumesResource, c.ns, peerpodVolume), &v1alpha1.PeerpodVolume{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.PeerpodVolume), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakePeerpodVolumes) UpdateStatus(ctx context.Context, peerpodVolume *v1alpha1.PeerpodVolume, opts v1.UpdateOptions) (*v1alpha1.PeerpodVolume, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(peerpodvolumesResource, "status", c.ns, peerpodVolume), &v1alpha1.PeerpodVolume{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.PeerpodVolume), err -} - -// Delete takes name of the peerpodVolume and deletes it. Returns an error if one occurs. -func (c *FakePeerpodVolumes) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteActionWithOptions(peerpodvolumesResource, c.ns, name, opts), &v1alpha1.PeerpodVolume{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakePeerpodVolumes) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(peerpodvolumesResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &v1alpha1.PeerpodVolumeList{}) - return err -} - -// Patch applies the patch and returns the patched peerpodVolume. -func (c *FakePeerpodVolumes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.PeerpodVolume, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(peerpodvolumesResource, c.ns, name, pt, data, subresources...), &v1alpha1.PeerpodVolume{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.PeerpodVolume), err -} diff --git a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/typed/peerpodvolume/v1alpha1/fake/fake_peerpodvolume_client.go b/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/typed/peerpodvolume/v1alpha1/fake/fake_peerpodvolume_client.go deleted file mode 100644 index 3a776b113c..0000000000 --- a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/typed/peerpodvolume/v1alpha1/fake/fake_peerpodvolume_client.go +++ /dev/null @@ -1,24 +0,0 @@ -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha1 "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/typed/peerpodvolume/v1alpha1" - rest "k8s.io/client-go/rest" - testing "k8s.io/client-go/testing" -) - -type FakeConfidentialcontainersV1alpha1 struct { - *testing.Fake -} - -func (c *FakeConfidentialcontainersV1alpha1) PeerpodVolumes(namespace string) v1alpha1.PeerpodVolumeInterface { - return &FakePeerpodVolumes{c, namespace} -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *FakeConfidentialcontainersV1alpha1) RESTClient() rest.Interface { - var ret *rest.RESTClient - return ret -} diff --git a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/typed/peerpodvolume/v1alpha1/generated_expansion.go b/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/typed/peerpodvolume/v1alpha1/generated_expansion.go deleted file mode 100644 index a48cfee399..0000000000 --- a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/typed/peerpodvolume/v1alpha1/generated_expansion.go +++ /dev/null @@ -1,5 +0,0 @@ -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -type PeerpodVolumeExpansion interface{} diff --git a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/typed/peerpodvolume/v1alpha1/peerpodvolume.go b/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/typed/peerpodvolume/v1alpha1/peerpodvolume.go deleted file mode 100644 index 3d2693dc70..0000000000 --- a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/typed/peerpodvolume/v1alpha1/peerpodvolume.go +++ /dev/null @@ -1,179 +0,0 @@ -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - "context" - "time" - - v1alpha1 "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/apis/peerpodvolume/v1alpha1" - scheme "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// PeerpodVolumesGetter has a method to return a PeerpodVolumeInterface. -// A group's client should implement this interface. -type PeerpodVolumesGetter interface { - PeerpodVolumes(namespace string) PeerpodVolumeInterface -} - -// PeerpodVolumeInterface has methods to work with PeerpodVolume resources. -type PeerpodVolumeInterface interface { - Create(ctx context.Context, peerpodVolume *v1alpha1.PeerpodVolume, opts v1.CreateOptions) (*v1alpha1.PeerpodVolume, error) - Update(ctx context.Context, peerpodVolume *v1alpha1.PeerpodVolume, opts v1.UpdateOptions) (*v1alpha1.PeerpodVolume, error) - UpdateStatus(ctx context.Context, peerpodVolume *v1alpha1.PeerpodVolume, opts v1.UpdateOptions) (*v1alpha1.PeerpodVolume, error) - Delete(ctx context.Context, name string, opts v1.DeleteOptions) error - DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error - Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.PeerpodVolume, error) - List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.PeerpodVolumeList, error) - Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.PeerpodVolume, err error) - PeerpodVolumeExpansion -} - -// peerpodVolumes implements PeerpodVolumeInterface -type peerpodVolumes struct { - client rest.Interface - ns string -} - -// newPeerpodVolumes returns a PeerpodVolumes -func newPeerpodVolumes(c *ConfidentialcontainersV1alpha1Client, namespace string) *peerpodVolumes { - return &peerpodVolumes{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the peerpodVolume, and returns the corresponding peerpodVolume object, and an error if there is any. -func (c *peerpodVolumes) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.PeerpodVolume, err error) { - result = &v1alpha1.PeerpodVolume{} - err = c.client.Get(). - Namespace(c.ns). - Resource("peerpodvolumes"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of PeerpodVolumes that match those selectors. -func (c *peerpodVolumes) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.PeerpodVolumeList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.PeerpodVolumeList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("peerpodvolumes"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested peerpodVolumes. -func (c *peerpodVolumes) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("peerpodvolumes"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a peerpodVolume and creates it. Returns the server's representation of the peerpodVolume, and an error, if there is any. -func (c *peerpodVolumes) Create(ctx context.Context, peerpodVolume *v1alpha1.PeerpodVolume, opts v1.CreateOptions) (result *v1alpha1.PeerpodVolume, err error) { - result = &v1alpha1.PeerpodVolume{} - err = c.client.Post(). - Namespace(c.ns). - Resource("peerpodvolumes"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(peerpodVolume). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a peerpodVolume and updates it. Returns the server's representation of the peerpodVolume, and an error, if there is any. -func (c *peerpodVolumes) Update(ctx context.Context, peerpodVolume *v1alpha1.PeerpodVolume, opts v1.UpdateOptions) (result *v1alpha1.PeerpodVolume, err error) { - result = &v1alpha1.PeerpodVolume{} - err = c.client.Put(). - Namespace(c.ns). - Resource("peerpodvolumes"). - Name(peerpodVolume.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(peerpodVolume). - Do(ctx). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *peerpodVolumes) UpdateStatus(ctx context.Context, peerpodVolume *v1alpha1.PeerpodVolume, opts v1.UpdateOptions) (result *v1alpha1.PeerpodVolume, err error) { - result = &v1alpha1.PeerpodVolume{} - err = c.client.Put(). - Namespace(c.ns). - Resource("peerpodvolumes"). - Name(peerpodVolume.Name). - SubResource("status"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(peerpodVolume). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the peerpodVolume and deletes it. Returns an error if one occurs. -func (c *peerpodVolumes) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("peerpodvolumes"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *peerpodVolumes) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("peerpodvolumes"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched peerpodVolume. -func (c *peerpodVolumes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.PeerpodVolume, err error) { - result = &v1alpha1.PeerpodVolume{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("peerpodvolumes"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/typed/peerpodvolume/v1alpha1/peerpodvolume_client.go b/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/typed/peerpodvolume/v1alpha1/peerpodvolume_client.go deleted file mode 100644 index ba1d69ef24..0000000000 --- a/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/typed/peerpodvolume/v1alpha1/peerpodvolume_client.go +++ /dev/null @@ -1,91 +0,0 @@ -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - "net/http" - - v1alpha1 "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/apis/peerpodvolume/v1alpha1" - "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned/scheme" - rest "k8s.io/client-go/rest" -) - -type ConfidentialcontainersV1alpha1Interface interface { - RESTClient() rest.Interface - PeerpodVolumesGetter -} - -// ConfidentialcontainersV1alpha1Client is used to interact with features provided by the confidentialcontainers.org group. -type ConfidentialcontainersV1alpha1Client struct { - restClient rest.Interface -} - -func (c *ConfidentialcontainersV1alpha1Client) PeerpodVolumes(namespace string) PeerpodVolumeInterface { - return newPeerpodVolumes(c, namespace) -} - -// NewForConfig creates a new ConfidentialcontainersV1alpha1Client for the given config. -// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), -// where httpClient was generated with rest.HTTPClientFor(c). -func NewForConfig(c *rest.Config) (*ConfidentialcontainersV1alpha1Client, error) { - config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } - httpClient, err := rest.HTTPClientFor(&config) - if err != nil { - return nil, err - } - return NewForConfigAndClient(&config, httpClient) -} - -// NewForConfigAndClient creates a new ConfidentialcontainersV1alpha1Client for the given config and http client. -// Note the http client provided takes precedence over the configured transport values. -func NewForConfigAndClient(c *rest.Config, h *http.Client) (*ConfidentialcontainersV1alpha1Client, error) { - config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } - client, err := rest.RESTClientForConfigAndClient(&config, h) - if err != nil { - return nil, err - } - return &ConfidentialcontainersV1alpha1Client{client}, nil -} - -// NewForConfigOrDie creates a new ConfidentialcontainersV1alpha1Client for the given config and -// panics if there is an error in the config. -func NewForConfigOrDie(c *rest.Config) *ConfidentialcontainersV1alpha1Client { - client, err := NewForConfig(c) - if err != nil { - panic(err) - } - return client -} - -// New creates a new ConfidentialcontainersV1alpha1Client for the given RESTClient. -func New(c rest.Interface) *ConfidentialcontainersV1alpha1Client { - return &ConfidentialcontainersV1alpha1Client{c} -} - -func setConfigDefaults(config *rest.Config) error { - gv := v1alpha1.SchemeGroupVersion - config.GroupVersion = &gv - config.APIPath = "/apis" - config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() - - if config.UserAgent == "" { - config.UserAgent = rest.DefaultKubernetesUserAgent() - } - - return nil -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *ConfidentialcontainersV1alpha1Client) RESTClient() rest.Interface { - if c == nil { - return nil - } - return c.restClient -} diff --git a/src/csi-wrapper/pkg/generated/peerpodvolume/informers/externalversions/factory.go b/src/csi-wrapper/pkg/generated/peerpodvolume/informers/externalversions/factory.go deleted file mode 100644 index 33eef6608c..0000000000 --- a/src/csi-wrapper/pkg/generated/peerpodvolume/informers/externalversions/factory.go +++ /dev/null @@ -1,235 +0,0 @@ -// Code generated by informer-gen. DO NOT EDIT. - -package externalversions - -import ( - reflect "reflect" - sync "sync" - time "time" - - versioned "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned" - internalinterfaces "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/informers/externalversions/internalinterfaces" - peerpodvolume "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/informers/externalversions/peerpodvolume" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - schema "k8s.io/apimachinery/pkg/runtime/schema" - cache "k8s.io/client-go/tools/cache" -) - -// SharedInformerOption defines the functional option type for SharedInformerFactory. -type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory - -type sharedInformerFactory struct { - client versioned.Interface - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc - lock sync.Mutex - defaultResync time.Duration - customResync map[reflect.Type]time.Duration - - informers map[reflect.Type]cache.SharedIndexInformer - // startedInformers is used for tracking which informers have been started. - // This allows Start() to be called multiple times safely. - startedInformers map[reflect.Type]bool - // wg tracks how many goroutines were started. - wg sync.WaitGroup - // shuttingDown is true when Shutdown has been called. It may still be running - // because it needs to wait for goroutines. - shuttingDown bool -} - -// WithCustomResyncConfig sets a custom resync period for the specified informer types. -func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption { - return func(factory *sharedInformerFactory) *sharedInformerFactory { - for k, v := range resyncConfig { - factory.customResync[reflect.TypeOf(k)] = v - } - return factory - } -} - -// WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory. -func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption { - return func(factory *sharedInformerFactory) *sharedInformerFactory { - factory.tweakListOptions = tweakListOptions - return factory - } -} - -// WithNamespace limits the SharedInformerFactory to the specified namespace. -func WithNamespace(namespace string) SharedInformerOption { - return func(factory *sharedInformerFactory) *sharedInformerFactory { - factory.namespace = namespace - return factory - } -} - -// NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces. -func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory { - return NewSharedInformerFactoryWithOptions(client, defaultResync) -} - -// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory. -// Listers obtained via this SharedInformerFactory will be subject to the same filters -// as specified here. -// Deprecated: Please use NewSharedInformerFactoryWithOptions instead -func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory { - return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions)) -} - -// NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options. -func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory { - factory := &sharedInformerFactory{ - client: client, - namespace: v1.NamespaceAll, - defaultResync: defaultResync, - informers: make(map[reflect.Type]cache.SharedIndexInformer), - startedInformers: make(map[reflect.Type]bool), - customResync: make(map[reflect.Type]time.Duration), - } - - // Apply all options - for _, opt := range options { - factory = opt(factory) - } - - return factory -} - -func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { - f.lock.Lock() - defer f.lock.Unlock() - - if f.shuttingDown { - return - } - - for informerType, informer := range f.informers { - if !f.startedInformers[informerType] { - f.wg.Add(1) - // We need a new variable in each loop iteration, - // otherwise the goroutine would use the loop variable - // and that keeps changing. - informer := informer - go func() { - defer f.wg.Done() - informer.Run(stopCh) - }() - f.startedInformers[informerType] = true - } - } -} - -func (f *sharedInformerFactory) Shutdown() { - f.lock.Lock() - f.shuttingDown = true - f.lock.Unlock() - - // Will return immediately if there is nothing to wait for. - f.wg.Wait() -} - -func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { - informers := func() map[reflect.Type]cache.SharedIndexInformer { - f.lock.Lock() - defer f.lock.Unlock() - - informers := map[reflect.Type]cache.SharedIndexInformer{} - for informerType, informer := range f.informers { - if f.startedInformers[informerType] { - informers[informerType] = informer - } - } - return informers - }() - - res := map[reflect.Type]bool{} - for informType, informer := range informers { - res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) - } - return res -} - -// InternalInformerFor returns the SharedIndexInformer for obj using an internal -// client. -func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { - f.lock.Lock() - defer f.lock.Unlock() - - informerType := reflect.TypeOf(obj) - informer, exists := f.informers[informerType] - if exists { - return informer - } - - resyncPeriod, exists := f.customResync[informerType] - if !exists { - resyncPeriod = f.defaultResync - } - - informer = newFunc(f.client, resyncPeriod) - f.informers[informerType] = informer - - return informer -} - -// SharedInformerFactory provides shared informers for resources in all known -// API group versions. -// -// It is typically used like this: -// -// ctx, cancel := context.Background() -// defer cancel() -// factory := NewSharedInformerFactory(client, resyncPeriod) -// defer factory.WaitForStop() // Returns immediately if nothing was started. -// genericInformer := factory.ForResource(resource) -// typedInformer := factory.SomeAPIGroup().V1().SomeType() -// factory.Start(ctx.Done()) // Start processing these informers. -// synced := factory.WaitForCacheSync(ctx.Done()) -// for v, ok := range synced { -// if !ok { -// fmt.Fprintf(os.Stderr, "caches failed to sync: %v", v) -// return -// } -// } -// -// // Creating informers can also be created after Start, but then -// // Start must be called again: -// anotherGenericInformer := factory.ForResource(resource) -// factory.Start(ctx.Done()) -type SharedInformerFactory interface { - internalinterfaces.SharedInformerFactory - - // Start initializes all requested informers. They are handled in goroutines - // which run until the stop channel gets closed. - Start(stopCh <-chan struct{}) - - // Shutdown marks a factory as shutting down. At that point no new - // informers can be started anymore and Start will return without - // doing anything. - // - // In addition, Shutdown blocks until all goroutines have terminated. For that - // to happen, the close channel(s) that they were started with must be closed, - // either before Shutdown gets called or while it is waiting. - // - // Shutdown may be called multiple times, even concurrently. All such calls will - // block until all goroutines have terminated. - Shutdown() - - // WaitForCacheSync blocks until all started informers' caches were synced - // or the stop channel gets closed. - WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool - - // ForResource gives generic access to a shared informer of the matching type. - ForResource(resource schema.GroupVersionResource) (GenericInformer, error) - - // InternalInformerFor returns the SharedIndexInformer for obj using an internal - // client. - InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer - - Confidentialcontainers() peerpodvolume.Interface -} - -func (f *sharedInformerFactory) Confidentialcontainers() peerpodvolume.Interface { - return peerpodvolume.New(f, f.namespace, f.tweakListOptions) -} diff --git a/src/csi-wrapper/pkg/generated/peerpodvolume/informers/externalversions/generic.go b/src/csi-wrapper/pkg/generated/peerpodvolume/informers/externalversions/generic.go deleted file mode 100644 index bd17da16ce..0000000000 --- a/src/csi-wrapper/pkg/generated/peerpodvolume/informers/externalversions/generic.go +++ /dev/null @@ -1,46 +0,0 @@ -// Code generated by informer-gen. DO NOT EDIT. - -package externalversions - -import ( - "fmt" - - v1alpha1 "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/apis/peerpodvolume/v1alpha1" - schema "k8s.io/apimachinery/pkg/runtime/schema" - cache "k8s.io/client-go/tools/cache" -) - -// GenericInformer is type of SharedIndexInformer which will locate and delegate to other -// sharedInformers based on type -type GenericInformer interface { - Informer() cache.SharedIndexInformer - Lister() cache.GenericLister -} - -type genericInformer struct { - informer cache.SharedIndexInformer - resource schema.GroupResource -} - -// Informer returns the SharedIndexInformer. -func (f *genericInformer) Informer() cache.SharedIndexInformer { - return f.informer -} - -// Lister returns the GenericLister. -func (f *genericInformer) Lister() cache.GenericLister { - return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) -} - -// ForResource gives generic access to a shared informer of the matching type -// TODO extend this to unknown resources with a client pool -func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { - switch resource { - // Group=confidentialcontainers.org, Version=v1alpha1 - case v1alpha1.SchemeGroupVersion.WithResource("peerpodvolumes"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Confidentialcontainers().V1alpha1().PeerpodVolumes().Informer()}, nil - - } - - return nil, fmt.Errorf("no informer found for %v", resource) -} diff --git a/src/csi-wrapper/pkg/generated/peerpodvolume/informers/externalversions/internalinterfaces/factory_interfaces.go b/src/csi-wrapper/pkg/generated/peerpodvolume/informers/externalversions/internalinterfaces/factory_interfaces.go deleted file mode 100644 index 4206540bdb..0000000000 --- a/src/csi-wrapper/pkg/generated/peerpodvolume/informers/externalversions/internalinterfaces/factory_interfaces.go +++ /dev/null @@ -1,24 +0,0 @@ -// Code generated by informer-gen. DO NOT EDIT. - -package internalinterfaces - -import ( - time "time" - - versioned "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - cache "k8s.io/client-go/tools/cache" -) - -// NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer. -type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer - -// SharedInformerFactory a small interface to allow for adding an informer without an import cycle -type SharedInformerFactory interface { - Start(stopCh <-chan struct{}) - InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer -} - -// TweakListOptionsFunc is a function that transforms a v1.ListOptions. -type TweakListOptionsFunc func(*v1.ListOptions) diff --git a/src/csi-wrapper/pkg/generated/peerpodvolume/informers/externalversions/peerpodvolume/interface.go b/src/csi-wrapper/pkg/generated/peerpodvolume/informers/externalversions/peerpodvolume/interface.go deleted file mode 100644 index 47f97605c8..0000000000 --- a/src/csi-wrapper/pkg/generated/peerpodvolume/informers/externalversions/peerpodvolume/interface.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated by informer-gen. DO NOT EDIT. - -package peerpodvolume - -import ( - internalinterfaces "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/informers/externalversions/peerpodvolume/v1alpha1" -) - -// Interface provides access to each of this group's versions. -type Interface interface { - // V1alpha1 provides access to shared informers for resources in V1alpha1. - V1alpha1() v1alpha1.Interface -} - -type group struct { - factory internalinterfaces.SharedInformerFactory - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// New returns a new Interface. -func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { - return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} -} - -// V1alpha1 returns a new v1alpha1.Interface. -func (g *group) V1alpha1() v1alpha1.Interface { - return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions) -} diff --git a/src/csi-wrapper/pkg/generated/peerpodvolume/informers/externalversions/peerpodvolume/v1alpha1/interface.go b/src/csi-wrapper/pkg/generated/peerpodvolume/informers/externalversions/peerpodvolume/v1alpha1/interface.go deleted file mode 100644 index 9208c6c434..0000000000 --- a/src/csi-wrapper/pkg/generated/peerpodvolume/informers/externalversions/peerpodvolume/v1alpha1/interface.go +++ /dev/null @@ -1,29 +0,0 @@ -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - internalinterfaces "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/informers/externalversions/internalinterfaces" -) - -// Interface provides access to all the informers in this group version. -type Interface interface { - // PeerpodVolumes returns a PeerpodVolumeInformer. - PeerpodVolumes() PeerpodVolumeInformer -} - -type version struct { - factory internalinterfaces.SharedInformerFactory - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// New returns a new Interface. -func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { - return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} -} - -// PeerpodVolumes returns a PeerpodVolumeInformer. -func (v *version) PeerpodVolumes() PeerpodVolumeInformer { - return &peerpodVolumeInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} diff --git a/src/csi-wrapper/pkg/generated/peerpodvolume/informers/externalversions/peerpodvolume/v1alpha1/peerpodvolume.go b/src/csi-wrapper/pkg/generated/peerpodvolume/informers/externalversions/peerpodvolume/v1alpha1/peerpodvolume.go deleted file mode 100644 index 4902d1eed2..0000000000 --- a/src/csi-wrapper/pkg/generated/peerpodvolume/informers/externalversions/peerpodvolume/v1alpha1/peerpodvolume.go +++ /dev/null @@ -1,74 +0,0 @@ -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - "context" - time "time" - - peerpodvolumev1alpha1 "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/apis/peerpodvolume/v1alpha1" - versioned "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned" - internalinterfaces "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/listers/peerpodvolume/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// PeerpodVolumeInformer provides access to a shared informer and lister for -// PeerpodVolumes. -type PeerpodVolumeInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1alpha1.PeerpodVolumeLister -} - -type peerpodVolumeInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewPeerpodVolumeInformer constructs a new informer for PeerpodVolume type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewPeerpodVolumeInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredPeerpodVolumeInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredPeerpodVolumeInformer constructs a new informer for PeerpodVolume type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredPeerpodVolumeInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options v1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.ConfidentialcontainersV1alpha1().PeerpodVolumes(namespace).List(context.TODO(), options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.ConfidentialcontainersV1alpha1().PeerpodVolumes(namespace).Watch(context.TODO(), options) - }, - }, - &peerpodvolumev1alpha1.PeerpodVolume{}, - resyncPeriod, - indexers, - ) -} - -func (f *peerpodVolumeInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredPeerpodVolumeInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *peerpodVolumeInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&peerpodvolumev1alpha1.PeerpodVolume{}, f.defaultInformer) -} - -func (f *peerpodVolumeInformer) Lister() v1alpha1.PeerpodVolumeLister { - return v1alpha1.NewPeerpodVolumeLister(f.Informer().GetIndexer()) -} diff --git a/src/csi-wrapper/pkg/generated/peerpodvolume/listers/peerpodvolume/v1alpha1/expansion_generated.go b/src/csi-wrapper/pkg/generated/peerpodvolume/listers/peerpodvolume/v1alpha1/expansion_generated.go deleted file mode 100644 index e95a01bb09..0000000000 --- a/src/csi-wrapper/pkg/generated/peerpodvolume/listers/peerpodvolume/v1alpha1/expansion_generated.go +++ /dev/null @@ -1,11 +0,0 @@ -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha1 - -// PeerpodVolumeListerExpansion allows custom methods to be added to -// PeerpodVolumeLister. -type PeerpodVolumeListerExpansion interface{} - -// PeerpodVolumeNamespaceListerExpansion allows custom methods to be added to -// PeerpodVolumeNamespaceLister. -type PeerpodVolumeNamespaceListerExpansion interface{} diff --git a/src/csi-wrapper/pkg/generated/peerpodvolume/listers/peerpodvolume/v1alpha1/peerpodvolume.go b/src/csi-wrapper/pkg/generated/peerpodvolume/listers/peerpodvolume/v1alpha1/peerpodvolume.go deleted file mode 100644 index 2b9fd144b7..0000000000 --- a/src/csi-wrapper/pkg/generated/peerpodvolume/listers/peerpodvolume/v1alpha1/peerpodvolume.go +++ /dev/null @@ -1,83 +0,0 @@ -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/apis/peerpodvolume/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// PeerpodVolumeLister helps list PeerpodVolumes. -// All objects returned here must be treated as read-only. -type PeerpodVolumeLister interface { - // List lists all PeerpodVolumes in the indexer. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.PeerpodVolume, err error) - // PeerpodVolumes returns an object that can list and get PeerpodVolumes. - PeerpodVolumes(namespace string) PeerpodVolumeNamespaceLister - PeerpodVolumeListerExpansion -} - -// peerpodVolumeLister implements the PeerpodVolumeLister interface. -type peerpodVolumeLister struct { - indexer cache.Indexer -} - -// NewPeerpodVolumeLister returns a new PeerpodVolumeLister. -func NewPeerpodVolumeLister(indexer cache.Indexer) PeerpodVolumeLister { - return &peerpodVolumeLister{indexer: indexer} -} - -// List lists all PeerpodVolumes in the indexer. -func (s *peerpodVolumeLister) List(selector labels.Selector) (ret []*v1alpha1.PeerpodVolume, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.PeerpodVolume)) - }) - return ret, err -} - -// PeerpodVolumes returns an object that can list and get PeerpodVolumes. -func (s *peerpodVolumeLister) PeerpodVolumes(namespace string) PeerpodVolumeNamespaceLister { - return peerpodVolumeNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// PeerpodVolumeNamespaceLister helps list and get PeerpodVolumes. -// All objects returned here must be treated as read-only. -type PeerpodVolumeNamespaceLister interface { - // List lists all PeerpodVolumes in the indexer for a given namespace. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.PeerpodVolume, err error) - // Get retrieves the PeerpodVolume from the indexer for a given namespace and name. - // Objects returned here must be treated as read-only. - Get(name string) (*v1alpha1.PeerpodVolume, error) - PeerpodVolumeNamespaceListerExpansion -} - -// peerpodVolumeNamespaceLister implements the PeerpodVolumeNamespaceLister -// interface. -type peerpodVolumeNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all PeerpodVolumes in the indexer for a given namespace. -func (s peerpodVolumeNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.PeerpodVolume, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.PeerpodVolume)) - }) - return ret, err -} - -// Get retrieves the PeerpodVolume from the indexer for a given namespace and name. -func (s peerpodVolumeNamespaceLister) Get(name string) (*v1alpha1.PeerpodVolume, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("peerpodvolume"), name) - } - return obj.(*v1alpha1.PeerpodVolume), nil -} diff --git a/src/csi-wrapper/pkg/peerpodvolume/controller.go b/src/csi-wrapper/pkg/peerpodvolume/controller.go deleted file mode 100644 index 817ac4c2f7..0000000000 --- a/src/csi-wrapper/pkg/peerpodvolume/controller.go +++ /dev/null @@ -1,168 +0,0 @@ -package peerpodvolume - -import ( - "encoding/json" - "fmt" - "time" - - peerpodvolumeV1alpha1 "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/apis/peerpodvolume/v1alpha1" - clientset "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned" - informersv1alpha1 "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/informers/externalversions/peerpodvolume/v1alpha1" - listers "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/listers/peerpodvolume/v1alpha1" - "github.com/golang/glog" - kubeErrors "k8s.io/apimachinery/pkg/api/errors" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/util/workqueue" -) - -// PeerpodvolumeController is the controller implementation for peerpodvolume resources -type PeerpodvolumeController struct { - namespace string - clientset clientset.Interface - lister listers.PeerpodVolumeLister - synced cache.InformerSynced - queue workqueue.TypedRateLimitingInterface[string] - syncFunction func(peerPodVolume *peerpodvolumeV1alpha1.PeerpodVolume) - deleteFunction func(peerPodVolume *peerpodvolumeV1alpha1.PeerpodVolume) -} - -// newPeerpodvolumeController returns a new sample controller -func newPeerpodvolumeController( - clientset clientset.Interface, - informer informersv1alpha1.PeerpodVolumeInformer, - namespace string, - syncFunction func(peerPodVolume *peerpodvolumeV1alpha1.PeerpodVolume), - deleteFunction func(peerPodVolume *peerpodvolumeV1alpha1.PeerpodVolume), -) *PeerpodvolumeController { - - controller := &PeerpodvolumeController{ - namespace: namespace, - clientset: clientset, - lister: informer.Lister(), - synced: informer.Informer().HasSynced, - queue: workqueue.NewTypedRateLimitingQueueWithConfig(workqueue.DefaultTypedControllerRateLimiter[string](), workqueue.TypedRateLimitingQueueConfig[string]{Name: "Peerpodvolumes"}), - syncFunction: syncFunction, - deleteFunction: deleteFunction, - } - - // TODO: error check - _, _ = informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: controller.enqueuePeerpodvolume, - UpdateFunc: func(old, new interface{}) { - oldPeerpodvolume := old.(*peerpodvolumeV1alpha1.PeerpodVolume) - newPeerpodvolume := new.(*peerpodvolumeV1alpha1.PeerpodVolume) - if oldPeerpodvolume.ResourceVersion == newPeerpodvolume.ResourceVersion { - return - } - controller.enqueuePeerpodvolume(new) - }, - DeleteFunc: controller.handleDeletedPeerpodvolume, - }) - - return controller -} - -func (c *PeerpodvolumeController) handleDeletedPeerpodvolume(obj interface{}) { - var peerPodVolume *peerpodvolumeV1alpha1.PeerpodVolume - - peerPodVolume, ok := obj.(*peerpodvolumeV1alpha1.PeerpodVolume) - if !ok { - glog.Infof("Not a Peerpodvolume object: %v", obj) - return - } - volumeID := peerPodVolume.Spec.VolumeID - glog.Infof("Got deleted csi peer pod volume Info for %s", volumeID) - // call deleteFunction from node service or controller service - if c.deleteFunction != nil { - c.deleteFunction(peerPodVolume) - } -} - -// Run sets up event handlers -func (c *PeerpodvolumeController) Run(threadiness int, stopCh chan struct{}) { - defer utilruntime.HandleCrash() - defer c.queue.ShutDown() - - if ok := cache.WaitForCacheSync(stopCh, c.synced); !ok { - glog.Infof("Failed to wait for caches to sync") - return - } - for i := 0; i < threadiness; i++ { - go wait.Until(c.runWorker, time.Second, stopCh) - } - - <-stopCh - glog.Infof("Shutting down controller") -} - -func (c *PeerpodvolumeController) runWorker() { - for c.processNextWorkItem() { - } -} - -func (c *PeerpodvolumeController) processNextWorkItem() bool { - key, quit := c.queue.Get() - if quit { - return false - } - defer c.queue.Done(key) - - err := c.syncHandler(key) - if err == nil { - c.queue.Forget(key) - return true - } - - utilruntime.HandleError(fmt.Errorf("%v failed with: %w", key, err)) - c.queue.AddRateLimited(key) - - return true -} - -func (c *PeerpodvolumeController) syncHandler(key string) error { - namespace, name, err := cache.SplitMetaNamespaceKey(key) - - if err != nil { - utilruntime.HandleError(fmt.Errorf("invalid resource key: %s", key)) - return nil - } - if namespace != c.namespace { - glog.Infof("Detected a out of scope Peerpodvolume object: %s \n, only handle objects under namespace: %s", key, c.namespace) - return nil - } - peerPodVolume, err := c.lister.PeerpodVolumes(namespace).Get(name) - if err != nil { - if kubeErrors.IsNotFound(err) { - utilruntime.HandleError(fmt.Errorf("Peerpodvolume %q in work queue no longer exists", key)) - return nil - } - - return err - } - - if len(peerPodVolume.Spec.VolumeID) == 0 { - return nil - } - - glog.Infof("Detected a Peerpodvolume object: %s \n", key) - objJSONString, _ := json.Marshal(peerPodVolume) - objString := string(objJSONString) - glog.Infof("Detected Peerpodvolume json.Marshal.string: %s\n", objString) - // call the syncFunction from node service or controller service - if c.syncFunction != nil { - c.syncFunction(peerPodVolume) - } - return nil -} - -func (c *PeerpodvolumeController) enqueuePeerpodvolume(obj interface{}) { - var key string - var err error - if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { - utilruntime.HandleError(err) - return - } - c.queue.Add(key) -} diff --git a/src/csi-wrapper/pkg/peerpodvolume/monitor.go b/src/csi-wrapper/pkg/peerpodvolume/monitor.go deleted file mode 100644 index 5209f7aa08..0000000000 --- a/src/csi-wrapper/pkg/peerpodvolume/monitor.go +++ /dev/null @@ -1,83 +0,0 @@ -package peerpodvolume - -import ( - "context" - "sync" - "time" - - peerpodvolumeV1alpha1 "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/apis/peerpodvolume/v1alpha1" - clientset "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned" - informers "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/informers/externalversions" -) - -// var logger = log.New(log.Writer(), "[peerpodvolume/monitor] ", log.LstdFlags|log.Lmsgprefix) - -type CsiPodVolumeMonitor interface { - Start(context.Context) error - Shutdown() error - Ready() chan struct{} -} - -type csiPodVolumeMonitor struct { - controller *PeerpodvolumeController - informerFactory informers.SharedInformerFactory - stopCh chan struct{} - stopOnce sync.Once - - readyCh chan struct{} -} - -func NewPodVolumeMonitor( - client *clientset.Clientset, - namespace string, - syncFunction func(peerPodVolume *peerpodvolumeV1alpha1.PeerpodVolume), - deleteFunction func(peerPodVolume *peerpodvolumeV1alpha1.PeerpodVolume), -) (CsiPodVolumeMonitor, error) { - - informerFactory := informers.NewSharedInformerFactory(client, time.Second*30) - informer := informerFactory.Confidentialcontainers().V1alpha1().PeerpodVolumes() - - controller := newPeerpodvolumeController( - client, - informer, - namespace, - syncFunction, - deleteFunction, - ) - - m := &csiPodVolumeMonitor{ - controller: controller, - informerFactory: informerFactory, - stopCh: make(chan struct{}), - readyCh: make(chan struct{}), - } - - return m, nil -} - -func (m *csiPodVolumeMonitor) Start(ctx context.Context) error { - - m.informerFactory.Start(m.stopCh) - go m.controller.Run(2, m.stopCh) - - close(m.readyCh) - - select { - case <-ctx.Done(): - _ = m.Shutdown() // TODO: error check - case <-m.stopCh: - } - - return nil -} - -func (m *csiPodVolumeMonitor) Shutdown() error { - m.stopOnce.Do(func() { - close(m.stopCh) - }) - return nil -} - -func (m *csiPodVolumeMonitor) Ready() chan struct{} { - return m.readyCh -} diff --git a/src/csi-wrapper/pkg/utils/endpoint.go b/src/csi-wrapper/pkg/utils/endpoint.go deleted file mode 100644 index d27d43733d..0000000000 --- a/src/csi-wrapper/pkg/utils/endpoint.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright Confidential Containers Contributors -// SPDX-License-Identifier: Apache-2.0 - -package utils - -import ( - "fmt" - "net" - "os" - "strings" -) - -func Parse(ep string) (string, string, error) { - if strings.HasPrefix(strings.ToLower(ep), "unix://") || strings.HasPrefix(strings.ToLower(ep), "tcp://") { - s := strings.SplitN(ep, "://", 2) - if s[1] != "" { - return s[0], s[1], nil - } - return "", "", fmt.Errorf("Invalid endpoint: %v", ep) - } - // Assume everything else is a file path for a Unix Domain Socket. - return "unix", ep, nil -} - -func Listen(endpoint string) (net.Listener, func(), error) { - proto, addr, err := Parse(endpoint) - if err != nil { - return nil, nil, err - } - - cleanup := func() {} - if proto == "unix" { - addr = "/" + addr - if err := os.Remove(addr); err != nil && !os.IsNotExist(err) { - return nil, nil, fmt.Errorf("%s: %q", addr, err) - } - cleanup = func() { - os.Remove(addr) - } - } - - l, err := net.Listen(proto, addr) - return l, cleanup, err -} diff --git a/src/csi-wrapper/pkg/utils/normalize.go b/src/csi-wrapper/pkg/utils/normalize.go deleted file mode 100644 index 8d24f6042d..0000000000 --- a/src/csi-wrapper/pkg/utils/normalize.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright Confidential Containers Contributors -// SPDX-License-Identifier: Apache-2.0 - -package utils - -import ( - "path/filepath" - "strings" -) - -// TODO make these normalize functions generic - -func NormalizeVolumeID(volumeID string) string { - normalizedVolumeID := strings.ReplaceAll(volumeID, "###", ".") - normalizedVolumeID = strings.ReplaceAll(normalizedVolumeID, "#", ".") - - return filepath.Base(normalizedVolumeID) -} - -func NormalizeVMID(vmID string) string { - split := strings.Split(vmID, "/") - return split[len(split)-1] -} diff --git a/src/csi-wrapper/pkg/wrapper/controllerservice.go b/src/csi-wrapper/pkg/wrapper/controllerservice.go deleted file mode 100644 index e173ec579f..0000000000 --- a/src/csi-wrapper/pkg/wrapper/controllerservice.go +++ /dev/null @@ -1,449 +0,0 @@ -// Copyright Confidential Containers Contributors -// SPDX-License-Identifier: Apache-2.0 - -package wrapper - -import ( - "bytes" - "context" - "fmt" - "path/filepath" - "regexp" - "strings" - - peerpodvolumeV1alpha1 "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/apis/peerpodvolume/v1alpha1" - peerpodvolume "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned" - "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/utils" - "github.com/container-storage-interface/spec/lib/go/csi" - uid "github.com/gofrs/uuid" - "github.com/golang/glog" - "github.com/golang/protobuf/jsonpb" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -const ( - // PublishInfoVolumeID ... - PublishInfoVolumeID = "volume-id" - - // PublishInfoNodeID ... - PublishInfoNodeID = "node-id" - - // PublishInfoStatus ... - PublishInfoStatus = "attach-status" - - // PublishInfoDevicePath ... - PublishInfoDevicePath = "device-path" - - // PublishInfoRequestID ... - PublishInfoRequestID = "request-id" - - // Parameter key for Peer Pod from StorageClass - PeerpodParamKey = "peerpod" - - // peerpodVolumeNamePlaceholder is used when creating a peerpodvolume from an existing PV. - // For existing PVs, we have to create the peerpodvolume CR without knowing the real volume name in [ControllerService.ControllerPublishVolume]. - // We only later know the real volume name when we process the CR again in [NodeService.NodePublishVolume]. - peerpodVolumeNamePlaceholder = "peerpod-volume-name-placeholder" -) - -// azureVMRegexp checks if used to validate an Azure resource ID for a VM, or scale set VM. -var azureVMRegexp = regexp.MustCompile(`(?i)^/subscriptions/[^/]+/resourceGroups/[^/]+/providers/Microsoft\.Compute/(virtualMachines|virtualMachineScaleSets/[^/]+/virtualMachines)/[^/]+$`) - -type ControllerService struct { - TargetEndpoint string - Namespace string - PeerpodvolumeClient *peerpodvolume.Clientset -} - -func NewControllerService(targetEndpoint, namespace string, peerpodvolumeClientSet *peerpodvolume.Clientset) *ControllerService { - return &ControllerService{ - Namespace: namespace, - TargetEndpoint: fmt.Sprintf("unix://%s", targetEndpoint), - PeerpodvolumeClient: peerpodvolumeClientSet, - } -} - -func (s *ControllerService) redirect(ctx context.Context, req interface{}, fn func(context.Context, csi.ControllerClient)) error { - // grpc.Dial is deprecated and supported only with grpc 1.x - //nolint:staticcheck - conn, err := grpc.Dial(s.TargetEndpoint, grpc.WithBlock(), grpc.WithTransportCredentials(insecure.NewCredentials())) - if err != nil { - return err - } - defer conn.Close() - - client := csi.NewControllerClient(conn) - - fn(ctx, client) - - return nil -} - -func (s *ControllerService) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest) (res *csi.CreateVolumeResponse, err error) { - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.ControllerClient) { - peerpod := req.Parameters[PeerpodParamKey] - if peerpod != "" { - // Delete peerpod key from req parameters because csi driver may check parameters strictly. - delete(req.Parameters, PeerpodParamKey) - } - - volumeName := req.Name - res, err = client.CreateVolume(ctx, req) - glog.Infof("Created volume response: %s\n", res) - - // Create PeerpodVolume CRD object only when peerpod parameter is found in request - if peerpod != "" { - volumeID := res.GetVolume().VolumeId - normalizedVolumeID := utils.NormalizeVolumeID(volumeID) - _, _ = s.createPeerpodVolume(normalizedVolumeID, volumeName) - } - }); e != nil { - return nil, e - } - - return -} - -func (s *ControllerService) DeleteVolume(ctx context.Context, req *csi.DeleteVolumeRequest) (res *csi.DeleteVolumeResponse, err error) { - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.ControllerClient) { - res, err = client.DeleteVolume(ctx, req) - - volumeID := utils.NormalizeVolumeID(req.GetVolumeId()) - _, err = s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).Get(context.Background(), volumeID, metav1.GetOptions{}) - if err != nil { - glog.Infof("Not found PeerpodVolume with volumeID: %v, err: %v", volumeID, err.Error()) - } else { - ppErr := s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).Delete(context.Background(), volumeID, metav1.DeleteOptions{}) - if ppErr != nil { - glog.Warningf("Failed to delete to Peerpodvolume by volumeID: %v, err: %v", volumeID, ppErr.Error()) - } else { - glog.Infof("The peerPodVolume is deleted, volumeID: %v", volumeID) - } - } - }); e != nil { - return nil, e - } - - return -} - -func (s *ControllerService) ControllerPublishVolume(ctx context.Context, req *csi.ControllerPublishVolumeRequest) (res *csi.ControllerPublishVolumeResponse, err error) { - volumeID := utils.NormalizeVolumeID(req.GetVolumeId()) - savedPeerpodvolume, err := s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).Get(context.Background(), volumeID, metav1.GetOptions{}) - if err != nil { - glog.Infof("Not found PeerpodVolume with volumeID: %v, err: %v", volumeID, err.Error()) - - // ControllerPublishVolume was called without a matching peerpod volume object existing. - // This can happen when the persistent volume was manually created, e.g. to consume an existing storage backend. - // In this case, we need to create the peerpod volume object here. - peerPod := req.VolumeContext[PeerpodParamKey] - if peerPod == "" { - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.ControllerClient) { - res, err = client.ControllerPublishVolume(ctx, req) - }); e != nil { - return nil, e - } - } - glog.Info("PeerPod parameter found in ControllerPublishVolumeRequest. Creating a new PeerpodVolume object") - - // Delete peerpod key from req parameters because csi driver may check parameters strictly. - delete(req.VolumeContext, PeerpodParamKey) - - volumeName := peerpodVolumeNamePlaceholder - savedPeerpodvolume, err = s.createPeerpodVolume(volumeID, volumeName) - if err != nil { - return nil, err - } - } - - nodeID := req.GetNodeId() - uuid, _ := uid.NewV4() // #nosec G104: Attempt to randomly generate uuid - requestID := uuid.String() - - fakePublishContext := map[string]string{ - PublishInfoVolumeID: volumeID, - PublishInfoNodeID: "", - PublishInfoStatus: "attached", - PublishInfoDevicePath: "/dev/fff", - PublishInfoRequestID: requestID, - } - res = &csi.ControllerPublishVolumeResponse{PublishContext: fakePublishContext} - - glog.Infof("The fake ControllerPublishVolumeResponse is :%v", res) - var reqBuf bytes.Buffer - if err := (&jsonpb.Marshaler{}).Marshal(&reqBuf, req); err != nil { - glog.Error(err, "Error happens while Marshal ControllerPublishVolumeRequest") - } - reqJSONString := reqBuf.String() - glog.Infof("ControllerPublishVolumeRequest JSON string: %s\n", reqJSONString) - - var resBuf bytes.Buffer - if err := (&jsonpb.Marshaler{}).Marshal(&resBuf, res); err != nil { - glog.Error(err, "Error happens while Marshal ControllerPublishVolumeResponse") - } - resJSONString := resBuf.String() - glog.Infof("ControllerPublishVolumeResponse JSON string: %s\n", resJSONString) - - savedPeerpodvolume.Labels["nodeID"] = nodeID - savedPeerpodvolume.Spec.NodeID = nodeID - savedPeerpodvolume.Spec.WrapperControllerPublishVolumeReq = string(reqJSONString) - savedPeerpodvolume.Spec.WrapperControllerPublishVolumeRes = string(resJSONString) - savedPeerpodvolume, err = s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).Update(context.Background(), savedPeerpodvolume, metav1.UpdateOptions{}) - if err != nil { - glog.Errorf("Error happens while Update PeerpodVolume in ControllerPublishVolume, err: %v", err.Error()) - return - } - savedPeerpodvolume.Status = peerpodvolumeV1alpha1.PeerpodVolumeStatus{ - State: peerpodvolumeV1alpha1.ControllerPublishVolumeCached, - } - _, err = s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).UpdateStatus(context.Background(), savedPeerpodvolume, metav1.UpdateOptions{}) - if err != nil { - glog.Errorf("Error happens while Update PeerpodVolume status to ControllerPublishVolumeCached, err: %v", err.Error()) - } - - return -} - -func (s *ControllerService) ControllerUnpublishVolume(ctx context.Context, req *csi.ControllerUnpublishVolumeRequest) (res *csi.ControllerUnpublishVolumeResponse, err error) { - volumeID := utils.NormalizeVolumeID(req.GetVolumeId()) - savedPeerpodvolume, err := s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).Get(context.Background(), volumeID, metav1.GetOptions{}) - if err != nil { - glog.Infof("Not found PeerpodVolume with volumeID: %v, err: %v", volumeID, err.Error()) - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.ControllerClient) { - res, err = client.ControllerUnpublishVolume(ctx, req) - }); e != nil { - return nil, e - } - } else { - statusString := string(savedPeerpodvolume.Status.State) - if strings.Contains(statusString, "Applied") { - // volume is attached to peer pod vm if the status.state end with `Applied` - req.NodeId = savedPeerpodvolume.Spec.VMID - glog.Infof("The modified ControllerUnpublishVolumeRequest is :%v", req) - ctx := context.Background() - // TODO: error check - _ = s.redirect(ctx, req, func(ctx context.Context, client csi.ControllerClient) { - response, err := client.ControllerUnpublishVolume(ctx, req) - if err != nil { - glog.Errorf("Failed to run ControllerUnpublishVolume with modified ControllerUnpublishVolume, err: %v", err.Error()) - } else { - glog.Infof("The ControllerUnpublishVolumeResponse for peer pod is :%v", response) - } - }) - } - - labels := map[string]string{ - "volumeName": savedPeerpodvolume.Spec.VolumeName, - } - savedPeerpodvolume.Labels = labels - savedPeerpodvolume.Spec.NodeID = "" - savedPeerpodvolume.Spec.DevicePath = "" - savedPeerpodvolume.Spec.NodeName = "" - savedPeerpodvolume.Spec.PodName = "" - savedPeerpodvolume.Spec.PodNamespace = "" - savedPeerpodvolume.Spec.PodUID = "" - savedPeerpodvolume.Spec.StagingTargetPath = "" - savedPeerpodvolume.Spec.TargetPath = "" - savedPeerpodvolume.Spec.VMID = "" - savedPeerpodvolume.Spec.VMName = "" - savedPeerpodvolume.Spec.WrapperControllerPublishVolumeReq = "" - savedPeerpodvolume.Spec.WrapperControllerPublishVolumeRes = "" - savedPeerpodvolume.Spec.WrapperNodePublishVolumeReq = "" - savedPeerpodvolume.Spec.WrapperNodeStageVolumeReq = "" - savedPeerpodvolume.Spec.WrapperNodeUnpublishVolumeReq = "" - savedPeerpodvolume.Spec.WrapperNodeUnstageVolumeReq = "" - updatedSavedPeerpodvolume, err := s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).Update(context.Background(), savedPeerpodvolume, metav1.UpdateOptions{}) - if err != nil { - glog.Errorf("Error happens while clean PeerpodVolume specs, err: %v", err.Error()) - } - - updatedSavedPeerpodvolume.Status = peerpodvolumeV1alpha1.PeerpodVolumeStatus{ - State: "", - } - _, err = s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).UpdateStatus(context.Background(), updatedSavedPeerpodvolume, metav1.UpdateOptions{}) - if err != nil { - glog.Errorf("Error happens while Update PeerpodVolume status to ControllerUnpublishVolumeApplied, err: %v", err.Error()) - } - - res = &csi.ControllerUnpublishVolumeResponse{} - } - - return -} - -func (s *ControllerService) ValidateVolumeCapabilities(ctx context.Context, req *csi.ValidateVolumeCapabilitiesRequest) (res *csi.ValidateVolumeCapabilitiesResponse, err error) { - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.ControllerClient) { - res, err = client.ValidateVolumeCapabilities(ctx, req) - }); e != nil { - return nil, e - } - - return -} - -func (s *ControllerService) ListVolumes(ctx context.Context, req *csi.ListVolumesRequest) (res *csi.ListVolumesResponse, err error) { - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.ControllerClient) { - res, err = client.ListVolumes(ctx, req) - }); e != nil { - return nil, e - } - - return -} - -func (s *ControllerService) GetCapacity(ctx context.Context, req *csi.GetCapacityRequest) (res *csi.GetCapacityResponse, err error) { - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.ControllerClient) { - res, err = client.GetCapacity(ctx, req) - }); e != nil { - return nil, e - } - - return -} - -func (s *ControllerService) ControllerGetCapabilities(ctx context.Context, req *csi.ControllerGetCapabilitiesRequest) (res *csi.ControllerGetCapabilitiesResponse, err error) { - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.ControllerClient) { - res, err = client.ControllerGetCapabilities(ctx, req) - }); e != nil { - return nil, e - } - - return -} - -func (s *ControllerService) CreateSnapshot(ctx context.Context, req *csi.CreateSnapshotRequest) (res *csi.CreateSnapshotResponse, err error) { - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.ControllerClient) { - res, err = client.CreateSnapshot(ctx, req) - }); e != nil { - return nil, e - } - - return -} - -func (s *ControllerService) DeleteSnapshot(ctx context.Context, req *csi.DeleteSnapshotRequest) (res *csi.DeleteSnapshotResponse, err error) { - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.ControllerClient) { - res, err = client.DeleteSnapshot(ctx, req) - }); e != nil { - return nil, e - } - - return -} - -func (s *ControllerService) ListSnapshots(ctx context.Context, req *csi.ListSnapshotsRequest) (res *csi.ListSnapshotsResponse, err error) { - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.ControllerClient) { - res, err = client.ListSnapshots(ctx, req) - }); e != nil { - return nil, e - } - - return -} - -func (s *ControllerService) ControllerExpandVolume(ctx context.Context, req *csi.ControllerExpandVolumeRequest) (res *csi.ControllerExpandVolumeResponse, err error) { - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.ControllerClient) { - res, err = client.ControllerExpandVolume(ctx, req) - }); e != nil { - return nil, e - } - - return -} - -func (s *ControllerService) ControllerGetVolume(ctx context.Context, req *csi.ControllerGetVolumeRequest) (res *csi.ControllerGetVolumeResponse, err error) { - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.ControllerClient) { - res, err = client.ControllerGetVolume(ctx, req) - }); e != nil { - return nil, e - } - - return -} - -func (s *ControllerService) SyncHandler(peerPodVolume *peerpodvolumeV1alpha1.PeerpodVolume) { - glog.Infof("syncHandler from ControllerService: %v ", peerPodVolume) - if peerPodVolume.Status.State == peerpodvolumeV1alpha1.PeerPodVSIIDReady && peerPodVolume.Spec.DevicePath == "" { - // After peerpod vsi id is ready in crd object, we can reproduce the ControllerPublishVolumeRequest - vsiID := peerPodVolume.Spec.VMID - - // The azure csi driver requires the nodeID to be just the name of the VM, - // not the full Azure Resource ID, as it is saved in the PeerpodVolume object - if azureVMRegexp.MatchString(vsiID) { - vsiID = filepath.Base(vsiID) - } - - // Replace the nodeID with peerpod vsi instance id in ControllerPublishVolumeRequest and pass - // the modified ControllerPublishVolumeRequest to original controller service - wrapperRequest := peerPodVolume.Spec.WrapperControllerPublishVolumeReq - var modifiedRequest csi.ControllerPublishVolumeRequest - if err := (&jsonpb.Unmarshaler{}).Unmarshal(bytes.NewReader([]byte(wrapperRequest)), &modifiedRequest); err != nil { - glog.Errorf("Failed to convert to ControllerPublishVolumeRequest, err: %v", err.Error()) - } else { - modifiedRequest.NodeId = vsiID - glog.Infof("The modified ControllerPublishVolumeRequest is :%v", modifiedRequest) - ctx := context.Background() - // TODO: error check - _ = s.redirect(ctx, modifiedRequest, func(ctx context.Context, client csi.ControllerClient) { - response, err := client.ControllerPublishVolume(ctx, &modifiedRequest) - if err != nil { - glog.Errorf("Failed to reproduce ControllerPublishVolume with modified ControllerPublishVolumeRequest, err: %v", err.Error()) - } else { - glog.Infof("The ControllerPublishVolumeResponse for peer pod is :%v", response) - var resBuf bytes.Buffer - if err := (&jsonpb.Marshaler{}).Marshal(&resBuf, response); err != nil { - glog.Error(err, "Error happens while Marshal ControllerPublishVolumeResponse") - } - resJSONString := resBuf.String() - glog.Infof("ControllerPublishVolumeResponse for peer pod JSON string: %s\n", resJSONString) - peerPodVolume.Spec.WrapperControllerPublishVolumeRes = resJSONString - devicePath := response.PublishContext["device-path"] - glog.Infof("device-path for peer pod VM: %s\n", devicePath) - peerPodVolume.Spec.DevicePath = devicePath - updatedPeerPodVolume, err := s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).Update(context.Background(), peerPodVolume, metav1.UpdateOptions{}) - if err != nil { - glog.Errorf("Error happens while Update PeerpodVolume with ControllerPublishVolumeResponse for peer pod, err: %v", err.Error()) - return - } - updatedPeerPodVolume.Status = peerpodvolumeV1alpha1.PeerpodVolumeStatus{ - State: peerpodvolumeV1alpha1.ControllerPublishVolumeApplied, - } - _, err = s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).UpdateStatus(context.Background(), updatedPeerPodVolume, metav1.UpdateOptions{}) - if err != nil { - glog.Errorf("Error happens while Update PeerpodVolume status to ControllerPublishVolumeApplied, err: %v", err.Error()) - } - } - }) - } - } -} - -func (s *ControllerService) DeleteFunction(peerPodVolume *peerpodvolumeV1alpha1.PeerpodVolume) { - glog.Infof("deleteFunction from controllerService: %v ", peerPodVolume) -} - -func (s *ControllerService) createPeerpodVolume(volumeID, volumeName string) (*peerpodvolumeV1alpha1.PeerpodVolume, error) { - labels := map[string]string{ - "volumeName": volumeName, - } - newPeerpodvolume := &peerpodvolumeV1alpha1.PeerpodVolume{ - ObjectMeta: metav1.ObjectMeta{ - Name: volumeID, - Namespace: s.Namespace, - Labels: labels, - }, - Spec: peerpodvolumeV1alpha1.PeerpodVolumeSpec{ - VolumeID: volumeID, - VolumeName: volumeName, - }, - } - peerpodVolume, err := s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).Create(context.Background(), newPeerpodvolume, metav1.CreateOptions{}) - if err != nil { - glog.Errorf("Error happens while creating peerPodVolume with volumeID: %v, err: %v", volumeID, err.Error()) - } else { - glog.Infof("Peerpodvolume object is created") - } - return peerpodVolume, err -} diff --git a/src/csi-wrapper/pkg/wrapper/identityservice.go b/src/csi-wrapper/pkg/wrapper/identityservice.go deleted file mode 100644 index 1ae214034e..0000000000 --- a/src/csi-wrapper/pkg/wrapper/identityservice.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright Confidential Containers Contributors -// SPDX-License-Identifier: Apache-2.0 - -package wrapper - -import ( - "context" - "fmt" - - "github.com/container-storage-interface/spec/lib/go/csi" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" -) - -type IdentityService struct { - TargetEndpoint string -} - -func NewIdentityService(targetEndpoint string) *IdentityService { - return &IdentityService{ - TargetEndpoint: fmt.Sprintf("unix://%s", targetEndpoint), - } -} - -func (s *IdentityService) redirect(ctx context.Context, req interface{}, fn func(context.Context, csi.IdentityClient)) error { - // grpc.Dial is deprecated and supported only with grpc 1.x - //nolint:staticcheck - conn, err := grpc.Dial(s.TargetEndpoint, grpc.WithBlock(), grpc.WithTransportCredentials(insecure.NewCredentials())) - if err != nil { - return err - } - defer conn.Close() - - client := csi.NewIdentityClient(conn) - - fn(ctx, client) - - return nil -} - -func (s *IdentityService) GetPluginInfo(ctx context.Context, req *csi.GetPluginInfoRequest) (res *csi.GetPluginInfoResponse, err error) { - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.IdentityClient) { - res, err = client.GetPluginInfo(ctx, req) - }); e != nil { - return nil, e - } - - return -} - -func (s *IdentityService) Probe(ctx context.Context, req *csi.ProbeRequest) (res *csi.ProbeResponse, err error) { - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.IdentityClient) { - res, err = client.Probe(ctx, req) - }); e != nil { - return nil, e - } - - return -} - -func (s *IdentityService) GetPluginCapabilities(ctx context.Context, req *csi.GetPluginCapabilitiesRequest) (res *csi.GetPluginCapabilitiesResponse, err error) { - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.IdentityClient) { - res, err = client.GetPluginCapabilities(ctx, req) - }); e != nil { - return nil, e - } - - return -} diff --git a/src/csi-wrapper/pkg/wrapper/nodeservice.go b/src/csi-wrapper/pkg/wrapper/nodeservice.go deleted file mode 100644 index 40f2496bc0..0000000000 --- a/src/csi-wrapper/pkg/wrapper/nodeservice.go +++ /dev/null @@ -1,398 +0,0 @@ -// Copyright Confidential Containers Contributors -// SPDX-License-Identifier: Apache-2.0 - -package wrapper - -import ( - "bytes" - "context" - "errors" - "fmt" - "net" - "os" - "strings" - - podvminfo "github.com/confidential-containers/cloud-api-adaptor/src/cloud-api-adaptor/proto/podvminfo" - peerpodvolumeV1alpha1 "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/apis/peerpodvolume/v1alpha1" - peerpodvolume "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned" - "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/utils" - "github.com/containerd/ttrpc" - volume "github.com/kata-containers/kata-containers/src/runtime/pkg/direct-volume" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/container-storage-interface/spec/lib/go/csi" - "github.com/golang/glog" - "github.com/golang/protobuf/jsonpb" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" -) - -const ( - DefaultKubeletLibDir = "/var/lib/kubelet" - DefaultKubeletDataDir = "/var/data/kubelet" - DefaultMountInfo = "{\"Device\": \"/dev/zero\", \"fstype\": \"ext4\"}" -) - -type NodeService struct { - TargetEndpoint string - Namespace string - PeerpodvolumeClient *peerpodvolume.Clientset - VMIDInformationEndpoint string -} - -// Leverage Kata’s mechanism of direct block device assignment to prevent CSI mount source from replacing by Kata shim code -func addKataDirectVolume(volumePath string) { - err := volume.Add(volumePath, DefaultMountInfo) - if err != nil { - glog.Warningf("Failed to add kata direct volume: %v", err.Error()) - } -} - -func removeKataDirectVolume(volumePath string) { - err := volume.Remove(volumePath) - if err != nil { - glog.Warningf("Failed to remove kata direct volume: %v", err.Error()) - } -} - -func NewNodeService(targetEndpoint, namespace string, peerpodvolumeClientSet *peerpodvolume.Clientset, vmIDInformationEndpoint string) *NodeService { - addKataDirectVolume(DefaultKubeletLibDir) - addKataDirectVolume(DefaultKubeletDataDir) - - return &NodeService{ - Namespace: namespace, - TargetEndpoint: fmt.Sprintf("unix://%s", targetEndpoint), - PeerpodvolumeClient: peerpodvolumeClientSet, - VMIDInformationEndpoint: vmIDInformationEndpoint, - } -} - -func (s *NodeService) redirect(ctx context.Context, req interface{}, fn func(context.Context, csi.NodeClient)) error { - // grpc.Dial is deprecated and supported only with grpc 1.x - //nolint:staticcheck - conn, err := grpc.Dial(s.TargetEndpoint, grpc.WithBlock(), grpc.WithTransportCredentials(insecure.NewCredentials())) - if err != nil { - return err - } - defer conn.Close() - - client := csi.NewNodeClient(conn) - - fn(ctx, client) - - return nil -} -func (s *NodeService) getPodUIDandVolumeName(targetPath string) (podUID, volumeName string) { - // /var/lib/kubelet/pods/69576836-28c2-447e-a726-fdf8866a0622/volumes/kubernetes.io~csi/pvc-e9d79b06-fd06-487f-ac93-ea6424819a7d/mount - paths := strings.Split(targetPath, "/") - glog.Infof("split paths is :%v", paths) - podUID = paths[5] - volumeName = paths[8] - glog.Infof("podUid is :%v, volumeName is: %v", podUID, volumeName) - return -} - -func (s *NodeService) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolumeRequest) (res *csi.NodePublishVolumeResponse, err error) { - volumeID := utils.NormalizeVolumeID(req.GetVolumeId()) - savedPeerpodvolume, err := s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).Get(context.Background(), volumeID, metav1.GetOptions{}) - if err != nil { - glog.Infof("Not found PeerpodVolume with volumeID: %v, err: %v", volumeID, err.Error()) - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.NodeClient) { - res, err = client.NodePublishVolume(ctx, req) - }); e != nil { - return nil, e - } - } else { - // Do fake action for NodePublishVolume on worker node - publishContext := req.GetPublishContext() - stagingTargetPath := req.GetStagingTargetPath() - targetPath := req.GetTargetPath() - osErr := os.MkdirAll(targetPath, os.FileMode(0755)) - if osErr != nil { - glog.Warningf("Failed to create fake targetPath, err is: %v", osErr) - } - glog.Infof("The stagingTargetPath is :%v", stagingTargetPath) - glog.Infof("The targetPath is :%v", targetPath) - glog.Infof("The publishContext is :%v", publishContext) - - addKataDirectVolume(targetPath) - - var reqBuf bytes.Buffer - if err := (&jsonpb.Marshaler{}).Marshal(&reqBuf, req); err != nil { - glog.Error(err, "Error happens while Marshal NodePublishVolumeRequest") - } - nodePublishVolumeRequest := reqBuf.String() - glog.Infof("NodePublishVolumeRequest JSON string: %s\n", nodePublishVolumeRequest) - savedPeerpodvolume.Spec.TargetPath = targetPath - podUID, volumeName := s.getPodUIDandVolumeName(targetPath) - savedPeerpodvolume.Labels["podUid"] = podUID - savedPeerpodvolume.Spec.PodUID = podUID - savedVolumeName := savedPeerpodvolume.Spec.VolumeName - if volumeName != savedVolumeName && savedVolumeName != peerpodVolumeNamePlaceholder { - glog.Error("The volume name from target path doesn't match with the CR") - return nil, errors.New("the volume name from target path doesn't match with the CR") - } - if savedVolumeName == peerpodVolumeNamePlaceholder { - glog.Info("Detected a placeholder volume name in the CR. Updating the CR with the volume name from the target path") - savedPeerpodvolume.Labels["volumeName"] = volumeName - savedPeerpodvolume.Spec.VolumeName = volumeName - } - - savedPeerpodvolume.Spec.WrapperNodePublishVolumeReq = nodePublishVolumeRequest - _, err = s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).Update(context.Background(), savedPeerpodvolume, metav1.UpdateOptions{}) - if err != nil { - glog.Errorf("Error happens while Update PeerpodVolume, err: %v", err.Error()) - return - } - // TODO: error check - savedPeerpodvolume, _ = s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).Get(context.Background(), volumeID, metav1.GetOptions{}) - savedPeerpodvolume.Status = peerpodvolumeV1alpha1.PeerpodVolumeStatus{ - State: peerpodvolumeV1alpha1.NodePublishVolumeCached, - } - _, err = s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).UpdateStatus(context.Background(), savedPeerpodvolume, metav1.UpdateOptions{}) - if err != nil { - glog.Errorf("Error happens while Update PeerpodVolume status to NodePublishVolumeCached, err: %v", err.Error()) - return - } - - res = &csi.NodePublishVolumeResponse{} - } - - return -} - -func (s *NodeService) NodeUnpublishVolume(ctx context.Context, req *csi.NodeUnpublishVolumeRequest) (res *csi.NodeUnpublishVolumeResponse, err error) { - volumeID := utils.NormalizeVolumeID(req.GetVolumeId()) - savedPeerpodvolume, err := s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).Get(context.Background(), volumeID, metav1.GetOptions{}) - if err != nil { - glog.Infof("Not found PeerpodVolume with volumeID: %v, err: %v", volumeID, err.Error()) - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.NodeClient) { - res, err = client.NodeUnpublishVolume(ctx, req) - }); e != nil { - return nil, e - } - } else { - // Do fake action for NodeUnpublishVolume on worker node - targetPath := req.GetTargetPath() - glog.Infof("The targetPath is :%v", targetPath) - osErr := os.RemoveAll(targetPath) - if osErr != nil { - glog.Warningf("Failed to remove fake targetPath, err is: %v", osErr) - } - - removeKataDirectVolume(targetPath) - - var reqBuf bytes.Buffer - if err := (&jsonpb.Marshaler{}).Marshal(&reqBuf, req); err != nil { - glog.Error(err, "Error happens while Marshal NodeUnpublishVolumeRequest") - } - nodeUnpublishVolumeRequest := reqBuf.String() - glog.Infof("NodeUnpublishVolumeRequest JSON string: %s\n", nodeUnpublishVolumeRequest) - - savedPeerpodvolume.Spec.WrapperNodeUnpublishVolumeReq = nodeUnpublishVolumeRequest - updatedPeerpodvolume, upErr := s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).Update(context.Background(), savedPeerpodvolume, metav1.UpdateOptions{}) - if upErr != nil { - glog.Errorf("Error happens while Update PeerpodVolume, err: %v", upErr.Error()) - return nil, upErr - } - updatedPeerpodvolume.Status = peerpodvolumeV1alpha1.PeerpodVolumeStatus{ - State: peerpodvolumeV1alpha1.NodeUnpublishVolumeCached, - } - _, err = s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).UpdateStatus(context.Background(), savedPeerpodvolume, metav1.UpdateOptions{}) - if err != nil { - glog.Errorf("Error happens while Update PeerpodVolume status to NodeUnpublishVolumeCached, err: %v", err.Error()) - return - } - - res = &csi.NodeUnpublishVolumeResponse{} - } - - return -} - -func (s *NodeService) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRequest) (res *csi.NodeStageVolumeResponse, err error) { - volumeID := utils.NormalizeVolumeID(req.GetVolumeId()) - savedPeerpodvolume, err := s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).Get(context.Background(), volumeID, metav1.GetOptions{}) - if err != nil { - glog.Infof("Not found PeerpodVolume with volumeID: %v, err: %v", volumeID, err.Error()) - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.NodeClient) { - res, err = client.NodeStageVolume(ctx, req) - }); e != nil { - return nil, e - } - } else { - // Create empty dummy folders on worker node - publishContext := req.GetPublishContext() - stagingTargetPath := req.GetStagingTargetPath() - _ = os.MkdirAll(stagingTargetPath, os.FileMode(0755)) // TODO: error check - glog.Infof("The stagingTargetPath for volume %v is :%v", volumeID, stagingTargetPath) - glog.Infof("The publishContext is :%v", publishContext) - - var reqBuf bytes.Buffer - if err := (&jsonpb.Marshaler{}).Marshal(&reqBuf, req); err != nil { - glog.Error(err, "Error happens while Marshal NodeStageVolumeRequest") - } - nodeStageVolumeRequest := reqBuf.String() - glog.Infof("NodeStageVolumeRequest JSON string: %s\n", nodeStageVolumeRequest) - savedPeerpodvolume.Spec.StagingTargetPath = stagingTargetPath - savedPeerpodvolume.Spec.WrapperNodeStageVolumeReq = nodeStageVolumeRequest - _, err = s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).Update(context.Background(), savedPeerpodvolume, metav1.UpdateOptions{}) - if err != nil { - glog.Errorf("Error happens while Update PeerpodVolume, err: %v", err.Error()) - return - } - // TODO: error check - savedPeerpodvolume, _ = s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).Get(context.Background(), volumeID, metav1.GetOptions{}) - savedPeerpodvolume.Status = peerpodvolumeV1alpha1.PeerpodVolumeStatus{ - State: peerpodvolumeV1alpha1.NodeStageVolumeCached, - } - _, err = s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).UpdateStatus(context.Background(), savedPeerpodvolume, metav1.UpdateOptions{}) - if err != nil { - glog.Errorf("Error happens while Update PeerpodVolume status to NodeStageVolumeCached, err: %v", err.Error()) - return - } - - res = &csi.NodeStageVolumeResponse{} - } - - return -} - -func (s *NodeService) NodeUnstageVolume(ctx context.Context, req *csi.NodeUnstageVolumeRequest) (res *csi.NodeUnstageVolumeResponse, err error) { - volumeID := utils.NormalizeVolumeID(req.GetVolumeId()) - savedPeerpodvolume, err := s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).Get(context.Background(), volumeID, metav1.GetOptions{}) - if err != nil { - glog.Infof("Not found PeerpodVolume with volumeID: %v, err: %v", volumeID, err.Error()) - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.NodeClient) { - res, err = client.NodeUnstageVolume(ctx, req) - }); e != nil { - return nil, e - } - } else { - // Do fake action for NodeUnstageVolume on worker node - stagingTargetPath := req.GetStagingTargetPath() - glog.Infof("The stagingTargetPath for volume %v is :%v", volumeID, stagingTargetPath) - osErr := os.RemoveAll(stagingTargetPath) - if osErr != nil { - glog.Warningf("Failed to remove fake stagingTargetPath for volume %v, err is: %v", volumeID, osErr) - } - - var reqBuf bytes.Buffer - if err := (&jsonpb.Marshaler{}).Marshal(&reqBuf, req); err != nil { - glog.Error(err, "Error happens while Marshal NodeUnstageVolumeRequest") - } - nodeUnstageVolumeRequest := reqBuf.String() - glog.Infof("NodeUnstageVolumeRequest JSON string: %s\n", nodeUnstageVolumeRequest) - - savedPeerpodvolume.Spec.WrapperNodeUnstageVolumeReq = nodeUnstageVolumeRequest - updatedPeerpodvolume, upErr := s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).Update(context.Background(), savedPeerpodvolume, metav1.UpdateOptions{}) - if upErr != nil { - glog.Errorf("Error happens while Update PeerpodVolume, err: %v", upErr.Error()) - return nil, upErr - } - updatedPeerpodvolume.Status = peerpodvolumeV1alpha1.PeerpodVolumeStatus{ - State: peerpodvolumeV1alpha1.NodeUnstageVolumeCached, - } - _, err = s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).UpdateStatus(context.Background(), savedPeerpodvolume, metav1.UpdateOptions{}) - if err != nil { - glog.Errorf("Error happens while Update PeerpodVolume status to NodeUnstageVolumeCached, err: %v", err.Error()) - return - } - - res = &csi.NodeUnstageVolumeResponse{} - } - - return -} - -func (s *NodeService) NodeGetInfo(ctx context.Context, req *csi.NodeGetInfoRequest) (res *csi.NodeGetInfoResponse, err error) { - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.NodeClient) { - res, err = client.NodeGetInfo(ctx, req) - }); e != nil { - return nil, e - } - - return -} - -func (s *NodeService) NodeGetCapabilities(ctx context.Context, req *csi.NodeGetCapabilitiesRequest) (res *csi.NodeGetCapabilitiesResponse, err error) { - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.NodeClient) { - res, err = client.NodeGetCapabilities(ctx, req) - }); e != nil { - return nil, e - } - - return -} - -func (s *NodeService) NodeGetVolumeStats(ctx context.Context, req *csi.NodeGetVolumeStatsRequest) (res *csi.NodeGetVolumeStatsResponse, err error) { - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.NodeClient) { - res, err = client.NodeGetVolumeStats(ctx, req) - }); e != nil { - return nil, e - } - - return -} - -func (s *NodeService) NodeExpandVolume(ctx context.Context, req *csi.NodeExpandVolumeRequest) (res *csi.NodeExpandVolumeResponse, err error) { - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.NodeClient) { - res, err = client.NodeExpandVolume(ctx, req) - }); e != nil { - return nil, e - } - - return -} - -func (s *NodeService) SyncHandler(peerPodVolume *peerpodvolumeV1alpha1.PeerpodVolume) { - if peerPodVolume.Spec.NodeName != os.Getenv("POD_NODE_NAME") { - // Only handle the PeerpodVolume CRD which is assigned to the same compute node - glog.Infof("Only handle the PeerpodVolume CRD which is assigned to %v", os.Getenv("POD_NODE_NAME")) - return - } - glog.Infof("syncHandler from nodeService: %v ", peerPodVolume) - switch peerPodVolume.Status.State { - case peerpodvolumeV1alpha1.PeerPodVSIRunning: - // The podName and podNamespace MUST be there when it's PeerPodVSIRunning status - glog.Infof("Getting vmID for podName:%v, podNamespace:%v", peerPodVolume.Spec.PodName, peerPodVolume.Spec.PodNamespace) - req := &podvminfo.GetInfoRequest{ - PodName: peerPodVolume.Spec.PodName, - PodNamespace: peerPodVolume.Spec.PodNamespace, - Wait: false, - } - glog.Infof("The get VM ID information request is: %v", req) - conn, err := net.Dial("unix", s.VMIDInformationEndpoint) - if err != nil { - glog.Fatalf("Connect to vm id information service failed: %v", err) - } - ttrpcClient := ttrpc.NewClient(conn) - defer ttrpcClient.Close() - podVMInfoClient := podvminfo.NewPodVMInfoClient(ttrpcClient) - res, err := podVMInfoClient.GetInfo(context.Background(), req) - if err != nil { - glog.Errorf("Error happens while get VM ID information, err: %v", err.Error()) - } else { - vmID := res.VMID - glog.Infof("Got the vm instance id from cloud-api-adaptor podVMInfoService vmID:%v", vmID) - peerPodVolume.Spec.VMID = vmID - peerPodVolume.Labels["vmID"] = utils.NormalizeVMID(vmID) - updatedPeerPodVolume, err := s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).Update(context.Background(), peerPodVolume, metav1.UpdateOptions{}) - if err != nil { - glog.Errorf("Error happens while Update vmID to PeerpodVolume, err: %v", err.Error()) - } - updatedPeerPodVolume.Status = peerpodvolumeV1alpha1.PeerpodVolumeStatus{ - State: peerpodvolumeV1alpha1.PeerPodVSIIDReady, - } - _, err = s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).UpdateStatus(context.Background(), updatedPeerPodVolume, metav1.UpdateOptions{}) - if err != nil { - glog.Errorf("Error happens while Update PeerpodVolume status to PeerPodVSIIDReady, err: %v", err.Error()) - } - glog.Infof("The PeerpodVolume status updated to PeerPodVSIIDReady") - } - } -} - -func (s *NodeService) DeleteFunction(peerPodVolume *peerpodvolumeV1alpha1.PeerpodVolume) { - glog.Infof("deleteFunction from nodeService: %v ", peerPodVolume) -} diff --git a/src/csi-wrapper/pkg/wrapper/podvmservice.go b/src/csi-wrapper/pkg/wrapper/podvmservice.go deleted file mode 100644 index 019feb381c..0000000000 --- a/src/csi-wrapper/pkg/wrapper/podvmservice.go +++ /dev/null @@ -1,315 +0,0 @@ -// Copyright Confidential Containers Contributors -// SPDX-License-Identifier: Apache-2.0 - -package wrapper - -import ( - "bytes" - "context" - "fmt" - "os" - - peerpodvolumeV1alpha1 "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/apis/peerpodvolume/v1alpha1" - peerpodvolume "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/generated/peerpodvolume/clientset/versioned" - "github.com/container-storage-interface/spec/lib/go/csi" - "github.com/golang/glog" - "github.com/golang/protobuf/jsonpb" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -type PodVMNodeService struct { - TargetEndpoint string - Namespace string - PeerpodvolumeClient *peerpodvolume.Clientset -} - -func NewPodVMNodeService(targetEndpoint, namespace string, peerpodvolumeClientSet *peerpodvolume.Clientset) *PodVMNodeService { - return &PodVMNodeService{ - Namespace: namespace, - TargetEndpoint: fmt.Sprintf("unix://%s", targetEndpoint), - PeerpodvolumeClient: peerpodvolumeClientSet, - } -} - -func (s *PodVMNodeService) redirect(ctx context.Context, req interface{}, fn func(context.Context, csi.NodeClient)) error { - // grpc.Dial is deprecated and supported only with grpc 1.x - //nolint:staticcheck - conn, err := grpc.Dial(s.TargetEndpoint, grpc.WithBlock(), grpc.WithTransportCredentials(insecure.NewCredentials())) - if err != nil { - glog.Errorf("failed to connect s.TargetEndpoint: %v, err:%v", s.TargetEndpoint, err) - return err - } - defer conn.Close() - - client := csi.NewNodeClient(conn) - glog.Infof("NewNodeClient client: %v", client) - fn(ctx, client) - - return nil -} - -func (s *PodVMNodeService) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolumeRequest) (res *csi.NodePublishVolumeResponse, err error) { - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.NodeClient) { - res, err = client.NodePublishVolume(ctx, req) - }); e != nil { - return nil, e - } - - return -} - -func (s *PodVMNodeService) NodeUnpublishVolume(ctx context.Context, req *csi.NodeUnpublishVolumeRequest) (res *csi.NodeUnpublishVolumeResponse, err error) { - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.NodeClient) { - res, err = client.NodeUnpublishVolume(ctx, req) - }); e != nil { - return nil, e - } - - return -} - -func (s *PodVMNodeService) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRequest) (res *csi.NodeStageVolumeResponse, err error) { - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.NodeClient) { - res, err = client.NodeStageVolume(ctx, req) - }); e != nil { - return nil, e - } - - return -} - -func (s *PodVMNodeService) NodeUnstageVolume(ctx context.Context, req *csi.NodeUnstageVolumeRequest) (res *csi.NodeUnstageVolumeResponse, err error) { - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.NodeClient) { - res, err = client.NodeUnstageVolume(ctx, req) - }); e != nil { - return nil, e - } - - return -} - -func (s *PodVMNodeService) NodeGetInfo(ctx context.Context, req *csi.NodeGetInfoRequest) (res *csi.NodeGetInfoResponse, err error) { - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.NodeClient) { - res, err = client.NodeGetInfo(ctx, req) - }); e != nil { - return nil, e - } - - return -} - -func (s *PodVMNodeService) NodeGetCapabilities(ctx context.Context, req *csi.NodeGetCapabilitiesRequest) (res *csi.NodeGetCapabilitiesResponse, err error) { - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.NodeClient) { - res, err = client.NodeGetCapabilities(ctx, req) - }); e != nil { - return nil, e - } - - return -} - -func (s *PodVMNodeService) NodeGetVolumeStats(ctx context.Context, req *csi.NodeGetVolumeStatsRequest) (res *csi.NodeGetVolumeStatsResponse, err error) { - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.NodeClient) { - res, err = client.NodeGetVolumeStats(ctx, req) - }); e != nil { - return nil, e - } - - return -} - -func (s *PodVMNodeService) NodeExpandVolume(ctx context.Context, req *csi.NodeExpandVolumeRequest) (res *csi.NodeExpandVolumeResponse, err error) { - if e := s.redirect(ctx, req, func(ctx context.Context, client csi.NodeClient) { - res, err = client.NodeExpandVolume(ctx, req) - }); e != nil { - return nil, e - } - - return -} - -func (s *PodVMNodeService) ReproduceNodeStageVolume(peerPodVolume *peerpodvolumeV1alpha1.PeerpodVolume) { - glog.Infof("Reproducing NodeStageVolumeRequest for peer pod") - wrapperRequest := peerPodVolume.Spec.WrapperNodeStageVolumeReq - var modifiedRequest csi.NodeStageVolumeRequest - if err := (&jsonpb.Unmarshaler{}).Unmarshal(bytes.NewReader([]byte(wrapperRequest)), &modifiedRequest); err != nil { - glog.Errorf("Failed to convert to NodeStageVolumeRequest, err: %v", err.Error()) - } else { - // The cached NodeStageVolumeRequest contains a faked PublishContext from [ControllerService.ControllerPublishVolume]. - // Since a CSI driver may depend on PublishContext to pass required information from ControllerPublishVolume to NodeStageVolume, - // we need to replace the PublishContext in the cached NodeStageVolumeRequest with the real one from - // the cached ControllerPublishVolumeResponse. - publishContext := make(map[string]string) - controllerPublishVolumeResJSON := peerPodVolume.Spec.WrapperControllerPublishVolumeRes - var controllerPublishVolumeRes csi.ControllerPublishVolumeResponse - if err := (&jsonpb.Unmarshaler{}).Unmarshal(bytes.NewReader([]byte(controllerPublishVolumeResJSON)), &controllerPublishVolumeRes); err != nil { - glog.Errorf("Failed to convert to ControllerPublishVolumeResponse, err: %s", err) - } - for k, v := range controllerPublishVolumeRes.PublishContext { - publishContext[k] = v - } - publishContext["device-path"] = peerPodVolume.Spec.DevicePath - modifiedRequest.PublishContext = publishContext - - glog.Infof("The modified NodeStageVolumeRequest is :%v", modifiedRequest) - ctx := context.Background() - count := 0 - reproduced := false - for { - glog.Infof("start to Reproducing NodeStageVolumeRequest for peer pod (retrying... %d/%d)", count, 20) - // TODO: error check - _ = s.redirect(ctx, modifiedRequest, func(ctx context.Context, client csi.NodeClient) { - response, err := client.NodeStageVolume(ctx, &modifiedRequest) - glog.Infof("The NodeStageVolumeResponse for peer pod is :%v", response) - if err != nil { - glog.Errorf("Failed to reproduce NodeStageVolume with modified NodeStageVolumeRequest, err: %v", err.Error()) - } else { - peerPodVolume.Status = peerpodvolumeV1alpha1.PeerpodVolumeStatus{ - State: peerpodvolumeV1alpha1.NodeStageVolumeApplied, - } - _, err := s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).UpdateStatus(context.Background(), peerPodVolume, metav1.UpdateOptions{}) - if err != nil { - glog.Errorf("Error happens while Update PeerpodVolume status to NodeStageVolumeApplied, err: %v", err.Error()) - } else { - reproduced = true - } - } - }) - if count == 20 { - glog.Error("reaches max retry count. gave up Reproducing NodeStageVolumeRequest for peer pod") - break - } - if reproduced { - break - } - glog.Infof("failed to Reproducing NodeStageVolumeRequest for peer pod (retrying... %d/%d)", count, 20) - count++ - } - - } -} - -func (s *PodVMNodeService) ReproduceNodePublishVolume(peerPodVolume *peerpodvolumeV1alpha1.PeerpodVolume) { - glog.Infof("Reproducing nodePublishVolumeRequest for peer pod") - wrapperRequest := peerPodVolume.Spec.WrapperNodePublishVolumeReq - var nodePublishVolumeRequest csi.NodePublishVolumeRequest - if err := (&jsonpb.Unmarshaler{}).Unmarshal(bytes.NewReader([]byte(wrapperRequest)), &nodePublishVolumeRequest); err != nil { - glog.Errorf("Failed to convert to NodePublishVolumeRequest, err: %v", err.Error()) - } else { - glog.Infof("The NodePublishVolumeRequest is :%v", nodePublishVolumeRequest) - ctx := context.Background() - count := 0 - reproduced := false - for { - glog.Infof("start to Reproducing nodePublishVolumeRequest for peer pod (retrying... %d/%d)", count, 20) - // TODO: error check - _ = s.redirect(ctx, nodePublishVolumeRequest, func(ctx context.Context, client csi.NodeClient) { - response, err := client.NodePublishVolume(ctx, &nodePublishVolumeRequest) - glog.Infof("The NodePublishVolumeResponse for peer pod is :%v", response) - if err != nil { - glog.Errorf("Failed to reproduce NodePublishVolume with the NodePublishVolumeRequest, err: %v", err.Error()) - } else { - peerPodVolume.Status = peerpodvolumeV1alpha1.PeerpodVolumeStatus{ - State: peerpodvolumeV1alpha1.NodePublishVolumeApplied, - } - _, err := s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).UpdateStatus(context.Background(), peerPodVolume, metav1.UpdateOptions{}) - if err != nil { - glog.Errorf("Error happens while Update PeerpodVolume status to NodePublishVolumeApplied, err: %v", err.Error()) - } else { - reproduced = true - } - } - }) - if count == 20 { - glog.Error("reaches max retry count. gave up Reproducing nodePublishVolumeRequest for peer pod") - break - } - if reproduced { - break - } - glog.Infof("failed to Reproducing nodePublishVolumeRequest for peer pod (retrying... %d/%d)", count, 20) - count++ - } - } -} - -func (s *PodVMNodeService) ReproduceNodeUnpublishVolume(peerPodVolume *peerpodvolumeV1alpha1.PeerpodVolume) { - glog.Infof("Reproducing nodeUnPublishVolumeRequest for peer pod") - wrapperRequest := peerPodVolume.Spec.WrapperNodeUnpublishVolumeReq - var nodeUnpublishVolumeRequest csi.NodeUnpublishVolumeRequest - if err := (&jsonpb.Unmarshaler{}).Unmarshal(bytes.NewReader([]byte(wrapperRequest)), &nodeUnpublishVolumeRequest); err != nil { - glog.Errorf("Failed to convert to NodeUnpublishVolumeRequest, err: %v", err.Error()) - } else { - glog.Infof("The NodeUnpublishVolumeRequest is :%v", nodeUnpublishVolumeRequest) - ctx := context.Background() - // TODO: error check - _ = s.redirect(ctx, nodeUnpublishVolumeRequest, func(ctx context.Context, client csi.NodeClient) { - response, err := client.NodeUnpublishVolume(ctx, &nodeUnpublishVolumeRequest) - if err != nil { - glog.Errorf("Failed to reproduce NodeUnpublishVolume with the NodeUnpublishVolumeRequest, err: %v", err.Error()) - } else { - peerPodVolume.Status = peerpodvolumeV1alpha1.PeerpodVolumeStatus{ - State: peerpodvolumeV1alpha1.NodeUnpublishVolumeApplied, - } - _, err := s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).UpdateStatus(context.Background(), peerPodVolume, metav1.UpdateOptions{}) - if err != nil { - glog.Errorf("Error happens while Update PeerpodVolume status to NodeUnpublishVolumeApplied, err: %v", err.Error()) - } - glog.Infof("The NodeUnpublishVolumeResponse for peer pod is :%v", response) - } - }) - } -} - -func (s *PodVMNodeService) ReproduceNodeUnstageVolume(peerPodVolume *peerpodvolumeV1alpha1.PeerpodVolume) { - glog.Infof("Reproducing nodeUnstageVolumeRequest for peer pod") - wrapperRequest := peerPodVolume.Spec.WrapperNodeUnstageVolumeReq - var nodeUnstageVolumeRequest csi.NodeUnstageVolumeRequest - if err := (&jsonpb.Unmarshaler{}).Unmarshal(bytes.NewReader([]byte(wrapperRequest)), &nodeUnstageVolumeRequest); err != nil { - glog.Errorf("Failed to convert to NodeUnstageVolumeRequest, err: %v", err.Error()) - } else { - glog.Infof("The NodeUnstageVolumeRequest is :%v", nodeUnstageVolumeRequest) - ctx := context.Background() - // TODO: error check - _ = s.redirect(ctx, nodeUnstageVolumeRequest, func(ctx context.Context, client csi.NodeClient) { - response, err := client.NodeUnstageVolume(ctx, &nodeUnstageVolumeRequest) - if err != nil { - glog.Errorf("Failed to reproduce NodeUnstageVolume with the NodeUnstageVolumeRequest, err: %v", err.Error()) - } else { - peerPodVolume.Status = peerpodvolumeV1alpha1.PeerpodVolumeStatus{ - State: peerpodvolumeV1alpha1.NodeUnstageVolumeApplied, - } - _, err := s.PeerpodvolumeClient.ConfidentialcontainersV1alpha1().PeerpodVolumes(s.Namespace).UpdateStatus(context.Background(), peerPodVolume, metav1.UpdateOptions{}) - if err != nil { - glog.Errorf("Error happens while Update PeerpodVolume status to NodeUnstageVolumeApplied, err: %v", err.Error()) - } - glog.Infof("The NodeUnstageVolumeResponse for peer pod is :%v", response) - } - }) - } -} - -func (s *PodVMNodeService) SyncHandler(peerPodVolume *peerpodvolumeV1alpha1.PeerpodVolume) { - if peerPodVolume.Spec.PodName != os.Getenv("POD_NAME") || peerPodVolume.Spec.PodNamespace != os.Getenv("POD_NAME_SPACE") { - // Only handle the podvm related PeerpodVolume CRD - glog.Infof("Only handle the PeerpodVolume crd object for POD_NAME:%v, POD_NAME_SPACE:%v", os.Getenv("POD_NAME"), os.Getenv("POD_NAME_SPACE")) - return - } - glog.Infof("syncHandler from podvm nodeService: %v ", peerPodVolume) - switch peerPodVolume.Status.State { - case peerpodvolumeV1alpha1.ControllerPublishVolumeApplied: - s.ReproduceNodeStageVolume(peerPodVolume) - case peerpodvolumeV1alpha1.NodeStageVolumeApplied: - s.ReproduceNodePublishVolume(peerPodVolume) - case peerpodvolumeV1alpha1.NodeUnpublishVolumeCached: - s.ReproduceNodeUnpublishVolume(peerPodVolume) - case peerpodvolumeV1alpha1.NodeUnstageVolumeCached: - s.ReproduceNodeUnstageVolume(peerPodVolume) - } -} - -func (s *PodVMNodeService) DeleteFunction(peerPodVolume *peerpodvolumeV1alpha1.PeerpodVolume) { - glog.Infof("deleteFunction from podvm nodeService: %v ", peerPodVolume) -} diff --git a/src/csi-wrapper/pkg/wrapper/server.go b/src/csi-wrapper/pkg/wrapper/server.go deleted file mode 100644 index 68761efbf3..0000000000 --- a/src/csi-wrapper/pkg/wrapper/server.go +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright Confidential Containers Contributors -// SPDX-License-Identifier: Apache-2.0 - -package wrapper - -import ( - "context" - "encoding/json" - "sync" - - "github.com/confidential-containers/cloud-api-adaptor/src/csi-wrapper/pkg/utils" - "google.golang.org/grpc" - - "github.com/container-storage-interface/spec/lib/go/csi" - "github.com/golang/glog" -) - -func NewNonBlockingGRPCServer() *nonBlockingGRPCServer { - return &nonBlockingGRPCServer{} -} - -// NonBlocking server -type nonBlockingGRPCServer struct { - wg sync.WaitGroup - server *grpc.Server - cleanup func() -} - -func Run(endpoint string, ids csi.IdentityServer, cs csi.ControllerServer, ns csi.NodeServer) error { - s := NewNonBlockingGRPCServer() - s.Start(endpoint, ids, cs, ns) - s.Wait() - return nil -} - -func (s *nonBlockingGRPCServer) Start(endpoint string, ids csi.IdentityServer, cs csi.ControllerServer, ns csi.NodeServer) { - - s.wg.Add(1) - - go s.serve(endpoint, ids, cs, ns) -} - -func (s *nonBlockingGRPCServer) Wait() { - s.wg.Wait() -} - -func (s *nonBlockingGRPCServer) Stop() { - s.server.GracefulStop() - s.cleanup() -} - -func (s *nonBlockingGRPCServer) ForceStop() { - s.server.Stop() - s.cleanup() -} - -func (s *nonBlockingGRPCServer) serve(endpoint string, ids csi.IdentityServer, cs csi.ControllerServer, ns csi.NodeServer) { - listener, cleanup, err := utils.Listen(endpoint) - if err != nil { - glog.Fatalf("Failed to listen on %s: %s", endpoint, err.Error()) - } else { - glog.Infof("Listening for connections on address: %#v", listener.Addr()) - } - - opts := []grpc.ServerOption{ - grpc.UnaryInterceptor(logGRPC), - } - server := grpc.NewServer(opts...) - s.server = server - s.cleanup = cleanup - - if ids != nil { - csi.RegisterIdentityServer(server, ids) - } - if cs != nil { - csi.RegisterControllerServer(server, cs) - } - if ns != nil { - csi.RegisterNodeServer(server, ns) - } - - _ = server.Serve(listener) // TODO: error check -} - -func logGRPC(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { - pri := glog.Level(3) - if info.FullMethod == "/csi.v1.Identity/Probe" { - // This call occurs frequently, therefore it only gets log at level 5. - pri = 5 - } - glog.V(pri).Infof("GRPC call: %s", info.FullMethod) - - v5 := glog.V(5) - if v5 { - v5.Infof("GRPC request: %s", req) - } - resp, err := handler(ctx, req) - if err != nil { - // Always log errors. Probably not useful though without the method name?! - glog.Errorf("GRPC error: %v", err) - } - - if v5 { - v5.Infof("GRPC response: %s", resp) - - logGRPCJson(info.FullMethod, req, resp, err) - } - - return resp, err -} - -// logGRPCJson logs the called GRPC call details in JSON format -func logGRPCJson(method string, request, reply interface{}, err error) { - // Log JSON with the request and response for easier parsing - logMessage := struct { - Method string - Request interface{} - Response interface{} - // Error as string, for backward compatibility. - // "" on no error. - Error string - // Full error dump, to be able to parse out full gRPC error code and message separately in a test. - FullError error - }{ - Method: method, - Request: request, - Response: reply, - FullError: err, - } - - if err != nil { - logMessage.Error = err.Error() - } - - msg, err := json.Marshal(logMessage) - if err != nil { - logMessage.Error = err.Error() - } - glog.V(5).Infof("gRPCCall: %s\n", msg) -} diff --git a/src/peerpod-ctrl/Dockerfile b/src/peerpod-ctrl/Dockerfile index 4fc32a28cc..4e59e65ea7 100644 --- a/src/peerpod-ctrl/Dockerfile +++ b/src/peerpod-ctrl/Dockerfile @@ -1,6 +1,6 @@ # Build the manager binary -# golang:1.25.10 -FROM --platform=$TARGETPLATFORM golang@sha256:3965b9511a9bed199f0b4eb146f99c31971a5c0adb3240d9f25e233cbd9e01c8 AS builder +# golang:1.25.11 +FROM --platform=$TARGETPLATFORM golang@sha256:dd7d32e19b28621cd982082397fc0510d396805b717d5e77466aa2dd692340de AS builder ARG TARGETOS ARG TARGETARCH ARG CGO_ENABLED=1 @@ -35,8 +35,8 @@ COPY peerpod-ctrl/controllers/ controllers/ RUN CC=gcc CGO_ENABLED=${CGO_ENABLED} GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build ${GOFLAGS} -a -o manager main.go # Target Image -# debian:trixie-slim (matches golang:1.25.10 base for library compatibility) -FROM --platform=$TARGETPLATFORM debian@sha256:109e2c65005bf160609e4ba6acf7783752f8502ad218e298253428690b9eaa4b +# debian:trixie-slim (matches golang:1.25.11 base for library compatibility) +FROM --platform=$TARGETPLATFORM debian@sha256:b6e2a152f22a40ff69d92cb397223c906017e1391a73c952b588e51af8883bf8 ARG CGO_ENABLED=1 RUN if [ "$CGO_ENABLED" = 1 ] ; then \ diff --git a/src/peerpod-ctrl/Makefile b/src/peerpod-ctrl/Makefile index 5e98020cca..e16665c734 100644 --- a/src/peerpod-ctrl/Makefile +++ b/src/peerpod-ctrl/Makefile @@ -19,7 +19,7 @@ endif SHELL = /usr/bin/env bash -o pipefail .SHELLFLAGS = -ec -BUILTIN_CLOUD_PROVIDERS ?= alibabacloud aws azure byom docker gcp ibmcloud libvirt +BUILTIN_CLOUD_PROVIDERS ?= alibabacloud aws azure byom gcp ibmcloud libvirt # Build tags required to build cloud-api-adaptor are derived from BUILTIN_CLOUD_PROVIDERS. # When libvirt is specified, CGO_ENABLED is set to 1. space := $() $() diff --git a/src/peerpod-ctrl/chart/Chart.yaml b/src/peerpod-ctrl/chart/Chart.yaml index 6035545c0b..6c1e254509 100644 --- a/src/peerpod-ctrl/chart/Chart.yaml +++ b/src/peerpod-ctrl/chart/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: peerpodctrl description: Controller for cleaning up dangling peer pod cloud resources type: application -version: 0.2.0 -appVersion: "v0.20.0" +version: 0.3.1 +appVersion: "v0.21.1" keywords: - confidential-containers diff --git a/src/peerpod-ctrl/go.mod b/src/peerpod-ctrl/go.mod index 466cf3e87f..f6dfed9659 100644 --- a/src/peerpod-ctrl/go.mod +++ b/src/peerpod-ctrl/go.mod @@ -1,6 +1,6 @@ module github.com/confidential-containers/cloud-api-adaptor/src/peerpod-ctrl -go 1.25.10 +go 1.25.11 require ( github.com/confidential-containers/cloud-api-adaptor/src/cloud-providers v0.0.0-00010101000000-000000000000 @@ -25,10 +25,10 @@ require ( github.com/alibabacloud-go/tea-xml v1.1.3 // indirect github.com/alibabacloud-go/vpc-20160428/v6 v6.12.5 // indirect github.com/aliyun/credentials-go v1.3.10 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.24 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9 // indirect - github.com/aws/aws-sdk-go-v2/service/signin v1.0.11 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.29 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.1.4 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.5 // indirect github.com/clbanning/mxj/v2 v2.7.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/gabriel-vasile/mimetype v1.4.11 // indirect @@ -55,9 +55,9 @@ require ( go.opentelemetry.io/otel/trace v1.43.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/mod v0.34.0 // indirect + golang.org/x/mod v0.35.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/tools v0.43.0 // indirect + golang.org/x/tools v0.44.0 // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect sigs.k8s.io/randfill v1.0.0 // indirect @@ -76,17 +76,17 @@ require ( github.com/IBM/platform-services-go-sdk v0.97.4 // indirect github.com/IBM/vpc-go-sdk v0.83.2 // indirect github.com/avast/retry-go/v4 v4.6.1 // indirect - github.com/aws/aws-sdk-go-v2 v1.41.7 // indirect - github.com/aws/aws-sdk-go-v2/config v1.32.17 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.19.16 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.23 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.23 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.23 // indirect - github.com/aws/aws-sdk-go-v2/service/ec2 v1.299.1 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.23 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.30.17 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.42.1 // indirect - github.com/aws/smithy-go v1.25.1 // indirect + github.com/aws/aws-sdk-go-v2 v1.41.12 // indirect + github.com/aws/aws-sdk-go-v2/config v1.32.23 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.19.22 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.28 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.28 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.28 // indirect + github.com/aws/aws-sdk-go-v2/service/ec2 v1.305.2 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.28 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.31.2 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.43.2 // indirect + github.com/aws/smithy-go v1.27.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -127,12 +127,12 @@ require ( go.mongodb.org/mongo-driver v1.17.6 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/crypto v0.50.0 // indirect - golang.org/x/net v0.53.0 // indirect + golang.org/x/crypto v0.52.0 // indirect + golang.org/x/net v0.55.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect - golang.org/x/sys v0.43.0 // indirect - golang.org/x/term v0.42.0 // indirect - golang.org/x/text v0.36.0 // indirect + golang.org/x/sys v0.45.0 // indirect + golang.org/x/term v0.43.0 // indirect + golang.org/x/text v0.37.0 // indirect golang.org/x/time v0.15.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/protobuf v1.36.11 // indirect @@ -142,8 +142,8 @@ require ( k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect; indirect// indirect k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect - libvirt.org/go/libvirt v1.11010.0 // indirect - libvirt.org/go/libvirtxml v1.11010.0 // indirect + libvirt.org/go/libvirt v1.12003.0 // indirect + libvirt.org/go/libvirtxml v1.12002.0 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/src/peerpod-ctrl/go.sum b/src/peerpod-ctrl/go.sum index be29a4190e..05064da71c 100644 --- a/src/peerpod-ctrl/go.sum +++ b/src/peerpod-ctrl/go.sum @@ -87,36 +87,36 @@ github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4t github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= github.com/avast/retry-go/v4 v4.6.1 h1:VkOLRubHdisGrHnTu89g08aQEWEgRU7LVEop3GbIcMk= github.com/avast/retry-go/v4 v4.6.1/go.mod h1:V6oF8njAwxJ5gRo1Q7Cxab24xs5NCWZBeaHHBklR8mA= -github.com/aws/aws-sdk-go-v2 v1.41.7 h1:DWpAJt66FmnnaRIOT/8ASTucrvuDPZASqhhLey6tLY8= -github.com/aws/aws-sdk-go-v2 v1.41.7/go.mod h1:4LAfZOPHNVNQEckOACQx60Y8pSRjIkNZQz1w92xpMJc= -github.com/aws/aws-sdk-go-v2/config v1.32.17 h1:FpL4/758/diKwqbytU0prpuiu60fgXKUWCpDJtApclU= -github.com/aws/aws-sdk-go-v2/config v1.32.17/go.mod h1:OXqUMzgXytfoF9JaKkhrOYsyh72t9G+MJH8mMRaexOE= -github.com/aws/aws-sdk-go-v2/credentials v1.19.16 h1:r3RJBuU7X9ibt8RHbMjWE6y60QbKBiII6wSrXnapxSU= -github.com/aws/aws-sdk-go-v2/credentials v1.19.16/go.mod h1:6cx7zqDENJDbBIIWX6P8s0h6hqHC8Avbjh9Dseo27ug= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.23 h1:UuSfcORqNSz/ey3VPRS8TcVH2Ikf0/sC+Hdj400QI6U= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.23/go.mod h1:+G/OSGiOFnSOkYloKj/9M35s74LgVAdJBSD5lsFfqKg= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.23 h1:GpT/TrnBYuE5gan2cZbTtvP+JlHsutdmlV2YfEyNde0= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.23/go.mod h1:xYWD6BS9ywC5bS3sz9Xh04whO/hzK2plt2Zkyrp4JuA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.23 h1:bpd8vxhlQi2r1hiueOw02f/duEPTMK59Q4QMAoTTtTo= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.23/go.mod h1:15DfR2nw+CRHIk0tqNyifu3G1YdAOy68RftkhMDDwYk= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.24 h1:OQqn11BtaYv1WLUowvcA30MpzIu8Ti4pcLPIIyoKZrA= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.24/go.mod h1:X5ZJyfwVrWA96GzPmUCWFQaEARPR7gCrpq2E92PJwAE= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.299.1 h1:gQ9fSyFk3Y9Vm2fVbphBeJfXJlkJvEvC35TszBVjprg= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.299.1/go.mod h1:Y95W0Hm6FYLPa6o0hbnJ+sWgmdc4ifcLFjGkdobWVhY= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9 h1:FLudkZLt5ci0ozzgkVo8BJGwvqNaZbTWb3UcucAateA= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9/go.mod h1:w7wZ/s9qK7c8g4al+UyoF1Sp/Z45UwMGcqIzLWVQHWk= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.23 h1:pbrxO/kuIwgEsOPLkaHu0O+m4fNgLU8B3vxQ+72jTPw= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.23/go.mod h1:/CMNUqoj46HpS3MNRDEDIwcgEnrtZlKRaHNaHxIFpNA= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.11 h1:TdJ+HdzOBhU8+iVAOGUTU63VXopcumCOF1paFulHWZc= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.11/go.mod h1:R82ZRExE/nheo0N+T8zHPcLRTcH8MGsnR3BiVGX0TwI= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.17 h1:7byT8HUWrgoRp6sXjxtZwgOKfhss5fW6SkLBtqzgRoE= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.17/go.mod h1:xNWknVi4Ezm1vg1QsB/5EWpAJURq22uqd38U8qKvOJc= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21 h1:+1Kl1zx6bWi4X7cKi3VYh29h8BvsCoHQEQ6ST9X8w7w= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21/go.mod h1:4vIRDq+CJB2xFAXZ+YgGUTiEft7oAQlhIs71xcSeuVg= -github.com/aws/aws-sdk-go-v2/service/sts v1.42.1 h1:F/M5Y9I3nwr2IEpshZgh1GeHpOItExNM9L1euNuh/fk= -github.com/aws/aws-sdk-go-v2/service/sts v1.42.1/go.mod h1:mTNxImtovCOEEuD65mKW7DCsL+2gjEH+RPEAexAzAio= -github.com/aws/smithy-go v1.25.1 h1:J8ERsGSU7d+aCmdQur5Txg6bVoYelvQJgtZehD12GkI= -github.com/aws/smithy-go v1.25.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/aws/aws-sdk-go-v2 v1.41.12 h1:DIKX2c31ekm9RA2D9FBj1EWXx++9AdAqRw+e78Tq2Ck= +github.com/aws/aws-sdk-go-v2 v1.41.12/go.mod h1:27+ACypSLljLAEKsCYOmrjKh83vuTRkuAe9Uv/3A4bg= +github.com/aws/aws-sdk-go-v2/config v1.32.23 h1:PYDobtcsJXK6bQe9I8RQk6s19Bz3xa3xRU08Hy1Em3Y= +github.com/aws/aws-sdk-go-v2/config v1.32.23/go.mod h1:QID4dqUQVgEOYPKsPWd1sNWCCR2c5g7o3jeEtIXPOZU= +github.com/aws/aws-sdk-go-v2/credentials v1.19.22 h1:SHfH6wyPsEgG7fVsi5rQxWEt7tuIcN2PGhb1mTFv6tE= +github.com/aws/aws-sdk-go-v2/credentials v1.19.22/go.mod h1:54nO8lKD4aQPOntM/VTWjnR+DYzTwx0YkSMZMhAgewQ= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.28 h1:b+kcDejJrXc30zU/w8Tc9klISwaO5wh+6T0sMBdDoHM= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.28/go.mod h1:LnI62O9GnSv6GcuLXxOYqlq0C8EmxMcgnF6m7LdYuOY= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.28 h1:Xf2j7NdVcUKomlZ4iihOP4AZ3Fzlr8h4yKpXeP+OFPg= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.28/go.mod h1:O8cDo1dW63jU7ki//kRe1z+tLGcpnD1jrouitsQddDw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.28 h1:KqIfN9kpkKkcBqBbNpNGTIrXO6ExTUvFKvXkC+YAzVo= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.28/go.mod h1:uxtQiKvLtNS4iXVsH2McVD/ls8FKN/uUhe1hGxPjrw0= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.29 h1:VkE9FuzTQVjBBrnj4+oCdxCLFIz7aqLYKUCjtvxVcOs= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.29/go.mod h1:H32Z2Qth9b+9LqjyBsCnozMQ8H2N7YBUDVXwbs0iggg= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.305.2 h1:xcHUdx4FNgUZmLuI6vqB5QDsVnWx1MVbPSoaXtLxUB8= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.305.2/go.mod h1:SUtka5kgdr5Wx2BdRrH/IGkCKHW63IVQoyBkP0P1gdo= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12 h1:ZD2+BSw9vFsNlKYIasSNt3uDbjqqXIBcM13UJv/Lx2k= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12/go.mod h1:Ms4zlcVBbXbiP7EVLhl+lgjvA/a7YphqQ3Ih3174EmI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.28 h1:axj4mEDletwKmTm/9jR+DkIMmCfcn5vE4jBMAAN+3Vg= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.28/go.mod h1:3Aaz69M0jqfSHLKqxgolgUBFT4hpwSNc7DzC95orEi8= +github.com/aws/aws-sdk-go-v2/service/signin v1.1.4 h1:YcpVyIPLCbiypN6KSphijN5fC7DDjX114SqA7prnnxg= +github.com/aws/aws-sdk-go-v2/service/signin v1.1.4/go.mod h1:5ZICS++oFTRPfa1GsBqFDWX/8WamZ/QQOcCzIuU/zLw= +github.com/aws/aws-sdk-go-v2/service/sso v1.31.2 h1:ySNWu7TPmj5fKFIa1GYvX+Ddxd5ccruqC20aMNuyWDM= +github.com/aws/aws-sdk-go-v2/service/sso v1.31.2/go.mod h1:A+U9luAOwFeB1kseyWCITVg7/NntoPebCFR9pQ4ch9A= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.5 h1:KSzGGqfk39O+WU3OEyYbx6F7sLDQCqxlOJ+2IksfK6U= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.5/go.mod h1:ATs88lXDeQB6CZOgQ5BIl9JbYS+EsCWUSDyff6L/oVo= +github.com/aws/aws-sdk-go-v2/service/sts v1.43.2 h1:RTO7mmGyedgnNmcPh3yQizNfc6GKoV5iqfdJavuf9vw= +github.com/aws/aws-sdk-go-v2/service/sts v1.43.2/go.mod h1:fBhUZXDin9YYqhcpOMjIcpdik25rVwWyxLdPH1RZd9s= +github.com/aws/smithy-go v1.27.1 h1:4T340VFndXtADGF52gYa1POyL7s9E4Z1OeZ1hCscIw8= +github.com/aws/smithy-go v1.27.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -382,8 +382,8 @@ golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOM golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= -golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -394,8 +394,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= -golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= +golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= +golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -417,8 +417,8 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= -golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= +golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= +golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= @@ -456,8 +456,8 @@ golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= -golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -471,8 +471,8 @@ golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= -golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY= -golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -484,8 +484,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= -golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -500,8 +500,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= -golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= +golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= +golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -559,10 +559,10 @@ k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZ k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -libvirt.org/go/libvirt v1.11010.0 h1:1EIh2x6qcRoIBBOvrgN62vq5FIpgUBrmGadprQ/4M0Y= -libvirt.org/go/libvirt v1.11010.0/go.mod h1:1WiFE8EjZfq+FCVog+rvr1yatKbKZ9FaFMZgEqxEJqQ= -libvirt.org/go/libvirtxml v1.11010.0 h1:lGUv6OQ4gz5Hm7F40G+swxmK/kcrMZGQ3M8/S+UyhME= -libvirt.org/go/libvirtxml v1.11010.0/go.mod h1:7Oq2BLDstLr/XtoQD8Fr3mfDNrzlI3utYKySXF2xkng= +libvirt.org/go/libvirt v1.12003.0 h1:3ek4ObakscdShZRloa9s8/mGhK7xVduqNmAkb15ZEDQ= +libvirt.org/go/libvirt v1.12003.0/go.mod h1:1WiFE8EjZfq+FCVog+rvr1yatKbKZ9FaFMZgEqxEJqQ= +libvirt.org/go/libvirtxml v1.12002.0 h1:NbEHw+R3IZE0vZF1deCQt+6tA+6Io4pAw9RjS7tM4fs= +libvirt.org/go/libvirtxml v1.12002.0/go.mod h1:7Oq2BLDstLr/XtoQD8Fr3mfDNrzlI3utYKySXF2xkng= sigs.k8s.io/controller-runtime v0.23.1 h1:TjJSM80Nf43Mg21+RCy3J70aj/W6KyvDtOlpKf+PupE= sigs.k8s.io/controller-runtime v0.23.1/go.mod h1:B6COOxKptp+YaUT5q4l6LqUJTRpizbgf9KSRNdQGns0= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= diff --git a/src/webhook/Dockerfile b/src/webhook/Dockerfile index a0ec8528c0..c8021fd304 100644 --- a/src/webhook/Dockerfile +++ b/src/webhook/Dockerfile @@ -1,6 +1,6 @@ # Build the manager binary -# golang:1.25.10 -FROM --platform=$BUILDPLATFORM golang@sha256:3965b9511a9bed199f0b4eb146f99c31971a5c0adb3240d9f25e233cbd9e01c8 AS builder +# golang:1.25.11 +FROM --platform=$BUILDPLATFORM golang@sha256:dd7d32e19b28621cd982082397fc0510d396805b717d5e77466aa2dd692340de AS builder WORKDIR /workspace # Copy the Go Modules manifests diff --git a/src/webhook/Makefile b/src/webhook/Makefile index 14d8f337ca..3037b820d9 100644 --- a/src/webhook/Makefile +++ b/src/webhook/Makefile @@ -165,11 +165,19 @@ kind-deploy: docker-build kind-load deploy ## deploy the webhook in the local ki deploy-cert-manager: ## Deploy cert-manager for webhook. curl -fsSL -o cmctl https://github.com/cert-manager/cmctl/releases/latest/download/cmctl_${OS}_${ARCH} chmod +x cmctl - # Deploy cert-manager - kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/$(CERT_MANAGER_VERSION)/cert-manager.yaml + # Deploy cert-manager (retry to tolerate transient etcd timeouts) + @for i in 1 2 3; do \ + echo "Applying cert-manager manifest (attempt $$i)..."; \ + kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/$(CERT_MANAGER_VERSION)/cert-manager.yaml && { \ + echo "cert-manager applied successfully"; break; }; \ + if [ $$i -eq 3 ]; then \ + echo "cert-manager apply failed after 3 attempts"; exit 1; \ + fi; \ + echo "Apply failed, retrying in 10s..."; sleep 10; \ + done # Wait for service to be up - kubectl wait --timeout=360s -n cert-manager endpoints/cert-manager --for=jsonpath='{.subsets[0].addresses[0].ip}' - kubectl wait --timeout=360s -n cert-manager endpoints/cert-manager-webhook --for=jsonpath='{.subsets[0].addresses[0].ip}' + kubectl wait --timeout=360s -n cert-manager --for=condition=Available deployment/cert-manager + kubectl wait --timeout=360s -n cert-manager --for=condition=Available deployment/cert-manager-webhook # Wait for few seconds for the cert-manager API to be ready # otherwise you'll hit the error "x509: certificate signed by unknown authority" # Best is to use cmctl - https://cert-manager.io/docs/installation/kubectl/#2-optional-wait-for-cert-manager-webhook-to-be-ready diff --git a/src/webhook/chart/Chart.yaml b/src/webhook/chart/Chart.yaml index 2357be283e..b0275bf360 100644 --- a/src/webhook/chart/Chart.yaml +++ b/src/webhook/chart/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: peerpods-webhook description: Mutating webhook that modifies pod specs to use peer pods runtime and resources type: application -version: 0.2.0 -appVersion: "v0.20.0" +version: 0.3.1 +appVersion: "v0.21.1" keywords: - confidential-containers diff --git a/src/webhook/go.mod b/src/webhook/go.mod index 1c0cab67d8..3d0a1c3a48 100644 --- a/src/webhook/go.mod +++ b/src/webhook/go.mod @@ -1,6 +1,6 @@ module github.com/confidential-containers/cloud-api-adaptor/src/webhook -go 1.25.10 +go 1.25.11 require ( k8s.io/api v0.35.2 @@ -45,12 +45,12 @@ require ( go.uber.org/zap v1.27.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/net v0.53.0 // indirect + golang.org/x/net v0.55.0 // indirect golang.org/x/oauth2 v0.32.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.43.0 // indirect - golang.org/x/term v0.42.0 // indirect - golang.org/x/text v0.36.0 // indirect + golang.org/x/sys v0.45.0 // indirect + golang.org/x/term v0.43.0 // indirect + golang.org/x/text v0.37.0 // indirect golang.org/x/time v0.9.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/protobuf v1.36.11 // indirect diff --git a/src/webhook/go.sum b/src/webhook/go.sum index 75b3d066f7..78a6e15367 100644 --- a/src/webhook/go.sum +++ b/src/webhook/go.sum @@ -111,24 +111,24 @@ go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= -golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= -golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= -golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= +golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= +golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= +golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= +golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY= golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= -golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY= -golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY= -golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= -golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= -golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= +golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= +golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=