Image Customizer: Add support for kernel command-line (#6881)
This commit is contained in:
Родитель
f4310527e5
Коммит
4742b8bf0b
|
@ -179,6 +179,21 @@ SystemConfig:
|
|||
Permissions: "664"
|
||||
```
|
||||
|
||||
## KernelCommandLine type
|
||||
|
||||
Options for configuring the kernel.
|
||||
|
||||
### ExtraCommandLine
|
||||
|
||||
Additional Linux kernel command line options to add to the image.
|
||||
|
||||
If the partitions are customized, then the `grub.cfg` file will be reset to handle the
|
||||
new partition layout.
|
||||
So, any existing ExtraCommandLine value in the base image will be replaced.
|
||||
|
||||
If the partitions are not customized, then the `ExtraCommandLine` value will be appended
|
||||
to the existing `grub.cfg` file.
|
||||
|
||||
## Module type
|
||||
|
||||
Options for configuring a kernel module.
|
||||
|
@ -461,6 +476,11 @@ SystemConfig:
|
|||
Hostname: example-image
|
||||
```
|
||||
|
||||
### KernelCommandLine [[KernelCommandLine](#kernelcommandline-type)]
|
||||
|
||||
Specifies extra kernel command line options, as well as other configuration values
|
||||
relating to the kernel.
|
||||
|
||||
### UpdateBaseImagePackages [bool]
|
||||
|
||||
Updates the packages that exist in the base image.
|
||||
|
|
|
@ -39,11 +39,16 @@ func (c *Config) IsValid() error {
|
|||
|
||||
hasDisks := c.Disks != nil
|
||||
hasBootType := c.SystemConfig.BootType != BootTypeUnset
|
||||
hasPartitionSettings := len(c.SystemConfig.PartitionSettings) > 0
|
||||
|
||||
if hasDisks != hasBootType {
|
||||
return fmt.Errorf("SystemConfig.BootType and Disks must be specified together")
|
||||
}
|
||||
|
||||
if hasPartitionSettings && !hasDisks {
|
||||
return fmt.Errorf("the Disks and SystemConfig.BootType values must also be specified if SystemConfig.PartitionSettings is specified")
|
||||
}
|
||||
|
||||
// Ensure the correct partitions exist to support the specified the boot type.
|
||||
switch c.SystemConfig.BootType {
|
||||
case BootTypeEfi:
|
||||
|
|
|
@ -262,3 +262,72 @@ func TestConfigIsValidInvalidPartitionId(t *testing.T) {
|
|||
assert.ErrorContains(t, err, "partition")
|
||||
assert.ErrorContains(t, err, "ID")
|
||||
}
|
||||
|
||||
func TestConfigIsValidPartitionSettingsMissingDisks(t *testing.T) {
|
||||
config := &Config{
|
||||
SystemConfig: SystemConfig{
|
||||
Hostname: "test",
|
||||
PartitionSettings: []PartitionSetting{
|
||||
{
|
||||
ID: "esp",
|
||||
MountPoint: "/boot/efi",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
err := config.IsValid()
|
||||
assert.Error(t, err)
|
||||
assert.ErrorContains(t, err, "Disks")
|
||||
assert.ErrorContains(t, err, "BootType")
|
||||
assert.ErrorContains(t, err, "PartitionSettings")
|
||||
}
|
||||
|
||||
func TestConfigIsValidBootTypeMissingDisks(t *testing.T) {
|
||||
config := &Config{
|
||||
SystemConfig: SystemConfig{
|
||||
Hostname: "test",
|
||||
BootType: BootTypeEfi,
|
||||
KernelCommandLine: KernelCommandLine{
|
||||
ExtraCommandLine: "console=ttyS0",
|
||||
},
|
||||
},
|
||||
}
|
||||
err := config.IsValid()
|
||||
assert.Error(t, err)
|
||||
assert.ErrorContains(t, err, "SystemConfig.BootType and Disks must be specified together")
|
||||
}
|
||||
|
||||
func TestConfigIsValidKernelCLI(t *testing.T) {
|
||||
config := &Config{
|
||||
Disks: &[]Disk{{
|
||||
PartitionTableType: "gpt",
|
||||
MaxSize: 2,
|
||||
Partitions: []Partition{
|
||||
{
|
||||
ID: "esp",
|
||||
FsType: "fat32",
|
||||
Start: 1,
|
||||
Flags: []PartitionFlag{
|
||||
"esp",
|
||||
"boot",
|
||||
},
|
||||
},
|
||||
},
|
||||
}},
|
||||
SystemConfig: SystemConfig{
|
||||
BootType: "efi",
|
||||
Hostname: "test",
|
||||
PartitionSettings: []PartitionSetting{
|
||||
{
|
||||
ID: "esp",
|
||||
MountPoint: "/boot/efi",
|
||||
},
|
||||
},
|
||||
KernelCommandLine: KernelCommandLine{
|
||||
ExtraCommandLine: "console=ttyS0",
|
||||
},
|
||||
},
|
||||
}
|
||||
err := config.IsValid()
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package imagecustomizerapi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type KernelCommandLine struct {
|
||||
// Extra kernel command line args.
|
||||
ExtraCommandLine string `yaml:"ExtraCommandLine"`
|
||||
}
|
||||
|
||||
func (s *KernelCommandLine) IsValid() error {
|
||||
err := commandLineIsValid(s.ExtraCommandLine, "ExtraCommandLine")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func commandLineIsValid(commandLine string, fieldName string) error {
|
||||
// Disallow special characters to avoid breaking the grub.cfg file.
|
||||
// In addition, disallow the "`" character, since it is used as the sed escape character by
|
||||
// `installutils.setGrubCfgAdditionalCmdLine()`.
|
||||
if strings.ContainsAny(commandLine, "\n'\"\\$`") {
|
||||
return fmt.Errorf("the %s value contains invalid characters", fieldName)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -21,6 +21,7 @@ type SystemConfig struct {
|
|||
PackagesRemove []string `yaml:"PackagesRemove"`
|
||||
PackageListsUpdate []string `yaml:"PackageListsUpdate"`
|
||||
PackagesUpdate []string `yaml:"PackagesUpdate"`
|
||||
KernelCommandLine KernelCommandLine `yaml:"KernelCommandLine"`
|
||||
AdditionalFiles map[string]FileConfigList `yaml:"AdditionalFiles"`
|
||||
PartitionSettings []PartitionSetting `yaml:"PartitionSettings"`
|
||||
PostInstallScripts []Script `yaml:"PostInstallScripts"`
|
||||
|
@ -44,6 +45,11 @@ func (s *SystemConfig) IsValid() error {
|
|||
}
|
||||
}
|
||||
|
||||
err = s.KernelCommandLine.IsValid()
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid KernelCommandLine: %w", err)
|
||||
}
|
||||
|
||||
for sourcePath, fileConfigList := range s.AdditionalFiles {
|
||||
err = fileConfigList.IsValid()
|
||||
if err != nil {
|
||||
|
|
|
@ -41,3 +41,15 @@ func TestSystemConfigIsValidDuplicatePartitionID(t *testing.T) {
|
|||
assert.Error(t, err)
|
||||
assert.ErrorContains(t, err, "duplicate PartitionSettings ID")
|
||||
}
|
||||
|
||||
func TestSystemConfigIsValidKernelCommandLineInvalidChars(t *testing.T) {
|
||||
value := SystemConfig{
|
||||
KernelCommandLine: KernelCommandLine{
|
||||
ExtraCommandLine: "example=\"example\"",
|
||||
},
|
||||
}
|
||||
|
||||
err := value.IsValid()
|
||||
assert.Error(t, err)
|
||||
assert.ErrorContains(t, err, "ExtraCommandLine")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package imagecustomizerlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
"github.com/microsoft/CBL-Mariner/toolkit/tools/internal/logger"
|
||||
"github.com/microsoft/CBL-Mariner/toolkit/tools/internal/safechroot"
|
||||
)
|
||||
|
||||
var (
|
||||
linuxCommandLineRegex = regexp.MustCompile(`\tlinux .* (\$kernelopts)`)
|
||||
)
|
||||
|
||||
func handleKernelCommandLine(extraCommandLine string, imageChroot *safechroot.Chroot, partitionsCustomized bool) error {
|
||||
var err error
|
||||
|
||||
if partitionsCustomized {
|
||||
// ExtraCommandLine was handled when the new image was created and the grub.cfg file was regenerated from
|
||||
// scatch.
|
||||
return nil
|
||||
}
|
||||
|
||||
if extraCommandLine == "" {
|
||||
// Nothing to do.
|
||||
return nil
|
||||
}
|
||||
|
||||
logger.Log.Infof("Setting KernelCommandLine.ExtraCommandLine")
|
||||
|
||||
grub2ConfigFilePath := filepath.Join(imageChroot.RootDir(), "/boot/grub2/grub.cfg")
|
||||
|
||||
// Read the existing grub.cfg file.
|
||||
grub2ConfigFileBytes, err := os.ReadFile(grub2ConfigFilePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read existing grub2 config file: %w", err)
|
||||
}
|
||||
|
||||
grub2ConfigFile := string(grub2ConfigFileBytes)
|
||||
|
||||
// Find the point where the new command line arguments should be added.
|
||||
match := linuxCommandLineRegex.FindStringSubmatchIndex(grub2ConfigFile)
|
||||
if match == nil {
|
||||
return fmt.Errorf("failed to find Linux kernel command line params in grub2 config file")
|
||||
}
|
||||
|
||||
// Get the location of "$kernelopts".
|
||||
// Note: regexp returns index pairs. So, [2] is the start index of the 1st group.
|
||||
insertIndex := match[2]
|
||||
|
||||
// Insert new command line arguments.
|
||||
newGrub2ConfigFile := grub2ConfigFile[:insertIndex] + extraCommandLine + " " + grub2ConfigFile[insertIndex:]
|
||||
|
||||
// Update grub.cfg file.
|
||||
err = os.WriteFile(grub2ConfigFilePath, []byte(newGrub2ConfigFile), 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write new grub2 config file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -9,7 +9,6 @@ import (
|
|||
"path/filepath"
|
||||
|
||||
"github.com/microsoft/CBL-Mariner/toolkit/tools/imagecustomizerapi"
|
||||
"github.com/microsoft/CBL-Mariner/toolkit/tools/internal/logger"
|
||||
"github.com/microsoft/CBL-Mariner/toolkit/tools/internal/safechroot"
|
||||
"github.com/microsoft/CBL-Mariner/toolkit/tools/internal/shell"
|
||||
)
|
||||
|
@ -30,7 +29,7 @@ func customizePartitionsUsingFileCopy(buildDir string, baseConfigPath string, co
|
|||
}
|
||||
|
||||
newImageConnection, err := createNewImage(newBuildImageFile, diskConfig, config.SystemConfig.PartitionSettings,
|
||||
config.SystemConfig.BootType, buildDir, "newimageroot", installOSFunc)
|
||||
config.SystemConfig.BootType, config.SystemConfig.KernelCommandLine, buildDir, "newimageroot", installOSFunc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -82,8 +81,7 @@ func copyFilesIntoNewDiskHelper(existingImageChroot *safechroot.Chroot, newImage
|
|||
copyArgs = append(copyArgs, fullFileName)
|
||||
}
|
||||
|
||||
err = shell.ExecuteLiveWithCallback(func(...interface{}) {}, logger.Log.Warn, false,
|
||||
"cp", copyArgs...)
|
||||
err = shell.ExecuteLiveWithErr(1, "cp", copyArgs...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to copy files:\n%w", err)
|
||||
}
|
||||
|
|
|
@ -83,6 +83,12 @@ func doCustomizations(buildDir string, baseConfigPath string, config *imagecusto
|
|||
return err
|
||||
}
|
||||
|
||||
err = handleKernelCommandLine(config.SystemConfig.KernelCommandLine.ExtraCommandLine, imageChroot,
|
||||
partitionsCustomized)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to add extra kernel command line: %w", err)
|
||||
}
|
||||
|
||||
err = runScripts(baseConfigPath, config.SystemConfig.FinalizeImageScripts, imageChroot)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -138,9 +138,16 @@ func toQemuImageFormat(imageFormat string) (string, error) {
|
|||
func validateConfig(baseConfigPath string, config *imagecustomizerapi.Config, rpmsSources []string,
|
||||
useBaseImageRpmRepos bool,
|
||||
) error {
|
||||
// Note: This IsValid() check does duplicate the one in UnmarshalYamlFile().
|
||||
// But it is useful for functions that call CustomizeImage() directly. For example, test code.
|
||||
err := config.IsValid()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
partitionsCustomized := hasPartitionCustomizations(config)
|
||||
|
||||
err := validateSystemConfig(baseConfigPath, &config.SystemConfig, rpmsSources, useBaseImageRpmRepos,
|
||||
err = validateSystemConfig(baseConfigPath, &config.SystemConfig, rpmsSources, useBaseImageRpmRepos,
|
||||
partitionsCustomized)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -8,16 +8,20 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/microsoft/CBL-Mariner/toolkit/tools/imagecustomizerapi"
|
||||
"github.com/microsoft/CBL-Mariner/toolkit/tools/internal/buildpipeline"
|
||||
"github.com/microsoft/CBL-Mariner/toolkit/tools/internal/ptrutils"
|
||||
"github.com/microsoft/CBL-Mariner/toolkit/tools/internal/safechroot"
|
||||
"github.com/microsoft/CBL-Mariner/toolkit/tools/internal/safeloopback"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
testImageRootDirName = "testimageroot"
|
||||
)
|
||||
|
||||
func TestCustomizeImageEmptyConfig(t *testing.T) {
|
||||
var err error
|
||||
|
||||
|
@ -88,34 +92,43 @@ func TestCustomizeImageCopyFiles(t *testing.T) {
|
|||
checkFileType(t, outImageFilePath, "raw")
|
||||
|
||||
// Mount the output disk image so that its contents can be checked.
|
||||
loopback, err := safeloopback.NewLoopback(outImageFilePath)
|
||||
imageConnection, err := reconnectToFakeEfiImage(buildDir, outImageFilePath)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
defer loopback.Close()
|
||||
defer imageConnection.Close()
|
||||
|
||||
// Create partition mount config.
|
||||
// Note: The assigned loopback device might be different from the one assigned when `createFakeEfiImage` ran.
|
||||
bootPartitionDevPath := fmt.Sprintf("%sp1", loopback.DevicePath())
|
||||
osPartitionDevPath := fmt.Sprintf("%sp2", loopback.DevicePath())
|
||||
// Check the contents of the copied file.
|
||||
file_contents, err := os.ReadFile(filepath.Join(imageConnection.Chroot().RootDir(), "a.txt"))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "abcdefg\n", string(file_contents))
|
||||
}
|
||||
|
||||
func reconnectToFakeEfiImage(buildDir string, imageFilePath string) (*ImageConnection, error) {
|
||||
imageConnection := NewImageConnection()
|
||||
err := imageConnection.ConnectLoopback(imageFilePath)
|
||||
if err != nil {
|
||||
imageConnection.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rootDir := filepath.Join(buildDir, testImageRootDirName)
|
||||
|
||||
bootPartitionDevPath := fmt.Sprintf("%sp1", imageConnection.Loopback().DevicePath())
|
||||
osPartitionDevPath := fmt.Sprintf("%sp2", imageConnection.Loopback().DevicePath())
|
||||
|
||||
newMountDirectories := []string{}
|
||||
mountPoints := []*safechroot.MountPoint{
|
||||
safechroot.NewPreDefaultsMountPoint(osPartitionDevPath, "/", "ext4", 0, ""),
|
||||
safechroot.NewMountPoint(bootPartitionDevPath, "/boot/efi", "vfat", 0, ""),
|
||||
}
|
||||
|
||||
imageChroot := safechroot.NewChroot(filepath.Join(buildDir, "imageroot"), false)
|
||||
err = imageChroot.Initialize("", newMountDirectories, mountPoints)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
err = imageConnection.ConnectChroot(rootDir, false, []string{}, mountPoints)
|
||||
if err != nil {
|
||||
imageConnection.Close()
|
||||
return nil, err
|
||||
}
|
||||
defer imageChroot.Close(false)
|
||||
|
||||
// Check the contents of the copied file.
|
||||
file_contents, err := os.ReadFile(filepath.Join(imageChroot.RootDir(), "a.txt"))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "abcdefg\n", string(file_contents))
|
||||
return imageConnection, nil
|
||||
}
|
||||
|
||||
func TestValidateConfigValidAdditionalFiles(t *testing.T) {
|
||||
|
@ -189,6 +202,69 @@ func TestValidateConfigScriptNonExecutable(t *testing.T) {
|
|||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestCustomizeImageKernelCommandLineAdd(t *testing.T) {
|
||||
var err error
|
||||
|
||||
if testing.Short() {
|
||||
t.Skip("Short mode enabled")
|
||||
}
|
||||
|
||||
if !buildpipeline.IsRegularBuild() {
|
||||
t.Skip("loopback block device not available")
|
||||
}
|
||||
|
||||
if os.Geteuid() != 0 {
|
||||
t.Skip("Test must be run as root because it uses a chroot")
|
||||
}
|
||||
|
||||
buildDir := filepath.Join(tmpDir, "TestCustomizeImageKernelCommandLine")
|
||||
outImageFilePath := filepath.Join(buildDir, "image.vhd")
|
||||
|
||||
// Create fake disk.
|
||||
diskFilePath, err := createFakeEfiImage(buildDir)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
// Customize image.
|
||||
config := &imagecustomizerapi.Config{
|
||||
SystemConfig: imagecustomizerapi.SystemConfig{
|
||||
KernelCommandLine: imagecustomizerapi.KernelCommandLine{
|
||||
ExtraCommandLine: "console=tty0 console=ttyS0",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = CustomizeImage(buildDir, buildDir, config, diskFilePath, nil, outImageFilePath, "raw", false)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
// Mount the output disk image so that its contents can be checked.
|
||||
imageConnection, err := reconnectToFakeEfiImage(buildDir, outImageFilePath)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
defer imageConnection.Close()
|
||||
|
||||
// Read the grub.cfg file.
|
||||
grub2ConfigFilePath := filepath.Join(imageConnection.Chroot().RootDir(), "/boot/grub2/grub.cfg")
|
||||
|
||||
grub2ConfigFile, err := os.ReadFile(grub2ConfigFilePath)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("%s", grub2ConfigFile)
|
||||
|
||||
linuxCommandLineRegex, err := regexp.Compile(`linux .* console=tty0 console=ttyS0 `)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
assert.True(t, linuxCommandLineRegex.Match(grub2ConfigFile))
|
||||
}
|
||||
|
||||
func createFakeEfiImage(buildDir string) (string, error) {
|
||||
var err error
|
||||
|
||||
|
@ -241,8 +317,8 @@ func createFakeEfiImage(buildDir string) (string, error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
imageConnection, err := createNewImage(rawDisk, diskConfig, partitionSettings, "efi", buildDir, "imageroot",
|
||||
installOS)
|
||||
imageConnection, err := createNewImage(rawDisk, diskConfig, partitionSettings, "efi",
|
||||
imagecustomizerapi.KernelCommandLine{}, buildDir, testImageRootDirName, installOS)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
|
@ -58,13 +58,14 @@ func connectToExistingImageHelper(imageConnection *ImageConnection, imageFilePat
|
|||
}
|
||||
|
||||
func createNewImage(filename string, diskConfig imagecustomizerapi.Disk,
|
||||
partitionSettings []imagecustomizerapi.PartitionSetting, bootType imagecustomizerapi.BootType, buildDir string,
|
||||
chrootDirName string, installOS installOSFunc,
|
||||
partitionSettings []imagecustomizerapi.PartitionSetting, bootType imagecustomizerapi.BootType,
|
||||
kernelCommandLine imagecustomizerapi.KernelCommandLine, buildDir string, chrootDirName string,
|
||||
installOS installOSFunc,
|
||||
) (*ImageConnection, error) {
|
||||
imageConnection := &ImageConnection{}
|
||||
|
||||
err := createNewImageHelper(imageConnection, filename, diskConfig, partitionSettings, bootType, buildDir,
|
||||
chrootDirName, installOS,
|
||||
err := createNewImageHelper(imageConnection, filename, diskConfig, partitionSettings, bootType, kernelCommandLine,
|
||||
buildDir, chrootDirName, installOS,
|
||||
)
|
||||
if err != nil {
|
||||
imageConnection.Close()
|
||||
|
@ -75,8 +76,9 @@ func createNewImage(filename string, diskConfig imagecustomizerapi.Disk,
|
|||
}
|
||||
|
||||
func createNewImageHelper(imageConnection *ImageConnection, filename string, diskConfig imagecustomizerapi.Disk,
|
||||
partitionSettings []imagecustomizerapi.PartitionSetting, bootType imagecustomizerapi.BootType, buildDir string,
|
||||
chrootDirName string, installOS installOSFunc,
|
||||
partitionSettings []imagecustomizerapi.PartitionSetting, bootType imagecustomizerapi.BootType,
|
||||
kernelCommandLine imagecustomizerapi.KernelCommandLine, buildDir string, chrootDirName string,
|
||||
installOS installOSFunc,
|
||||
) error {
|
||||
// Convert config to image config types, so that the imager's utils can be used.
|
||||
imagerBootType, err := bootTypeToImager(bootType)
|
||||
|
@ -94,13 +96,18 @@ func createNewImageHelper(imageConnection *ImageConnection, filename string, dis
|
|||
return err
|
||||
}
|
||||
|
||||
imagerKernelCommandLine, err := kernelCommandLineToImager(kernelCommandLine)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Sort the partitions so that they are mounted in the correct oder.
|
||||
sort.Slice(imagerPartitionSettings, func(i, j int) bool {
|
||||
return imagerPartitionSettings[i].MountPoint < imagerPartitionSettings[j].MountPoint
|
||||
})
|
||||
|
||||
// Create imager boilerplate.
|
||||
mountPointMap, err := createImageBoilerplate(imageConnection, filename, buildDir, chrootDirName, imagerDiskConfig,
|
||||
mountPointMap, tmpFstabFile, err := createImageBoilerplate(imageConnection, filename, buildDir, chrootDirName, imagerDiskConfig,
|
||||
imagerPartitionSettings)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -112,9 +119,17 @@ func createNewImageHelper(imageConnection *ImageConnection, filename string, dis
|
|||
return err
|
||||
}
|
||||
|
||||
// Move the fstab file into the image.
|
||||
imageFstabFilePath := filepath.Join(imageConnection.Chroot().RootDir(), "etc/fstab")
|
||||
|
||||
err = file.Move(tmpFstabFile, imageFstabFilePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to move fstab into new image:\n%w", err)
|
||||
}
|
||||
|
||||
// Configure the boot loader.
|
||||
err = installutils.ConfigureDiskBootloader(imagerBootType, false, false, imagerPartitionSettings,
|
||||
configuration.KernelCommandLine{}, imageConnection.Chroot(), imageConnection.Loopback().DevicePath(),
|
||||
imagerKernelCommandLine, imageConnection.Chroot(), imageConnection.Loopback().DevicePath(),
|
||||
mountPointMap, diskutils.EncryptedRootDevice{}, diskutils.VerityDevice{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to install bootloader:\n%w", err)
|
||||
|
@ -125,17 +140,17 @@ func createNewImageHelper(imageConnection *ImageConnection, filename string, dis
|
|||
|
||||
func createImageBoilerplate(imageConnection *ImageConnection, filename string, buildDir string, chrootDirName string,
|
||||
imagerDiskConfig configuration.Disk, imagerPartitionSettings []configuration.PartitionSetting,
|
||||
) (map[string]string, error) {
|
||||
) (map[string]string, string, error) {
|
||||
// Create raw disk image file.
|
||||
err := diskutils.CreateSparseDisk(filename, imagerDiskConfig.MaxSize, 0o644)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create empty disk file (%s):\n%w", filename, err)
|
||||
return nil, "", fmt.Errorf("failed to create empty disk file (%s):\n%w", filename, err)
|
||||
}
|
||||
|
||||
// Connect raw disk image file.
|
||||
err = imageConnection.ConnectLoopback(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
// Set up partitions.
|
||||
|
@ -143,13 +158,13 @@ func createImageBoilerplate(imageConnection *ImageConnection, filename string, b
|
|||
imageConnection.Loopback().DevicePath(), imagerDiskConfig, configuration.RootEncryption{},
|
||||
configuration.ReadOnlyVerityRoot{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create partitions on disk (%s):\n%w", imageConnection.Loopback().DevicePath(), err)
|
||||
return nil, "", fmt.Errorf("failed to create partitions on disk (%s):\n%w", imageConnection.Loopback().DevicePath(), err)
|
||||
}
|
||||
|
||||
// Read the disk partitions.
|
||||
diskPartitions, err := diskutils.GetDiskPartitions(imageConnection.Loopback().DevicePath())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
// Create the fstab file.
|
||||
|
@ -166,13 +181,13 @@ func createImageBoilerplate(imageConnection *ImageConnection, filename string, b
|
|||
mountPointToMountArgsMap, partIDToDevPathMap, partIDToFsTypeMap, false, /*hidepidEnabled*/
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to write temp fstab file:\n%w", err)
|
||||
return nil, "", fmt.Errorf("failed to write temp fstab file:\n%w", err)
|
||||
}
|
||||
|
||||
// Read back the fstab file.
|
||||
mountPoints, err := findMountsFromFstabFile(tmpFstabFile, diskPartitions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
// Create chroot environment.
|
||||
|
@ -180,16 +195,8 @@ func createImageBoilerplate(imageConnection *ImageConnection, filename string, b
|
|||
|
||||
err = imageConnection.ConnectChroot(imageChrootDir, false, nil, mountPoints)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
// Move the fstab file into the image.
|
||||
imageFstabFilePath := filepath.Join(imageConnection.Chroot().RootDir(), "etc/fstab")
|
||||
|
||||
err = file.Move(tmpFstabFile, imageFstabFilePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to move fstab into new image:\n%w", err)
|
||||
}
|
||||
|
||||
return mountPointMap, nil
|
||||
return mountPointMap, tmpFstabFile, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
SystemConfig:
|
||||
KernelCommandLine:
|
||||
ExtraCommandLine: console=tty0 console=ttyS0
|
|
@ -39,3 +39,6 @@ SystemConfig:
|
|||
|
||||
- ID: var
|
||||
MountPoint: /var
|
||||
|
||||
KernelCommandLine:
|
||||
ExtraCommandLine: console=tty0 console=ttyS0
|
||||
|
|
|
@ -161,3 +161,11 @@ func mountIdentifierTypeToImager(mountIdentifierType imagecustomizerapi.MountIde
|
|||
return "", fmt.Errorf("unknwon MountIdentifierType value (%s)", mountIdentifierType)
|
||||
}
|
||||
}
|
||||
|
||||
func kernelCommandLineToImager(kernelCommandLine imagecustomizerapi.KernelCommandLine,
|
||||
) (configuration.KernelCommandLine, error) {
|
||||
imagerKernelCommandLine := configuration.KernelCommandLine{
|
||||
ExtraCommandLine: kernelCommandLine.ExtraCommandLine,
|
||||
}
|
||||
return imagerKernelCommandLine, nil
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче