Skip to content

Add support for Canonical MaaS Provider #1695

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions OWNERS_ALIASES
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,7 @@ aliases:
- tuxtof
cluster-api-nutanix-reviewers:
- tuxtof
cluster-api-maas-maintainers:
- vasartori
cluster-api-maas-reviewers:
- vasartori
1 change: 1 addition & 0 deletions docs/book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
- [vSphere](./capi/providers/vsphere.md)
- [Proxmox](./capi/providers/proxmox.md)
- [Windows](./capi/windows/windows.md)
- [MaaS](./capi/providers/maas.md)
- [Including ECR Credential Provider](./capi/ecr-credential-provider.md)
- [Testing the Images](./capi/goss/goss.md)
- [Using Container Images](./capi/container-image.md)
Expand Down
1 change: 1 addition & 0 deletions docs/book/src/capi/capi.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ If any needed binaries are not present, they can be installed to `images/capi/.b
* [VirtualBox](./providers/virtualbox.md)
* [vSphere](./providers/vsphere.md)
* [Proxmox](./providers/proxmox.md)
* [MaaS](./providers/maas.md)

## Make targets

Expand Down
76 changes: 76 additions & 0 deletions docs/book/src/capi/providers/maas.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Building Images for MaaS

The image is built using the KVM hypervisor (QEMU).

## Building Images

The build [prerequisites](../capi.md#prerequisites) for using `image-builder` to create QEMU images are installed with:

```bash
cd image-builder
make deps-qemu
```

## Building a MaaS Image

From the `image-builder` directory, run:

```bash
make build-maas-ubuntu-xxxx-efi
```

The image will be located in `images/capi/output/BUILD_NAME+kube-KUBERNETES_VERSION`. Replace `xxxx` with `2204` or `2404`, depending on the Ubuntu version.

To build a Ubuntu 22.04-based CAPI image:

```bash
git clone https://github.com/kubernetes-sigs/image-builder.git
cd image-builder
make build-qemu-ubuntu-2204-efi
```

## Uploading to MaaS

### Prerequisites

- Ubuntu 22.04 (required for the MaaS client)
- Command-line MaaS client installed

### Installing the MaaS Client

```bash
apt update && apt install -y tzdata software-properties-common
apt-add-repository -y ppa:maas/3.5
apt install -y maas-cli python3-openssl
```

### Logging into MaaS

```bash
maas login admin <MAAS_HTTP_ENDPOINT>/MAAS/ '<TOKEN>'
```

#### Creating a Token

Log into the MaaS interface, go to your preferences (your username), click "API Keys," and copy an existing key or generate a new one.

### Uploading the Image

Navigate to `images/capi/output/`, find the generated image, and enter its directory. Inside, you will see two files:

```bash
cd images/capi/output/ubuntu-2204-efi-kube-v1.30.5/

