immucore/pkg/state/state.go
2024-03-20 11:48:51 +01:00

138 lines
4.0 KiB
Go

package state
import (
"context"
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"github.com/deniswernert/go-fstab"
internalUtils "github.com/kairos-io/immucore/internal/utils"
"github.com/spectrocloud-labs/herd"
)
type State struct {
Rootdir string // where to mount the root partition e.g. /sysroot inside initrd with pivot, / with nopivot
TargetImage string // image from the state partition to mount as loop device e.g. /cOS/active.img
TargetDevice string // e.g. /dev/disk/by-label/COS_ACTIVE
RootMountMode string // How to mount the root partition e.g. ro or rw
// /run/cos-layout.env (different!)
OverlayDirs []string // e.g. /var
BindMounts []string // e.g. /etc/kubernetes
CustomMounts map[string]string // e.g. diskid : mountpoint
OverlayBase string // Overlay config, defaults to tmpfs:20%
StateDir string // e.g. "/usr/local/.state"
fstabs []*fstab.Mount
}
// SortedBindMounts returns the nodes with less depth first and in alphabetical order.
func (s *State) SortedBindMounts() []string {
bindMountsCopy := s.BindMounts
sort.Slice(bindMountsCopy, func(i, j int) bool {
iAry := strings.Split(bindMountsCopy[i], "/")
jAry := strings.Split(bindMountsCopy[j], "/")
iSize := len(iAry)
jSize := len(jAry)
if iSize == jSize {
return strings.Compare(iAry[len(iAry)-1], jAry[len(jAry)-1]) == -1
}
return iSize < jSize
})
return bindMountsCopy
}
func (s *State) path(p ...string) string {
return filepath.Join(append([]string{s.Rootdir}, p...)...)
}
func (s *State) WriteFstab() func(context.Context) error {
return func(ctx context.Context) error {
// Create the file first, override if something is there, we don't care, we are on initramfs
fstabFile := s.path("/etc/fstab")
f, err := os.Create(fstabFile)
if err != nil {
return err
}
f.Close()
for _, fst := range s.fstabs {
internalUtils.Log.Debug().Str("what", fst.String()).Msg("Adding line to fstab")
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
}
if _, err := f.WriteString(fmt.Sprintf("%s\n", fst.String())); err != nil {
_ = f.Close()
return err
}
_ = f.Close()
}
}
return nil
}
}
// WriteDAG writes the dag.
func (s *State) WriteDAG(g *herd.Graph) (out string) {
for i, layer := range g.Analyze() {
out += fmt.Sprintf("%d.\n", i+1)
for _, op := range layer {
if op.Error != nil {
out += fmt.Sprintf(" <%s> (error: %s) (background: %t) (weak: %t) (run: %t)\n", op.Name, op.Error.Error(), op.Background, op.WeakDeps, op.Executed)
} else {
out += fmt.Sprintf(" <%s> (background: %t) (weak: %t) (run: %t)\n", op.Name, op.Background, op.WeakDeps, op.Executed)
}
}
}
return
}
// LogIfError will log if there is an error with the given context as message
// Context can be empty.
func (s *State) LogIfError(e error, msgContext string) {
if e != nil {
internalUtils.Log.Err(e).Msg(msgContext)
}
}
// LogIfErrorAndReturn will log if there is an error with the given context as message
// Context can be empty
// Will also return the error.
func (s *State) LogIfErrorAndReturn(e error, msgContext string) error {
if e != nil {
internalUtils.Log.Err(e).Msg(msgContext)
}
return e
}
// LogIfErrorAndPanic will log if there is an error with the given context as message
// Context can be empty
// Will also panic.
func (s *State) LogIfErrorAndPanic(e error, msgContext string) {
if e != nil {
internalUtils.Log.Err(e).Msg(msgContext)
internalUtils.Log.Fatal().Msg(e.Error())
}
}
// AddToFstab will try to add an entry to the fstab list
// Will check if the entry exists before adding it to avoid duplicates.
func (s *State) AddToFstab(tmpFstab *fstab.Mount) {
found := false
for _, f := range s.fstabs {
if f.Spec == tmpFstab.Spec {
internalUtils.Log.Debug().Interface("existing", f).Interface("duplicated", tmpFstab).Msg("Duplicated fstab entry found, not adding")
found = true
}
}
if !found {
s.fstabs = append(s.fstabs, tmpFstab)
}
}