Skip to content

Commit

Permalink
Added support for AzureLinux (CBL-Mariner) 2.0 (#234)
Browse files Browse the repository at this point in the history
This implements support for amd64 edition of AzureLinux (CBL-Mariner)
2.0 on both UEFI and BIOS based deployments.
  • Loading branch information
alanbach authored May 24, 2024
1 parent 49e3ea4 commit 330f04f
Show file tree
Hide file tree
Showing 10 changed files with 500 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Read more about how [custom images](https://maas.io/docs/how-to-customise-images
|-----------------|:------------------:|:-----------------|
| AlmaLinux 8 | Beta | >= 3.5 |
| AlmaLinux 9 | Beta | >= 3.5 |
| AzureLinux 2.0 | Beta | >= 3.3 |
| CentOS 6 | EOL | >= 1.6 |
| CentOS 7 | Stable | >= 2.3 |
| CentOS 8 | EOL | >= 2.7 |
Expand Down
43 changes: 43 additions & 0 deletions azurelinux/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/make -f

include ../scripts/check.mk

PACKER ?= packer
PACKER_LOG ?= 0
ISO ?= ${ISO_PATH}
export PACKER_LOG

TIMEOUT ?= 1h

ifeq ($(wildcard /usr/share/OVMF/OVMF_CODE.fd),)
OVMF_SFX ?= _4M
else
OVMF_SFX ?=
endif

.PHONY: all clean

all: azurelinux.tar.gz

$(eval $(call check_packages_deps,cloud-image-utils ovmf parted,cloud-image-utils ovmf parted))

lint:
packer validate .
packer fmt -check -diff .

format:
packer fmt .

OVMF_VARS.fd: /usr/share/OVMF/OVMF_VARS*.fd
cp -v $< $@

azurelinux.tar.gz: check-deps clean OVMF_VARS.fd
${PACKER} init . && ${PACKER} build \
-var ovmf_suffix=${OVMF_SFX} \
-var iso_url=${ISO} \
-var timeout=${TIMEOUT} .
clean:
${RM} -rf output* azurelinux.tar.gz \
OVMF_VARS.fd

.INTERMEDIATE: OVMF_VARS.fd
64 changes: 64 additions & 0 deletions azurelinux/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Azure Linux (CBL-Mariner) Packer template for MAAS

## Introduction

The Packer template in this directory creates a [Azure Linux 2.0](https://github.com/microsoft/azurelinux) AMD64 image for use with MAAS.
This distribution was formerly known as `CBL-Mariner`.

## Prerequisites to create the image

* A machine running Ubuntu 22.04+ with the ability to run KVM virtual machines.
* qemu-utils, libnbd-bin, nbdkit and fuse2fs
* [Packer.](https://www.packer.io/intro/getting-started/install.html), v1.7.0 or newer

## Requirements to deploy the image

* [MAAS](https://maas.io) 3.3 or later
* [Curtin](https://launchpad.net/curtin) 22.1 or later

## Customizing the image

See curtin/install-custom-packages file.

## Building the image

The Packer template needs the [CBL-Mariner 2.0 ISO image](https://aka.ms/mariner-2.0-x86_64-iso).

You can build the image using the Makefile:

```shell
make azurelinux.tar.gz ISO=/path/to/Mariner-2.0-x86_64.iso
```

The installation runs in a non-interactive mode.

Note: mariner-packer.pkr.hcl runs Packer in headless mode, with the serial port output from qemu redirected to stdio to give feedback on image creation process. If you wish to see more, change the value of `headless` to `false` in mariner-packer.pkr.hcl, and remove `[ "-serial", "stdio" ]` from `qemuargs` section. This lets you watch progress of the image build script. Press `ctrl-b 2` to switch to shell to explore more, and `ctrl-b 1` to go back to log view.

### Makefile Parameters

#### PACKER_LOG

Setting this to `PACKER_LOG=1` will show verbose output during the build process.

#### ISO

The path to the ISO image.

## Uploading an image to MAAS

```shell
maas $PROFILE boot-resources create name='custom/cbl-mariner-2.0' \
title='CBL Mariner 2.0 Custom' architecture='amd64/generic' \
base_image='rhel/8' filetype='tgz' \
content@=azurelinux.tar.gz
```

## Default username

MAAS uses cloud-init to create ```mariner``` account using the ssh keys configured for the MAAS admin user (e.g. imported from Launchpad). Log in to the machine:

```shell
ssh -i ~/.ssh/<your_identity_file> mariner@<machine-ip-address>
```

The autoinstall script sets the `mariner` account password to `mariner`.
96 changes: 96 additions & 0 deletions azurelinux/curtin/curtin-hooks
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#!/usr/bin/env python3
# curtin-hooks - Curtin installation hooks for CBL Mariner (Azure Linux)
#
# Copyright (C) 2024 Canonical
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.


import os
import shutil
import platform

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

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)

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')

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

# set cloud-init maas datasource
if cfg.get('cloudconfig'):
curthooks.handle_cloudconfig(
cfg['cloudconfig'],
base_dir=target_path(target,
'etc/cloud/cloud.cfg.d'))

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']

curthook(config, target, state)
cleanup()


if __name__ == "__main__":
main()
3 changes: 3 additions & 0 deletions azurelinux/curtin/install-custom-packages
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash
yum install --assumeyes --quiet bash-completion vim
exit 0
33 changes: 33 additions & 0 deletions azurelinux/curtin/setup-bootloader
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/bin/bash

# Update initrd
for k in $(ls -l /boot/vmlinuz-* | awk -F 'vmlinuz-' '{print $2}'); do dracut -f /boot/initrd.img-${k} ${k}; done

# Restore the Correct os-release file
mv /etc/os-release.orig /etc/os-release

# Generate the GRUB config file
grub2-mkconfig -o /boot/grub2/grub.cfg

# Install GRUB and update the configuration
if [ -d /sys/firmware/efi/efivars/ ]; then
# Sorry, we have to go through this process due to:
# grub2-install: error: this utility cannot be used for EFI platforms because it does not support UEFI Secure Boot.
mkdir -p /boot/efi/EFI/BOOT
mkdir -p /boot/efi/boot/grub2

yum install --assumeyes --quiet shim grub2-efi-binary

cat /boot/grub2/grub.cfg | grep search | sort -u | sed 's/^[ \t]*//' > /boot/efi/boot/grub2/grub.cfg
echo 'set prefix=($root)"/grub2"' >> /boot/efi/boot/grub2/grub.cfg
echo 'configfile $prefix/grub.cfg' >> /boot/efi/boot/grub2/grub.cfg
else
yum install --assumeyes --quiet grub2-pc

grub_dev="/dev/$(lsblk -r | grep 'part /$' | awk '{print $1}' | sed s/[0-9]//g)"

grub2-install \
--target=i386-pc \
--recheck ${grub_dev}
fi
exit 0
110 changes: 110 additions & 0 deletions azurelinux/http/mariner_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
{
"Disks": [
{
"PartitionTableType": "gpt",
"MaxSize": 0,
"TargetDisk": {
"Type": "path",
"Value": "/dev/sda"
},
"Artifacts": null,
"Partitions": [
{
"FsType": "fat32",
"ID": "boot",
"Name": "",
"End": 200,
"Start": 1,
"Flags": [
"esp",
"boot"
],
"Artifacts": null
},
{
"FsType": "ext4",
"ID": "rootfs",
"Name": "",
"End": 0,
"Start": 200,
"Flags": null,
"Artifacts": null
}
],
"RawBinaries": null
}
],
"SystemConfigs": [
{
"IsDefault": true,
"BootType": "efi",
"Hostname": "azurelinux",
"Name": "CBL-Mariner",
"PackageLists": [ "packages.json" ],
"KernelOptions": {
"default": "kernel"
},
"KernelCommandLine": {
"ImaPolicy": null,
"ExtraCommandLine": ""
},
"AdditionalFiles": {},
"PartitionSettings": [
{
"RemoveDocs": false,
"ID": "boot",
"MountOptions": "umask=0077",
"MountPoint": "/boot/efi",
"OverlayBaseImage": "",
"RdiffBaseImage": ""
},
{
"RemoveDocs": false,
"ID": "rootfs",
"MountOptions": "",
"MountPoint": "/",
"OverlayBaseImage": "",
"RdiffBaseImage": ""
}
],
"PostInstallScripts": [
{
"Args": "",
"Path": "@POSTINSTALLSCRIPT@"
}
],
"Groups": null,
"Users": [
{
"Name": "@USERNAME@",
"UID": "",
"PasswordHashed": false,
"Password": "@PASSWORD@",
"PasswordExpiresDays": 0,
"SSHPubKeyPaths": null,
"PrimaryGroup": "sudo",
"SecondaryGroups": null,
"StartupCommand": ""
}
],
"Encryption": {
"Enable": false,
"Password": ""
},
"RemoveRpmDb": false,
"ReadOnlyVerityRoot": {
"Enable": false,
"Name": "",
"ErrorCorrectionEnable": false,
"ErrorCorrectionEncodingRoots": 0,
"RootHashSignatureEnable": false,
"ValidateOnBoot": false,
"VerityErrorBehavior": "",
"TmpfsOverlays": null,
"TmpfsOverlaySize": "",
"TmpfsOverlayDebugEnabled": false
},
"HidepidDisabled": false
}
]
}
12 changes: 12 additions & 0 deletions azurelinux/http/packages.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"packages": [
"openssh-server",
"grub2",
"ca-certificates",
"cronie-anacron",
"logrotate",
"core-packages-base-image",
"initramfs"
]
}

20 changes: 20 additions & 0 deletions azurelinux/http/scripts/postinstall.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/bin/bash

# Make curtin happy by faking RHEL8 this needs to be fixed in curtin
cp /etc/os-release /etc/os-release.orig
sed -i 's/^ID=.*/ID=redhat/g;s/^VERSION_ID=.*/VERSION_ID="8.0"/g;s/^VERSION=.*/VERSION="8.0"/g;s/^NAME=.*/NAME="Red Hat Enterprise Linux"/g' /etc/os-release
mkdir -p /etc/rpm/
echo "%rhel 8" | tee -a /etc/rpm/macros.dist

# Add Cloud-Init
echo "nameserver 8.8.8.8" | tee -a /etc/resolv.conf
yum update
yum install -y cloud-init awk python3-pip xfsprogs

# Missing cloud-init dependencies
pip3 install pyserial
systemctl enable cloud-init

# Clean-up
rm /etc/resolv.conf
exit 0
Loading

0 comments on commit 330f04f

Please sign in to comment.