stages/vagrant: add virtualbox support

This adds support generating a virtualbox vagrant image. It differs from
libvirt by requiring an xml file and a vmdk image.

When the provider is set to libvirt it is required to pass a
`virtualbox` configuration section to this stage which must include the
mac address.

Signed-off-by: Simon de Vlieger <supakeen@redhat.com>
This commit is contained in:
Jelle van der Waa 2022-10-07 23:10:00 +02:00 committed by Achilleas Koutsou
parent 4ec94759a1
commit f7ef1d6464
5 changed files with 1685 additions and 23 deletions

View file

@ -6,6 +6,18 @@ import sys
import osbuild.api
VAGRANTFILE = """Vagrant.configure("2") do |config|
{content}
end
"""
VAGRANTFILE_LIBVIRT = """config.vm.provider :libvirt do |libvirt|
libvirt.driver = "kvm"
end
"""
VAGRANTFILE_VIRTUALBOX = 'config.vm.base_mac = "{mac_address}"\n'
def parse_input(inputs):
image = inputs["image"]
@ -19,32 +31,42 @@ def parse_input(inputs):
def main(tree, options, inputs):
source = parse_input(inputs)
# vagrant-libvirt expects box.img to be the qcow2 image
# https://github.com/vagrant-libvirt/vagrant-libvirt/tree/master/example_box
target = os.path.join(tree, "box.img")
provider = options["provider"]
if provider == "virtualbox":
# the box has to be named .vmdk because Virtualbox does not detect the mimetype..
target = os.path.join(tree, "box.vmdk")
subprocess.run(["cp", "-a", "--reflink=auto", source, target], check=True)
vagrant_content = ""
metadata = {"provider": options["provider"]}
if provider == "libvirt":
metadata["format"] = "qcow2"
# virtual image size as rounded numbwr
data = json.loads(subprocess.check_output(["qemu-img", "info", "--output", "json", target]))
metadata["virtual_size"] = data['virtual-size'] // 1000 ** 3
data = json.loads(
subprocess.check_output(
["qemu-img", "info", "--output", "json", target]
)
)
metadata["virtual_size"] = data["virtual-size"] // 1000**3
vagrant_content = VAGRANTFILE_LIBVIRT
if provider == "virtualbox":
vagrant_content = VAGRANTFILE_VIRTUALBOX.format(mac_address=options["virtualbox"]["mac_address"])
with open(f"{tree}/Vagrantfile", "w", encoding="utf8") as fp:
fp.write(VAGRANTFILE.format(content=vagrant_content))
vagrantfile = """
Vagrant.configure("2") do |config|
config.vm.provider :libvirt do |libvirt|
libvirt.driver = "kvm"
end
end
"""
open(f"{tree}/Vagrantfile", "w", encoding="utf8").write(vagrantfile)
with open(f"{tree}/metadata.json", "w", encoding="utf8") as fp:
json.dump(metadata, fp)
if __name__ == '__main__':
if __name__ == "__main__":
args = osbuild.api.arguments()
r = main(args["tree"], args["options"], args["inputs"])
sys.exit(r)

View file

