Skip to content

Commit 9fbf9e7

Browse files
committed
feat(ci): add unified "OpenNebula: Build and Test" workflow
Single workflow_dispatch that builds the OpenNebula .qcow2 images with Packer and boot-tests each one in-job, on the build runner, under QEMU/KVM with a one-context CONTEXT ISO - no S3 round-trip, no separate test job. Both build runners are Ubuntu-with-KVM so the apt-based opennebula-test-steps composite runs on the machine that just built the image: x86_64 stays on ubuntu24-full-x64, and aarch64 now builds on ubuntu24-full-arm64 (a1.metal) instead of almalinux-9-aarch64. shared-steps' runner_os detection builds on Ubuntu for both arches. Every built image is tested; for a build-only run use opennebula-build.yml. opennebula-test-steps gains an optional image_file input: when set it tests the locally-built qcow2 (chowns the root-owned Packer output, copies it to base.qcow2) instead of downloading image_url. Backward-compatible - the standalone opennebula-test.yml keeps passing image_url and is unchanged.
1 parent b7cb283 commit 9fbf9e7

2 files changed

Lines changed: 276 additions & 7 deletions

File tree

.github/actions/opennebula-test-steps/action.yml

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@ description: "Boots an OpenNebula .qcow2 under QEMU/KVM with a one-context CONTE
33

44
inputs:
55
image_url:
6-
description: "Public S3 URL to the OpenNebula .qcow2 image"
7-
required: true
6+
description: "Public URL to the OpenNebula .qcow2 image (provide this OR image_file)"
7+
required: false
8+
default: ''
9+
image_file:
10+
description: "Path to a locally-built OpenNebula .qcow2 (provide this OR image_url; takes precedence)"
11+
required: false
12+
default: ''
813
image_filename:
914
description: "Image filename (last path segment of image_url)"
1015
required: true
@@ -76,13 +81,24 @@ runs:
7681
chmod 700 ~/.ssh
7782
ssh-keygen -t ed25519 -N '' -C "opennebula-test-${GITHUB_RUN_ID}" -f ~/.ssh/opennebula_test
7883
79-
- name: Download base image
84+
- name: Obtain base image
8085
shell: bash
8186
env:
8287
IMAGE_URL: ${{ inputs.image_url }}
88+
IMAGE_FILE: ${{ inputs.image_file }}
8389
run: |
84-
# Download base image
85-
curl -fL --retry 5 --retry-delay 5 -o base.qcow2 "${IMAGE_URL}"
90+
# Obtain base image: prefer a locally-built file, else download the URL
91+
if [ -n "${IMAGE_FILE}" ]; then
92+
# Packer runs under sudo, so the build output is root-owned; copy it
93+
# out and take ownership before QEMU opens it.
94+
sudo cp "${IMAGE_FILE}" base.qcow2
95+
sudo chown "$(id -u):$(id -g)" base.qcow2
96+
elif [ -n "${IMAGE_URL}" ]; then
97+
curl -fL --retry 5 --retry-delay 5 -o base.qcow2 "${IMAGE_URL}"
98+
else
99+
echo "[Error] one of image_file or image_url must be provided"
100+
exit 1
101+
fi
86102
qemu-img info base.qcow2
87103
88104
- name: Create writable qcow2 overlay (100 GiB)
@@ -330,7 +346,11 @@ runs:
330346
{
331347
echo "## OpenNebula Image Test"
332348
echo ""
333-
echo "- **Image**: [${IMAGE_FILENAME}](${IMAGE_URL})"
349+
if [ -n "${IMAGE_URL:-}" ]; then
350+
echo "- **Image**: [${IMAGE_FILENAME}](${IMAGE_URL})"
351+
else
352+
echo "- **Image**: \`${IMAGE_FILENAME}\`"
353+
fi
334354
echo "- **Arch (filename)**: \`${ALMA_ARCH_FULL}\`"
335355
if [ -n "${ALMA_RELEASE:-}" ]; then
336356
echo "- **AlmaLinux release**: \`${ALMA_RELEASE}\`"
@@ -375,7 +395,7 @@ runs:
375395
TEXT: |
376396
:almalinux: **${{ inputs.image_filename }}**, OpenNebula image test, by the GitHub [Action](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
377397
378-
**Image**: [${{ inputs.image_filename }}](${{ inputs.image_url }})
398+
${{ inputs.image_url && format('**Image**: [{0}]({1})', inputs.image_filename, inputs.image_url) || format('**Image**: `{0}`', inputs.image_filename) }}
379399
**Arch (filename)**: `${{ inputs.alma_arch_full }}`
380400
${{ env.ALMA_RELEASE && format('**AlmaLinux release**: `{0}`', env.ALMA_RELEASE) || '' }}
381401
${{ env.SYSTEM_ARCH && format('**System architecture**: `{0}`', env.SYSTEM_ARCH) || '' }}
Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
name: "OpenNebula: Build and Test"
2+
3+
# Unified OpenNebula pipeline: build the .qcow2 images with Packer and
4+
# boot-test each one in-job, directly on the build runner, under QEMU/KVM
5+
# with a one-context CONTEXT ISO (no S3 round-trip, no separate test job).
6+
#
7+
# Both build runners are Ubuntu-with-KVM (x86_64 -> ubuntu24-full-x64,
8+
# aarch64 -> ubuntu24-full-arm64), so the opennebula-test-steps composite
9+
# (apt-based, needs /dev/kvm) runs on the same machine that just built the
10+
# image. shared-steps' runner_os detection builds happily on Ubuntu for
11+
# both arches.
12+
#
13+
# Every built image is tested; for a build-only run use opennebula-build.yml.
14+
#
15+
# Stage implementations live in composite actions:
16+
# .github/actions/shared-steps - Packer build (all clouds)
17+
# .github/actions/opennebula-test-steps - QEMU/KVM boot test of the local qcow2
18+
19+
on:
20+
workflow_dispatch:
21+
inputs:
22+
23+
date_time_stamp:
24+
description: 'Custom date+time stamp, YYYYMMDDhhmmss'
25+
required: false
26+
default: ''
27+
28+
version_major:
29+
description: 'AlmaLinux major version'
30+
required: true
31+
default: '10'
32+
type: choice
33+
options:
34+
- 10-kitten
35+
- 10
36+
- 9
37+
- 8
38+
39+
self-hosted:
40+
description: "Build aarch64 image on self-hosted runner"
41+
required: true
42+
type: boolean
43+
default: true
44+
45+
store_as_artifact:
46+
description: "Store images to the workflow Artifacts"
47+
required: true
48+
type: boolean
49+
default: false
50+
51+
upload_to_s3:
52+
description: "Upload to S3 Bucket"
53+
required: true
54+
type: boolean
55+
default: true
56+
57+
notify_mattermost:
58+
description: "Send notification to Mattermost"
59+
required: true
60+
type: boolean
61+
default: true
62+
env:
63+
PACKER_GITHUB_API_TOKEN: ${{ secrets.GIT_HUB_TOKEN }}
64+
65+
jobs:
66+
init-data:
67+
name: Initialize common data
68+
runs-on: ubuntu-24.04
69+
outputs:
70+
time_stamp: ${{ steps.date-time-stamp.outputs.time_stamp }}
71+
date_stamp: ${{ steps.date-time-stamp.outputs.date_stamp }}
72+
steps:
73+
- name: Date+time stamp
74+
id: date-time-stamp
75+
run: |
76+
# date+time stamp, YYYYMMDDhhmmss
77+
if [ "${{ inputs.date_time_stamp }}" != "" ]; then
78+
date_time_stamp="${{ inputs.date_time_stamp }}"
79+
else
80+
date_time_stamp=$(date -u '+%Y%m%d%H%M%S')
81+
fi
82+
echo "time_stamp=${date_time_stamp}" >> $GITHUB_OUTPUT
83+
84+
# date stamp, YYYYMMDD
85+
date_stamp=${date_time_stamp:0:-6}
86+
echo "date_stamp=${date_stamp}" >> "$GITHUB_OUTPUT"
87+
88+
build-gh-hosted:
89+
name: ${{ matrix.variant }} opennebula-x86_64 build+test
90+
permissions:
91+
id-token: write
92+
contents: read
93+
needs: [init-data]
94+
# use runs-on runners if within the almalinux org, otherwise GH runners.
95+
# Both are Ubuntu-with-KVM (metal in the org, nested-KVM on the gh-hosted
96+
# fallback), so the in-job QEMU test can boot the image just built.
97+
runs-on: >-
98+
${{
99+
github.repository_owner == 'AlmaLinux' &&
100+
format('runs-on={0}/family=c7i.metal-24xl+c7a.metal-48xl+*8gd.metal*/image=ubuntu24-full-x64', github.run_id)
101+
||
102+
'ubuntu-24.04'
103+
}}
104+
strategy:
105+
fail-fast: false
106+
matrix:
107+
variant: >-
108+
${{ fromJSON(
109+
( inputs.version_major == '10-kitten' || inputs.version_major == '10' )
110+
&& format('["{0}", "{0}-v2"]', inputs.version_major)
111+
|| format('["{0}"]', inputs.version_major)
112+
) }}
113+
114+
env:
115+
TIME_STAMP: ${{ needs.init-data.outputs.time_stamp }}
116+
DATE_STAMP: ${{ needs.init-data.outputs.date_stamp }}
117+
118+
steps:
119+
- name: Checkout ${{ github.action_repository }}
120+
uses: actions/checkout@v6
121+
122+
- uses: ./.github/actions/shared-steps
123+
name: ${{ matrix.variant }} opennebula-x86_64 image
124+
with:
125+
type: opennebula
126+
variant: ${{ matrix.variant }}
127+
arch: x86_64
128+
S3_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
129+
S3_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
130+
AWS_REGION: ${{ vars.AWS_REGION }}
131+
AWS_S3_BUCKET: ${{ vars.AWS_S3_BUCKET }}
132+
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }}
133+
MATTERMOST_CHANNEL: ${{ vars.MATTERMOST_CHANNEL }}
134+
store_as_artifact: ${{ inputs.store_as_artifact }}
135+
upload_to_s3: ${{ inputs.upload_to_s3 }}
136+
notify_mattermost: ${{ inputs.notify_mattermost }}
137+
run_test: 'false'
138+
runner: ${{ github.repository_owner == 'AlmaLinux' && 'aws-ec2' || 'gh_hosted' }}
139+
env:
140+
PACKER_GITHUB_API_TOKEN: ${{ secrets.GIT_HUB_TOKEN }}
141+
142+
- uses: ./.github/actions/opennebula-test-steps
143+
name: Test ${{ matrix.variant }} opennebula-x86_64 image
144+
with:
145+
image_file: ${{ env.IMAGE_FILE }}
146+
image_url: ${{ inputs.upload_to_s3 && format('https://{0}.s3-accelerate.dualstack.amazonaws.com/{1}/{2}', vars.AWS_S3_BUCKET, env.AWS_S3_PATH, env.IMAGE_NAME) || '' }}
147+
image_filename: ${{ env.IMAGE_NAME }}
148+
alma_arch: x86_64
149+
alma_arch_full: ${{ contains(matrix.variant, 'v2') && 'x86_64_v2' || 'x86_64' }}
150+
release_string: ${{ env.RELEASE_STRING }}
151+
notify_mattermost: ${{ inputs.notify_mattermost }}
152+
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }}
153+
MATTERMOST_CHANNEL: ${{ vars.MATTERMOST_CHANNEL }}
154+
155+
# The job is to start self-hosted runner on AWS EC2 instance if not in the almalinux org
156+
# It does nothing if in the almalinux org, so 'Setup and start runner' step is skipped
157+
start-self-hosted-runner:
158+
name: ${{ inputs.version_major }} opennebula-aarch64 runner
159+
if: ${{ inputs.self-hosted }}
160+
runs-on: ubuntu-24.04
161+
needs: [init-data]
162+
163+
steps:
164+
- name: Setup and start runner
165+
if: github.repository_owner != 'AlmaLinux'
166+
uses: unblocked/ec2-action-builder@v1.12
167+
with:
168+
github_token: ${{ secrets.GIT_HUB_TOKEN }}
169+
aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID }}
170+
aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
171+
aws_region: ${{ vars.AWS_REGION }}
172+
ec2_ami_id: ${{ secrets.EC2_AMI_ID_AL9_AARCH64 }}
173+
174+
ec2_subnet_id: ${{ secrets.EC2_SUBNET_ID}} # Subnet and Security Group should match
175+
ec2_security_group_id: ${{ secrets.EC2_SECURITY_GROUP_ID }} # Availability Zones list for 'a1.metal' Instance Type
176+
ec2_instance_type: a1.metal
177+
178+
ec2_root_disk_size_gb: "16" # override default size which is too small for actions and tests stuff
179+
ec2_root_disk_ebs_class: "gp3" # use faster and cheeper storage instead of default 'gp2'
180+
ec2_instance_ttl: 30 # Optional (default is 60 minutes)
181+
ec2_spot_instance_strategy: None # Other options are: SpotOnly, BestEffort, MaxPerformance
182+
ec2_instance_tags: > # Required for IAM role resource permission scoping
183+
[
184+
{"Key": "Project", "Value": "GitHub Actions Self-hosted Runners"}
185+
]
186+
187+
build-self-hosted:
188+
name: ${{ inputs.version_major }} opennebula-aarch64 build+test
189+
permissions:
190+
id-token: write
191+
contents: read
192+
if: ${{ inputs.self-hosted }}
193+
needs: [init-data, start-self-hosted-runner]
194+
# AlmaLinux org: RunsOn a1.metal with the Ubuntu arm64 image (NOT
195+
# almalinux-9-aarch64) so the in-job QEMU test can run - shared-steps
196+
# builds fine on Ubuntu via its runner_os detection, and the bare-metal
197+
# a1 has /dev/kvm.
198+
#
199+
# Forks fall back to the self-hosted EC2 a1.metal runner. That AMI is
200+
# AlmaLinux 9 (EL9), where the apt-based test composite cannot run - the
201+
# in-job test step will fail there; fork CI should test via opennebula-test.yml.
202+
runs-on: >-
203+
${{
204+
github.repository_owner == 'AlmaLinux' &&
205+
format('runs-on={0}/family=a1.metal/image=ubuntu24-full-arm64/volume=40g', github.run_id)
206+
||
207+
github.run_id
208+
}}
209+
210+
env:
211+
TIME_STAMP: ${{ needs.init-data.outputs.time_stamp }}
212+
DATE_STAMP: ${{ needs.init-data.outputs.date_stamp }}
213+
214+
steps:
215+
- name: Checkout ${{ github.action_repository }}
216+
uses: actions/checkout@v6
217+
218+
- uses: ./.github/actions/shared-steps
219+
name: ${{ inputs.version_major }} opennebula-aarch64 image
220+
with:
221+
type: opennebula
222+
variant: ${{ inputs.version_major }}
223+
arch: aarch64
224+
S3_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
225+
S3_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
226+
AWS_REGION: ${{ vars.AWS_REGION }}
227+
AWS_S3_BUCKET: ${{ vars.AWS_S3_BUCKET }}
228+
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }}
229+
MATTERMOST_CHANNEL: ${{ vars.MATTERMOST_CHANNEL }}
230+
store_as_artifact: ${{ inputs.store_as_artifact }}
231+
upload_to_s3: ${{ inputs.upload_to_s3 }}
232+
notify_mattermost: ${{ inputs.notify_mattermost }}
233+
run_test: 'false'
234+
runner: aws-ec2
235+
env:
236+
PACKER_GITHUB_API_TOKEN: ${{ secrets.GIT_HUB_TOKEN }}
237+
238+
- uses: ./.github/actions/opennebula-test-steps
239+
name: Test ${{ inputs.version_major }} opennebula-aarch64 image
240+
with:
241+
image_file: ${{ env.IMAGE_FILE }}
242+
image_url: ${{ inputs.upload_to_s3 && format('https://{0}.s3-accelerate.dualstack.amazonaws.com/{1}/{2}', vars.AWS_S3_BUCKET, env.AWS_S3_PATH, env.IMAGE_NAME) || '' }}
243+
image_filename: ${{ env.IMAGE_NAME }}
244+
alma_arch: aarch64
245+
alma_arch_full: aarch64
246+
release_string: ${{ env.RELEASE_STRING }}
247+
notify_mattermost: ${{ inputs.notify_mattermost }}
248+
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }}
249+
MATTERMOST_CHANNEL: ${{ vars.MATTERMOST_CHANNEL }}

0 commit comments

Comments
 (0)