2023-01-12 19:10:10 +01:00
|
|
|
package mount
|
|
|
|
|
|
|
|
import (
|
2023-02-01 18:01:58 +01:00
|
|
|
"context"
|
2023-01-12 19:10:10 +01:00
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2023-02-01 18:01:58 +01:00
|
|
|
"time"
|
2023-01-12 19:10:10 +01:00
|
|
|
|
|
|
|
"github.com/containerd/containerd/mount"
|
|
|
|
"github.com/deniswernert/go-fstab"
|
|
|
|
"github.com/kairos-io/immucore/pkg/profile"
|
2023-02-01 18:01:58 +01:00
|
|
|
"github.com/kairos-io/kairos/pkg/utils"
|
|
|
|
"github.com/spectrocloud-labs/herd"
|
2023-01-12 19:10:10 +01:00
|
|
|
)
|
|
|
|
|
2023-02-01 18:01:58 +01:00
|
|
|
type State struct {
|
2023-02-01 19:06:51 +01:00
|
|
|
Rootdir string
|
|
|
|
TargetImage string // e.g. /cOS/active.img
|
|
|
|
OverlayDir []string // e.g. /var
|
|
|
|
BindMounts []string // e.g. /etc/kubernetes
|
2023-02-01 22:33:44 +01:00
|
|
|
StateDir string // e.g. "/usr/local/.state"
|
2023-02-01 22:51:31 +01:00
|
|
|
TargetLabel string // e.g. COS_ACTIVE
|
|
|
|
|
|
|
|
MountRoot bool // e.g. if true, it tries to find the image to loopback mount
|
2023-02-01 19:06:51 +01:00
|
|
|
|
|
|
|
CustomMounts map[string]string // e.g. diskid : mountpoint
|
|
|
|
|
|
|
|
fstabs []*fstab.Mount
|
2023-02-01 18:01:58 +01:00
|
|
|
}
|
|
|
|
|
2023-02-01 19:27:01 +01:00
|
|
|
func genOpreferenceName(op, s string) string {
|
|
|
|
return fmt.Sprintf("%s-%s", op, s)
|
|
|
|
}
|
|
|
|
|
|
|
|
func genOpreferenceFromMap(op string, m map[string]string) (res []string) {
|
|
|
|
values := []string{}
|
|
|
|
for _, n := range m {
|
|
|
|
values = append(values, n)
|
|
|
|
}
|
|
|
|
|
|
|
|
res = genOpreference(op, values)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
func genOpreference(op string, s []string) (res []string) {
|
|
|
|
for _, n := range s {
|
|
|
|
res = append(res, genOpreferenceName(op, n))
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
2023-02-01 22:51:31 +01:00
|
|
|
opCustomMounts = "custom-mount"
|
|
|
|
opDiscoverState = "discover-state"
|
|
|
|
opMountState = "mount-state"
|
|
|
|
opMountRoot = "mount-root"
|
2023-02-01 19:27:01 +01:00
|
|
|
)
|
|
|
|
|
2023-02-01 18:01:58 +01:00
|
|
|
func (s *State) Register(g *herd.Graph) error {
|
|
|
|
|
2023-02-01 19:27:01 +01:00
|
|
|
// TODO: add, hooks, fstab, systemd compat
|
|
|
|
|
2023-02-01 22:51:31 +01:00
|
|
|
// This is legacy - in UKI we don't need to found the img, this needs to run in a conditional
|
|
|
|
if s.MountRoot {
|
|
|
|
g.Add(opDiscoverState,
|
|
|
|
herd.WithDeps(opMountState),
|
|
|
|
herd.WithCallback(
|
|
|
|
func(ctx context.Context) error {
|
|
|
|
_, err := utils.SH(fmt.Sprintf("losetup --show -f /run/initramfs/cos-state%s", s.TargetImage))
|
|
|
|
return err
|
|
|
|
},
|
|
|
|
))
|
2023-02-01 18:01:58 +01:00
|
|
|
|
2023-02-01 22:51:31 +01:00
|
|
|
g.Add(opMountState,
|
|
|
|
herd.WithCallback(
|
|
|
|
s.MountOP(
|
|
|
|
"/dev/disk/by-label/COS_STATE",
|
|
|
|
s.path("/run/initramfs/cos-state"),
|
|
|
|
"auto",
|
|
|
|
[]string{
|
|
|
|
"ro", // or rw
|
|
|
|
}, 60*time.Second),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
g.Add(opMountRoot,
|
|
|
|
herd.WithDeps(opDiscoverState),
|
|
|
|
herd.WithCallback(
|
|
|
|
s.MountOP(
|
|
|
|
fmt.Sprintf("/dev/disk/by-label/%s", s.TargetLabel),
|
|
|
|
s.Rootdir,
|
|
|
|
"auto",
|
|
|
|
[]string{
|
|
|
|
"ro", // or rw
|
|
|
|
"suid",
|
|
|
|
"dev",
|
|
|
|
"exec",
|
|
|
|
"auto",
|
|
|
|
"nouser",
|
|
|
|
"async",
|
|
|
|
}, 60*time.Second),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
// end sysroot mount
|
2023-02-01 18:01:58 +01:00
|
|
|
|
2023-02-01 22:33:44 +01:00
|
|
|
// overlay mount start
|
|
|
|
if rootFSType(s.Rootdir) != "overlay" {
|
|
|
|
g.Add("mount-overlay-base",
|
|
|
|
herd.WithCallback(
|
|
|
|
func(ctx context.Context) error {
|
|
|
|
op, err := baseOverlay(profile.Overlay{
|
|
|
|
Base: "/run/overlay",
|
|
|
|
BackingBase: "tmpfs:20%",
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
s.fstabs = append(s.fstabs, &op.FstabEntry)
|
|
|
|
return op.run()
|
|
|
|
},
|
|
|
|
),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
overlayCondition := herd.ConditionalOption(func() bool { return rootFSType(s.Rootdir) != "overlay" }, herd.WithDeps("mount-overlay-base"))
|
2023-02-01 19:06:51 +01:00
|
|
|
|
|
|
|
// TODO: Add fsck
|
|
|
|
// mount overlay
|
|
|
|
for _, p := range s.OverlayDir {
|
|
|
|
g.Add("mount-overlays-base",
|
2023-02-01 22:33:44 +01:00
|
|
|
overlayCondition,
|
2023-02-01 19:06:51 +01:00
|
|
|
herd.WithCallback(
|
|
|
|
func(ctx context.Context) error {
|
|
|
|
op, err := mountWithBaseOverlay(p, s.Rootdir, "/run/overlay")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
s.fstabs = append(s.fstabs, &op.FstabEntry)
|
2023-02-01 22:33:44 +01:00
|
|
|
return op.run()
|
2023-02-01 19:06:51 +01:00
|
|
|
},
|
|
|
|
),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// custom mounts TODO: disk/path
|
|
|
|
for id, mountpoint := range s.CustomMounts {
|
2023-02-01 19:27:01 +01:00
|
|
|
g.Add(
|
|
|
|
genOpreferenceName(opCustomMounts, mountpoint),
|
2023-02-01 22:33:44 +01:00
|
|
|
overlayCondition,
|
2023-02-01 19:06:51 +01:00
|
|
|
herd.WithCallback(
|
|
|
|
s.MountOP(
|
|
|
|
id,
|
|
|
|
s.path(mountpoint),
|
|
|
|
"auto",
|
|
|
|
[]string{
|
|
|
|
"ro", // or rw
|
|
|
|
}, 60*time.Second),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// mount state
|
2023-02-01 19:27:01 +01:00
|
|
|
// mount state is defined over a custom mount (/usr/local/.state for instance, needs to be mounted over a device)
|
2023-02-01 19:06:51 +01:00
|
|
|
for _, p := range s.BindMounts {
|
2023-02-01 19:27:01 +01:00
|
|
|
g.Add(
|
|
|
|
genOpreferenceName("mount-state", p),
|
2023-02-01 22:33:44 +01:00
|
|
|
overlayCondition,
|
2023-02-01 19:27:01 +01:00
|
|
|
herd.WithDeps(genOpreferenceFromMap(opCustomMounts, s.CustomMounts)...),
|
2023-02-01 19:06:51 +01:00
|
|
|
herd.WithCallback(
|
|
|
|
func(ctx context.Context) error {
|
2023-02-01 22:33:44 +01:00
|
|
|
op, err := mountBind(p, s.Rootdir, s.StateDir)
|
2023-02-01 19:06:51 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
s.fstabs = append(s.fstabs, &op.FstabEntry)
|
2023-02-01 22:33:44 +01:00
|
|
|
return op.run()
|
2023-02-01 19:06:51 +01:00
|
|
|
},
|
|
|
|
),
|
|
|
|
)
|
|
|
|
}
|
2023-02-01 22:33:44 +01:00
|
|
|
|
|
|
|
// overlay mount end
|
2023-02-01 22:51:31 +01:00
|
|
|
g.Add(opMountRoot,
|
|
|
|
herd.ConditionalOption(func() bool { return s.MountRoot }, herd.WithDeps("mount-overlay-base")),
|
2023-02-01 18:01:58 +01:00
|
|
|
herd.WithCallback(
|
|
|
|
s.MountOP(
|
|
|
|
"/dev/disk/by-label/COS_OEM",
|
2023-02-01 22:33:44 +01:00
|
|
|
s.path("/oem"),
|
2023-02-01 18:01:58 +01:00
|
|
|
"auto",
|
|
|
|
[]string{
|
|
|
|
"rw",
|
|
|
|
"suid",
|
|
|
|
"dev",
|
|
|
|
"exec",
|
|
|
|
"noauto",
|
|
|
|
"nouser",
|
|
|
|
"async",
|
|
|
|
}, 60*time.Second),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
g.Add("write-fstab", herd.WithCallback(s.WriteFstab("foo")))
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *State) path(p ...string) string {
|
|
|
|
return filepath.Join(append([]string{s.Rootdir}, p...)...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *State) WriteFstab(fstabFile string) func(context.Context) error {
|
|
|
|
return func(ctx context.Context) error {
|
|
|
|
for _, fst := range s.fstabs {
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
default:
|
|
|
|
f, err := os.OpenFile(fstabFile,
|
|
|
|
os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
if _, err := f.WriteString(fmt.Sprintf("%s\n", fst.String())); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *State) MountOP(what, where, t string, options []string, timeout time.Duration) func(context.Context) error {
|
|
|
|
return func(c context.Context) error {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
default:
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
mountPoint := mount.Mount{
|
|
|
|
Type: t,
|
|
|
|
Source: what,
|
|
|
|
Options: options,
|
|
|
|
}
|
|
|
|
fstab := mountToStab(mountPoint)
|
|
|
|
fstab.File = where
|
2023-02-01 22:33:44 +01:00
|
|
|
op := mountOperation{
|
2023-02-01 18:01:58 +01:00
|
|
|
MountOption: mountPoint,
|
|
|
|
FstabEntry: *fstab,
|
|
|
|
Target: where,
|
|
|
|
}
|
|
|
|
|
2023-02-01 22:33:44 +01:00
|
|
|
err := op.run()
|
2023-02-01 18:01:58 +01:00
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
s.fstabs = append(s.fstabs, fstab)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
case <-c.Done():
|
|
|
|
return fmt.Errorf("context canceled")
|
|
|
|
case <-time.After(timeout):
|
|
|
|
return fmt.Errorf("timeout exhausted")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|