Skip to content
Open
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
35 changes: 13 additions & 22 deletions .github/instructions/kiwi.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,29 @@ Kiwi files define Azure Linux image builds. They use the [KIWI NG](https://osins

## How images are registered

Images are defined in `base/images/images.toml`. Each image is
declared as a canonical (unsuffixed) entry plus a `-dev` variant,
each selecting the matching kiwi `profile`:
Images are defined in `base/images/images.toml`. Each image selects a leaf
profile from the shared `base/images/AzureLinux.kiwi` description:

```toml
[images.container-base]
description = "Container Base Image"
definition = { type = "kiwi", path = "container-base/container-base.kiwi", profile = "core" }

[images.container-base-dev]
description = "Container Base Image (dev)"
definition = { type = "kiwi", path = "container-base/container-base.kiwi", profile = "core-dev" }
definition = { type = "kiwi", path = "AzureLinux.kiwi", profile = "core" }
```

The two variants share the same kiwi description; they differ only
in which `azurelinux-repos*` package is shipped (controlling where
the resulting OS points at runtime), and — for the `core` container
specifically — the OCI tag (`:4.0` + `:latest` for canonical,
`:4.0-dev` for the dev variant). Both variants build their RPMs
from the same source (the kiwi `<repository>`); koji overrides this
during distro builds.

Distroless container images strip the package manager entirely, so
they ship no `-repos` package and have only a single (canonical)
entry — there's no `-dev` sibling because it would be byte-identical.
`AzureLinux.kiwi` includes reusable fragments from `repositories/`,
`components/`, and `teams/`. Includes remain flat in the root description;
profile requirements express inheritance between fragments.

Each image has its own directory under `base/images/` containing the
`.kiwi` file.
KIWI hook scripts, bootloader templates, and `<file>` sources live directly
under `base/images/`, because that directory is the shared description root.
The root `config.sh` dispatches profile-specific behavior using
`kiwi_profiles`, following Fedora's shared-description model. `<file>` entries
remain scoped to the owning profile so their payloads do not leak into other
images.

## Image types

- **Container** (`image="docker"`): OCI container images with `<containerconfig>` for name, tag, entrypoint
- **Container** (`image="oci"`): OCI container images with `<containerconfig>` for name, tag, entrypoint
- **VM** (`image="oem"`): Virtual machine images with disk format (`vhdx`, `qcow2`), filesystem, bootloader, and partition config

## Key elements
Expand Down
19 changes: 19 additions & 0 deletions base/images/AzureLinux.kiwi
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml-model href="https://raw.githubusercontent.com/OSInside/kiwi/refs/tags/v10.2.33/kiwi/schema/kiwi.rng" type="application/xml"?>
<image schemaversion="8.3" name="azurelinux-4">
Comment thread
binujp marked this conversation as resolved.
Comment thread
binujp marked this conversation as resolved.
<description type="system">
<author>Azure Linux</author>
<contact>azurelinux@microsoft.com</contact>
<specification>Azure Linux 4 image hierarchy</specification>
</description>
<include from="this://./repositories/core.xml" />
<include from="this://./components/common.xml" />
<include from="this://./components/boot.xml" />
<include from="this://./teams/cloud/vm-base.xml" />
<include from="this://./teams/cloud/onep.xml" />
<include from="this://./teams/cloud/marketplace.xml" />
<include from="this://./teams/installer.xml" />
<include from="this://./teams/wsl.xml" />
<include from="this://./teams/container.xml" />
<include from="this://./teams/minimal-os.xml" />
</image>
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ network --hostname=azurelinux
services --enabled=sshd,systemd-networkd,systemd-resolved

# Bootloader
bootloader --location=mbr --append="console=ttyS0,115200 console=tty0"
bootloader --location=mbr --append="console=@@SERIAL_TTY@@,115200 console=tty0"

# Eject installation media and reboot automatically after install
reboot --eject
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ network --hostname=azurelinux
services --enabled=sshd,systemd-networkd,systemd-resolved

# Bootloader
bootloader --location=mbr --append="console=ttyS0,115200 console=tty0"
bootloader --location=mbr --append="console=@@SERIAL_TTY@@,115200 console=tty0"

# Eject installation media and reboot automatically after install
reboot --eject
Expand Down
23 changes: 23 additions & 0 deletions base/images/components/boot.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<image>
<profiles>
<profile name="Fips" description="FIPS boot support" import="false" />
<profile name="UefiBoot" description="Architecture-specific UEFI boot packages" import="false" />
<profile name="LegacyBoot" description="Legacy BIOS boot packages" import="false" />
</profiles>
<packages type="image" profiles="Fips">
<package name="dracut-fips" />
</packages>
<packages type="image" profiles="UefiBoot">
<package name="grub2-efi-aa64" arch="aarch64" />
<package name="grub2-efi-aa64-modules" arch="aarch64" />
<package name="grub2-efi-x64" arch="x86_64" />
<package name="grub2-efi-x64-modules" arch="x86_64" />
<package name="shim" arch="aarch64" />
<package name="shim" arch="x86_64" />
</packages>
<packages type="image" profiles="LegacyBoot">
<package name="grub2-pc" />
<package name="grub2-pc-modules" />
</packages>
</image>
65 changes: 65 additions & 0 deletions base/images/components/common.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<image>
<profiles>
<profile name="BootableCore" description="Packages shared by every bootable image" import="false" />
<profile name="CloudCore" description="Packages shared by Azure guest images" import="false">
<requires profile="BootableCore" />
</profile>
<profile name="RuntimeRepos" description="Canonical runtime repository configuration" import="false" />
<profile name="BootstrapRepos" description="Canonical bootstrap repository configuration" import="false" />
</profiles>
<packages type="bootstrap" profiles="BootableCore">
<package name="filesystem" />
</packages>
<packages type="image" profiles="BootableCore">
<package name="bash" />
<package name="ca-certificates" />
<package name="dnf5" />
<package name="grub2" />
<package name="iproute" />
<package name="kernel" />
<package name="shadow-utils" />
<package name="systemd" />
<package name="systemd-networkd" />
<package name="systemd-resolved" />
<package name="util-linux" />
</packages>
<packages type="image" profiles="CloudCore">
<package name="WALinuxAgent" />
<package name="azurelinux-release-cloud" />
<package name="bzip2" />
<package name="chrony" />
<package name="cloud-init" />
<package name="cloud-utils-growpart" />
<package name="cracklib-dicts" />
<package name="cryptsetup" />
<package name="file" />
<package name="glibc" />
<package name="glibc-langpack-en" />
<package name="grubby" />
<package name="gzip" />
<package name="hyperv-daemons" />
<package name="iputils" />
<package name="irqbalance" />
<package name="kernel-modules" />
<package name="lvm2" />
<package name="lz4" />
<package name="net-tools" />
<package name="nftables" />
<package name="openssh-clients" />
<package name="openssh-server" />
<package name="selinux-policy-targeted" />
<package name="setup" />
<package name="sudo" />
<package name="tar" />
<package name="wget" />
<package name="which" />
<package name="zlib" />
</packages>
<packages type="image" profiles="RuntimeRepos">
<package name="azurelinux-repos" />
</packages>
<packages type="bootstrap" profiles="BootstrapRepos">
<package name="azurelinux-repos" />
</packages>
</image>
File renamed without changes.
38 changes: 14 additions & 24 deletions base/images/vm-iso-installer/config.sh → base/images/config-vm-iso-installer.sh
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ case "$ARCH" in
SHIM_EFI="shimx64.efi"
GRUB_EFI="grubx64.efi"
BOOT_EFI="BOOTX64.EFI"
SERIAL_TTY="ttyS0"
;;
aarch64)
GRUB_EFI_PKG="grub2-efi-aa64"
Expand All @@ -22,6 +23,7 @@ case "$ARCH" in
SHIM_EFI="shimaa64.efi"
GRUB_EFI="grubaa64.efi"
BOOT_EFI="BOOTAA64.EFI"
SERIAL_TTY="ttyAMA0"
;;
*)
echo "ERROR: Unsupported architecture: $ARCH" >&2
Expand All @@ -31,26 +33,12 @@ esac
echo "=== Architecture: $ARCH ==="
echo " GRUB EFI package: $GRUB_EFI_PKG"
echo " Shim EFI binary: $SHIM_EFI"
echo " Serial console: $SERIAL_TTY"