@ -11,19 +11,53 @@
],
"schema_2": {
"options": {
"additionalProperties": false,
"required": [
"provider"
],
"properties": {
"provider": {
"type": "string",
"description": "type of Vagrant box",
"enum": [
"libvirt"
]
"anyOf": [
{
"type": "object",
"additionalProperties": false,
"required": [
"provider"
],
"properties": {
"provider": {
"type": "string",
"enum": [
"libvirt"
]
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": [
"provider",
"virtualbox"
],
"properties": {
"provider": {
"type": "string",
"enum": [
"virtualbox"
]
},
"virtualbox": {
"type": "object",
"description": "VirtualBox specific settings",
"additionalProperties": false,
"required": [
"mac_address"
],
"properties": {
"mac_address": {
"type": "string",
"pattern": "^[a-fA-F0-9]{12}$"
}
}
}
}
}
}
]
},
"inputs": {
"type": "object",

View file

@ -0,0 +1,41 @@
#!/usr/bin/python3
import pytest
from osbuild import testutil
STAGE_NAME = "org.osbuild.vagrant"
# Prepare dataset containing good and bad API call parameters
@pytest.mark.parametrize("test_data, expected_err", [
# Bad API parameters
({}, "not valid under any of the given schemas"),
({"provider": "none"}, "not valid under any of the given schemas"),
({"provider": "virtualbox"}, "not valid under any of the given schemas"),
({"provider": "virtualbox", "virtualbox": {}}, "not valid under any of the given schemas"),
({"provider": "libvirt", "virtualbox": {"mac_address": "1"}}, "not valid under any of the given schemas"),
# Good API parameters
({"provider": "libvirt"}, ""),
({"provider": "virtualbox", "virtualbox": {"mac_address": "000000000000"}}, ""),
])
# This test validates only API calls using correct and incorrect queries
def test_schema_validation_vagrant(stage_schema, test_data, expected_err):
test_input = {
"type": STAGE_NAME,
"devices": {
"device": {
"path": "some-path",
},
},
"options": {
}
}
test_input["options"].update(test_data)
res = stage_schema.validate(test_input)
if expected_err == "":
assert res.valid is True, f"err: {[e.as_dict() for e in res.errors]}"
else:
assert res.valid is False, f"err: {[e.as_dict() for e in res.errors]}"
testutil.assert_jsonschema_error_contains(res, expected_err)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,472 @@
{
"version": "2",
"pipelines": [
{
"name": "build",
"runner": "org.osbuild.arch",
"stages": [
{
"type": "org.osbuild.pacman.conf"
},
{
"type": "org.osbuild.pacman",
"inputs": {
"packages": {
"type": "org.osbuild.files",
"origin": "org.osbuild.source",
"mpp-depsolve": {
"architecture": "x86_64",
"module-platform-id": "f34",
"solver": "alpm",
"repos": [
{
"id": "core",
"baseurl": "https://archive.archlinux.org/repos/2022/04/14/$repo/os/$arch"
},
{
"id": "community",
"baseurl": "https://archive.archlinux.org/repos/2022/04/14/$repo/os/$arch"
},
{
"id": "extra",
"baseurl": "https://archive.archlinux.org/repos/2022/04/14/$repo/os/$arch"
}
],
"packages": [
"pacman",
"btrfs-progs",
"dosfstools",
"e2fsprogs",
"qemu",
"systemd",
"tar",
"xfsprogs",
"xz",
"python",
"pyalpm",
"grub",
"mkinitcpio"
]
}
}
},
"options": {}
}
]
},
{
"name": "os",
"build": "name:build",
"stages": [
{
"type": "org.osbuild.pacman.conf",
"options": {
"repositories": [
{
"name": "core",
"include": "/etc/pacman.d/mirrorlist"
},
{
"name": "extra",
"include": "/etc/pacman.d/mirrorlist"
},
{
"name": "community",
"include": "/etc/pacman.d/mirrorlist"
}
]
}
},
{
"type": "org.osbuild.pacman.mirrorlist.conf",
"options": {
"mirrors": [
"https://europe.mirror.pkgbuild.com/$repo/os/$arch"
]
}
},
{
"type": "org.osbuild.pacman",
"inputs": {
"packages": {
"type": "org.osbuild.files",
"origin": "org.osbuild.source",
"mpp-depsolve": {
"architecture": "x86_64",
"module-platform-id": "f34",
"solver": "alpm",
"repos": [
{
"id": "core",
"baseurl": "https://archive.archlinux.org/repos/2022/01/22/$repo/os/$arch"
},
{
"id": "community",
"baseurl": "https://archive.archlinux.org/repos/2022/01/22/$repo/os/$arch"
},
{
"id": "extra",
"baseurl": "https://archive.archlinux.org/repos/2022/01/22/$repo/os/$arch"
}
],
"packages": [
"base",
"bash",
"pacman",
"btrfs-progs",
"dosfstools",
"e2fsprogs",
"systemd",
"linux",
"mkinitcpio",
"tar",
"grub",
"openssh",
"sudo",
"nfs-utils",
"ntp",
"virtualbox-guest-utils-nox",
"polkit",
"networkmanager",
"rsync"
]
}
}
},
"options": {}
},
{
"type": "org.osbuild.pacman-keyring"
},
{
"type": "org.osbuild.systemd",
"options": {
"enabled_services": [
"vboxservice.service",
"sshd.service",
"NetworkManager.service"
]
}
},
{
"type": "org.osbuild.users",
"options": {
"users": {
"vagrant": {
"password": "$6$8dbFyte9oE3ugPHO$q0cTMv1oAgK/ZVXUTSUqj1aXzrhniNqfylqbcW.LgElYRJNRGSr4hBE7hghu2oP1nKn68u13/YmDkKH.s6yil0",
"home": "/home/vagrant",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key"
}
}
}
},
{
"type": "org.osbuild.fstab",
"options": {
"filesystems": [
{
"uuid": "0194fdc2-fa2f-4cc0-81d3-ff12045b73c8",
"vfs_type": "xfs",
"path": "/",
"options": "defaults"
},
{
"uuid": "7B77-95E7",
"vfs_type": "vfat",
"path": "/boot/efi",
"options": "defaults,uid=0,gid=0,umask=077,shortname=winnt",
"passno": 2
}
]
}
},
{
"type": "org.osbuild.grub2.legacy",
"options": {
"rootfs": {
"uuid": "0194fdc2-fa2f-4cc0-81d3-ff12045b73c8"
},
"bios": {
"platform": "i386-pc"
},
"entries": [
{
"id": "76a22bf4-f153-4541-b6c7-0332c0dfaeac",
"default": true,
"product": {
"name": "Arch Linux",
"version": "latest",
"nick": "Arch"
},
"kernel": "linux"
}
],
"config": {
"cmdline": "ro crashkernel=auto console=tty1 console=ttyS0 earlyprintk=ttyS0 rootdelay=300 scsi_mod.use_blk_mq=y enforcing=0",
"distributor": "$(sed 's, release .*$,,g' /etc/system-release)",
"serial": "serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1",
"terminal_input": [
"serial",
"console"
],
"terminal_output": [
"serial",
"console"
]
}
}
},
{
"type": "org.osbuild.mkinitcpio"
},
{
"type": "org.osbuild.copy",
"inputs": {
"inlinefile": {
"type": "org.osbuild.files",
"origin": "org.osbuild.source",
"mpp-embed": {
"id": "vagrant-sudoers",
"text": "Defaults:vagrant !requiretty\nvagrant ALL=(ALL) NOPASSWD: ALL"
}
}
},
"options": {
"paths": [
{
"from": {
"mpp-format-string": "input://inlinefile/{embedded['vagrant-sudoers']}"
},
"to": "tree:////etc/sudoers.d/vagrant"
}
]
}
}
]
},
{
"name": "image",
"build": "name:build",
"stages": [
{
"type": "org.osbuild.truncate",
"options": {
"filename": "disk.img",
"size": "10737418240"
}
},
{
"type": "org.osbuild.sfdisk",
"options": {
"label": "gpt",
"uuid": "D209C89E-EA5E-4FBD-B161-B461CCE297E0",
"partitions": [
{
"bootable": true,
"size": 2048,
"start": 2048,
"type": "21686148-6449-6E6F-744E-656564454649",
"uuid": "FAC7F1FB-3E8D-4137-A512-961DE09A5549"
},
{
"size": 204800,
"start": 4096,
"type": "C12A7328-F81F-11D2-BA4B-00A0C93EC93B",
"uuid": "68B2905B-DF3E-4FB3-80FA-49D1E773AA33"
},
{
"size": 20762524,
"start": 208896,
"type": "0FC63DAF-8483-4772-8E79-3D69D8477DE4",
"uuid": "6264D520-3FB9-423F-8AB8-7A0A8E3D3562"
}
]
},
"devices": {
"device": {
"type": "org.osbuild.loopback",
"options": {
"filename": "disk.img"
}
}
}
},
{
"type": "org.osbuild.mkfs.fat",
"options": {
"volid": "7B7795E7"
},
"devices": {
"device": {
"type": "org.osbuild.loopback",
"options": {
"filename": "disk.img",
"start": 4096,
"size": 204800
}
}
}
},
{
"type": "org.osbuild.mkfs.xfs",
"options": {
"uuid": "0194fdc2-fa2f-4cc0-81d3-ff12045b73c8",
"label": "root"
},
"devices": {
"device": {
"type": "org.osbuild.loopback",
"options": {
"filename": "disk.img",
"start": 208896,
"size": 20762524
}
}
}
},
{
"type": "org.osbuild.copy",
"inputs": {
"root-tree": {
"type": "org.osbuild.tree",
"origin": "org.osbuild.pipeline",
"references": [
"name:os"
]
}
},
"options": {
"paths": [
{
"from": "input://root-tree/",
"to": "mount://root/"
}
]
},
"devices": {
"efi": {
"type": "org.osbuild.loopback",
"options": {
"filename": "disk.img",
"start": 4096,
"size": 204800
}
},
"root": {
"type": "org.osbuild.loopback",
"options": {
"filename": "disk.img",
"start": 208896,
"size": 20762524
}
}
},
"mounts": [
{
"name": "root",
"type": "org.osbuild.xfs",
"source": "root",
"target": "/"
},
{
"name": "efi",
"type": "org.osbuild.fat",
"source": "efi",
"target": "/boot/efi"
}
]
},
{
"type": "org.osbuild.grub2.inst",
"options": {
"filename": "disk.img",
"platform": "i386-pc",
"location": 2048,
"core": {
"type": "mkimage",
"partlabel": "gpt",
"filesystem": "xfs",
"binary": "grub-mkimage"
},
"prefix": {
"type": "partition",
"partlabel": "gpt",
"number": 2,
"path": "/boot/grub2"
}
}
}
]
},
{
"name": "vmdk",
"build": "name:build",
"stages": [
{
"type": "org.osbuild.qemu",
"inputs": {
"image": {
"type": "org.osbuild.files",
"origin": "org.osbuild.pipeline",
"references": {
"name:image": {
"file": "disk.img"
}
}
}
},
"options": {
"filename": "disk.vmdk",
"format": {
"type": "vmdk"
}
}
}
]
},
{
"name": "vagrant",
"build": "name:build",
"stages": [
{
"type": "org.osbuild.vagrant",
"inputs": {
"image": {
"type": "org.osbuild.files",
"origin": "org.osbuild.pipeline",
"references": {
"name:vmdk": {
"file": "disk.vmdk"
}
}
}
},
"options": {
"provider": "virtualbox"
}
}
]
},
{
"name": "vagrant-virtualbox",
"build": "name:build",
"stages": [
{
"type": "org.osbuild.tar",
"options": {
"filename": "vagrant-virtualbox.box"
},
"inputs": {
"tree": {
"type": "org.osbuild.tree",
"origin": "org.osbuild.pipeline",
"references": [
"name:vagrant"
]
}
}
}
]
}
]
}