147 lines
3.3 KiB
Go
147 lines
3.3 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
yaml "gopkg.in/yaml.v2"
|
|
|
|
"novit.tech/direktil/pkg/config/apply"
|
|
)
|
|
|
|
var loopOffset = 0
|
|
|
|
func applyConfig(cfgPath string, bootMounted bool) (cfg *configV1) {
|
|
cfgBytes, err := os.ReadFile(cfgPath)
|
|
if err != nil {
|
|
fatalf("failed to read %s: %v", cfgPath, err)
|
|
}
|
|
|
|
cfg = &configV1{}
|
|
if err := yaml.Unmarshal(cfgBytes, cfg); err != nil {
|
|
fatal("failed to load config: ", err)
|
|
}
|
|
|
|
// mount layers
|
|
if len(cfg.Layers) == 0 {
|
|
fatal("no layers configured!")
|
|
}
|
|
|
|
layersInMemory := paramBool("layers-in-mem", false)
|
|
|
|
log.Info().Strs("layers", cfg.Layers).Bool("in-memory", layersInMemory).Msg("mounting layers")
|
|
|
|
const layersInMemDir = "/layers-in-mem"
|
|
if layersInMemory {
|
|
mkdir(layersInMemDir, 0700)
|
|
mount("layers-mem", layersInMemDir, "tmpfs", 0, "")
|
|
}
|
|
|
|
lowers := make([]string, len(cfg.Layers))
|
|
for i, layer := range cfg.Layers {
|
|
log := log.With().Str("layer", layer).Logger()
|
|
|
|
path := layerPath(layer)
|
|
|
|
info, err := os.Stat(path)
|
|
if err != nil {
|
|
fatal(err)
|
|
}
|
|
|
|
log.Info().Int64("size", info.Size()).Msg("layer found")
|
|
|
|
if layersInMemory {
|
|
log.Info().Msg("copying to memory")
|
|
targetPath := filepath.Join(layersInMemDir, layer)
|
|
cp(path, targetPath)
|
|
path = targetPath
|
|
}
|
|
|
|
dir := "/layers/" + layer
|
|
|
|
lowers[i] = dir
|
|
|
|
mountSquahfs(path, dir)
|
|
}
|
|
|
|
// prepare system root
|
|
mount("mem", "/changes", "tmpfs", 0, "")
|
|
|
|
mkdir("/changes/workdir", 0755)
|
|
mkdir("/changes/upperdir", 0755)
|
|
|
|
mount("overlay", "/system", "overlay", rootMountFlags,
|
|
"lowerdir="+strings.Join(lowers, ":")+",upperdir=/changes/upperdir,workdir=/changes/workdir")
|
|
|
|
// make root rshared (default in systemd, required by Kubernetes 1.10+)
|
|
// equivalent to "mount --make-rshared /"
|
|
// see kernel's Documentation/sharedsubtree.txt (search rshared)
|
|
if err := syscall.Mount("", "/system", "", syscall.MS_SHARED|syscall.MS_REC, ""); err != nil {
|
|
fatalf("FATAL: mount --make-rshared / failed: %v", err)
|
|
}
|
|
|
|
if bootMounted {
|
|
if layersInMemory {
|
|
if err := syscall.Unmount("/boot", 0); err != nil {
|
|
log.Warn().Err(err).Msg("failed to unmount /boot")
|
|
time.Sleep(2 * time.Second)
|
|
}
|
|
|
|
} else {
|
|
mount("/boot", "/system/boot", "", syscall.MS_BIND, "")
|
|
}
|
|
}
|
|
|
|
// - write configuration
|
|
log.Info().Msg("writing /config.yaml")
|
|
if err := os.WriteFile("/system/config.yaml", cfgBytes, 0600); err != nil {
|
|
fatal("failed: ", err)
|
|
}
|
|
|
|
// - write files
|
|
apply.Files(cfg.Files, "/system")
|
|
|
|
// - groups
|
|
for _, group := range cfg.Groups {
|
|
logEvt := log.Info().Str("group", group.Name)
|
|
|
|
opts := make([]string, 0)
|
|
opts = append(opts /* chroot */, "/system", "groupadd", "-r")
|
|
if group.Gid != 0 {
|
|
opts = append(opts, "-g", strconv.Itoa(group.Gid))
|
|
logEvt.Int("gid", group.Gid)
|
|
}
|
|
opts = append(opts, group.Name)
|
|
|
|
logEvt.Msg("creating group")
|
|
run("chroot", opts...)
|
|
}
|
|
|
|
// - user
|
|
for _, user := range cfg.Users {
|
|
logEvt := log.Info().Str("user", user.Name)
|
|
|
|
opts := make([]string, 0)
|
|
opts = append(opts /* chroot */, "/system", "useradd", "-r")
|
|
if user.Gid != 0 {
|
|
opts = append(opts, "-g", strconv.Itoa(user.Gid))
|
|
logEvt.Int("gid", user.Gid)
|
|
}
|
|
if user.Uid != 0 {
|
|
opts = append(opts, "-u", strconv.Itoa(user.Uid))
|
|
logEvt.Int("uid", user.Uid)
|
|
}
|
|
opts = append(opts, user.Name)
|
|
|
|
logEvt.Msg("creating user")
|
|
run("chroot", opts...)
|
|
}
|
|
|
|
return
|
|
}
|