#----------------------------------------------------------------------
# Variant detection
# Repository package selection
#----------------------------------------------------------------------
# kiwi sets `kiwi_profiles` to a comma-separated list of active profiles
# (one per build, set by --profile). The variant decides which
# `azurelinux-repos*` package goes into the image and the kickstart,
# which in turn controls the runtime repo of the installed system.
#
# OFFLINE_REPO_BLOCKLIST lists packages that must NOT appear in the
# offline repo. The opposite-variant repos package goes here:
# `azurelinux-repos` and `-dev` Conflict: with each other, and
# `azurelinux-release-common` has `Recommends: azurelinux-repos`, which
# would otherwise drag the canonical package into the dev offline repo
# via --resolve --alldeps and risk dnf picking the wrong one.
case ",${kiwi_profiles:-}," in
*,vm-iso-installer-dev,*)
AZL_REPOS_PKG="azurelinux-repos-dev"
OFFLINE_REPO_BLOCKLIST=( "azurelinux-repos" )
;;
*,vm-iso-installer,*)
AZL_REPOS_PKG="azurelinux-repos"
OFFLINE_REPO_BLOCKLIST=( "azurelinux-repos-dev" )
Expand Down Expand Up @@ -130,9 +118,9 @@ echo "=== Downloading target-install packages + dependencies ==="
# Kiwi removes repo configs after package installation, so repos from the .kiwi
# file are NOT available during config.sh. Use --repofrompath with the CDN URL
# directly. Keep this URL in sync with the `azurelinux-base` repo in
# vm-iso-installer.kiwi.
# repositories/core.xml.

