Skip to content

Commit

Permalink
actions: Add mmdebstrap action
Browse files Browse the repository at this point in the history
Add mmdebstrap [1] as a faster and more flexible alternative to debootstrap.

[1] https://gitlab.mister-muffin.de/josch/mmdebstrap

Signed-off-by: Nicolai Buchwitz <[email protected]>
  • Loading branch information
nbuchwitz committed Aug 25, 2024
1 parent 25468f1 commit a08740d
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 1 deletion.
1 change: 1 addition & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ jobs:
- { name: "partitioning", case: "partitioning" }
- { name: "msdos partitioning", case: "msdos" }
- { name: "debian (amd64)", case: "debian", variables: "-t architecture:amd64" }
- { name: "debian with mmdebstrap (amd64)", case: "debian", variables: "-t architecture:amd64 -t tool:mmdebstrap" }
exclude:
- backend: nofakemachine
test: { name: "partitioning", case: "partitioning" }
Expand Down
194 changes: 194 additions & 0 deletions actions/mmdebstrap_action.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/*
mmdebstrap Action
Construct the target rootfs with mmdebstrap tool.
Please keep in mind -- file `/etc/resolv.conf` will be removed after execution.
Most of the OS scripts used by `mmdebstrap` copy `resolv.conf` from the host,
and this may lead to incorrect configuration when becoming part of the created rootfs.
# Yaml syntax:
- action: mmdebstrap
mirrors: <list of URLs>
suite: "name"
components: <list of components>
variant: "name"
keyring-packages:
keyring-files:
Mandatory properties:
- suite -- release code name or symbolic name (e.g. "stable")
Optional properties:
- mirrors -- list of URLs with Debian-compatible repository
If no mirror is specified debos will use http://deb.debian.org/debian as default.
- variant -- name of the bootstrap script variant to use
- components -- list of components to use for packages selection.
If no components are specified debos will use main as default.
Example:
components: [ main, contrib ]
- keyring-packages -- list of keyrings for package validation.
- keyring-files -- list keyring files for repository validation.
- merged-usr -- use merged '/usr' filesystem, true by default.
*/
package actions

import (
"fmt"
"os"
"path"
"strings"
"runtime"

"github.com/go-debos/debos"
"github.com/go-debos/fakemachine"
)

type MmdebstrapAction struct {
debos.BaseAction `yaml:",inline"`
Suite string
Mirrors []string
Variant string
KeyringPackages []string `yaml:"keyring-packages"`
KeyringFiles []string `yaml:"keyring-files"`
Components []string
MergedUsr *bool `yaml:"merged-usr"`
}

func NewMmdebstrapAction() *MmdebstrapAction {
d := MmdebstrapAction{}
// Use filesystem with merged '/usr' by default
d.MergedUsr = nil
// Be secure by default
// Use main as default component
//d.Components = []string{"main"}

return &d
}

func (d *MmdebstrapAction) listOptionFiles(context *debos.DebosContext) []string {
files := []string{}

if d.KeyringFiles != nil {
for _, file := range d.KeyringFiles {
file = debos.CleanPathAt(file, context.RecipeDir)
files = append(files, file)
}
}

return files
}

func (d *MmdebstrapAction) Verify(context *debos.DebosContext) error {
if len(d.Suite) == 0 {
return fmt.Errorf("suite property not specified")
}

files := d.listOptionFiles(context)

// Check if all needed files exists
for _, f := range files {
if _, err := os.Stat(f); os.IsNotExist(err) {
return err
}
}
return nil
}

func (d *MmdebstrapAction) PreMachine(context *debos.DebosContext, m *fakemachine.Machine, args *[]string) error {

mounts := d.listOptionFiles(context)

// Mount configuration files outside of recipes directory
for _, mount := range mounts {
m.AddVolume(path.Dir(mount))
}

return nil
}

