Skip to content
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
206 changes: 205 additions & 1 deletion toolkit/tools/imagecustomizer/docs/configuration.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Mariner Image Customizer configuration

The Mariner Image Customizer is configured using a YAML (or JSON) file.
The top level type for this YAML file is the [Config](#config-type) type.

### Operation ordering

Expand Down Expand Up @@ -63,19 +62,86 @@ SystemConfig:
- kernel-hci
```

## Top-level

The top level type for the YAML file is the [Config](#config-type) type.

## Config type

The top-level type of the configuration.

### Disks [[Disk](#disk-type)[]]

Contains the options for provisioning disks and their partitions.

If the Disks field isn't specified, then the partitions of the base image aren't
changed.

If Disks is specified, then [SystemConfig.BootType](#boottype-boottype) must also be
specified.

While Disks is a list, only 1 disk is supported at the moment.
Support for multiple disks may (or may not) be added in the future.

```yaml
Disks:
- PartitionTableType: gpt
MaxSize: 4096
Partitions:
- ID: esp
Flags:
- esp
- boot
Start: 1
End: 9
FsType: fat32

- ID: rootfs
Start: 9
FsType: ext4

SystemConfig:
BootType: efi
PartitionSettings:
- ID: esp
MountPoint: /boot/efi
MountOptions: umask=0077

- ID: rootfs
MountPoint: /
```

### SystemConfig [[SystemConfig](#systemconfig-type)]

Contains the configuration options for the OS.

Example:

```yaml
SystemConfig:
Hostname: example-image
```

## Disk type

Specifies the properties of a disk, including its partitions.

### PartitionTableType [string]

Specifies how the partition tables are laid out.

Supported options:

- `gpt`: Use the GUID Partition Table (GPT) format.

### MaxSize [uint64]

The size of the disk, specified in mebibytes (MiB).

### Partitions [[Partition](#partition-type)]

The partitions to provision on the disk.

## FileConfig type

Specifies options for placing a file in the OS.
Expand Down Expand Up @@ -180,6 +246,124 @@ Packages:
- openssh-server
```

## Partition type

### ID [string]

Required.

The ID of the partition.
This is used correlate Partition objects with [PartitionSetting](#partitionsetting-type)
objects.

### FsType [string]

Required.

The filesystem type of the partition.

Supported options:

- `ext4`
- `fat32`
- `xfs`

### Name [string]

The label to assign to the partition.

### Start [uint64]

Required.

The start location (inclusive) of the partition, specified in MiBs.

### End [uint64]

The end location (exclusive) of the partition, specified in MiBs.

The End and Size fields cannot be specified at the same time.

Either the Size or End field is required for all partitions except for the last
partition.
When both the Size and End fields are omitted, the last partition will fill the
remainder of the disk (based on the disk's [MaxSize](#maxsize-uint64) field).

### Size [uint64]

The size of the partition, specified in MiBs.

### Flags [string[]]

Specifies options for the partition.

Supported options:

- `esp`: The UEFI System Partition (ESP).
The partition must have a `FsType` of `fat32`.

When specified on a GPT formatted disk, the `boot` flag must also be added.

- `bios_grub`: Specifies this partition is the BIOS boot partition.
This is required for GPT disks that wish to be bootable using legacy BIOS mode.

This partition must start at block 1.

This flag is only supported on GPT formatted disks.

For further details, see: https://en.wikipedia.org/wiki/BIOS_boot_partition

- `boot`: Specifies that this partition contains the boot loader.

When specified on a GPT formatted disk, the `esp` flag must also be added.

These options mirror those in
[parted](https://www.gnu.org/software/parted/manual/html_node/set.html).

## PartitionSetting type

Specifies the mount options for a partition.

### ID [string]

Required.

The ID of the partition.
This is used correlate [Partition](#partition-type) objects with PartitionSetting
objects.

### MountIdentifier [string]

Default: `partuuid`

The partition ID type that should be used to recognize the partition on the disk.

Supported options:

- `uuid`: The filesystem's partition UUID.

- `partuuid`: The partition UUID specified in the partition table.

- `partlabel`: The partition label specified in the partition table.

### MountOptions [string]

The additional options used when mounting the file system.

These options are in the same format as [mount](https://linux.die.net/man/8/mount)'s
`-o` option (or the `fs_mntops` field of the
[fstab](https://man7.org/linux/man-pages/man5/fstab.5.html) file).

### MountPoint [string]

Required.

The absolute path of where the partition should be mounted.

The mounts will be sorted to ensure that parent directories are mounted before child
directories.
For example, `/boot` will be mounted before `/boot/efi`.

## Script type

Points to a script file (typically a Bash script) to be run during customization.
Expand Down Expand Up @@ -248,6 +432,22 @@ SystemConfig:

Contains the configuration options for the OS.

### BootType [string]

Specifies the boot system that the image supports.

Supported options:

- `legacy`: Support booting from BIOS firmware.

When this option is specified, the partition layout must contain a partition with the
`bios_grub` flag.

- `efi`: Support booting from UEFI firmware.

When this option is specified, the partition layout must contain a partition with the
`esp` flag.

### Hostname [string]

Specifies the hostname for the OS.
Expand Down Expand Up @@ -393,6 +593,10 @@ SystemConfig:
Permissions: "664"
```

### PartitionSettings [[PartitionSetting](#partitionsetting-type)[]]

Specifies the mount options of the partitions.

### PostInstallScripts [[Script](#script-type)[]]

Scripts to run against the image after the packages have been added and removed.
Expand Down
27 changes: 27 additions & 0 deletions toolkit/tools/imagecustomizerapi/boottype.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

package imagecustomizerapi

import (
"fmt"
)

type BootType string

const (
BootTypeEfi BootType = "efi"
BootTypeLegacy BootType = "legacy"
BootTypeUnset BootType = ""
)

func (t BootType) IsValid() error {
switch t {
case BootTypeEfi, BootTypeLegacy, BootTypeUnset:
// All good.
return nil

default:
return fmt.Errorf("invalid BootType value (%v)", t)
}
}
21 changes: 21 additions & 0 deletions toolkit/tools/imagecustomizerapi/boottype_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

package imagecustomizerapi

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestBootTypeIsValid(t *testing.T) {
err := BootTypeEfi.IsValid()
assert.NoError(t, err)
}

func TestBootTypeIsValidBadValue(t *testing.T) {
err := BootType("bad").IsValid()
assert.Error(t, err)
assert.ErrorContains(t, err, "invalid BootType value")
}
67 changes: 67 additions & 0 deletions toolkit/tools/imagecustomizerapi/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,82 @@

package imagecustomizerapi

import (
"fmt"

"github.com/microsoft/CBL-Mariner/toolkit/tools/internal/sliceutils"
)

type Config struct {
Disks *[]Disk `yaml:"Disks"`
SystemConfig SystemConfig `yaml:"SystemConfig"`
}

func (c *Config) IsValid() error {
if c.Disks != nil {
disks := *c.Disks
if len(disks) < 1 {
return fmt.Errorf("at least 1 disk must be specified (or the Disks field should be ommited)")
}
if len(disks) > 1 {
return fmt.Errorf("multiple disks is not currently supported")
}

for i, disk := range disks {
err := disk.IsValid()
if err != nil {
return fmt.Errorf("invalid disk at index %d:\n%w", i, err)
}
}
}

err := c.SystemConfig.IsValid()
if err != nil {
return err
}

hasDisks := c.Disks != nil
hasBootType := c.SystemConfig.BootType != BootTypeUnset

if hasDisks != hasBootType {
return fmt.Errorf("SystemConfig.BootType and Disks must be specified together")
}

// Ensure the correct partitions exist to support the specified the boot type.
switch c.SystemConfig.BootType {
case BootTypeEfi:
hasEsp := sliceutils.ContainsFunc(*c.Disks, func(disk Disk) bool {
Comment thread
cwize1 marked this conversation as resolved.
return sliceutils.ContainsFunc(disk.Partitions, func(partition Partition) bool {
return sliceutils.ContainsValue(partition.Flags, PartitionFlagESP)
})
})
if !hasEsp {
return fmt.Errorf("'esp' partition must be provided for 'efi' boot type")
}

case BootTypeLegacy:
hasBiosBoot := sliceutils.ContainsFunc(*c.Disks, func(disk Disk) bool {
return sliceutils.ContainsFunc(disk.Partitions, func(partition Partition) bool {
return sliceutils.ContainsValue(partition.Flags, PartitionFlagBiosGrub)
})
})
if !hasBiosBoot {
return fmt.Errorf("'bios_grub' partition must be provided for 'legacy' boot type")
}
}

// Ensure all the partition settings object have an equivalent partition object.
Comment thread
cwize1 marked this conversation as resolved.
for i, partitionSetting := range c.SystemConfig.PartitionSettings {
diskExists := sliceutils.ContainsFunc(*c.Disks, func(disk Disk) bool {
return sliceutils.ContainsFunc(disk.Partitions, func(partition Partition) bool {
return partition.ID == partitionSetting.ID
})
})
if !diskExists {
return fmt.Errorf("invalid PartitionSetting at index %d:\nno partition with matching ID (%s)", i,
partitionSetting.ID)
}
}

return nil
}
Loading