mirror of
https://github.com/linka-cloud/d2vm.git
synced 2024-11-22 15:56:24 +00:00
add grub support
Signed-off-by: Adphi <philippe.adrien.nousse@gmail.com>
This commit is contained in:
parent
a003e176f5
commit
4e533b8044
@ -38,6 +38,7 @@ RUN apt-get update && \
|
|||||||
mount \
|
mount \
|
||||||
tar \
|
tar \
|
||||||
extlinux \
|
extlinux \
|
||||||
|
grub2 \
|
||||||
cryptsetup-bin \
|
cryptsetup-bin \
|
||||||
qemu-utils && \
|
qemu-utils && \
|
||||||
apt-get clean && \
|
apt-get clean && \
|
||||||
|
@ -33,10 +33,10 @@ func BootloaderByName(name string) (BootloaderProvider, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type BootloaderProvider interface {
|
type BootloaderProvider interface {
|
||||||
New(c Config) (Bootloader, error)
|
New(c Config, r OSRelease) (Bootloader, error)
|
||||||
Name() string
|
Name() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Bootloader interface {
|
type Bootloader interface {
|
||||||
Setup(ctx context.Context, raw, path, cmdline string) error
|
Setup(ctx context.Context, dev, root, cmdline string) error
|
||||||
}
|
}
|
||||||
|
10
builder.go
10
builder.go
@ -127,7 +127,7 @@ func NewBuilder(ctx context.Context, workdir, imgTag, disk string, size uint64,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
bl, err := blp.New(config)
|
bl, err := blp.New(config, osRelease)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -200,7 +200,7 @@ func (b *builder) Build(ctx context.Context) (err error) {
|
|||||||
if err = b.setupRootFS(ctx); err != nil {
|
if err = b.setupRootFS(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err = b.installKernel(ctx); err != nil {
|
if err = b.installBootloader(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err = b.unmountImg(ctx); err != nil {
|
if err = b.unmountImg(ctx); err != nil {
|
||||||
@ -452,9 +452,9 @@ func (b *builder) cmdline(_ context.Context) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *builder) installKernel(ctx context.Context) error {
|
func (b *builder) installBootloader(ctx context.Context) error {
|
||||||
logrus.Infof("installing linux kernel")
|
logrus.Infof("installing bootloader")
|
||||||
return b.bootloader.Setup(ctx, b.diskRaw, b.chPath("/boot"), b.cmdline(ctx))
|
return b.bootloader.Setup(ctx, b.loDevice, b.mntPoint, b.cmdline(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *builder) convert2Img(ctx context.Context) error {
|
func (b *builder) convert2Img(ctx context.Context) error {
|
||||||
|
@ -110,6 +110,7 @@ var (
|
|||||||
d2vm.WithOutput(output),
|
d2vm.WithOutput(output),
|
||||||
d2vm.WithCmdLineExtra(cmdLineExtra),
|
d2vm.WithCmdLineExtra(cmdLineExtra),
|
||||||
d2vm.WithNetworkManager(d2vm.NetworkManager(networkManager)),
|
d2vm.WithNetworkManager(d2vm.NetworkManager(networkManager)),
|
||||||
|
d2vm.WithBootLoader(bootloader),
|
||||||
d2vm.WithRaw(raw),
|
d2vm.WithRaw(raw),
|
||||||
d2vm.WithSplitBoot(splitBoot),
|
d2vm.WithSplitBoot(splitBoot),
|
||||||
d2vm.WithBootSize(bootSize),
|
d2vm.WithBootSize(bootSize),
|
||||||
|
@ -98,6 +98,7 @@ var (
|
|||||||
d2vm.WithOutput(output),
|
d2vm.WithOutput(output),
|
||||||
d2vm.WithCmdLineExtra(cmdLineExtra),
|
d2vm.WithCmdLineExtra(cmdLineExtra),
|
||||||
d2vm.WithNetworkManager(d2vm.NetworkManager(networkManager)),
|
d2vm.WithNetworkManager(d2vm.NetworkManager(networkManager)),
|
||||||
|
d2vm.WithBootLoader(bootloader),
|
||||||
d2vm.WithRaw(raw),
|
d2vm.WithRaw(raw),
|
||||||
d2vm.WithSplitBoot(splitBoot),
|
d2vm.WithSplitBoot(splitBoot),
|
||||||
d2vm.WithBootSize(bootSize),
|
d2vm.WithBootSize(bootSize),
|
||||||
|
@ -33,6 +33,7 @@ var (
|
|||||||
containerDiskTag = ""
|
containerDiskTag = ""
|
||||||
push bool
|
push bool
|
||||||
networkManager string
|
networkManager string
|
||||||
|
bootloader string
|
||||||
splitBoot bool
|
splitBoot bool
|
||||||
bootSize uint64
|
bootSize uint64
|
||||||
luksPassword string
|
luksPassword string
|
||||||
@ -53,6 +54,7 @@ func buildFlags() *pflag.FlagSet {
|
|||||||
flags.BoolVar(&push, "push", false, "Push the container disk image to the registry")
|
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.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")
|
flags.Uint64Var(&bootSize, "boot-size", 100, "Size of the boot partition in MB")
|
||||||
|
flags.StringVar(&bootloader, "bootloader", "syslinux", "Bootloader to use: syslinux, grub")
|
||||||
flags.StringVar(&luksPassword, "luks-password", "", "Password to use for the LUKS encrypted root partition. If not set, the root partition will not be encrypted")
|
flags.StringVar(&luksPassword, "luks-password", "", "Password to use for the LUKS encrypted root partition. If not set, the root partition will not be encrypted")
|
||||||
flags.BoolVar(&keepCache, "keep-cache", false, "Keep the images after the build")
|
flags.BoolVar(&keepCache, "keep-cache", false, "Keep the images after the build")
|
||||||
return flags
|
return flags
|
||||||
|
@ -38,7 +38,7 @@ func testConfig(t *testing.T, ctx context.Context, img string, config Config) {
|
|||||||
r, err := FetchDockerImageOSRelease(ctx, img, tmpPath)
|
r, err := FetchDockerImageOSRelease(ctx, img, tmpPath)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer docker.Remove(ctx, img)
|
defer docker.Remove(ctx, img)
|
||||||
d, err := NewDockerfile(r, img, "root", "", false)
|
d, err := NewDockerfile(r, img, "root", "", false, false)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
logrus.Infof("docker image based on %s", d.Release.Name)
|
logrus.Infof("docker image based on %s", d.Release.Name)
|
||||||
p := filepath.Join(tmpPath, docker.FormatImgName(img))
|
p := filepath.Join(tmpPath, docker.FormatImgName(img))
|
||||||
|
@ -51,7 +51,7 @@ func Convert(ctx context.Context, img string, opts ...ConvertOption) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !o.raw {
|
if !o.raw {
|
||||||
d, err := NewDockerfile(r, img, o.password, o.networkManager, o.luksPassword != "")
|
d, err := NewDockerfile(r, img, o.password, o.networkManager, o.luksPassword != "", o.bootLoader == "grub")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -88,7 +88,7 @@ func Convert(ctx context.Context, img string, opts ...ConvertOption) error {
|
|||||||
if format == "" {
|
if format == "" {
|
||||||
format = "raw"
|
format = "raw"
|
||||||
}
|
}
|
||||||
b, err := NewBuilder(ctx, tmpPath, imgUUID, "", o.size, r, format, o.cmdLineExtra, o.splitBoot, o.bootSize, o.luksPassword, "")
|
b, err := NewBuilder(ctx, tmpPath, imgUUID, "", o.size, r, format, o.cmdLineExtra, o.splitBoot, o.bootSize, o.luksPassword, o.bootLoader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ type convertOptions struct {
|
|||||||
output string
|
output string
|
||||||
cmdLineExtra string
|
cmdLineExtra string
|
||||||
networkManager NetworkManager
|
networkManager NetworkManager
|
||||||
|
bootLoader string
|
||||||
raw bool
|
raw bool
|
||||||
|
|
||||||
splitBoot bool
|
splitBoot bool
|
||||||
@ -62,6 +63,12 @@ func WithNetworkManager(networkManager NetworkManager) ConvertOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithBootLoader(bootLoader string) ConvertOption {
|
||||||
|
return func(o *convertOptions) {
|
||||||
|
o.bootLoader = bootLoader
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func WithRaw(raw bool) ConvertOption {
|
func WithRaw(raw bool) ConvertOption {
|
||||||
return func(o *convertOptions) {
|
return func(o *convertOptions) {
|
||||||
o.raw = raw
|
o.raw = raw
|
||||||
|
@ -65,6 +65,7 @@ type Dockerfile struct {
|
|||||||
Release OSRelease
|
Release OSRelease
|
||||||
NetworkManager NetworkManager
|
NetworkManager NetworkManager
|
||||||
Luks bool
|
Luks bool
|
||||||
|
Grub bool
|
||||||
tmpl *template.Template
|
tmpl *template.Template
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,8 +73,8 @@ func (d Dockerfile) Render(w io.Writer) error {
|
|||||||
return d.tmpl.Execute(w, d)
|
return d.tmpl.Execute(w, d)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDockerfile(release OSRelease, img, password string, networkManager NetworkManager, luks bool) (Dockerfile, error) {
|
func NewDockerfile(release OSRelease, img, password string, networkManager NetworkManager, luks, grub bool) (Dockerfile, error) {
|
||||||
d := Dockerfile{Release: release, Image: img, Password: password, NetworkManager: networkManager, Luks: luks}
|
d := Dockerfile{Release: release, Image: img, Password: password, NetworkManager: networkManager, Luks: luks, Grub: grub}
|
||||||
var net NetworkManager
|
var net NetworkManager
|
||||||
switch release.ID {
|
switch release.ID {
|
||||||
case ReleaseDebian:
|
case ReleaseDebian:
|
||||||
|
@ -66,6 +66,14 @@ func TestConvert(t *testing.T) {
|
|||||||
name: "luks",
|
name: "luks",
|
||||||
args: []string{"--luks-password=root"},
|
args: []string{"--luks-password=root"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "grub",
|
||||||
|
args: []string{"--bootloader=grub"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "grub-luks",
|
||||||
|
args: []string{"--bootloader=grub", "--luks-password=root"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
105
grub.go
Normal file
105
grub.go
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
// Copyright 2023 Linka Cloud All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package d2vm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
exec2 "os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"go.linka.cloud/d2vm/pkg/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
const grubCfg = `GRUB_DEFAULT=0
|
||||||
|
GRUB_HIDDEN_TIMEOUT=0
|
||||||
|
GRUB_HIDDEN_TIMEOUT_QUIET=true
|
||||||
|
GRUB_TIMEOUT=0
|
||||||
|
GRUB_CMDLINE_LINUX_DEFAULT="%s"
|
||||||
|
GRUB_CMDLINE_LINUX=""
|
||||||
|
GRUB_TERMINAL=console
|
||||||
|
`
|
||||||
|
|
||||||
|
type grub struct {
|
||||||
|
name string
|
||||||
|
c Config
|
||||||
|
r OSRelease
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g grub) Setup(ctx context.Context, dev, root string, cmdline string) error {
|
||||||
|
logrus.Infof("setting up grub bootloader")
|
||||||
|
if err := os.WriteFile(filepath.Join(root, "etc", "default", "grub"), []byte(fmt.Sprintf(grubCfg, cmdline)), perm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(filepath.Join(root, "boot", g.name), os.ModePerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
mounts := []string{"dev", "proc", "sys"}
|
||||||
|
var unmounts []string
|
||||||
|
defer func() {
|
||||||
|
for _, v := range unmounts {
|
||||||
|
if err := exec.Run(ctx, "umount", filepath.Join(root, v)); err != nil {
|
||||||
|
logrus.Errorf("failed to unmount /%s: %s", v, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
for _, v := range mounts {
|
||||||
|
if err := exec.Run(ctx, "mount", "-o", "bind", "/"+v, filepath.Join(root, v)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
unmounts = append(unmounts, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := exec.Run(ctx, "chroot", root, g.name+"-install", "--target=i386-pc", "--boot-directory", "/boot", dev); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := exec.Run(ctx, "chroot", root, g.name+"-mkconfig", "-o", "/boot/"+g.name+"/grub.cfg"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type grubBootloaderProvider struct {
|
||||||
|
config Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g grubBootloaderProvider) New(c Config, r OSRelease) (Bootloader, error) {
|
||||||
|
name := "grub"
|
||||||
|
if r.ID == "centos" {
|
||||||
|
name = "grub2"
|
||||||
|
}
|
||||||
|
if _, err := exec2.LookPath("grub-install"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, err := exec2.LookPath("grub-mkconfig"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return grub{
|
||||||
|
name: name,
|
||||||
|
c: c,
|
||||||
|
r: r,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g grubBootloaderProvider) Name() string {
|
||||||
|
return "grub"
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
RegisterBootloaderProvider(grubBootloaderProvider{})
|
||||||
|
}
|
11
syslinux.go
11
syslinux.go
@ -50,15 +50,16 @@ type syslinux struct {
|
|||||||
mbrBin string
|
mbrBin string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s syslinux) Setup(ctx context.Context, raw, path string, cmdline string) error {
|
func (s syslinux) Setup(ctx context.Context, dev, root string, cmdline string) error {
|
||||||
if err := exec.Run(ctx, "extlinux", "--install", path); err != nil {
|
logrus.Infof("setting up syslinux bootloader")
|
||||||
|
if err := exec.Run(ctx, "extlinux", "--install", filepath.Join(root, "boot")); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := os.WriteFile(filepath.Join(path, "syslinux.cfg"), []byte(fmt.Sprintf(syslinuxCfg, s.c.Kernel, cmdline)), perm); err != nil {
|
if err := os.WriteFile(filepath.Join(root, "boot", "syslinux.cfg"), []byte(fmt.Sprintf(syslinuxCfg, s.c.Kernel, cmdline)), perm); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logrus.Infof("writing MBR")
|
logrus.Infof("writing MBR")
|
||||||
if err := exec.Run(ctx, "dd", fmt.Sprintf("if=%s", s.mbrBin), fmt.Sprintf("of=%s", raw), "bs=440", "count=1", "conv=notrunc"); err != nil {
|
if err := exec.Run(ctx, "dd", fmt.Sprintf("if=%s", s.mbrBin), fmt.Sprintf("of=%s", dev), "bs=440", "count=1", "conv=notrunc"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -66,7 +67,7 @@ func (s syslinux) Setup(ctx context.Context, raw, path string, cmdline string) e
|
|||||||
|
|
||||||
type syslinuxProvider struct{}
|
type syslinuxProvider struct{}
|
||||||
|
|
||||||
func (s syslinuxProvider) New(c Config) (Bootloader, error) {
|
func (s syslinuxProvider) New(c Config, _ OSRelease) (Bootloader, error) {
|
||||||
mbrBin := ""
|
mbrBin := ""
|
||||||
for _, v := range mbrPaths {
|
for _, v := range mbrPaths {
|
||||||
if _, err := os.Stat(v); err == nil {
|
if _, err := os.Stat(v); err == nil {
|
||||||
|
@ -36,3 +36,7 @@ RUN apk add --no-cache cryptsetup && \
|
|||||||
echo "features=\"${features} cryptsetup\"" > /etc/mkinitfs/mkinitfs.conf && \
|
echo "features=\"${features} cryptsetup\"" > /etc/mkinitfs/mkinitfs.conf && \
|
||||||
mkinitfs $(ls /lib/modules)
|
mkinitfs $(ls /lib/modules)
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
|
{{- if .Grub }} \
|
||||||
|
RUN apk add --no-cache grub grub-bios
|
||||||
|
{{- end }}
|
||||||
|
@ -11,6 +11,9 @@ RUN yum install -y \
|
|||||||
kernel \
|
kernel \
|
||||||
systemd \
|
systemd \
|
||||||
NetworkManager \
|
NetworkManager \
|
||||||
|
{{- if .Grub }}
|
||||||
|
grub2 \
|
||||||
|
{{- end }}
|
||||||
e2fsprogs \
|
e2fsprogs \
|
||||||
sudo && \
|
sudo && \
|
||||||
systemctl enable NetworkManager && \
|
systemctl enable NetworkManager && \
|
||||||
|
@ -9,6 +9,9 @@ RUN apt-get -y update && \
|
|||||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||||
systemd-sysv \
|
systemd-sysv \
|
||||||
systemd \
|
systemd \
|
||||||
|
{{- if .Grub }}
|
||||||
|
grub2 \
|
||||||
|
{{- end }}
|
||||||
dbus \
|
dbus \
|
||||||
iproute2 \
|
iproute2 \
|
||||||
isc-dhcp-client \
|
isc-dhcp-client \
|
||||||
|
@ -8,6 +8,9 @@ RUN apt-get update -y && \
|
|||||||
initramfs-tools \
|
initramfs-tools \
|
||||||
systemd-sysv \
|
systemd-sysv \
|
||||||
systemd \
|
systemd \
|
||||||
|
{{- if .Grub }}
|
||||||
|
grub2 \
|
||||||
|
{{- end }}
|
||||||
dbus \
|
dbus \
|
||||||
isc-dhcp-client \
|
isc-dhcp-client \
|
||||||
iproute2 \
|
iproute2 \
|
||||||
|
Loading…
Reference in New Issue
Block a user