mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-31 04:53:01 +02:00
187 lines
7.5 KiB
Python
Executable File
187 lines
7.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Container Upgrade
|
|
|
|
Verify container upgrade functionality by testing the optimization
|
|
that skips recreation when the container configuration hasn't changed.
|
|
|
|
This test uses two versions of curios-httpd (24.05.0 and 24.11.0) served
|
|
over HTTP with :latest tag. The test verifies that:
|
|
1. Container starts successfully from the :latest image
|
|
2. When the :latest tag points to a new version, upgrade is triggered
|
|
3. The container runs the new version after upgrade
|
|
"""
|
|
import os
|
|
import time
|
|
import infamy
|
|
import infamy.file_server as srv
|
|
from infamy.util import until
|
|
|
|
SRVPORT = 8008
|
|
SRVDIR = "/srv"
|
|
|
|
with infamy.Test() as test:
|
|
NAME = "web"
|
|
|
|
with test.step("Set up topology and attach to target DUT"):
|
|
env = infamy.Env()
|
|
target = env.attach("target", "mgmt")
|
|
tgtssh = env.attach("target", "mgmt", "ssh")
|
|
|
|
if not target.has_model("infix-containers"):
|
|
test.skip()
|
|
|
|
_, hport = env.ltop.xlate("host", "data")
|
|
_, tport = env.ltop.xlate("target", "data")
|
|
|
|
with test.step("Detect target architecture"):
|
|
# Query operational datastore for machine architecture
|
|
system_state = target.get_data("/ietf-system:system-state")
|
|
arch = system_state["system-state"]["platform"]["machine"]
|
|
|
|
# Map kernel arch to our image naming
|
|
arch_map = {
|
|
"amd64": "amd64",
|
|
"x86_64": "amd64",
|
|
"arm64": "arm64",
|
|
"aarch64": "arm64",
|
|
"armv7l": "arm64",
|
|
}
|
|
image_arch = arch_map.get(arch, "amd64")
|
|
print(f"Detected architecture: {arch} -> using {image_arch} images")
|
|
|
|
with test.step("Set up isolated network and file server"):
|
|
netns = infamy.IsolatedMacVlan(hport).start()
|
|
netns.addip("192.168.0.1")
|
|
|
|
target.put_config_dicts({
|
|
"ietf-interfaces": {
|
|
"interfaces": {
|
|
"interface": [
|
|
{
|
|
"name": tport,
|
|
"ipv4": {
|
|
"address": [
|
|
{
|
|
"ip": "192.168.0.2",
|
|
"prefix-length": 24
|
|
}
|
|
]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}
|
|
})
|
|
netns.must_reach("192.168.0.2")
|
|
|
|
with srv.FileServer(netns, "192.168.0.1", SRVPORT, SRVDIR):
|
|
with test.step("Create symlink for curios-httpd:latest -> 24.05.0"):
|
|
# Create symlink in the file server directory
|
|
old_img = f"{SRVDIR}/curios-httpd-24.05.0-{image_arch}.tar.gz"
|
|
new_img = f"{SRVDIR}/curios-httpd-24.11.0-{image_arch}.tar.gz"
|
|
latest_link = f"{SRVDIR}/curios-httpd-latest.tar.gz"
|
|
|
|
# Remove any existing symlink
|
|
if os.path.exists(latest_link):
|
|
os.unlink(latest_link)
|
|
|
|
# Point to old version
|
|
os.symlink(old_img, latest_link)
|
|
print(f"Created symlink: {latest_link} -> {old_img}")
|
|
|
|
with test.step("Create container 'web' from curios-httpd:latest (24.05.0)"):
|
|
target.put_config_dicts({
|
|
"infix-containers": {
|
|
"containers": {
|
|
"container": [
|
|
{
|
|
"name": f"{NAME}",
|
|
"image": f"http://192.168.0.1:{SRVPORT}/curios-httpd-latest.tar.gz",
|
|
"command": "/usr/sbin/httpd -f -v -p 91",
|
|
"network": {
|
|
"host": True
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}
|
|
})
|
|
|
|
with test.step("Verify container 'web' has started with version 24.05.0"):
|
|
c = infamy.Container(target)
|
|
until(lambda: c.running(NAME), attempts=60)
|
|
|
|
# Get initial operational data to capture container ID and image ID
|
|
containers_data = target.get_data("/infix-containers:containers")
|
|
container = containers_data["containers"]["container"][NAME]
|
|
initial_container_id = container["id"]
|
|
initial_image_id = container["image-id"]
|
|
|
|
print(f"Container started with container-id: {initial_container_id[:12]}...")
|
|
print(f" and image-id: {initial_image_id[:12]}...")
|
|
|
|
# Get baseline disk usage for /var/lib/containers
|
|
result = tgtssh.runsh("doas du -s /var/lib/containers")
|
|
initial_disk_usage = int(result.stdout.split()[0])
|
|
print(f"Disk usage after initial creation: {initial_disk_usage} KiB")
|
|
|
|
with test.step("Update symlink to point to curios-httpd:24.11.0"):
|
|
# Remove old symlink and point to new version
|
|
os.unlink(latest_link)
|
|
os.symlink(new_img, latest_link)
|
|
print(f"Updated symlink: {latest_link} -> {new_img}")
|
|
|
|
with test.step("Trigger container upgrade by calling upgrade action"):
|
|
c = infamy.Container(target)
|
|
c.action(NAME, "upgrade")
|
|
|
|
with test.step("Wait for container 'web' to complete uprgade"):
|
|
time.sleep(3)
|
|
until(lambda: c.running(NAME), attempts=30)
|
|
|
|
with test.step("Verify container 'web' is running new version 24.11.0"):
|
|
c = infamy.Container(target)
|
|
# Wait for upgrade to complete and container to restart
|
|
until(lambda: c.running(NAME), attempts=60)
|
|
|
|
# Get operational data after upgrade
|
|
containers_data = target.get_data("/infix-containers:containers")
|
|
container = containers_data["containers"]["container"][NAME]
|
|
new_container_id = container["id"]
|
|
new_image_id = container["image-id"]
|
|
|
|
print(f"After upgrade container-id: {new_container_id[:12]}...")
|
|
print(f" image-id: {new_image_id[:12]}...")
|
|
|
|
# Verify that both IDs have changed
|
|
if new_container_id == initial_container_id:
|
|
test.fail("Container ID did not change after upgrade!")
|
|
if new_image_id == initial_image_id:
|
|
test.fail("Image ID did not change after upgrade!")
|
|
|
|
print("✓ Both container ID and image ID changed after upgrade")
|
|
|
|
with test.step("Verify old image was pruned and disk usage is reasonable"):
|
|
# We expect minimal growth — the new image replaces old
|
|
# image, and they are each around 500-600 KiB. We allow for
|
|
# some overhead, but should be < 200 KiB. If the old image
|
|
# is not pruned properly, we'll see ~600 KiB extra growth.
|
|
MAX_ACCEPTABLE_GROWTH = 1000
|
|
|
|
# Get disk usage after upgrade
|
|
result = tgtssh.runsh("doas du -s /var/lib/containers")
|
|
final_disk_usage = int(result.stdout.split()[0])
|
|
print(f"Disk usage after upgrade: {final_disk_usage} KiB")
|
|
|
|
# Calculate the difference
|
|
disk_growth = final_disk_usage - initial_disk_usage
|
|
print(f"Disk usage growth: {disk_growth} KiB")
|
|
|
|
if disk_growth > MAX_ACCEPTABLE_GROWTH:
|
|
test.fail(f"Disk usage grew by {disk_growth} KiB (expected < {MAX_ACCEPTABLE_GROWTH} KiB). "
|
|
f"Old image may not have been pruned!")
|
|
|
|
print(f"✓ Disk usage growth is acceptable ({disk_growth} KiB < {MAX_ACCEPTABLE_GROWTH} KiB)")
|
|
|
|
test.succeed()
|