ls -l
total 7165084
-rw-r--r-- 1 vasartori vasartori 5132255232 Feb 25 08:33 ubuntu-2204-efi-kube-v1.30.5
-rw-r--r-- 1 root root 2203701699 Feb 25 08:33 ubuntu-2204-efi-kube-v1.30.5.tar.gz
```

Use the **.tar.gz** file for the upload:

```bash
maas admin boot-resources create name=custom/your-image architecture=amd64/generic title=your-image subarches=generic base_image=ubuntu/jammy content@=./ubuntu-2204-efi-kube-v1.30.5.tar.gz
```

**Note:** Set `base_image=ubuntu/jammy` for Ubuntu 22.04 or `ubuntu/noble` for 24.04.
12 changes: 11 additions & 1 deletion docs/book/src/glossary.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Table of Contents

[A](#a) | [C](#c) | [E](#e) | [G](#g) | [K](#k) | [O](#o) | [V](#v)
[A](#a) | [C](#c) | [E](#e) | [G](#g) | [K](#k) | [M](#m) | [O](#o) | [V](#v)

# A
---
Expand Down Expand Up @@ -76,6 +76,16 @@ Kubernetes (K8s) is an open-source system for automating deployment, scaling, an
[source](https://github.com/kubernetes/kubernetes)
[docs](https://kubernetes.io)

# M
---

## MaaS

Metal As A Service (a.k.a MaaS) is an open-source tool developed by Canonical that automates the provisioning and management of bare-metal servers.

[docs](https://maas.io/docs)


# O
---

Expand Down
16 changes: 16 additions & 0 deletions images/capi/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,8 @@ PROXMOX_BUILD_NAMES ?= proxmox-ubuntu-2204 proxmox-ubuntu-2404 proxmox-ubuntu-

VULTR_BUILD_NAMES ?= vultr-ubuntu-2204

MAAS_BUILD_NAMES ?= maas-ubuntu-2204-efi maas-ubuntu-2404-efi

## --------------------------------------
## Dynamic build targets
## --------------------------------------
Expand Down Expand Up @@ -448,6 +450,8 @@ PROXMOX_BUILD_TARGETS := $(addprefix build-,$(PROXMOX_BUILD_NAMES))
PROXMOX_VALIDATE_TARGETS := $(addprefix validate-,$(PROXMOX_BUILD_NAMES))
VULTR_BUILD_TARGETS := $(addprefix build-,$(VULTR_BUILD_NAMES))
VULTR_VALIDATE_TARGETS := $(addprefix validate-,$(VULTR_BUILD_NAMES))
MAAS_BUILD_TARGETS := $(addprefix build-,$(MAAS_BUILD_NAMES))
MAAS_VALIDATE_TARGETS := $(addprefix validate-,$(MAAS_BUILD_NAMES))

.PHONY: $(NODE_OVA_LOCAL_BUILD_TARGETS)
$(NODE_OVA_LOCAL_BUILD_TARGETS): deps-ova set-ssh-password
Expand Down Expand Up @@ -640,6 +644,15 @@ $(VULTR_BUILD_TARGETS): deps-vultr
$(VULTR_VALIDATE_TARGETS): deps-vultr
$(PACKER) validate $(PACKER_NODE_FLAGS) -var-file="$(abspath packer/vultr/$(subst validate-vultr-,,$@).json)" $(ABSOLUTE_PACKER_VAR_FILES) packer/vultr/packer.json

.PHONY: $(MAAS_BUILD_TARGETS)
$(MAAS_BUILD_TARGETS): deps-qemu set-ssh-password
$(PACKER) build $(PACKER_NODE_FLAGS) -var-file="$(abspath packer/maas/$(subst build-,,$@).json)" --var="ansible_user_vars=provider=maas" $(ABSOLUTE_PACKER_VAR_FILES) packer/maas/packer.json

.PHONY: $(MAAS_VALIDATE_TARGETS)
$(MAAS_VALIDATE_TARGETS): deps-qemu set-ssh-password
$(PACKER) validate $(PACKER_NODE_FLAGS) -var-file="$(abspath packer/maas/$(subst validate-,,$@).json)" --var="ansible_user_vars=provider=maas" $(ABSOLUTE_PACKER_VAR_FILES) packer/maas/packer.json


## --------------------------------------
## Dynamic clean targets
## --------------------------------------
Expand Down Expand Up @@ -847,6 +860,9 @@ build-qemu-rockylinux-9: ## Builds Rocky 9 QEMU image
build-qemu-rockylinux-9-cloudimg: ## Builds Rocky 9 QEMU image using cloud image
build-qemu-all: $(QEMU_BUILD_TARGETS) ## Builds all Qemu images

build-maas-ubuntu-2204-efi: ## Builds Ubuntu 22.04 MaaS image that EFI boots
build-maas-ubuntu-2404-efi: ## Builds Ubuntu 24.04 MaaS image that EFI boots

build-raw-flatcar: ## Builds Flatcar RAW image
build-raw-ubuntu-2004: ## Builds Ubuntu 20.04 RAW image
build-raw-ubuntu-2004-efi: ## Builds Ubuntu 20.04 RAW image that EFI boots
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Attribution
All the script in this folder is derived from the original work by Alexsander de Souza (Canonical),
available at https://github.com/canonical/packer-maas.
105 changes: 105 additions & 0 deletions images/capi/ansible/roles/providers/files/maas/curtin/curtin-hooks
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/usr/bin/env python3
#
# This script was copied as-is from:
# Source: https://github.com/canonical/packer-maas
# Original Author: Alexsander de Souza <[email protected]>
#


import os
import platform
import shutil
import sys

from curtin import distro, util
from curtin.commands import apt_config, curthooks
from curtin.config import load_command_config
from curtin.log import DEBUG, LOG, basicConfig
from curtin.paths import target_path
from curtin.util import ChrootableTarget, load_command_environment


def run_hook_in_target(target, hook):
"""Look for "hook" in "target" and run in a chroot"""
target_hook = target_path(target, "/curtin/" + hook)
if os.path.isfile(target_hook):
LOG.debug("running %s" % target_hook)
with ChrootableTarget(target=target) as in_chroot:
in_chroot.subp(["/curtin/" + hook])
return True
return False


def curthook(cfg, target, state):
"""Configure network and bootloader"""
LOG.info("Running curtin builtin curthooks")
state_etcd = os.path.split(state["fstab"])[0]
machine = platform.machine()

distro_info = distro.get_distroinfo(target=target)
if not distro_info:
raise RuntimeError("Failed to determine target distro")
osfamily = distro_info.family
LOG.info(
"Configuring target system for distro: %s osfamily: %s",
distro_info.variant,
osfamily,
)

sources = cfg.get("sources", {})
dd_image = len(util.get_dd_images(sources)) > 0

curthooks.disable_overlayroot(cfg, target)
curthooks.disable_update_initramfs(cfg, target, machine)
curthooks.install_missing_packages(cfg, target, osfamily=osfamily)

if not dd_image:
curthooks.configure_iscsi(cfg, state_etcd, target, osfamily=osfamily)
curthooks.configure_mdadm(cfg, state_etcd, target, osfamily=osfamily)
curthooks.copy_fstab(state.get("fstab"), target)
curthooks.add_swap(cfg, target, state.get("fstab"))

run_hook_in_target(target, "install-custom-packages")

if not dd_image:
curthooks.setup_kernel_img_conf(target)

crypttab_location = os.path.join(os.path.split(state["fstab"])[0], "crypttab")
if os.path.exists(crypttab_location):
curthooks.copy_crypttab(crypttab_location, target)

udev_rules_d = os.path.join(state["scratch"], "rules.d")
if os.path.isdir(udev_rules_d):
curthooks.copy_dname_rules(udev_rules_d, target)

apt_config.apply_debconf_selections(cfg, target)

curthooks.apply_networking(target, state)
curthooks.handle_pollinate_user_agent(cfg, target)

# re-enable update_initramfs
curthooks.enable_update_initramfs(cfg, target, machine)
curthooks.update_initramfs(target, all_kernels=True)

run_hook_in_target(target, "setup-bootloader")


def cleanup():
"""Remove curtin-hooks so its as if we were never here."""
curtin_dir = os.path.dirname(__file__)
shutil.rmtree(curtin_dir)


def main():
state = load_command_environment()
config = load_command_config(None, state)
target = state["target"]

basicConfig(stream=sys.stderr, verbosity=DEBUG)

curthook(config, target, state)
cleanup()


if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash -ex
#
exit 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/bash -ex
#
# This script was copied as-is from:
# Source: https://github.com/canonical/packer-maas
# Original Author: Alexsander de Souza <[email protected]>

export DEBIAN_FRONTEND=noninteractive

# Clean up remnants from packer-maas vm install
rm /var/cache/debconf/config.dat
dpkg --configure -a

# Update the package lists before attempting to install the kernel
apt-get update
# Ensure the existence of linux-image-generic for non-cloudimg images.
#apt-get -y install linux-image-generic

dpkg-reconfigure grub-efi-amd64
update-grub

grub-install \
--target=x86_64-efi \
--efi-directory=/boot/efi \
--bootloader-id=ubuntu \
--recheck

update-initramfs -uk all

efibootmgr -v

15 changes: 15 additions & 0 deletions images/capi/ansible/roles/providers/tasks/maas-ubuntu.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
- name: Create /curtin directory
ansible.builtin.file:
path: /curtin
state: directory
mode: "0775"

- name: Copy curtin scripts to /curtin
ansible.builtin.copy:
src: "files/maas/curtin/{{ item }}"
dest: "/curtin/{{ item }}"
mode: "0750"
loop:
- curtin-hooks
- install-custom-packages
- setup-bootloader
6 changes: 6 additions & 0 deletions images/capi/ansible/roles/providers/tasks/maas.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
- name: Include QEMU tasks
ansible.builtin.include_tasks: qemu.yml

- name: Include MaaS Specific configs for Ubuntu Distro
ansible.builtin.include_tasks: maas-ubuntu.yml
when: ansible_os_family == "Debian"
3 changes: 3 additions & 0 deletions images/capi/ansible/roles/providers/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@
- ansible.builtin.include_tasks: huaweicloud.yml
when: packer_builder_type.startswith('huaweicloud')

- ansible.builtin.include_tasks: maas.yml
when: packer_builder_type is search('qemu') and provider is defined and provider is search('maas')

# Create a boot order configuration
# b/w containerd and cloud final, cloud config services

Expand Down
5 changes: 5 additions & 0 deletions images/capi/packer/maas/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
packer.json
ks.cfg
preseed.cfg
preseed-efi.cfg
user-data
7 changes: 7 additions & 0 deletions images/capi/packer/maas/OWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# See the OWNERS docs at https://go.k8s.io/owners

approvers:
- cluster-api-maas-maintainers

reviewers:
- cluster-api-maas-reviewers
18 changes: 18 additions & 0 deletions images/capi/packer/maas/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
To build an image using a specific version of Kubernetes use the "PACKER_FLAGS" env var like in the example below:

PACKER_FLAGS="--var 'kubernetes_rpm_version=1.30.5' --var 'kubernetes_semver=v1.30.5' --var 'kubernetes_series=v1.30' --var 'kubernetes_deb_version=1.30.5-1.1'" make build-maas-ubuntu-2204-efi

P.S: In order to change disk size(defaults to 20GB as of 31.10.22) you can update PACKER_FLAGS with:
--var 'disk_size=<disk size in mb>'


Upload images to MaaS

```
maas <PROFILE> boot-resources create name=custom/<IMAGE NAME> architecture=amd64/generic title=<IMAGE NAME> subarches=generic base_image=ubuntu/<SEE NOTES> content@=./<FILE>.tar.gz
```

Notes / Things you need to known:

- If you are using ubuntu **22.04**, set the `base_image` field to: `ubuntu/jammy`. For 24.04, use: `ubuntu/noble`
- Use **UEFI** to boot the machines, if you use BIOS, your MaaS deployment will **probably** fail.
Empty file.
9 changes: 9 additions & 0 deletions images/capi/packer/maas/cloud-init/user-data.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#cloud-config
ssh_pwauth: true
users:
- name: builder
passwd: $ENCRYPTED_SSH_PASSWORD
groups: [adm, cdrom, dip, plugdev, lxd, sudo]
lock-passwd: false
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
Loading