Skip to content

Commit 35a9b51

Browse files
committed
.github: add pipeline to validate ch on mshv
1 parent c34baac commit 35a9b51

File tree

5 files changed

+625
-0
lines changed

5 files changed

+625
-0
lines changed

.github/workflows/mshv-build.yaml

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
name: MSHV Build
2+
on:
3+
workflow_dispatch:
4+
jobs:
5+
infrasetup:
6+
uses: ./.github/workflows/mshv-infra.yaml
7+
name: MSHV Infra Setup
8+
with:
9+
# PR_NUMBER: ${{ github.event.pull_request.number }}
10+
RG: "MSHVBuild"
11+
SKU: "Standard_d16s_v5"
12+
secrets:
13+
MSHV_MI_CLIENT_ID: ${{ secrets.MSHV_MI_CLIENT_ID }}
14+
MSHV_STORAGE_ACCOUNT_PATHS: ${{ secrets.MSHV_STORAGE_ACCOUNT_PATHS }}
15+
MSHV_USERNAME: ${{ secrets.MSHV_USERNAME }}
16+
MSHV_PASSWORD: ${{ secrets.MSHV_PASSWORD }}
17+
MSHV_X86_SOURCE_PATH: ${{ secrets.MSHV_X86_SOURCE_PATH }}
18+
MSHV_RUNNER_RG: ${{ secrets.MSHV_RUNNER_RG }}
19+
MSHV_RUNNER: ${{ secrets.MSHV_RUNNER }}
20+
build:
21+
name: Build
22+
runs-on:
23+
- self-hosted
24+
- Linux
25+
needs: infrasetup
26+
strategy:
27+
fail-fast: false
28+
matrix:
29+
RUNNER:
30+
- 'test-runner'
31+
BUILD_CMD:
32+
- 'cargo build'
33+
- 'cargo build --features "mshv"'
34+
- 'cargo build --features "mshv,sev_snp,igvm"'
35+
- 'cargo build --package vhost_user_net'
36+
- 'cargo build --package vhost_user_block'
37+
steps:
38+
- name: Checkout repo
39+
uses: actions/checkout@v4
40+
41+
- name: AZ Login
42+
run: az login --identity --client-id ${{ secrets.MSHV_MI_CLIENT_ID }}
43+
44+
- name: Build MSHV
45+
run: |
46+
set -x
47+
echo "Retrieving VM Private IP address"
48+
PRIVATE_IP=$(az vm show -g MSHVBuild-${GITHUB_RUN_ID} -n x86_${GITHUB_RUN_ID} -d --query privateIps -o tsv)
49+
if [ $? -ne 0 ]; then
50+
echo "Failed to retrieve private IP address"
51+
exit 1
52+
fi
53+
echo "Successfully retrieved private IP address"
54+
echo $PRIVATE_IP
55+
56+
sshpass -p "${{ secrets.MSHV_PASSWORD }}" ssh -o StrictHostKeyChecking=no "${{ secrets.MSHV_USERNAME }}"@$PRIVATE_IP << EOF
57+
set -x
58+
echo "Logged in"
59+
sudo tdnf install git moby-engine moby-cli dosfstools mtools build-essential m4 bison flex libuuid-devel qemu-img -y
60+
curl -sSf https://sh.rustup.rs | sh -s -- --default-toolchain stable --profile default -y
61+
export PATH="\$HOME/.cargo/bin:$PATH"
62+
cd cloud-hypervisor
63+
# Build Cloud-Hypervisor
64+
echo "Building Cloud-Hypervisor"
65+
${{ matrix.BUILD_CMD }} --release
66+
EOF
67+
68+
cleanup:
69+
needs: build
70+
if: always()
71+
runs-on:
72+
- self-hosted
73+
- Linux
74+
steps:
75+
- name: Delete Resource Group
76+
run: |
77+
az group delete --name MSHVBuild-${GITHUB_RUN_ID} --yes --no-wait
78+
echo "Resource group deleted"

