Skip to content

Commit 53cdc65

Browse files
authored
Merge pull request #649 from gibmat/flasher-tests
Add tests to exercise `flasher-tool`
2 parents cad5116 + a707d80 commit 53cdc65

17 files changed

Lines changed: 295 additions & 219 deletions

.github/workflows/daily.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ jobs:
2323
- name: Checkout
2424
uses: actions/checkout@v4
2525

26+
- name: Install Go
27+
uses: actions/setup-go@v5
28+
with:
29+
go-version: stable
30+
2631
- name: Install dependencies
2732
run: |
2833
sudo apt-get install --yes \

incus-osd/cmd/flasher-tool/main.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -718,8 +718,14 @@ func downloadCurrentIncusOSRelease(ctx context.Context, asker ask.Asker, imageFo
718718

719719
slog.InfoContext(ctx, "Downloading and decompressing IncusOS image ("+imageFormat+") version "+release.Version()+" from Linux Containers CDN")
720720

721+
// In the update metadata, the USB image installer is called "raw", so fixup the format name if needed.
722+
providerFormatName := imageFormat
723+
if providerFormatName == "img" {
724+
providerFormatName = "raw"
725+
}
726+
721727
// Download and decompress the image.
722-
return release.DownloadImage(ctx, imageFormat, ".", nil)
728+
return release.DownloadImage(ctx, providerFormatName, ".", nil)
723729
}
724730

725731
// Spawn the editor with a temporary YAML file for editing configs.

incus-osd/tests/api-tests.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import urllib.request
1010

1111
from incusos_tests import IncusOSTests
12+
from incusos_tests.incus_test_vm import IncusOSException
1213

1314
current_release = None
1415
prior_stable_release = None
@@ -32,7 +33,7 @@
3233
break
3334

3435
if prior_stable_release is None:
35-
raise Exception("need at least two published stable releases")
36+
raise IncusOSException("need at least two published stable releases")
3637

3738
for file in current_release["files"]:
3839
if file["architecture"] == "x86_64":
@@ -80,7 +81,7 @@
8081

8182
try:
8283
data = future.result()
83-
except Exception as e:
84+
except IncusOSException as e:
8485
num_fail += 1
8586
print("FAIL: %s: %s" % (name, e.args[0]), flush=True)
8687

@@ -89,6 +90,9 @@
8990
for line in e.args[1]:
9091
if line != "":
9192
print(" %s" % line, flush=True)
93+
except Exception as e:
94+
num_fail += 1
95+
print("FAIL: %s: %s" % (name, e), flush=True)
9296
else:
9397
num_pass += 1
9498
print("PASS: %s" % name, flush=True)

incus-osd/tests/incusos_tests/__init__.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from inspect import getmembers, isfunction
22

3-
from . import tests_incusos_api, tests_incusos_api_applications, tests_incusos_api_debug, tests_incusos_api_services, \
4-
tests_incusos_api_system, tests_incusos_api_system_logging, tests_incusos_api_system_provider, \
3+
from . import tests_flasher_tool, tests_incusos_api, tests_incusos_api_applications, tests_incusos_api_debug, \
4+
tests_incusos_api_services, tests_incusos_api_system, tests_incusos_api_system_logging, tests_incusos_api_system_provider, \
55
tests_incusos_api_system_resources, tests_incusos_api_system_security, tests_incusos_api_system_storage_import_pool, \
66
tests_incusos_api_system_storage_local_pool, tests_install_smoke, tests_install_system_checks, tests_seed_applications, \
77
tests_seed_install, tests_upgrade
@@ -47,11 +47,15 @@ def GetTests(self):
4747
tests_incusos_api_system_storage_local_pool,
4848
]
4949

50+
# Test the flasher-tool utility
51+
flasher_tool_tests = [tests_flasher_tool]
52+
5053
ret = []
5154

5255
ret.extend(self._get_tests(core_tests, self.current_image_img))
5356
ret.extend(self._get_tests(upgrade_tests, self.prior_image_img))
5457
ret.extend(self._get_tests(iso_install_tests, self.current_image_iso))
58+
ret.extend(self._get_tests(flasher_tool_tests, ""))
5559

5660
return ret
5761

incus-osd/tests/incusos_tests/incus_test_vm/__init__.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66

77
from . import util
88

9+
class IncusOSException(Exception):
10+
"""A custom exception raised by test code."""
11+
12+
pass
13+
914
class IncusTestVM:
1015
def __init__(self, vm_name_base, install_image, root_size="50GiB"):
1116
self.vm_name = vm_name_base + "-" + util._get_random_string()
@@ -114,7 +119,7 @@ def WaitAgentRunning(self, timeout=300):
114119
pass
115120

116121
if time.time() - start > timeout:
117-
raise Exception("timed out waiting for agent to start")
122+
raise IncusOSException("timed out waiting for agent to start")
118123

119124
time.sleep(1)
120125

@@ -136,7 +141,7 @@ def WaitExpectedLog(self, unit, log, timeout=480, regex=False):
136141
return None
137142

138143
if time.time() - start > timeout:
139-
raise Exception(f"timed out waiting for log entry '{log}' to appear", result.stdout.decode("utf-8").split("\n"))
144+
raise IncusOSException(f"timed out waiting for log entry '{log}' to appear", result.stdout.decode("utf-8").split("\n"))
140145

141146
time.sleep(1)
142147

@@ -146,7 +151,7 @@ def LogDoesntContain(self, unit, log):
146151
result = subprocess.run(["incus", "exec", self.vm_name, "--", "journalctl", "-b", "-u", unit], capture_output=True, check=True)
147152

148153
if log in str(result.stdout):
149-
raise Exception(f"wasn't expecting log entry '{log}' to appear")
154+
raise IncusOSException(f"wasn't expecting log entry '{log}' to appear")
150155

151156
def APIRequest(self, path, method="GET", body=None, content_type=None, return_raw_content=False):
152157
"""Perform a HTTP REST API call, and return the result."""
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import io
2+
import os
3+
import re
4+
import subprocess
5+
import tarfile
6+
import tempfile
7+
8+
from .incus_test_vm import IncusTestVM, IncusOSException
9+
10+
def TestFlasherToolStableIMG(_):
11+
test_name = "flasher-tool-stable-img"
12+
13+
test_image = _flasher_download_image("stable", "img")
14+
15+
with IncusTestVM(test_name, test_image) as vm:
16+
vm.StartVM()
17+
vm.WaitAgentRunning()
18+
vm.WaitExpectedLog("incus-osd", "Installing IncusOS source=/dev/sdb target=/dev/sda")
19+
vm.WaitExpectedLog("incus-osd", "IncusOS was successfully installed")
20+
21+
def TestFlasherToolTestingISO(_):
22+
test_name = "flasher-tool-testing-iso"
23+
24+
test_image = _flasher_download_image("testing", "iso")
25+
26+
with IncusTestVM(test_name, test_image) as vm:
27+
vm.StartVM()
28+
vm.WaitAgentRunning()
29+
vm.WaitExpectedLog("incus-osd", "Installing IncusOS source=/dev/mapper/sr0 target=/dev/sda")
30+
vm.WaitExpectedLog("incus-osd", "IncusOS was successfully installed")
31+
32+
def _flasher_download_image(channel, image_format):
33+
if not os.path.exists("./incus-osd/flasher-tool"):
34+
subprocess.run(["go", "build", "./cmd/flasher-tool/"], cwd=os.path.join(os.getcwd(), "incus-osd"), capture_output=True, check=True)
35+
36+
with tempfile.TemporaryDirectory(dir=os.getcwd()) as tmp_dir:
37+
with tarfile.open(os.path.join(tmp_dir, "seed.tar"), mode="w") as tar:
38+
raw = "force_reboot: true".encode("utf-8")
39+
buf = io.BytesIO(raw)
40+
ti = tarfile.TarInfo(name="install.yaml")
41+
ti.size = len(raw)
42+
tar.addfile(ti, buf)
43+
44+
result = subprocess.run(["../incus-osd/flasher-tool", "--channel", channel, "--format", image_format, "--seed", "seed.tar"], cwd=tmp_dir, capture_output=True, check=True)
45+
46+
match = re.search("Downloading and decompressing IncusOS image \\(" + image_format + "\\) version (\\d+) from Linux Containers CDN", str(result.stderr))
47+
if not match:
48+
raise IncusOSException("Failed to download image")
49+
50+
os.rename(os.path.join(tmp_dir, "IncusOS_" + match.group(1) + "." + image_format), "flasher-install-image." + image_format)
51+
52+
return os.path.join(os.getcwd(), "flasher-install-image." + image_format)

incus-osd/tests/incusos_tests/tests_incusos_api.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from .incus_test_vm import IncusTestVM, util
1+
from .incus_test_vm import IncusTestVM, IncusOSException, util
22

33
def TestIncusOSAPI(install_image):
44
test_name = "incusos-api"
@@ -14,10 +14,10 @@ def TestIncusOSAPI(install_image):
1414
# Test top-level /1.0 endpoint.
1515
result = vm.APIRequest("/1.0")
1616
if result["status_code"] != 200:
17-
raise Exception("unexpected status code %d: %s" % (result["status_code"], result["error"]))
17+
raise IncusOSException("unexpected status code %d: %s" % (result["status_code"], result["error"]))
1818

1919
if result["metadata"]["environment"]["os_name"] != "IncusOS":
20-
raise Exception("unexpected OS Name: " + result["metadata"]["environment"]["os_name"])
20+
raise IncusOSException("unexpected OS Name: " + result["metadata"]["environment"]["os_name"])
2121

2222
if result["metadata"]["environment"]["os_version"] != incusos_version:
23-
raise Exception("unexpected OS Version: " + result["metadata"]["environment"]["os_version"])
23+
raise IncusOSException("unexpected OS Version: " + result["metadata"]["environment"]["os_version"])

0 commit comments

Comments
 (0)