func (d *MmdebstrapAction) Run(context *debos.DebosContext) error {
cmdline := []string{"mmdebstrap"}

if d.MergedUsr != nil {
if *d.MergedUsr {
cmdline = append(cmdline, "--hook-dir=/usr/share/mmdebstrap/hooks/merged-usr")
} else {
cmdline = append(cmdline, "--hook-dir=/usr/share/mmdebstrap/hooks/no-merged-usr")
}
}

if d.KeyringFiles != nil {
s := strings.Join(d.KeyringFiles, ",")
cmdline = append(cmdline, fmt.Sprintf("--keyring=%s", s))
}

if d.KeyringPackages != nil {
s := strings.Join(d.KeyringPackages, ",")
cmdline = append(cmdline, fmt.Sprintf("--include=%s", s))
}

if d.Components != nil {
s := strings.Join(d.Components, ",")
cmdline = append(cmdline, fmt.Sprintf("--components=%s", s))
}

/* Only works for amd64, arm64 and riscv64 hosts, which should be enough */
foreign := context.Architecture != runtime.GOARCH

if foreign {
cmdline = append(cmdline, fmt.Sprintf("--architectures=%s", context.Architecture))

}

if d.Variant != "" {
cmdline = append(cmdline, fmt.Sprintf("--variant=%s", d.Variant))
}

cmdline = append(cmdline, d.Suite)
cmdline = append(cmdline, context.Rootdir)

if d.Mirrors != nil {
cmdline = append(cmdline, d.Mirrors...)
}

/* Make sure files in /etc/apt/ exist inside the fakemachine otherwise
mmdebstrap prints a warning about the path not existing. */
if fakemachine.InMachine() {
if err := os.MkdirAll(path.Join("/etc/apt/apt.conf.d"), os.ModePerm); err != nil {
return err
}
if err := os.MkdirAll(path.Join("/etc/apt/trusted.gpg.d"), os.ModePerm); err != nil {
return err
}
}

err := debos.Command{}.Run("Mmdebstrap", cmdline...)

if err != nil {
log := path.Join(context.Rootdir, "mmdebstrap/mmdebstrap.log")
_ = debos.Command{}.Run("mmdebstrap.log", "cat", log)
return err
}

/* Cleanup resolv.conf after mmdebstrap */
resolvconf := path.Join(context.Rootdir, "/etc/resolv.conf")
if _, err = os.Stat(resolvconf); !os.IsNotExist(err) {
if err = os.Remove(resolvconf); err != nil {
return err
}
}

c := debos.NewChrootCommandForContext(*context)

return c.Run("apt clean", "/usr/bin/apt-get", "clean")
}
4 changes: 4 additions & 0 deletions actions/recipe.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ Supported actions
- debootstrap -- https://godoc.org/github.com/go-debos/debos/actions#hdr-Debootstrap_Action
- debootstrap -- https://godoc.org/github.com/go-debos/debos/actions#hdr-Mmdebstrap_Action
- download -- https://godoc.org/github.com/go-debos/debos/actions#hdr-Download_Action
- filesystem-deploy -- https://godoc.org/github.com/go-debos/debos/actions#hdr-FilesystemDeploy_Action
Expand Down Expand Up @@ -115,6 +117,8 @@ func (y *YamlAction) UnmarshalYAML(unmarshal func(interface{}) error) error {
switch aux.Action {
case "debootstrap":
y.Action = NewDebootstrapAction()
case "mmdebstrap":
y.Action = NewMmdebstrapAction()
case "pacstrap":
y.Action = &PacstrapAction{}
case "pack":
Expand Down
1 change: 1 addition & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ RUN apt-get update && \
ca-certificates \
debian-ports-archive-keyring \
debootstrap \
mmdebstrap \
dosfstools \
e2fsprogs \
equivs \
Expand Down
3 changes: 2 additions & 1 deletion tests/debian/test.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
---
{{- $architecture := or .architecture "amd64"}}
{{- $tool := or .tool "debootstrap" }}
architecture: {{$architecture}}

actions:
- action: debootstrap
- action: {{ $tool }}
suite: bullseye
variant: minbase
merged-usr: true
Expand Down

0 comments on commit a08740d

Please sign in to comment.