AZL_BASE_URL="https://stcontroltowerdevjwisitg.blob.core.windows.net/azl4-dev/base/$ARCH"
AZL_BASE_URL="https://packages.microsoft.com/azurelinux/4.0/beta/base/$ARCH"

EXCLUDE_ARGS=()
for pkg in "${OFFLINE_REPO_BLOCKLIST[@]}"; do
Expand Down Expand Up @@ -222,7 +210,7 @@ cat > /root/.bash_profile << 'PROFILEEOF'
#
# Console selection logic:
# - Hyper-V (systemd-detect-virt = "microsoft") → user is on VGA (tty1)
# - QEMU/KVM/bare-metal with serial in cmdline → user is on serial (ttyS0)
# - QEMU/KVM/bare-metal with serial in cmdline → user is on the architecture's serial TTY
# - QEMU/KVM/bare-metal without serial → user is on VGA (tty1)
# This prevents the invisible tty1 from stealing the installer on QEMU -nographic.
if grep -q 'azl\.autoinstall' /proc/cmdline 2>/dev/null; then
Expand All @@ -235,13 +223,13 @@ if grep -q 'azl\.autoinstall' /proc/cmdline 2>/dev/null; then
else
# QEMU/KVM/bare-metal
case "$MY_TTY" in
/dev/ttyS0)
/dev/@@SERIAL_TTY@@)
LAUNCH=true
;;
/dev/tty1|/dev/hvc0)
# Only autoinstall on VGA if serial is NOT in kernel cmdline
# (otherwise ttyS0 will handle it)
if ! grep -q 'console=ttyS' /proc/cmdline 2>/dev/null; then
# (otherwise the serial TTY will handle it)
if ! grep -q 'console=@@SERIAL_TTY@@' /proc/cmdline 2>/dev/null; then
LAUNCH=true
fi
;;
Expand Down Expand Up @@ -270,6 +258,7 @@ echo ""
echo "========================================"
echo ""
PROFILEEOF
sed -i "s/@@SERIAL_TTY@@/$SERIAL_TTY/g" /root/.bash_profile

cat > /root/.bashrc << 'RCEOF'
if [[ $- == *i* ]] && [ ! -f /tmp/.azl-banner-shown ]; then
Expand All @@ -282,8 +271,8 @@ RCEOF
# Autologin on serial and VGA consoles
#----------------------------------------------------------------------

mkdir -p /etc/systemd/system/serial-getty@ttyS0.service.d
cat > /etc/systemd/system/serial-getty@ttyS0.service.d/autologin.conf << 'AUTOEOF'
mkdir -p "/etc/systemd/system/serial-getty@${SERIAL_TTY}.service.d"
cat > "/etc/systemd/system/serial-getty@${SERIAL_TTY}.service.d/autologin.conf" << 'AUTOEOF'
[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin root --noclear %I 115200 linux
Expand Down Expand Up @@ -314,6 +303,7 @@ generate_packages_section() {
# Expand @@PACKAGES@@ placeholder in each template
for ks_in in /root/azl-install.ks.in /root/azl-install-encrypted.ks.in; do
ks_out="${ks_in%.in}"
sed -i "s/@@SERIAL_TTY@@/$SERIAL_TTY/g" "$ks_in"
{
sed '/@@PACKAGES@@/,$d' "$ks_in"
generate_packages_section
Expand Down
12 changes: 12 additions & 0 deletions base/images/config.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash

set -euo pipefail

case ",${kiwi_profiles:-}," in
*,vm-iso-installer,*)
exec /image/config-vm-iso-installer.sh
;;
*,distroless-minimal,*|*,distroless-base,*|*,distroless-debug,*)
exec /image/config-container-base.sh
;;
esac
Loading
Loading