mirror of
https://github.com/linka-cloud/d2vm.git
synced 2024-11-22 07:46:25 +00:00
add split boot partiton support
Signed-off-by: Adphi <philippe.adrien.nousse@gmail.com>
This commit is contained in:
parent
490f235c6d
commit
532ee3f1a3
@ -38,7 +38,9 @@ RUN apt-get update && \
|
||||
mount \
|
||||
tar \
|
||||
extlinux \
|
||||
qemu-utils
|
||||
qemu-utils && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY --from=docker:dind /usr/local/bin/docker /usr/local/bin/
|
||||
|
||||
|
@ -154,6 +154,7 @@ Usage:
|
||||
|
||||
Flags:
|
||||
--append-to-cmdline string Extra kernel cmdline arguments to append to the generated one
|
||||
--boot-size uint Size of the boot partition in MB (default 100)
|
||||
--force Override output qcow2 image
|
||||
-h, --help help for convert
|
||||
--network-manager string Network manager to use for the image: none, netplan, ifupdown
|
||||
@ -163,6 +164,7 @@ Flags:
|
||||
--push Push the container disk image to the registry
|
||||
--raw Just convert the container to virtual machine image without installing anything more
|
||||
-s, --size string The output image size (default "10G")
|
||||
--split-boot Split the boot partition from the root partition
|
||||
-t, --tag string Container disk Docker image tag
|
||||
|
||||
Global Flags:
|
||||
@ -305,6 +307,7 @@ Usage:
|
||||
|
||||
Flags:
|
||||
--append-to-cmdline string Extra kernel cmdline arguments to append to the generated one
|
||||
--boot-size uint Size of the boot partition in MB (default 100)
|
||||
--build-arg stringArray Set build-time variables
|
||||
-f, --file string Name of the Dockerfile
|
||||
--force Override output qcow2 image
|
||||
@ -315,6 +318,7 @@ Flags:
|
||||
--push Push the container disk image to the registry
|
||||
--raw Just convert the container to virtual machine image without installing anything more
|
||||
-s, --size string The output image size (default "10G")
|
||||
--split-boot Split the boot partition from the root partition
|
||||
-t, --tag string Container disk Docker image tag
|
||||
|
||||
Global Flags:
|
||||
|
130
builder.go
130
builder.go
@ -121,18 +121,24 @@ type builder struct {
|
||||
diskOut string
|
||||
format string
|
||||
|
||||
size int64
|
||||
size uint64
|
||||
mntPoint string
|
||||
|
||||
splitBoot bool
|
||||
bootSize uint64
|
||||
|
||||
mbrPath string
|
||||
|
||||
loDevice string
|
||||
loPart string
|
||||
diskUUD string
|
||||
bootPart string
|
||||
rootPart string
|
||||
bootUUID string
|
||||
rootUUID string
|
||||
|
||||
cmdLineExtra string
|
||||
}
|
||||
|
||||
func NewBuilder(ctx context.Context, workdir, imgTag, disk string, size int64, osRelease OSRelease, format string, cmdLineExtra string) (Builder, error) {
|
||||
func NewBuilder(ctx context.Context, workdir, imgTag, disk string, size uint64, osRelease OSRelease, format string, cmdLineExtra string, splitBoot bool, bootSize uint64) (Builder, error) {
|
||||
if err := checkDependencies(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -147,6 +153,14 @@ func NewBuilder(ctx context.Context, workdir, imgTag, disk string, size int64, o
|
||||
return nil, fmt.Errorf("invalid format: %s valid formats are: %s", f, strings.Join(formats, " "))
|
||||
}
|
||||
|
||||
if splitBoot && bootSize < 50 {
|
||||
return nil, fmt.Errorf("boot partition size must be at least 50MiB")
|
||||
}
|
||||
|
||||
if splitBoot && bootSize >= size {
|
||||
return nil, fmt.Errorf("boot partition size must be less than the disk size")
|
||||
}
|
||||
|
||||
mbrBin := ""
|
||||
for _, v := range mbrPaths {
|
||||
if _, err := os.Stat(v); err == nil {
|
||||
@ -158,7 +172,7 @@ func NewBuilder(ctx context.Context, workdir, imgTag, disk string, size int64, o
|
||||
return nil, fmt.Errorf("unable to find syslinux's mbr.bin path")
|
||||
}
|
||||
if size == 0 {
|
||||
size = 10 * int64(datasize.GB)
|
||||
size = 10 * uint64(datasize.GB)
|
||||
}
|
||||
if disk == "" {
|
||||
disk = "disk0"
|
||||
@ -186,6 +200,8 @@ func NewBuilder(ctx context.Context, workdir, imgTag, disk string, size int64, o
|
||||
mbrPath: mbrBin,
|
||||
mntPoint: filepath.Join(workdir, "/mnt"),
|
||||
cmdLineExtra: cmdLineExtra,
|
||||
splitBoot: splitBoot,
|
||||
bootSize: bootSize,
|
||||
}
|
||||
if err := os.MkdirAll(b.mntPoint, os.ModePerm); err != nil {
|
||||
return nil, err
|
||||
@ -252,9 +268,21 @@ func (b *builder) makeImg(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := exec.Run(ctx, "parted", "-s", b.diskRaw, "mklabel", "msdos", "mkpart", "primary", "1Mib", "100%", "set", "1", "boot", "on"); err != nil {
|
||||
var args []string
|
||||
if b.splitBoot {
|
||||
args = []string{"-s", b.diskRaw,
|
||||
"mklabel", "msdos", "mkpart", "primary", "1Mib", fmt.Sprintf("%dMib", b.bootSize),
|
||||
"mkpart", "primary", fmt.Sprintf("%dMib", b.bootSize), "100%",
|
||||
"set", "1", "boot", "on",
|
||||
}
|
||||
} else {
|
||||
args = []string{"-s", b.diskRaw, "mklabel", "msdos", "mkpart", "primary", "1Mib", "100%", "set", "1", "boot", "on"}
|
||||
}
|
||||
|
||||
if err := exec.Run(ctx, "parted", args...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -268,12 +296,29 @@ func (b *builder) mountImg(ctx context.Context) error {
|
||||
if err := exec.Run(ctx, "kpartx", "-a", b.loDevice); err != nil {
|
||||
return err
|
||||
}
|
||||
b.loPart = fmt.Sprintf("/dev/mapper/%sp1", filepath.Base(b.loDevice))
|
||||
b.bootPart = fmt.Sprintf("/dev/mapper/%sp1", filepath.Base(b.loDevice))
|
||||
if b.splitBoot {
|
||||
b.rootPart = fmt.Sprintf("/dev/mapper/%sp2", filepath.Base(b.loDevice))
|
||||
} else {
|
||||
b.rootPart = b.bootPart
|
||||
}
|
||||
logrus.Infof("creating raw image file system")
|
||||
if err := exec.Run(ctx, "mkfs.ext4", b.loPart); err != nil {
|
||||
if err := exec.Run(ctx, "mkfs.ext4", b.rootPart); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := exec.Run(ctx, "mount", b.loPart, b.mntPoint); err != nil {
|
||||
if err := exec.Run(ctx, "mount", b.rootPart, b.mntPoint); err != nil {
|
||||
return err
|
||||
}
|
||||
if !b.splitBoot {
|
||||
return nil
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Join(b.mntPoint, "boot"), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := exec.Run(ctx, "mkfs.ext4", b.bootPart); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := exec.Run(ctx, "mount", b.bootPart, filepath.Join(b.mntPoint, "boot")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@ -282,6 +327,11 @@ func (b *builder) mountImg(ctx context.Context) error {
|
||||
func (b *builder) unmountImg(ctx context.Context) error {
|
||||
logrus.Infof("unmounting raw image")
|
||||
var merr error
|
||||
if b.splitBoot {
|
||||
if err := exec.Run(ctx, "umount", filepath.Join(b.mntPoint, "boot")); err != nil {
|
||||
merr = multierr.Append(merr, err)
|
||||
}
|
||||
}
|
||||
if err := exec.Run(ctx, "umount", b.mntPoint); err != nil {
|
||||
merr = multierr.Append(merr, err)
|
||||
}
|
||||
@ -302,14 +352,28 @@ func (b *builder) copyRootFS(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *builder) setupRootFS(ctx context.Context) error {
|
||||
func diskUUID(ctx context.Context, disk string) (string, error) {
|
||||
o, _, err := exec.RunOut(ctx, "blkid", "-s", "UUID", "-o", "value", disk)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSuffix(o, "\n"), nil
|
||||
}
|
||||
|
||||
func (b *builder) setupRootFS(ctx context.Context) (err error) {
|
||||
logrus.Infof("setting up rootfs")
|
||||
o, _, err := exec.RunOut(ctx, "blkid", "-s", "UUID", "-o", "value", b.loPart)
|
||||
b.rootUUID, err = diskUUID(ctx, b.rootPart)
|
||||
var fstab string
|
||||
if b.splitBoot {
|
||||
b.bootUUID, err = diskUUID(ctx, b.bootPart)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.diskUUD = strings.TrimSuffix(o, "\n")
|
||||
fstab := fmt.Sprintf("UUID=%s / ext4 errors=remount-ro 0 1\n", b.diskUUD)
|
||||
fstab = fmt.Sprintf("UUID=%s / ext4 errors=remount-ro 0 1\nUUID=%s /boot ext4 errors=remount-ro 0 2\n", b.rootUUID, b.bootUUID)
|
||||
} else {
|
||||
b.bootUUID = b.rootUUID
|
||||
fstab = fmt.Sprintf("UUID=%s / ext4 errors=remount-ro 0 1\n", b.bootUUID)
|
||||
}
|
||||
if err := b.chWriteFile("/etc/fstab", fstab, perm); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -329,9 +393,15 @@ func (b *builder) setupRootFS(ctx context.Context) error {
|
||||
if err := os.RemoveAll(b.chPath("/.dockerenv")); err != nil {
|
||||
return err
|
||||
}
|
||||
if b.osRelease.ID != ReleaseAlpine {
|
||||
return nil
|
||||
// create a symlink to /boot for non-alpine images in order to have a consistent path
|
||||
// even if the image is not split
|
||||
if _, err := os.Stat(b.chPath("/boot/boot")); os.IsNotExist(err) {
|
||||
if err := os.Symlink(".", b.chPath("/boot/boot")); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
switch b.osRelease.ID {
|
||||
case ReleaseAlpine:
|
||||
by, err := os.ReadFile(b.chPath("/etc/inittab"))
|
||||
if err != nil {
|
||||
return err
|
||||
@ -344,6 +414,30 @@ func (b *builder) setupRootFS(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
case ReleaseUbuntu:
|
||||
if b.osRelease.VersionID >= "20.04" {
|
||||
return nil
|
||||
}
|
||||
fallthrough
|
||||
case ReleaseDebian, ReleaseKali:
|
||||
t, err := os.Readlink(b.chPath("/vmlinuz"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Symlink(t, b.chPath("/boot/vmlinuz")); err != nil {
|
||||
return err
|
||||
}
|
||||
t, err = os.Readlink(b.chPath("/initrd.img"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Symlink(t, b.chPath("/boot/initrd.img")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (b *builder) installKernel(ctx context.Context) error {
|
||||
@ -355,7 +449,7 @@ func (b *builder) installKernel(ctx context.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := b.chWriteFile("/boot/syslinux.cfg", fmt.Sprintf(sysconfig, b.diskUUD, b.cmdLineExtra), perm); err != nil {
|
||||
if err := b.chWriteFile("/boot/syslinux.cfg", fmt.Sprintf(sysconfig, b.rootUUID, b.cmdLineExtra), perm); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@ -393,13 +487,13 @@ func (b *builder) Close() error {
|
||||
return b.img.Close()
|
||||
}
|
||||
|
||||
func block(path string, size int64) error {
|
||||
func block(path string, size uint64) error {
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
return f.Truncate(size)
|
||||
return f.Truncate(int64(size))
|
||||
}
|
||||
|
||||
func checkDependencies() error {
|
||||
|
@ -107,6 +107,8 @@ var (
|
||||
d2vm.WithCmdLineExtra(cmdLineExtra),
|
||||
d2vm.WithNetworkManager(d2vm.NetworkManager(networkManager)),
|
||||
d2vm.WithRaw(raw),
|
||||
d2vm.WithSplitBoot(splitBoot),
|
||||
d2vm.WithBootSize(bootSize),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -95,6 +95,8 @@ var (
|
||||
d2vm.WithCmdLineExtra(cmdLineExtra),
|
||||
d2vm.WithNetworkManager(d2vm.NetworkManager(networkManager)),
|
||||
d2vm.WithRaw(raw),
|
||||
d2vm.WithSplitBoot(splitBoot),
|
||||
d2vm.WithBootSize(bootSize),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -109,12 +111,12 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func parseSize(s string) (int64, error) {
|
||||
func parseSize(s string) (uint64, error) {
|
||||
var v datasize.ByteSize
|
||||
if err := v.UnmarshalText([]byte(s)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int64(v), nil
|
||||
return uint64(v), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
@ -33,6 +33,8 @@ var (
|
||||
containerDiskTag = ""
|
||||
push bool
|
||||
networkManager string
|
||||
splitBoot bool
|
||||
bootSize uint64
|
||||
)
|
||||
|
||||
func buildFlags() *pflag.FlagSet {
|
||||
@ -46,5 +48,7 @@ func buildFlags() *pflag.FlagSet {
|
||||
flags.BoolVar(&raw, "raw", false, "Just convert the container to virtual machine image without installing anything more")
|
||||
flags.StringVarP(&containerDiskTag, "tag", "t", "", "Container disk Docker image tag")
|
||||
flags.BoolVar(&push, "push", false, "Push the container disk image to the registry")
|
||||
flags.BoolVar(&splitBoot, "split-boot", false, "Split the boot partition from the root partition")
|
||||
flags.Uint64Var(&bootSize, "boot-size", 100, "Size of the boot partition in MB")
|
||||
return flags
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ func Convert(ctx context.Context, img string, opts ...ConvertOption) error {
|
||||
if format == "" {
|
||||
format = "raw"
|
||||
}
|
||||
b, err := NewBuilder(ctx, tmpPath, imgUUID, "", o.size, r, format, o.cmdLineExtra)
|
||||
b, err := NewBuilder(ctx, tmpPath, imgUUID, "", o.size, r, format, o.cmdLineExtra, o.splitBoot, o.bootSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -17,15 +17,18 @@ package d2vm
|
||||
type ConvertOption func(o *convertOptions)
|
||||
|
||||
type convertOptions struct {
|
||||
size int64
|
||||
size uint64
|
||||
password string
|
||||
output string
|
||||
cmdLineExtra string
|
||||
networkManager NetworkManager
|
||||
raw bool
|
||||
|
||||
splitBoot bool
|
||||
bootSize uint64
|
||||
}
|
||||
|
||||
func WithSize(size int64) ConvertOption {
|
||||
func WithSize(size uint64) ConvertOption {
|
||||
return func(o *convertOptions) {
|
||||
o.size = size
|
||||
}
|
||||
@ -60,3 +63,15 @@ func WithRaw(raw bool) ConvertOption {
|
||||
o.raw = raw
|
||||
}
|
||||
}
|
||||
|
||||
func WithSplitBoot(b bool) ConvertOption {
|
||||
return func(o *convertOptions) {
|
||||
o.splitBoot = b
|
||||
}
|
||||
}
|
||||
|
||||
func WithBootSize(bootSize uint64) ConvertOption {
|
||||
return func(o *convertOptions) {
|
||||
o.bootSize = bootSize
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ d2vm build [context directory] [flags]
|
||||
|
||||
```
|
||||
--append-to-cmdline string Extra kernel cmdline arguments to append to the generated one
|
||||
--boot-size uint Size of the boot partition in MB (default 100)
|
||||
--build-arg stringArray Set build-time variables
|
||||
-f, --file string Name of the Dockerfile
|
||||
--force Override output qcow2 image
|
||||
@ -20,6 +21,7 @@ d2vm build [context directory] [flags]
|
||||
--push Push the container disk image to the registry
|
||||
--raw Just convert the container to virtual machine image without installing anything more
|
||||
-s, --size string The output image size (default "10G")
|
||||
--split-boot Split the boot partition from the root partition
|
||||
-t, --tag string Container disk Docker image tag
|
||||
```
|
||||
|
||||
|
@ -10,6 +10,7 @@ d2vm convert [docker image] [flags]
|
||||
|
||||
```
|
||||
--append-to-cmdline string Extra kernel cmdline arguments to append to the generated one
|
||||
--boot-size uint Size of the boot partition in MB (default 100)
|
||||
--force Override output qcow2 image
|
||||
-h, --help help for convert
|
||||
--network-manager string Network manager to use for the image: none, netplan, ifupdown
|
||||
@ -19,6 +20,7 @@ d2vm convert [docker image] [flags]
|
||||
--push Push the container disk image to the registry
|
||||
--raw Just convert the container to virtual machine image without installing anything more
|
||||
-s, --size string The output image size (default "10G")
|
||||
--split-boot Split the boot partition from the root partition
|
||||
-t, --tag string Container disk Docker image tag
|
||||
```
|
||||
|
||||
|
@ -6,14 +6,14 @@ RUN apk update --no-cache && \
|
||||
apk add \
|
||||
util-linux \
|
||||
linux-virt \
|
||||
{{ if ge .Release.VersionID "3.17" }} \
|
||||
{{- if ge .Release.VersionID "3.17" }}
|
||||
busybox-openrc \
|
||||
busybox-mdev-openrc \
|
||||
busybox-extras-openrc \
|
||||
busybox-mdev-openrc \
|
||||
{{ else }}
|
||||
{{- else }}
|
||||
busybox-initscripts \
|
||||
{{ end }}
|
||||
{{- end }}
|
||||
openrc
|
||||
|
||||
RUN for s in bootmisc hostname hwclock modules networking swap sysctl urandom syslog; do rc-update add $s boot; done
|
||||
|
Loading…
Reference in New Issue
Block a user