Skip to content

Commit 778d16e

Browse files
Add VM tests for image creator (#352)
<!-- Description: Please provide a summary of the changes and the motivation behind them. --> This PR adds vm tests to test image creator --- creates a seed image, customizes it with image customizer to boot in a qemu VM. does some basic checking on the image, latest pipeline run https://github.com/microsoft/azure-linux-image-tools/actions/runs/16761942070 ### **Checklist** - [x] Tests added/updated - [x] Documentation updated (if needed) - [x] Code conforms to style guidelines
1 parent 3d34234 commit 778d16e

File tree

13 files changed

+730
-21
lines changed

13 files changed

+730
-21
lines changed

.github/workflows/build.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,30 @@ jobs:
116116
with:
117117
hostArch: arm64
118118
hostDistro: ubuntu2404
119+
120+
tests-vmtests-imagecreator-azl3-amd64:
121+
name: VMTests suite image creator AZL3 AMD64
122+
if: ${{ inputs.runVMTests }}
123+
needs: binary-build-amd64
124+
uses: ./.github/workflows/tests-vmtests-imagecreator.yml
125+
with:
126+
hostArch: amd64
127+
hostDistro: azl3
128+
129+
tests-vmtests-imagecreator-ubuntu2404-amd64:
130+
name: VMTests suite image creator Ubuntu24.04 AMD64
131+
if: ${{ inputs.runVMTests }}
132+
needs: binary-build-amd64
133+
uses: ./.github/workflows/tests-vmtests-imagecreator.yml
134+
with:
135+
hostArch: amd64
136+
hostDistro: ubuntu2404
137+
138+
tests-vmtests-imagecreator-ubuntu2404-arm64:
139+
name: VMTests suite image creator Ubuntu24.04 ARM64
140+
if: ${{ inputs.runVMTests }}
141+
needs: binary-build-arm64
142+
uses: ./.github/workflows/tests-vmtests-imagecreator.yml
143+
with:
144+
hostArch: arm64
145+
hostDistro: ubuntu2404
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
3+
4+
name: Tests VMTests suite for Image Creator
5+
6+
permissions:
7+
contents: read
8+
9+
on:
10+
workflow_call:
11+
inputs:
12+
hostArch:
13+
required: true
14+
type: string
15+
hostDistro:
16+
required: true
17+
type: string
18+
19+
env:
20+
EXPECTED_GO_VERSION: "1.24.1"
21+
ORAS_VERSION: "1.2.3"
22+
23+
jobs:
24+
tests-vmtests:
25+
name: Tests VMTests suite
26+
runs-on:
27+
- self-hosted
28+
- 1ES.Pool=${{ inputs.hostDistro == 'azl3' && (inputs.hostArch == 'amd64' && 'maritimus-github-runner-azl3-amd64' || 'maritimus-github-runner-azl3-arm64') || (inputs.hostArch == 'amd64' && 'maritimus-github-runner-ubuntu2404-amd64' || 'maritimus-github-runner-ubuntu2404-arm64') }}
29+
permissions:
30+
contents: read
31+
steps:
32+
- name: setup go 1.x
33+
uses: actions/setup-go@v5
34+
with:
35+
go-version: "${{ env.EXPECTED_GO_VERSION }}"
36+
37+
- name: Install prerequisites (AZL3)
38+
if: inputs.hostDistro == 'azl3'
39+
env:
40+
HOST_ARCH: ${{ inputs.hostArch }}
41+
run: |
42+
set -eux
43+
44+
# Install all prerequisites in a single batch
45+
sudo tdnf install -y \
46+
libvirt libvirt-daemon libvirt-daemon-config-network \
47+
libvirt-daemon-kvm libvirt-devel qemu-kvm qemu-img python3-libvirt \
48+
python3-devel edk2-ovmf azure-cli \
49+
rpm coreutils util-linux systemd openssl \
50+
sed createrepo_c squashfs-tools cdrkit e2fsprogs dosfstools \
51+
xfsprogs zstd veritysetup grub2 binutils lsof git
52+
53+
# grub2-pc is only available on x86.
54+
if [[ "$HOST_ARCH" == "amd64" ]]; then
55+
sudo tdnf install -y grub2-pc
56+
fi
57+
58+
sudo tdnf list installed
59+
sudo systemctl restart libvirtd
60+
sudo systemctl status libvirtd
61+
62+
- name: Install prerequisites (Ubuntu 24.04)
63+
if: inputs.hostDistro == 'ubuntu2404'
64+
env:
65+
HOST_ARCH: ${{ inputs.hostArch }}
66+
run: |
67+
set -eux
68+
69+
sudo apt update -y
70+
sudo apt list --installed
71+
72+
# Install all prerequisites in a single batch
73+
sudo apt -y install \
74+
qemu-utils rpm coreutils util-linux mount fdisk udev openssl \
75+
sed createrepo-c squashfs-tools genisoimage e2fsprogs dosfstools \
76+
xfsprogs zstd cryptsetup-bin grub2-common binutils lsof \
77+
python3-venv python3-pip python3-dev \
78+
libvirt-dev libvirt-daemon libvirt-daemon-system libvirt-clients \
79+
qemu-kvm virt-manager
80+
81+
# Install arm64 specific packages
82+
if [[ "$HOST_ARCH" == "arm64" ]]; then
83+
sudo apt -y install qemu-system-arm qemu-efi-aarch64 ovmf vgabios
84+
fi
85+
86+
sudo apt list --installed
87+
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
88+
89+
- name: Checkout
90+
uses: actions/checkout@v4
91+
with:
92+
path: repo
93+
94+
- name: Build imagecreator and imagecustomizer
95+
run: |
96+
set -eux
97+
98+
pushd ./repo/toolkit/
99+
100+
# Build imagecreator and imagecustomizer.
101+
sudo env "PATH=$PATH" make go-imagecreator go-imagecustomizer
102+
103+
popd
104+
105+
- name: Test setup
106+
run: |
107+
set -eux
108+
109+
pushd ./repo/test/vmtests
110+
111+
# Ensure an ssh key exists.
112+
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N ""
113+
114+
make create-venv
115+
116+
- name: Download RPMS and tools file
117+
run: |
118+
set -eux
119+
120+
# Download the test RPM and tools file.
121+
./repo/toolkit/tools/internal/testutils/testrpms/download-test-utils.sh -t 3.0 -s true
122+
123+
124+
- name: Run image creator tests
125+
run: |
126+
set -eux
127+
128+
pushd ./repo/test/vmtests
129+
130+
sudo make test-imagecreator \
131+
IMAGE_CREATOR_BIN="../../toolkit/out/tools/imagecreator" \
132+
TOOLS_TAR="../../toolkit/tools/internal/testutils/testrpms/tools.tar.gz" \
133+
RPM_SOURCES="../../toolkit/tools/internal/testutils/testrpms/downloadedrpms/3.0" \
134+
IMAGE_CUSTOMIZER_BINARY_PATH="../../toolkit/out/tools/imagecustomizer" \
135+
SSH_PRIVATE_KEY_FILE=~/.ssh/id_ed25519
136+
137+
- uses: actions/upload-artifact@v4
138+
if: ${{ !cancelled() }}
139+
with:
140+
name: tests-results-vmtests-imagecreator-${{ inputs.hostDistro }}-${{ inputs.hostArch }}
141+
path: repo/test/vmtests/out

.github/workflows/tests-vmtests.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,15 +138,15 @@ jobs:
138138
set -eux
139139
140140
pushd ./repo/test/vmtests
141-
141+
142142
CORE_LEGACY_AZL2=""
143143
CORE_LEGACY_AZL3=""
144144
if [ "$HOST_ARCH" == "amd64" ]; then
145145
CORE_LEGACY_AZL2="../../../azl-core-legacy-2.0/image.vhd"
146146
CORE_LEGACY_AZL3="../../../azl-core-legacy-3.0/image.vhd"
147147
fi
148-
149-
sudo make test \
148+
149+
sudo make test-imagecustomizer \
150150
IMAGE_CUSTOMIZER_CONTAINER_TAG="$CONTAINER_TAG" \
151151
CORE_EFI_AZL2="../../../azl-core-efi-2.0/image.vhdx" \
152152
CORE_EFI_AZL3="../../../azl-core-efi-3.0/image.vhdx" \
@@ -160,5 +160,5 @@ jobs:
160160
- uses: actions/upload-artifact@v4
161161
if: ${{ !cancelled() }}
162162
with:
163-
name: tests-results-vmtests-${{ inputs.hostDistro }}-${{ inputs.hostArch }}
163+
name: tests-results-vmtests-imagecustomizer-${{ inputs.hostDistro }}-${{ inputs.hostArch }}
164164
path: repo/test/vmtests/out

test/vmtests/Makefile

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ MYPY=${VENV_BIN_DIR}/mypy
1515
TOOLKIT_DIR=../../toolkit
1616
TOOLS_BIN_DIR=${TOOLKIT_DIR}/out/tools
1717
IMAGE_CUSTOMIZER_BIN=${TOOLS_BIN_DIR}/imagecustomizer
18+
IMAGE_CREATOR_BIN=${TOOLS_BIN_DIR}/imagecreator
1819

1920
IMAGE_CUSTOMIZER_CONTAINER_TAG?=imagecustomizer:dev
2021

@@ -68,12 +69,16 @@ check-black:
6869
${IMAGE_CUSTOMIZER_BIN}:
6970
${MAKE} -C ${TOOLKIT_DIR} go-imagecustomizer
7071

72+
.PHONY: ${IMAGE_CREATOR_BIN}
73+
${IMAGE_CREATOR_BIN}:
74+
${MAKE} -C ${TOOLKIT_DIR} go-imagecreator
75+
7176
.PHONY: image-customizer-container
7277
image-customizer-container: ${IMAGE_CUSTOMIZER_BIN}
7378
${TOOLKIT_DIR}/tools/imagecustomizer/container/build-container.sh -t ${IMAGE_CUSTOMIZER_CONTAINER_TAG}
7479

75-
.PHONY: test
76-
test:
80+
.PHONY: test-imagecustomizer
81+
test-imagecustomizer:
7782
${PYTEST} \
7883
--image-customizer-container-url="${IMAGE_CUSTOMIZER_CONTAINER_TAG}" \
7984
--logs-dir="${LOGS_DIR}" \
@@ -87,7 +92,25 @@ test:
8792
--show-capture=all \
8893
--tb=short \
8994
--junitxml=./out/$(DATETIME_AS_VERSION)/report.xml \
90-
./vmtests
95+
./vmtests/test_min_change.py
96+
97+
.PHONY: test-imagecreator
98+
test-imagecreator:
99+
${PYTEST} \
100+
--image-creator-binary-path="${IMAGE_CREATOR_BIN}" \
101+
--rpm-sources="${RPM_SOURCES}" \
102+
--tools-tar="${TOOLS_TAR}" \
103+
--image-customizer-binary-path="${IMAGE_CUSTOMIZER_BINARY_PATH}" \
104+
--ssh-private-key="${SSH_PRIVATE_KEY_FILE}" \
105+
$(if $(filter y,$(KEEP_ENVIRONMENT)),--keep-environment) \
106+
--log-cli-level=DEBUG \
107+
--show-capture=all \
108+
--tb=short \
109+
--junitxml=./out/$(DATETIME_AS_VERSION)/report.xml \
110+
./vmtests/imagecreator/test_imagecreator.py
111+
112+
.PHONY: run-imagecustomizer
113+
run-imagecustomizer: image-customizer-container test-imagecustomizer
91114

92-
.PHONY: run
93-
run: image-customizer-container test
115+
.PHONY: run-imagecreator
116+
run-imagecreator: ${IMAGE_CREATOR_BIN} test-imagecreator
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Image Creator Tests
2+
3+
This directory contains VM tests for the Image Creator tool, similar to the existing Image
4+
Customizer tests in the parent directory.
5+
6+
## Overview
7+
8+
The Image Creator tests create new Azure Linux images from scratch using the Image Creator binary
9+
tool, then boot them in VMs to verify they work correctly.
10+
11+
## Test Structure
12+
13+
- `test_imagecreator.py` - Main test file containing the Image Creator VM tests
14+
- `conftest.py` - Pytest configuration and fixtures specific to Image Creator tests
15+
- `../utils/imagecreator.py` - Utility functions for running the Image Creator binary
16+
17+
**Configuration Files:**
18+
19+
- SSH base config: `toolkit/tools/pkg/imagecreatorlib/testdata/ssh-base-config.yaml` - Base
20+
ImageCustomizer configuration used with `add_ssh_to_config()` function to add SSH access and
21+
VM-friendly settings
22+
23+
## Key Differences from Image Customizer Tests
24+
25+
1. **No Input Images**: Image Creator creates images from scratch, so there are no input image
26+
fixtures
27+
2. **No Docker**: Image Creator is a binary tool, not a containerized tool like Image Customizer
28+
3. **RPM Sources Required**: Tests need `--rpm-sources` to specify package repositories
29+
4. **Tools Tar Required**: Tests need `--tools-tar` to specify the tools tarball
30+
5. **Limited Output Formats**: Image Creator supports fewer output formats (no ISO)
31+
32+
## Test Functions
33+
34+
- `test_create_image_efi_qcow_output()` - Creates a QCOW2 image with EFI boot
35+
- `test_create_image_efi_raw_output()` - Creates a RAW image with EFI boot
36+
37+
## Configuration
38+
39+
Tests use the `minimal-os.yaml` configuration file from the Image Creator library testdata directory
40+
for creating the base image. SSH access and VM-friendly settings are added using the
41+
`ssh-base-config.yaml` file with the `add_ssh_to_config()` function.
42+
43+
## Running the Tests
44+
45+
```bash
46+
# From the vmtests directory
47+
pytest imagecreator/test_imagecreator.py \
48+
--image-creator-binary-path <path-to-imagecreator-binary> \
49+
--rpm-sources <path-to-rpm-repo> \
50+
--tools-tar <path-to-tools.tar.gz> \
51+
--ssh-private-key <path-to-ssh-key> \
52+
--logs-dir <path-to-logs>
53+
```
54+
55+
## Building the Image Creator Binary
56+
57+
Before running tests, you need to build the Image Creator binary:
58+
59+
```bash
60+
sudo make -C ./toolkit go-imagecreator
61+
```
62+
63+
The binary will be located at `./toolkit/out/tools/imagecreator`.
64+
65+
## Test Validation
66+
67+
Each test:
68+
69+
1. Creates a new image using the Image Creator binary with `minimal-os.yaml`
70+
2. Customizes the image with SSH access using ImageCustomizer and the `add_ssh_to_config()` function
71+
with `ssh-base-config.yaml`
72+
3. Creates a differencing disk for VM testing
73+
4. Boots the image in a libvirt VM
74+
5. Connects via SSH and runs basic validation:
75+
- Checks that the OS is Azure Linux 3.0
76+
- Verifies essential packages are installed (kernel, systemd, grub2, bash)
77+
- Validates the system can boot and respond to commands
78+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
3+
4+
from pathlib import Path
5+
from typing import Generator, List
6+
7+
import pytest
8+
9+
10+
def pytest_addoption(parser: pytest.Parser) -> None:
11+
parser.addoption("--image-creator-binary-path", action="store", help="Path to Image Creator binary")
12+
parser.addoption("--rpm-sources", action="append", help="Path to RPM source files or directories")
13+
parser.addoption("--tools-tar", action="store", help="Path to tools tar file")
14+
parser.addoption("--image-customizer-binary-path", action="store", help="Path to Image Customizer binary")
15+
16+
17+
@pytest.fixture(scope="session")
18+
def image_creator_binary_path(request: pytest.FixtureRequest) -> Generator[Path, None, None]:
19+
binary_path = request.config.getoption("--image-creator-binary-path")
20+
if not binary_path:
21+
raise Exception("--image-creator-binary-path is required for imagecreator tests")
22+
yield Path(binary_path)
23+
24+
25+
@pytest.fixture(scope="session")
26+
def rpm_sources(request: pytest.FixtureRequest) -> Generator[List[Path], None, None]:
27+
sources = request.config.getoption("--rpm-sources")
28+
if not sources:
29+
raise Exception("--rpm-sources is required for test")
30+
yield [Path(source) for source in sources]
31+
32+
33+
@pytest.fixture(scope="session")
34+
def tools_tar(request: pytest.FixtureRequest) -> Generator[Path, None, None]:
35+
tar_path = request.config.getoption("--tools-tar")
36+
if not tar_path:
37+
raise Exception("--tools-tar is required for test")
38+
yield Path(tar_path)
39+
40+
41+
@pytest.fixture(scope="session")
42+
def image_customizer_binary_path(request: pytest.FixtureRequest) -> Generator[Path, None, None]:
43+
binary_path = request.config.getoption("--image-customizer-binary-path")
44+
if not binary_path:
45+
raise Exception("--image-customizer-binary-path is required for test")
46+
yield Path(binary_path)

0 commit comments

Comments
 (0)