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