.github/workflows/mshv-infra.yaml

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
name: MSHV Infra Setup
2+
on:
3+
workflow_call:
4+
inputs:
5+
ARCH:
6+
description: 'Architecture for the VM'
7+
required: true
8+
type: string
9+
KEY:
10+
description: 'SSH Key Name'
11+
required: true
12+
type: string
13+
OS_DISK_SIZE:
14+
description: 'OS Disk Size in GB'
15+
required: true
16+
type: string
17+
RG:
18+
description: 'Resource Group Name'
19+
required: true
20+
type: string
21+
VM_SKU:
22+
description: 'VM SKU'
23+
required: true
24+
type: string
25+
secrets:
26+
MI_CLIENT_ID:
27+
required: true
28+
RUNNER_RG:
29+
required: true
30+
STORAGE_ACCOUNT_PATHS:
31+
required: true
32+
ARCH_SOURCE_PATH:
33+
required: true
34+
USERNAME:
35+
required: true
36+
outputs:
37+
PRIVATE_IP:
38+
description: 'Private IP of the VM'
39+
value: ${{ jobs.infra-setup.outputs.PRIVATE_IP }}
40+
concurrency:
41+
group: ${{ github.workflow }}-${{ github.ref }}
42+
cancel-in-progress: true
43+
jobs:
44+
infra-setup:
45+
name: ${{ inputs.ARCH }} VM Provision
46+
runs-on:
47+
- self-hosted
48+
- Linux
49+
outputs:
50+
PRIVATE_IP: ${{ steps.get-vm-ip.outputs.PRIVATE_IP }}
51+
steps:
52+
- name: Install & login to AZ CLI
53+
env:
54+
MI_CLIENT_ID: ${{ secrets.MI_CLIENT_ID }}
55+
run: |
56+
set -e
57+
echo "Installing Azure CLI if not already installed"
58+
if ! command -v az &>/dev/null; then
59+
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
60+
else
61+
echo "Azure CLI already installed"
62+
fi
63+
az --version
64+
echo "Logging into Azure CLI using Managed Identity"
65+
az login --identity --client-id ${MI_CLIENT_ID}
66+
67+
- name: Get Location
68+
id: get-location
69+
env:
70+
SKU: ${{ inputs.VM_SKU }}
71+
STORAGE_ACCOUNT_PATHS: ${{ secrets.STORAGE_ACCOUNT_PATHS }}
72+
run: |
73+
set -e
74+
# Extract vCPU count from SKU (e.g., "Standard_D2s_v3" => 2)
75+
vcpu=$(echo "$SKU" | sed -n 's/^Standard_[A-Za-z]\+\([0-9]\+\).*/\1/p')
76+
if [[ -z "$vcpu" ]]; then
77+
echo "Cannot extract vCPU count from SKU: $SKU"
78+
exit 1
79+
fi
80+
81+
SUPPORTED_LOCATIONS=$(echo "$STORAGE_ACCOUNT_PATHS" | jq -r 'to_entries[] | .key')
82+
83+
for location in $SUPPORTED_LOCATIONS; do
84+
family=$(az vm list-skus --size "$SKU" --location "$location" --resource-type "virtualMachines" --query '[0].family' -o tsv)
85+
if [[ -z "$family" ]]; then
86+
echo "Cannot determine VM family for SKU: $SKU in $location"
87+
continue
88+
fi
89+
90+
usage=$(az vm list-usage --location "$location" --query "[?name.value=='$family'] | [0]" -o json)
91+
current=$(echo "$usage" | jq -r '.currentValue')
92+
limit=$(echo "$usage" | jq -r '.limit')
93+
94+
if [[ $((limit - current)) -ge $vcpu ]]; then
95+
echo "Sufficient quota found in $location"
96+
echo "location=$location" >> "$GITHUB_OUTPUT"
97+
exit 0
98+
fi
99+
done
100+
101+
echo "No location found with sufficient vCPU quota for SKU: $SKU"
102+
exit 1
103+
104+
- name: Create Resource Group
105+
id: rg-setup
106+
env:
107+
LOCATION: ${{ steps.get-location.outputs.location }}
108+
RG: ${{ inputs.RG }}
109+
STORAGE_ACCOUNT_PATHS: ${{ secrets.STORAGE_ACCOUNT_PATHS }}
110+
run: |
111+
set -e
112+
echo "Creating Resource Group: $RG"
113+
# Create the resource group
114+
echo "Creating resource group in location: ${LOCATION}"
115+
az group create --name ${RG} --location ${LOCATION}
116+
echo "Resource group created successfully."
117+
118+
- name: Generate SSH Key
119+
id: generate-ssh-key
120+
env:
121+
KEY: ${{ inputs.KEY }}
122+
run: |
123+
set -e
124+
echo "Generating SSH key: $KEY"
125+
mkdir -p ~/.ssh
126+
ssh-keygen -t rsa -b 4096 -f ~/.ssh/${KEY} -N ""
127+
128+
- name: Create VM
129+
id: vm-setup
130+
env:
131+
KEY: ${{ inputs.KEY }}
132+
LOCATION: ${{ steps.get-location.outputs.location }}
133+
OS_DISK_SIZE: ${{ inputs.OS_DISK_SIZE }}
134+
RG: ${{ inputs.RG }}
135+
RUNNER_RG: ${{ secrets.RUNNER_RG }}
136+
USERNAME: ${{ secrets.USERNAME }}
137+
VM_SKU: ${{ inputs.VM_SKU }}
138+
VM_IMAGE_NAME: ${{ inputs.ARCH }}_${{ steps.get-location.outputs.location }}_image
139+
VM_NAME: ${{ inputs.ARCH }}_${{ steps.get-location.outputs.location }}_${{ github.run_id }}
140+
run: |
141+
set -e
142+
echo "Creating $VM_SKU VM: $VM_NAME"
143+
144+
# Extract subnet ID from the runner VM
145+
echo "Retrieving subnet ID..."
146+
SUBNET_ID=$(az network vnet list --resource-group ${RUNNER_RG} --query "[?contains(location, '${LOCATION}')].{SUBNETS:subnets}" | jq -r ".[0].SUBNETS[0].id")
147+
if [[ -z "${SUBNET_ID}" ]]; then
148+
echo "ERROR: Failed to retrieve Subnet ID."
149+
exit 1
150+
fi
151+
152+
# Extract image ID from the runner VM
153+
echo "Retrieving image ID..."
154+
IMAGE_ID=$(az image show --resource-group ${RUNNER_RG} --name ${VM_IMAGE_NAME} --query "id" -o tsv)
155+
if [[ -z "${IMAGE_ID}" ]]; then
156+
echo "ERROR: Failed to retrieve Image ID."
157+
exit 1
158+
fi
159+
160+
# Create VM
161+
az vm create \
162+
--resource-group ${RG} \
163+
--name ${VM_NAME} \
164+
--subnet ${SUBNET_ID} \
165+
--size ${VM_SKU} \
166+
--location ${LOCATION} \
167+
--image ${IMAGE_ID} \
168+
--os-disk-size-gb ${OS_DISK_SIZE} \
169+
--public-ip-sku Standard \
170+
--storage-sku Premium_LRS \
171+
--public-ip-address "" \
172+
--admin-username ${USERNAME} \
173+
--ssh-key-value ~/.ssh/${KEY}.pub \
174+
--security-type Standard \
175+
--output json
176+
177+
echo "VM creation process completed successfully."
178+
179+
- name: Get VM Private IP
180+
id: get-vm-ip
181+
env:
182+
RG: ${{ inputs.RG }}
183+
VM_NAME: ${{ inputs.ARCH }}_${{ steps.get-location.outputs.location }}_${{ github.run_id }}
184+
run: |
185+
set -e
186+
echo "Retrieving VM Private IP address..."
187+
# Retrieve VM Private IP address
188+
PRIVATE_IP=$(az vm show -g ${RG} -n ${VM_NAME} -d --query privateIps -o tsv)
189+
if [[ -z "$PRIVATE_IP" ]]; then
190+
echo "ERROR: Failed to retrieve private IP address."
191+
exit 1
192+
fi
193+
echo "PRIVATE_IP=$PRIVATE_IP" >> $GITHUB_OUTPUT
194+
195+
- name: Wait for SSH availability
196+
env:
197+
KEY: ${{ inputs.KEY }}
198+
PRIVATE_IP: ${{ steps.get-vm-ip.outputs.PRIVATE_IP }}
199+
USERNAME: ${{ secrets.USERNAME }}
200+
run: |
201+
echo "Waiting for SSH to be accessible..."
202+
timeout 120 bash -c 'until ssh -o StrictHostKeyChecking=no -i ~/.ssh/${KEY} ${USERNAME}@${PRIVATE_IP} "exit" 2>/dev/null; do sleep 5; done'
203+
echo "VM is accessible!"
204+
205+
- name: Remove Old Host Key
206+
env:
207+
PRIVATE_IP: ${{ steps.get-vm-ip.outputs.PRIVATE_IP }}
208+
run: |
209+
set -e
210+
echo "Removing the old host key"
211+
ssh-keygen -R $PRIVATE_IP
212+
213+
- name: SSH into VM and Install Dependencies
214+
env:
215+
KEY: ${{ inputs.KEY }}
216+
PRIVATE_IP: ${{ steps.get-vm-ip.outputs.PRIVATE_IP }}
217+
USERNAME: ${{ secrets.USERNAME }}
218+
run: |
219+
set -e
220+
ssh -i ~/.ssh/${KEY} -o StrictHostKeyChecking=no ${USERNAME}@${PRIVATE_IP} << EOF
221+
set -e
222+
echo "Logged in successfully."
223+
echo "Installing dependencies..."
224+
sudo tdnf install -y git moby-engine moby-cli clang llvm pkg-config make gcc glibc-devel
225+
echo "Installing Rust..."
226+
curl -sSf https://sh.rustup.rs | sh -s -- --default-toolchain stable --profile default -y
227+
export PATH="\$HOME/.cargo/bin:\$PATH"
228+
cargo --version
229+
sudo mkdir -p /etc/docker/
230+
echo '{"default-ulimits":{"nofile":{"Hard":65535,"Name":"nofile","Soft":65535}}}' | sudo tee /etc/docker/daemon.json
231+
sudo systemctl stop docker
232+
sudo systemctl enable docker.service
233+
sudo systemctl enable containerd.service
234+
sudo systemctl start docker
235+
sudo groupadd -f docker
236+
sudo usermod -a -G docker ${USERNAME}
237+
sudo systemctl restart docker
238+
EOF

0 commit comments

Comments
 (0)