mirror of
https://github.com/linka-cloud/d2vm.git
synced 2024-11-22 07:46:25 +00:00
chore: bootloader abtraction
Signed-off-by: Adphi <philippe.adrien.nousse@gmail.com>
This commit is contained in:
parent
ec33a7ad74
commit
a003e176f5
2
.gitignore
vendored
2
.gitignore
vendored
@ -11,6 +11,8 @@ dist/
|
|||||||
images
|
images
|
||||||
/d2vm
|
/d2vm
|
||||||
/examples/build
|
/examples/build
|
||||||
|
/examples/full/demo-magic
|
||||||
|
/examples/full/inside
|
||||||
.goreleaser.yaml
|
.goreleaser.yaml
|
||||||
docs/build
|
docs/build
|
||||||
docs-src
|
docs-src
|
||||||
|
4
Makefile
4
Makefile
@ -87,7 +87,7 @@ install: docker-build
|
|||||||
|
|
||||||
.build:
|
.build:
|
||||||
@go generate ./...
|
@go generate ./...
|
||||||
@go build -o d2vm -ldflags "-s -w -X '$(MODULE).Version=$(VERSION)' -X '$(MODULE).BuildDate=$(shell date)'" ./cmd/d2vm
|
@CGO_ENABLED=0 go build -o d2vm -ldflags "-s -w -X '$(MODULE).Version=$(VERSION)' -X '$(MODULE).BuildDate=$(shell date)'" ./cmd/d2vm
|
||||||
|
|
||||||
.PHONY: build-snapshot
|
.PHONY: build-snapshot
|
||||||
build-snapshot: bin
|
build-snapshot: bin
|
||||||
@ -116,7 +116,7 @@ completions: .build
|
|||||||
.PHONY: examples
|
.PHONY: examples
|
||||||
examples: build-dev
|
examples: build-dev
|
||||||
@mkdir -p examples/build
|
@mkdir -p examples/build
|
||||||
@for f in $$(find examples -type f -name '*Dockerfile' -maxdepth 1); do \
|
@for f in $$(find examples -maxdepth 1 -type f -name '*Dockerfile'); do \
|
||||||
echo "Building $$f"; \
|
echo "Building $$f"; \
|
||||||
./d2vm build -o examples/build/$$(basename $$f|cut -d'.' -f1).qcow2 -p root -f $$f examples --force; \
|
./d2vm build -o examples/build/$$(basename $$f|cut -d'.' -f1).qcow2 -p root -f $$f examples --force; \
|
||||||
done
|
done
|
||||||
|
42
bootloader.go
Normal file
42
bootloader.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
var bootloaderProviders = map[string]BootloaderProvider{}
|
||||||
|
|
||||||
|
func RegisterBootloaderProvider(provider BootloaderProvider) {
|
||||||
|
bootloaderProviders[provider.Name()] = provider
|
||||||
|
}
|
||||||
|
|
||||||
|
func BootloaderByName(name string) (BootloaderProvider, error) {
|
||||||
|
if p, ok := bootloaderProviders[name]; ok {
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("bootloader provider %s not found", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
type BootloaderProvider interface {
|
||||||
|
New(c Config) (Bootloader, error)
|
||||||
|
Name() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bootloader interface {
|
||||||
|
Setup(ctx context.Context, raw, path, cmdline string) error
|
||||||
|
}
|
136
builder.go
136
builder.go
@ -41,72 +41,10 @@ ff02::1 ip6-allnodes
|
|||||||
ff02::2 ip6-allrouters
|
ff02::2 ip6-allrouters
|
||||||
ff02::3 ip6-allhosts
|
ff02::3 ip6-allhosts
|
||||||
`
|
`
|
||||||
syslinuxCfgUbuntu = `DEFAULT linux
|
|
||||||
SAY Now booting the kernel from SYSLINUX...
|
|
||||||
LABEL linux
|
|
||||||
KERNEL /boot/vmlinuz
|
|
||||||
APPEND ro root=UUID=%s initrd=/boot/initrd.img net.ifnames=0 console=tty0 console=ttyS0,115200n8 %s
|
|
||||||
`
|
|
||||||
syslinuxCfgDebian = `DEFAULT linux
|
|
||||||
SAY Now booting the kernel from SYSLINUX...
|
|
||||||
LABEL linux
|
|
||||||
KERNEL /vmlinuz
|
|
||||||
APPEND ro root=UUID=%s initrd=/initrd.img net.ifnames=0 console=tty0 console=ttyS0,115200n8 %s
|
|
||||||
`
|
|
||||||
syslinuxCfgAlpine = `DEFAULT linux
|
|
||||||
SAY Now booting the kernel from SYSLINUX...
|
|
||||||
LABEL linux
|
|
||||||
KERNEL /boot/vmlinuz-virt
|
|
||||||
APPEND ro root=UUID=%s rootfstype=ext4 initrd=/boot/initramfs-virt console=ttyS0,115200 %s
|
|
||||||
`
|
|
||||||
syslinuxCfgCentOS = `DEFAULT linux
|
|
||||||
SAY Now booting the kernel from SYSLINUX...
|
|
||||||
LABEL linux
|
|
||||||
KERNEL /boot/vmlinuz
|
|
||||||
APPEND ro root=UUID=%s initrd=/boot/initrd.img net.ifnames=0 console=tty0 console=ttyS0,115200n8 %s
|
|
||||||
`
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
formats = []string{"qcow2", "qed", "raw", "vdi", "vhd", "vmdk"}
|
|
||||||
|
|
||||||
mbrPaths = []string{
|
|
||||||
// debian path
|
|
||||||
"/usr/lib/syslinux/mbr/mbr.bin",
|
|
||||||
// ubuntu path
|
|
||||||
"/usr/lib/EXTLINUX/mbr.bin",
|
|
||||||
// alpine path
|
|
||||||
"/usr/share/syslinux/mbr.bin",
|
|
||||||
// centos path
|
|
||||||
"/usr/share/syslinux/mbr.bin",
|
|
||||||
// archlinux path
|
|
||||||
"/usr/lib/syslinux/bios/mbr.bin",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
perm os.FileMode = 0644
|
perm os.FileMode = 0644
|
||||||
)
|
)
|
||||||
|
|
||||||
func sysconfig(osRelease OSRelease) (string, error) {
|
var formats = []string{"qcow2", "qed", "raw", "vdi", "vhd", "vmdk"}
|
||||||
switch osRelease.ID {
|
|
||||||
case ReleaseUbuntu:
|
|
||||||
if osRelease.VersionID < "20.04" {
|
|
||||||
return syslinuxCfgDebian, nil
|
|
||||||
}
|
|
||||||
return syslinuxCfgUbuntu, nil
|
|
||||||
case ReleaseDebian:
|
|
||||||
return syslinuxCfgDebian, nil
|
|
||||||
case ReleaseKali:
|
|
||||||
return syslinuxCfgDebian, nil
|
|
||||||
case ReleaseAlpine:
|
|
||||||
return syslinuxCfgAlpine, nil
|
|
||||||
case ReleaseCentOS:
|
|
||||||
return syslinuxCfgCentOS, nil
|
|
||||||
default:
|
|
||||||
return "", fmt.Errorf("%s: distribution not supported", osRelease.ID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Builder interface {
|
type Builder interface {
|
||||||
Build(ctx context.Context) (err error)
|
Build(ctx context.Context) (err error)
|
||||||
@ -114,7 +52,9 @@ type Builder interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type builder struct {
|
type builder struct {
|
||||||
osRelease OSRelease
|
osRelease OSRelease
|
||||||
|
config Config
|
||||||
|
bootloader Bootloader
|
||||||
|
|
||||||
src string
|
src string
|
||||||
img *image
|
img *image
|
||||||
@ -128,8 +68,6 @@ type builder struct {
|
|||||||
splitBoot bool
|
splitBoot bool
|
||||||
bootSize uint64
|
bootSize uint64
|
||||||
|
|
||||||
mbrPath string
|
|
||||||
|
|
||||||
loDevice string
|
loDevice string
|
||||||
bootPart string
|
bootPart string
|
||||||
rootPart string
|
rootPart string
|
||||||
@ -145,7 +83,7 @@ type builder struct {
|
|||||||
cmdLineExtra string
|
cmdLineExtra string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBuilder(ctx context.Context, workdir, imgTag, disk string, size uint64, osRelease OSRelease, format string, cmdLineExtra string, splitBoot bool, bootSize uint64, luksPassword string) (Builder, error) {
|
func NewBuilder(ctx context.Context, workdir, imgTag, disk string, size uint64, osRelease OSRelease, format string, cmdLineExtra string, splitBoot bool, bootSize uint64, luksPassword string, bootLoader string) (Builder, error) {
|
||||||
if err := checkDependencies(); err != nil {
|
if err := checkDependencies(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -176,16 +114,24 @@ func NewBuilder(ctx context.Context, workdir, imgTag, disk string, size uint64,
|
|||||||
return nil, fmt.Errorf("boot partition size must be less than the disk size")
|
return nil, fmt.Errorf("boot partition size must be less than the disk size")
|
||||||
}
|
}
|
||||||
|
|
||||||
mbrBin := ""
|
if bootLoader == "" {
|
||||||
for _, v := range mbrPaths {
|
bootLoader = "syslinux"
|
||||||
if _, err := os.Stat(v); err == nil {
|
|
||||||
mbrBin = v
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if mbrBin == "" {
|
|
||||||
return nil, fmt.Errorf("unable to find syslinux's mbr.bin path")
|
config, err := osRelease.Config()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
blp, err := BootloaderByName(bootLoader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
bl, err := blp.New(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
size = 10 * uint64(datasize.GB)
|
size = 10 * uint64(datasize.GB)
|
||||||
}
|
}
|
||||||
@ -207,12 +153,13 @@ func NewBuilder(ctx context.Context, workdir, imgTag, disk string, size uint64,
|
|||||||
// }
|
// }
|
||||||
b := &builder{
|
b := &builder{
|
||||||
osRelease: osRelease,
|
osRelease: osRelease,
|
||||||
|
config: config,
|
||||||
|
bootloader: bl,
|
||||||
img: img,
|
img: img,
|
||||||
diskRaw: filepath.Join(workdir, disk+".d2vm.raw"),
|
diskRaw: filepath.Join(workdir, disk+".d2vm.raw"),
|
||||||
diskOut: filepath.Join(workdir, disk+"."+format),
|
diskOut: filepath.Join(workdir, disk+"."+format),
|
||||||
format: f,
|
format: f,
|
||||||
size: size,
|
size: size,
|
||||||
mbrPath: mbrBin,
|
|
||||||
mntPoint: filepath.Join(workdir, "/mnt"),
|
mntPoint: filepath.Join(workdir, "/mnt"),
|
||||||
cmdLineExtra: cmdLineExtra,
|
cmdLineExtra: cmdLineExtra,
|
||||||
splitBoot: splitBoot,
|
splitBoot: splitBoot,
|
||||||
@ -259,9 +206,6 @@ func (b *builder) Build(ctx context.Context) (err error) {
|
|||||||
if err = b.unmountImg(ctx); err != nil {
|
if err = b.unmountImg(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err = b.setupMBR(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err = b.convert2Img(ctx); err != nil {
|
if err = b.convert2Img(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -490,45 +434,27 @@ func (b *builder) setupRootFS(ctx context.Context) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *builder) installKernel(ctx context.Context) error {
|
func (b *builder) cmdline(_ context.Context) string {
|
||||||
logrus.Infof("installing linux kernel")
|
|
||||||
if err := exec.Run(ctx, "extlinux", "--install", b.chPath("/boot")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
sysconfig, err := sysconfig(b.osRelease)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var cfg string
|
|
||||||
if b.isLuksEnabled() {
|
if b.isLuksEnabled() {
|
||||||
switch b.osRelease.ID {
|
switch b.osRelease.ID {
|
||||||
case ReleaseAlpine:
|
case ReleaseAlpine:
|
||||||
cfg = fmt.Sprintf(sysconfig, b.rootUUID, fmt.Sprintf("%s root=/dev/mapper/root cryptdm=root", b.cmdLineExtra))
|
return b.config.Cmdline(RootUUID(b.rootUUID), "root=/dev/mapper/root", "cryptdm=root", "cryptroot=UUID="+b.cryptUUID, b.cmdLineExtra)
|
||||||
cfg = strings.Replace(cfg, "root=UUID="+b.rootUUID, "cryptroot=UUID="+b.cryptUUID, 1)
|
|
||||||
case ReleaseCentOS:
|
case ReleaseCentOS:
|
||||||
cfg = fmt.Sprintf(sysconfig, b.rootUUID, fmt.Sprintf("%s rd.luks.name=UUID=%s rd.luks.uuid=%s rd.luks.crypttab=0", b.cmdLineExtra, b.rootUUID, b.cryptUUID))
|
return b.config.Cmdline(RootUUID(b.rootUUID), "rd.luks.name=UUID="+b.rootUUID+" rd.luks.uuid="+b.cryptUUID+" rd.luks.crypttab=0", b.cmdLineExtra)
|
||||||
default:
|
default:
|
||||||
// for some versions of debian, the cryptopts parameter MUST contain all the following: target,source,key,opts...
|
// for some versions of debian, the cryptopts parameter MUST contain all the following: target,source,key,opts...
|
||||||
// see https://salsa.debian.org/cryptsetup-team/cryptsetup/-/blob/debian/buster/debian/functions
|
// see https://salsa.debian.org/cryptsetup-team/cryptsetup/-/blob/debian/buster/debian/functions
|
||||||
// and https://cryptsetup-team.pages.debian.net/cryptsetup/README.initramfs.html
|
// and https://cryptsetup-team.pages.debian.net/cryptsetup/README.initramfs.html
|
||||||
cfg = fmt.Sprintf(sysconfig, b.rootUUID, fmt.Sprintf("%s root=/dev/mapper/root cryptopts=target=root,source=UUID=%s,key=none,luks", b.cmdLineExtra, b.cryptUUID))
|
return b.config.Cmdline(nil, "root=/dev/mapper/root", "cryptopts=target=root,source=UUID="+b.cryptUUID+",key=none,luks", b.cmdLineExtra)
|
||||||
cfg = strings.Replace(cfg, "root=UUID="+b.rootUUID, "", 1)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cfg = fmt.Sprintf(sysconfig, b.rootUUID, b.cmdLineExtra)
|
return b.config.Cmdline(RootUUID(b.rootUUID), b.cmdLineExtra)
|
||||||
}
|
}
|
||||||
if err := b.chWriteFile("/boot/syslinux.cfg", cfg, perm); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *builder) setupMBR(ctx context.Context) error {
|
func (b *builder) installKernel(ctx context.Context) error {
|
||||||
logrus.Infof("writing MBR")
|
logrus.Infof("installing linux kernel")
|
||||||
if err := exec.Run(ctx, "dd", fmt.Sprintf("if=%s", b.mbrPath), fmt.Sprintf("of=%s", b.diskRaw), "bs=440", "count=1", "conv=notrunc"); err != nil {
|
return b.bootloader.Setup(ctx, b.diskRaw, b.chPath("/boot"), b.cmdline(ctx))
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *builder) convert2Img(ctx context.Context) error {
|
func (b *builder) convert2Img(ctx context.Context) error {
|
||||||
|
89
config.go
Normal file
89
config.go
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
// 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 (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
configUbuntu = Config{
|
||||||
|
Kernel: "/boot/vmlinuz",
|
||||||
|
Initrd: "/boot/initrd.img",
|
||||||
|
}
|
||||||
|
configDebian = Config{
|
||||||
|
Kernel: "/vmlinuz",
|
||||||
|
Initrd: "/initrd.img",
|
||||||
|
}
|
||||||
|
configAlpine = Config{
|
||||||
|
Kernel: "/boot/vmlinuz-virt",
|
||||||
|
Initrd: "/boot/initramfs-virt",
|
||||||
|
}
|
||||||
|
configCentOS = Config{
|
||||||
|
Kernel: "/boot/vmlinuz",
|
||||||
|
Initrd: "/boot/initrd.img",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type Root interface {
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type RootUUID string
|
||||||
|
|
||||||
|
func (r RootUUID) String() string {
|
||||||
|
return "UUID=" + string(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
type RootPath string
|
||||||
|
|
||||||
|
func (r RootPath) String() string {
|
||||||
|
return string(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Kernel string
|
||||||
|
Initrd string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Config) Cmdline(root Root, args ...string) string {
|
||||||
|
var r string
|
||||||
|
if root != nil {
|
||||||
|
r = fmt.Sprintf("root=%s", root.String())
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("ro initrd=%s %s net.ifnames=0 rootfstype=ext4 console=tty0 console=ttyS0,115200n8 %s", c.Initrd, r, strings.Join(args, " "))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r OSRelease) Config() (Config, error) {
|
||||||
|
switch r.ID {
|
||||||
|
case ReleaseUbuntu:
|
||||||
|
if r.VersionID < "20.04" {
|
||||||
|
return configDebian, nil
|
||||||
|
}
|
||||||
|
return configUbuntu, nil
|
||||||
|
case ReleaseDebian:
|
||||||
|
return configDebian, nil
|
||||||
|
case ReleaseKali:
|
||||||
|
return configDebian, nil
|
||||||
|
case ReleaseAlpine:
|
||||||
|
return configAlpine, nil
|
||||||
|
case ReleaseCentOS:
|
||||||
|
return configCentOS, nil
|
||||||
|
default:
|
||||||
|
return Config{}, fmt.Errorf("%s: distribution not supported", r.ID)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -23,14 +23,13 @@ import (
|
|||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"go.linka.cloud/d2vm/pkg/docker"
|
"go.linka.cloud/d2vm/pkg/docker"
|
||||||
"go.linka.cloud/d2vm/pkg/exec"
|
"go.linka.cloud/d2vm/pkg/exec"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testSysconfig(t *testing.T, ctx context.Context, img, sysconf, kernel, initrd string) {
|
func testConfig(t *testing.T, ctx context.Context, img string, config Config) {
|
||||||
require.NoError(t, docker.Pull(ctx, img))
|
require.NoError(t, docker.Pull(ctx, img))
|
||||||
tmpPath := filepath.Join(os.TempDir(), "d2vm-tests", strings.NewReplacer(":", "-", ".", "-").Replace(img))
|
tmpPath := filepath.Join(os.TempDir(), "d2vm-tests", strings.NewReplacer(":", "-", ".", "-").Replace(img))
|
||||||
require.NoError(t, os.MkdirAll(tmpPath, 0755))
|
require.NoError(t, os.MkdirAll(tmpPath, 0755))
|
||||||
@ -39,9 +38,6 @@ func testSysconfig(t *testing.T, ctx context.Context, img, sysconf, kernel, init
|
|||||||
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)
|
||||||
sys, err := sysconfig(r)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, sysconf, sys)
|
|
||||||
d, err := NewDockerfile(r, img, "root", "", false)
|
d, err := NewDockerfile(r, img, "root", "", 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)
|
||||||
@ -55,95 +51,67 @@ func testSysconfig(t *testing.T, ctx context.Context, img, sysconf, kernel, init
|
|||||||
logrus.Infof("building kernel enabled image")
|
logrus.Infof("building kernel enabled image")
|
||||||
require.NoError(t, docker.Build(ctx, imgUUID, p, dir))
|
require.NoError(t, docker.Build(ctx, imgUUID, p, dir))
|
||||||
defer docker.Remove(ctx, imgUUID)
|
defer docker.Remove(ctx, imgUUID)
|
||||||
require.NoError(t, docker.RunAndRemove(ctx, imgUUID, "test", "-f", kernel))
|
require.NoError(t, docker.RunAndRemove(ctx, imgUUID, "test", "-f", config.Kernel))
|
||||||
require.NoError(t, docker.RunAndRemove(ctx, imgUUID, "test", "-f", initrd))
|
require.NoError(t, docker.RunAndRemove(ctx, imgUUID, "test", "-f", config.Initrd))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSyslinuxCfg(t *testing.T) {
|
func TestConfig(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
image string
|
image string
|
||||||
kernel string
|
config Config
|
||||||
initrd string
|
|
||||||
sysconfig string
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
image: "ubuntu:18.04",
|
image: "ubuntu:18.04",
|
||||||
kernel: "/vmlinuz",
|
config: configDebian,
|
||||||
initrd: "/initrd.img",
|
|
||||||
sysconfig: syslinuxCfgDebian,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
image: "ubuntu:20.04",
|
image: "ubuntu:20.04",
|
||||||
kernel: "/boot/vmlinuz",
|
config: configUbuntu,
|
||||||
initrd: "/boot/initrd.img",
|
|
||||||
sysconfig: syslinuxCfgUbuntu,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
image: "ubuntu:22.04",
|
image: "ubuntu:22.04",
|
||||||
kernel: "/boot/vmlinuz",
|
config: configUbuntu,
|
||||||
initrd: "/boot/initrd.img",
|
|
||||||
sysconfig: syslinuxCfgUbuntu,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
image: "ubuntu:latest",
|
image: "ubuntu:latest",
|
||||||
kernel: "/boot/vmlinuz",
|
config: configUbuntu,
|
||||||
initrd: "/boot/initrd.img",
|
|
||||||
sysconfig: syslinuxCfgUbuntu,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
image: "debian:9",
|
image: "debian:9",
|
||||||
kernel: "/vmlinuz",
|
config: configDebian,
|
||||||
initrd: "/initrd.img",
|
|
||||||
sysconfig: syslinuxCfgDebian,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
image: "debian:10",
|
image: "debian:10",
|
||||||
kernel: "/vmlinuz",
|
config: configDebian,
|
||||||
initrd: "/initrd.img",
|
|
||||||
sysconfig: syslinuxCfgDebian,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
image: "debian:11",
|
image: "debian:11",
|
||||||
kernel: "/vmlinuz",
|
config: configDebian,
|
||||||
initrd: "/initrd.img",
|
|
||||||
sysconfig: syslinuxCfgDebian,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
image: "debian:latest",
|
image: "debian:latest",
|
||||||
kernel: "/vmlinuz",
|
config: configDebian,
|
||||||
initrd: "/initrd.img",
|
|
||||||
sysconfig: syslinuxCfgDebian,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
image: "kalilinux/kali-rolling:latest",
|
image: "kalilinux/kali-rolling:latest",
|
||||||
kernel: "/vmlinuz",
|
config: configDebian,
|
||||||
initrd: "/initrd.img",
|
|
||||||
sysconfig: syslinuxCfgDebian,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
image: "alpine:3.16",
|
image: "alpine:3.16",
|
||||||
kernel: "/boot/vmlinuz-virt",
|
config: configAlpine,
|
||||||
initrd: "/boot/initramfs-virt",
|
|
||||||
sysconfig: syslinuxCfgAlpine,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
image: "alpine",
|
image: "alpine",
|
||||||
kernel: "/boot/vmlinuz-virt",
|
config: configAlpine,
|
||||||
initrd: "/boot/initramfs-virt",
|
|
||||||
sysconfig: syslinuxCfgAlpine,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
image: "centos:8",
|
image: "centos:8",
|
||||||
kernel: "/boot/vmlinuz",
|
config: configCentOS,
|
||||||
initrd: "/boot/initrd.img",
|
|
||||||
sysconfig: syslinuxCfgCentOS,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
image: "centos:latest",
|
image: "centos:latest",
|
||||||
kernel: "/boot/vmlinuz",
|
config: configCentOS,
|
||||||
initrd: "/boot/initrd.img",
|
|
||||||
sysconfig: syslinuxCfgCentOS,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
exec.SetDebug(true)
|
exec.SetDebug(true)
|
||||||
@ -154,7 +122,7 @@ func TestSyslinuxCfg(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
testSysconfig(t, ctx, test.image, test.sysconfig, test.kernel, test.initrd)
|
testConfig(t, ctx, test.image, test.config)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
92
syslinux.go
Normal file
92
syslinux.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
// 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"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"go.linka.cloud/d2vm/pkg/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
const syslinuxCfg = `DEFAULT linux
|
||||||
|
SAY Now booting the kernel from SYSLINUX...
|
||||||
|
LABEL linux
|
||||||
|
KERNEL %s
|
||||||
|
APPEND %s
|
||||||
|
`
|
||||||
|
|
||||||
|
var mbrPaths = []string{
|
||||||
|
// debian path
|
||||||
|
"/usr/lib/syslinux/mbr/mbr.bin",
|
||||||
|
// ubuntu path
|
||||||
|
"/usr/lib/EXTLINUX/mbr.bin",
|
||||||
|
// alpine path
|
||||||
|
"/usr/share/syslinux/mbr.bin",
|
||||||
|
// centos path
|
||||||
|
"/usr/share/syslinux/mbr.bin",
|
||||||
|
// archlinux path
|
||||||
|
"/usr/lib/syslinux/bios/mbr.bin",
|
||||||
|
}
|
||||||
|
|
||||||
|
type syslinux struct {
|
||||||
|
c Config
|
||||||
|
mbrBin string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s syslinux) Setup(ctx context.Context, raw, path string, cmdline string) error {
|
||||||
|
if err := exec.Run(ctx, "extlinux", "--install", path); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(filepath.Join(path, "syslinux.cfg"), []byte(fmt.Sprintf(syslinuxCfg, s.c.Kernel, cmdline)), perm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type syslinuxProvider struct{}
|
||||||
|
|
||||||
|
func (s syslinuxProvider) New(c Config) (Bootloader, error) {
|
||||||
|
mbrBin := ""
|
||||||
|
for _, v := range mbrPaths {
|
||||||
|
if _, err := os.Stat(v); err == nil {
|
||||||
|
mbrBin = v
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if mbrBin == "" {
|
||||||
|
return nil, fmt.Errorf("unable to find syslinux's mbr.bin path")
|
||||||
|
}
|
||||||
|
return &syslinux{
|
||||||
|
c: c,
|
||||||
|
mbrBin: mbrBin,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s syslinuxProvider) Name() string {
|
||||||
|
return "syslinux"
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
RegisterBootloaderProvider(syslinuxProvider{})
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user