Uki Support (#67)

Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>
This commit is contained in:
Itxaka
2023-03-01 11:42:46 +01:00
committed by GitHub
parent de9ed759eb
commit 086227d672
16 changed files with 384 additions and 145 deletions

4
go.mod
View File

@@ -9,11 +9,13 @@ require (
github.com/joho/godotenv v1.5.1 github.com/joho/godotenv v1.5.1
github.com/kairos-io/kairos v1.5.0 github.com/kairos-io/kairos v1.5.0
github.com/moby/sys/mountinfo v0.6.2 github.com/moby/sys/mountinfo v0.6.2
github.com/mudler/go-kdetect v0.0.0-20210802130128-dd92e121bed8
github.com/onsi/ginkgo/v2 v2.8.4 github.com/onsi/ginkgo/v2 v2.8.4
github.com/onsi/gomega v1.27.2 github.com/onsi/gomega v1.27.2
github.com/rs/zerolog v1.29.0 github.com/rs/zerolog v1.29.0
github.com/spectrocloud-labs/herd v0.4.2 github.com/spectrocloud-labs/herd v0.4.2
github.com/urfave/cli/v2 v2.24.4 github.com/urfave/cli/v2 v2.24.4
golang.org/x/sys v0.5.0
) )
require ( require (
@@ -49,6 +51,7 @@ require (
github.com/mattn/go-runewidth v0.0.14 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
github.com/pilebones/go-udev v0.0.0-20210126000448-a3c2a7a4afb7 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/pterm/pterm v0.12.54 // indirect github.com/pterm/pterm v0.12.54 // indirect
github.com/qeesung/image2ascii v1.0.1 // indirect github.com/qeesung/image2ascii v1.0.1 // indirect
@@ -62,7 +65,6 @@ require (
go.opencensus.io v0.23.0 // indirect go.opencensus.io v0.23.0 // indirect
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 // indirect golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 // indirect
golang.org/x/net v0.7.0 // indirect golang.org/x/net v0.7.0 // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/term v0.5.0 // indirect golang.org/x/term v0.5.0 // indirect
golang.org/x/text v0.7.0 // indirect golang.org/x/text v0.7.0 // indirect
golang.org/x/tools v0.6.0 // indirect golang.org/x/tools v0.6.0 // indirect

12
go.sum
View File

@@ -68,8 +68,6 @@ github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2
github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00=
github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600=
github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4=
github.com/Microsoft/hcsshim v0.9.6 h1:VwnDOgLeoi2du6dAznfmspNqTiwczvjv4K7NxuY9jsY=
github.com/Microsoft/hcsshim v0.9.6/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc=
github.com/Microsoft/hcsshim v0.9.7 h1:mKNHW/Xvv1aFH87Jb6ERDzXTJTLPlmzfZ28VBFD/bfg= github.com/Microsoft/hcsshim v0.9.7 h1:mKNHW/Xvv1aFH87Jb6ERDzXTJTLPlmzfZ28VBFD/bfg=
github.com/Microsoft/hcsshim v0.9.7/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= github.com/Microsoft/hcsshim v0.9.7/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc=
github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU=
@@ -166,8 +164,6 @@ github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09Zvgq
github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s=
github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g=
github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c=
github.com/containerd/containerd v1.6.18 h1:qZbsLvmyu+Vlty0/Ex5xc0z2YtKpIsb5n45mAMI+2Ns=
github.com/containerd/containerd v1.6.18/go.mod h1:1RdCUu95+gc2v9t3IL+zIlpClSmew7/0YS8O5eQZrOw=
github.com/containerd/containerd v1.6.19 h1:F0qgQPrG0P2JPgwpxWxYavrVeXAG0ezUIB9Z/4FTUAU= github.com/containerd/containerd v1.6.19 h1:F0qgQPrG0P2JPgwpxWxYavrVeXAG0ezUIB9Z/4FTUAU=
github.com/containerd/containerd v1.6.19/go.mod h1:HZCDMn4v/Xl2579/MvtOC2M206i+JJ6VxFWU/NetrGY= github.com/containerd/containerd v1.6.19/go.mod h1:HZCDMn4v/Xl2579/MvtOC2M206i+JJ6VxFWU/NetrGY=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
@@ -543,6 +539,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
github.com/mudler/go-kdetect v0.0.0-20210802130128-dd92e121bed8 h1:+g0budy5fEFMX5+ChzEhXbb7YnNWNveQgHF/iQ/UXmE=
github.com/mudler/go-kdetect v0.0.0-20210802130128-dd92e121bed8/go.mod h1:826dAJvIa3X5kfBoxSupdQbIFO4egt+hoUiw4BfPKdI=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@@ -564,8 +562,6 @@ github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo/v2 v2.8.3 h1:RpbK1G8nWPNaCVFBWsOGnEQQGgASi6b8fxcWBvDYjxQ=
github.com/onsi/ginkgo/v2 v2.8.3/go.mod h1:6OaUA8BCi0aZfmzYT/q9AacwTzDpNbxILUT+TlBq6MY=
github.com/onsi/ginkgo/v2 v2.8.4 h1:gf5mIQ8cLFieruNLAdgijHF1PYfLphKm2dxxcUtcqK0= github.com/onsi/ginkgo/v2 v2.8.4 h1:gf5mIQ8cLFieruNLAdgijHF1PYfLphKm2dxxcUtcqK0=
github.com/onsi/ginkgo/v2 v2.8.4/go.mod h1:427dEDQZkDKsBvCjc2A/ZPefhKxsTTrsQegMlayL730= github.com/onsi/ginkgo/v2 v2.8.4/go.mod h1:427dEDQZkDKsBvCjc2A/ZPefhKxsTTrsQegMlayL730=
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
@@ -575,8 +571,6 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
github.com/onsi/gomega v1.27.1 h1:rfztXRbg6nv/5f+Raen9RcGoSecHIFgBBLQK3Wdj754=
github.com/onsi/gomega v1.27.1/go.mod h1:aHX5xOykVYzWOV4WqQy0sy8BQptgukenXpCXfadcIAw=
github.com/onsi/gomega v1.27.2 h1:SKU0CXeKE/WVgIV1T61kSa3+IRE8Ekrv9rdXDwwTqnY= github.com/onsi/gomega v1.27.2 h1:SKU0CXeKE/WVgIV1T61kSa3+IRE8Ekrv9rdXDwwTqnY=
github.com/onsi/gomega v1.27.2/go.mod h1:5mR3phAHpkAVIDkHEUBY6HGVsU+cpcEscrGPB4oPlZI= github.com/onsi/gomega v1.27.2/go.mod h1:5mR3phAHpkAVIDkHEUBY6HGVsU+cpcEscrGPB4oPlZI=
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
@@ -606,6 +600,8 @@ github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xA
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pilebones/go-udev v0.0.0-20210126000448-a3c2a7a4afb7 h1:1If5vu3Qy1ZL3NshlsrSQMsdRhujenaVFbHcFM9XQUk=
github.com/pilebones/go-udev v0.0.0-20210126000448-a3c2a7a4afb7/go.mod h1:T2eI2tUSK0hA2WS5QLjXJUfQkluZQu+18Cqvem3CaXI=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=

View File

@@ -10,22 +10,22 @@ func DefaultRWPaths() []string {
var ErrAlreadyMounted = errors.New("already mounted") var ErrAlreadyMounted = errors.New("already mounted")
const ( const (
OpCustomMounts = "custom-mount" OpCustomMounts = "custom-mount"
OpDiscoverState = "discover-state" OpDiscoverState = "discover-state"
OpMountState = "mount-state" OpMountState = "mount-state"
OpMountBind = "mount-bind" OpMountBind = "mount-bind"
OpMountRoot = "mount-root"
OpMountRoot = "mount-root" OpOverlayMount = "overlay-mount"
OpOverlayMount = "overlay-mount" OpWriteFstab = "write-fstab"
OpWriteFstab = "write-fstab" OpMountBaseOverlay = "mount-base-overlay"
OpMountBaseOverlay = "mount-base-overlay" OpMountOEM = "mount-oem"
OpMountOEM = "mount-oem" OpRootfsHook = "rootfs-hook"
OpInitramfsHook = "initramfs-hook"
OpRootfsHook = "rootfs-hook" OpLoadConfig = "load-config"
OpLoadConfig = "load-config" OpMountTmpfs = "mount-tmpfs"
OpMountTmpfs = "mount-tmpfs" OpRemountRootRO = "remount-ro"
OpUkiInit = "uki-init"
OpSentinel = "create-sentinel" OpSentinel = "create-sentinel"
OpUkiUdev = "uki-udev"
PersistentStateTarget = "/usr/local/.state" PersistentStateTarget = "/usr/local/.state"
) )

View File

@@ -3,8 +3,8 @@ package utils
import ( import (
"github.com/joho/godotenv" "github.com/joho/godotenv"
"github.com/kairos-io/kairos/sdk/state" "github.com/kairos-io/kairos/sdk/state"
"github.com/rs/zerolog/log"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"strings" "strings"
) )
@@ -32,7 +32,7 @@ func BootStateToLabelDevice() string {
func GetRootDir() string { func GetRootDir() string {
cmdline, _ := os.ReadFile("/proc/cmdline") cmdline, _ := os.ReadFile("/proc/cmdline")
switch { switch {
case strings.Contains(string(cmdline), "IMMUCORE_NOPIVOT"): case strings.Contains(string(cmdline), "rd.immucore.uki"):
return "/" return "/"
default: default:
// Default is sysroot for normal no-pivot boot // Default is sysroot for normal no-pivot boot
@@ -99,7 +99,7 @@ func CleanupSlice(slice []string) []string {
// GetTarget gets the target image and device to mount in /sysroot // GetTarget gets the target image and device to mount in /sysroot
func GetTarget(dryRun bool) (string, string) { func GetTarget(dryRun bool) (string, string) {
var img, label string var label string
label = BootStateToLabelDevice() label = BootStateToLabelDevice()
@@ -108,16 +108,20 @@ func GetTarget(dryRun bool) (string, string) {
return "fake", label return "fake", label
} }
img = ReadCMDLineArg("cos-img/filename=")[0] imgs := ReadCMDLineArg("cos-img/filename=")
// If no image just panic here, we cannot longer continue // If no image just panic here, we cannot longer continue
if img == "" { if len(imgs) == 0 {
log.Logger.Fatal().Msg("Could not get the image name from cmdline (i.e. cos-img/filename=/cOS/active.img)") if IsUKI() {
imgs = []string{""}
} else {
Log.Fatal().Msg("could not get the image name from cmdline (i.e. cos-img/filename=/cOS/active.img)")
}
} }
log.Debug().Str("what", img).Msg("Target device") Log.Debug().Str("what", imgs[0]).Msg("Target device")
log.Debug().Str("what", label).Msg("Target label") Log.Debug().Str("what", label).Msg("Target label")
return img, label return imgs[0], label
} }
// DisableImmucore identifies if we need to be disabled // DisableImmucore identifies if we need to be disabled
@@ -132,7 +136,7 @@ func DisableImmucore() bool {
// RootRW tells us if the mode to mount root // RootRW tells us if the mode to mount root
func RootRW() string { func RootRW() string {
if len(ReadCMDLineArg("rd.cos.debugrw")) > 0 { if len(ReadCMDLineArg("rd.cos.debugrw")) > 0 {
log.Logger.Warn().Msg("Mounting root as RW") Log.Warn().Msg("Mounting root as RW")
return "rw" return "rw"
} }
return "ro" return "ro"
@@ -152,6 +156,24 @@ func GetState() string {
case state.Recovery: case state.Recovery:
label = filepath.Join("/dev/disk/by-label/", runtime.Recovery.Label) label = filepath.Join("/dev/disk/by-label/", runtime.Recovery.Label)
} }
log.Logger.Debug().Str("what", label).Msg("Get state label") Log.Debug().Str("what", label).Msg("Get state label")
return label return label
} }
func IsUKI() bool {
if len(ReadCMDLineArg("rd.immucore.uki")) > 0 {
return true
}
return false
}
// CommandWithPath runs a command adding the usual PATH to environment
// Useful under UKI as there is nothing setting the PATH
func CommandWithPath(c string) (string, error) {
cmd := exec.Command("/bin/sh", "-c", c)
cmd.Env = os.Environ()
// TODO: extract PATH from env and append to existing instead of overwriting
cmd.Env = append(cmd.Env, "PATH=/usr/bin:/usr/sbin")
o, err := cmd.CombinedOutput()
return string(o), err
}

45
internal/utils/log.go Normal file
View File

@@ -0,0 +1,45 @@
package utils
import (
"github.com/rs/zerolog"
"golang.org/x/sys/unix"
"io"
"os"
)
var Log zerolog.Logger
var devKmsgFile *os.File
var logFile *os.File
func CloseLogFiles() {
devKmsgFile.Close()
logFile.Close()
}
func SetLogger() {
var loggers []io.Writer
devKmsgFile, err := os.OpenFile("/dev/kmsg", unix.O_WRONLY, 0o600)
if err == nil {
loggers = append(loggers, zerolog.ConsoleWriter{Out: devKmsgFile})
}
logFile, err := os.Create("/run/immucore.log")
if err == nil {
loggers = append(loggers, zerolog.ConsoleWriter{Out: logFile})
}
// No loggers? Then stdout ¯\_(ツ)_/¯
if len(loggers) == 0 {
loggers = append(loggers, zerolog.ConsoleWriter{Out: os.Stderr})
}
multi := zerolog.MultiLevelWriter(loggers...)
Log = zerolog.New(multi).With().Logger()
Log.WithLevel(zerolog.InfoLevel)
// Set debug logger
debug := len(ReadCMDLineArg("rd.immucore.debug")) > 0
debugFromEnv := os.Getenv("IMMUCORE_DEBUG") != ""
if debug || debugFromEnv {
Log = zerolog.New(multi).With().Caller().Logger()
Log.WithLevel(zerolog.DebugLevel)
}
}

View File

@@ -4,11 +4,9 @@ import (
"fmt" "fmt"
"github.com/containerd/containerd/mount" "github.com/containerd/containerd/mount"
"github.com/deniswernert/go-fstab" "github.com/deniswernert/go-fstab"
"github.com/kairos-io/kairos/pkg/utils"
"github.com/rs/zerolog/log"
"os" "os"
"os/exec"
"strings" "strings"
"syscall"
) )
// https://github.com/kairos-io/packages/blob/7c3581a8ba6371e5ce10c3a98bae54fde6a505af/packages/system/dracut/immutable-rootfs/30cos-immutable-rootfs/cos-mount-layout.sh#L58 // https://github.com/kairos-io/packages/blob/7c3581a8ba6371e5ce10c3a98bae54fde6a505af/packages/system/dracut/immutable-rootfs/30cos-immutable-rootfs/cos-mount-layout.sh#L58
@@ -54,7 +52,7 @@ func ReadCMDLineArg(arg string) []string {
// IsMounted lets us know if the given device is currently mounted // IsMounted lets us know if the given device is currently mounted
func IsMounted(dev string) bool { func IsMounted(dev string) bool {
_, err := utils.SH(fmt.Sprintf("findmnt %s", dev)) _, err := CommandWithPath(fmt.Sprintf("findmnt %s", dev))
return err == nil return err == nil
} }
@@ -62,18 +60,19 @@ func IsMounted(dev string) bool {
// Does NOT need to be mounted // Does NOT need to be mounted
// Needs full path so either /dev/sda1 or /dev/disk/by-{label,uuid}/{label,uuid} // Needs full path so either /dev/sda1 or /dev/disk/by-{label,uuid}/{label,uuid}
func DiskFSType(s string) string { func DiskFSType(s string) string {
out, e := utils.SH(fmt.Sprintf("blkid %s -s TYPE -o value", s)) out, e := CommandWithPath(fmt.Sprintf("blkid %s -s TYPE -o value", s))
if e != nil { if e != nil {
log.Logger.Err(e).Msg("blkid") Log.Err(e).Msg("blkid")
} }
out = strings.Trim(strings.Trim(out, " "), "\n") out = strings.Trim(strings.Trim(out, " "), "\n")
log.Logger.Debug().Str("what", s).Str("type", out).Msg("Partition FS type") Log.Debug().Str("what", s).Str("type", out).Msg("Partition FS type")
return out return out
} }
// SyncState will rsync source into destination. Useful for Bind mounts. // SyncState will rsync source into destination. Useful for Bind mounts.
func SyncState(src, dst string) error { func SyncState(src, dst string) error {
return exec.Command("rsync", "-aqAX", src, dst).Run() _, err := CommandWithPath(fmt.Sprintf("rsync -aqAX %s %s", src, dst))
return err
} }
// AppendSlash it's in the name. Appends a slash. // AppendSlash it's in the name. Appends a slash.
@@ -113,6 +112,9 @@ func MountToFstab(m mount.Mount) *fstab.Mount {
// Special care for the root (/sysroot) path as we can't just simple remove that path and call it a day // Special care for the root (/sysroot) path as we can't just simple remove that path and call it a day
// as that will return an empty mountpoint which will break fstab mounting // as that will return an empty mountpoint which will break fstab mounting
func CleanSysrootForFstab(path string) string { func CleanSysrootForFstab(path string) string {
if IsUKI() {
return path
}
cleaned := strings.ReplaceAll(path, "/sysroot", "") cleaned := strings.ReplaceAll(path, "/sysroot", "")
if cleaned == "" { if cleaned == "" {
cleaned = "/" cleaned = "/"
@@ -161,11 +163,41 @@ func Fsck(device string) error {
args = append(args, "-n") args = append(args, "-n")
} }
cmd := strings.Join(args, " ") cmd := strings.Join(args, " ")
log.Logger.Debug().Str("cmd", cmd).Msg("fsck command") Log.Debug().Str("cmd", cmd).Msg("fsck command")
out, e := utils.SH(cmd) out, e := CommandWithPath(cmd)
log.Logger.Debug().Str("output", out).Msg("fsck output") Log.Debug().Str("output", out).Msg("fsck output")
if e != nil { if e != nil {
log.Logger.Warn().Str("error", e.Error()).Str("what", device).Msg("fsck") Log.Warn().Str("error", e.Error()).Str("what", device).Msg("fsck")
} }
return e return e
} }
// MinimalMounts will set the minimal mounts needed for immucore
// For now only proc is needed to read the cmdline fully in uki mode
// in normal modes this should already be done by the initramfs process, so we can ignore errors
// Just mount dev, tmp and sys just in case
func MinimalMounts() {
type m struct {
source string
target string
t string
flags int
data string
}
toMount := []m{
{"dev", "/dev", "devtmpfs", syscall.MS_NOSUID, "mode=755"},
{"proc", "/proc", "proc", syscall.MS_NOSUID | syscall.MS_NODEV | syscall.MS_NOEXEC | syscall.MS_RELATIME, ""},
{"sys", "/sys", "sysfs", syscall.MS_NOSUID | syscall.MS_NODEV | syscall.MS_NOEXEC | syscall.MS_RELATIME, ""},
{"tmp", "/tmp", "tmpfs", syscall.MS_NOSUID | syscall.MS_NODEV, ""},
{"run", "/run", "tmpfs", syscall.MS_NOSUID | syscall.MS_NODEV | syscall.MS_NOEXEC | syscall.MS_RELATIME, "mode=755"},
}
for _, mnt := range toMount {
_ = os.MkdirAll(mnt.target, 0755)
if !IsMounted(mnt.target) {
err := syscall.Mount(mnt.source, mnt.target, mnt.t, uintptr(mnt.flags), mnt.data)
if err != nil {
fmt.Println(err.Error())
}
}
}
}

View File

@@ -5,7 +5,7 @@ import "runtime"
var ( var (
version = "v0.0.1" version = "v0.0.1"
// gitCommit is the git sha1 + dirty if build from a dirty git // gitCommit is the git sha1 + dirty if build from a dirty git
gitCommit = "" gitCommit = "none"
) )
func GetVersion() string { func GetVersion() string {

44
main.go
View File

@@ -6,8 +6,6 @@ import (
"github.com/kairos-io/immucore/internal/utils" "github.com/kairos-io/immucore/internal/utils"
"github.com/kairos-io/immucore/internal/version" "github.com/kairos-io/immucore/internal/version"
"github.com/kairos-io/immucore/pkg/mount" "github.com/kairos-io/immucore/pkg/mount"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/spectrocloud-labs/herd" "github.com/spectrocloud-labs/herd"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"os" "os"
@@ -20,27 +18,23 @@ func main() {
app.Authors = []*cli.Author{{Name: "Kairos authors"}} app.Authors = []*cli.Author{{Name: "Kairos authors"}}
app.Copyright = "kairos authors" app.Copyright = "kairos authors"
app.Action = func(c *cli.Context) (err error) { app.Action = func(c *cli.Context) (err error) {
debug := len(utils.ReadCMDLineArg("rd.immucore.debug")) > 0 var targetDevice, targetImage string
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).With().Logger() var state *mount.State
zerolog.SetGlobalLevel(zerolog.InfoLevel)
debugFromEnv := os.Getenv("IMMUCORE_DEBUG") != "" utils.MinimalMounts()
if debug || debugFromEnv { utils.SetLogger()
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).With().Caller().Logger()
zerolog.SetGlobalLevel(zerolog.DebugLevel)
}
v := version.Get() v := version.Get()
log.Logger.Info().Str("commit", v.GitCommit).Str("compiled with", v.GoVersion).Str("version", v.Version).Msg("Immucore") utils.Log.Info().Str("commit", v.GitCommit).Str("compiled with", v.GoVersion).Str("version", v.Version).Msg("Immucore")
cmdline, _ := os.ReadFile("/proc/cmdline") cmdline, _ := os.ReadFile("/proc/cmdline")
log.Logger.Debug().Msg(string(cmdline)) utils.Log.Debug().Str("content", string(cmdline)).Msg("cmdline")
g := herd.DAG(herd.EnableInit) g := herd.DAG(herd.EnableInit)
// Get targets and state // Get targets and state
targetImage, targetDevice := utils.GetTarget(c.Bool("dry-run")) targetImage, targetDevice = utils.GetTarget(c.Bool("dry-run"))
s := &mount.State{ state = &mount.State{
Logger: log.Logger,
Rootdir: utils.GetRootDir(), Rootdir: utils.GetRootDir(),
TargetDevice: targetDevice, TargetDevice: targetDevice,
TargetImage: targetImage, TargetImage: targetImage,
@@ -48,19 +42,21 @@ func main() {
} }
if utils.DisableImmucore() { if utils.DisableImmucore() {
log.Logger.Info().Msg("Stanza rd.cos.disable on the cmdline or booting from CDROM/Netboot/Squash recovery. Disabling immucore.") utils.Log.Info().Msg("Stanza rd.cos.disable on the cmdline or booting from CDROM/Netboot/Squash recovery. Disabling immucore.")
err = s.RegisterLiveMedia(g) err = state.RegisterLiveMedia(g)
} else if utils.IsUKI() {
utils.Log.Info().Msg("UKI booting!")
err = state.RegisterUKI(g)
} else { } else {
log.Logger.Info().Msg("Booting on active/passive/recovery.") utils.Log.Info().Msg("Booting on active/passive/recovery.")
err = s.RegisterNormalBoot(g) err = state.RegisterNormalBoot(g)
} }
if err != nil { if err != nil {
s.Logger.Err(err)
return err return err
} }
log.Info().Msg(s.WriteDAG(g)) utils.Log.Info().Msg(state.WriteDAG(g))
// Once we print the dag we can exit already // Once we print the dag we can exit already
if c.Bool("dry-run") { if c.Bool("dry-run") {
@@ -68,7 +64,7 @@ func main() {
} }
err = g.Run(context.Background()) err = g.Run(context.Background())
log.Info().Msg(s.WriteDAG(g)) utils.Log.Info().Msg(state.WriteDAG(g))
return err return err
} }
app.Flags = []cli.Flag{ app.Flags = []cli.Flag{
@@ -81,10 +77,8 @@ func main() {
Name: "version", Name: "version",
Usage: "version", Usage: "version",
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).With().Logger()
zerolog.SetGlobalLevel(zerolog.InfoLevel)
v := version.Get() v := version.Get()
log.Logger.Info().Str("commit", v.GitCommit).Str("compiled with", v.GoVersion).Str("version", v.Version).Msg("Immucore") utils.Log.Info().Str("commit", v.GitCommit).Str("compiled with", v.GoVersion).Str("version", v.Version).Msg("Immucore")
return nil return nil
}, },
}, },

View File

@@ -1,6 +1,7 @@
package mount package mount
import ( import (
internalUtils "github.com/kairos-io/immucore/internal/utils"
"github.com/spectrocloud-labs/herd" "github.com/spectrocloud-labs/herd"
) )
@@ -10,5 +11,6 @@ func (s *State) RegisterLiveMedia(g *herd.Graph) error {
var err error var err error
// Maybe LogIfErrorAndPanic ? If no sentinel, a lot of config files are not going to run // Maybe LogIfErrorAndPanic ? If no sentinel, a lot of config files are not going to run
err = s.LogIfErrorAndReturn(s.WriteSentinelDagStep(g), "write sentinel") err = s.LogIfErrorAndReturn(s.WriteSentinelDagStep(g), "write sentinel")
internalUtils.CloseLogFiles()
return err return err
} }

View File

@@ -2,6 +2,7 @@ package mount
import ( import (
cnst "github.com/kairos-io/immucore/internal/constants" cnst "github.com/kairos-io/immucore/internal/constants"
internalUtils "github.com/kairos-io/immucore/internal/utils"
"github.com/spectrocloud-labs/herd" "github.com/spectrocloud-labs/herd"
) )
@@ -31,7 +32,7 @@ func (s *State) RegisterNormalBoot(g *herd.Graph) error {
// Populate state bind mounts, overlay mounts, custom-mounts from /run/cos/cos-layout.env // Populate state bind mounts, overlay mounts, custom-mounts from /run/cos/cos-layout.env
// Requires stage rootfs to have run, which usually creates the cos-layout.env file // Requires stage rootfs to have run, which usually creates the cos-layout.env file
s.LogIfError(s.LoadEnvLayoutDagStep(g), "loading cos-layout.env") s.LogIfError(s.LoadEnvLayoutDagStep(g, cnst.OpRootfsHook), "loading cos-layout.env")
// Mount base overlay under /run/overlay // Mount base overlay under /run/overlay
s.LogIfError(s.MountBaseOverlayDagStep(g), "base overlay mount") s.LogIfError(s.MountBaseOverlayDagStep(g), "base overlay mount")
@@ -51,6 +52,6 @@ func (s *State) RegisterNormalBoot(g *herd.Graph) error {
// Write fstab file // Write fstab file
s.LogIfError(s.WriteFstabDagStep(g), "write fstab") s.LogIfError(s.WriteFstabDagStep(g), "write fstab")
internalUtils.CloseLogFiles()
return err return err
} }

View File

@@ -9,12 +9,13 @@ import (
internalUtils "github.com/kairos-io/immucore/internal/utils" internalUtils "github.com/kairos-io/immucore/internal/utils"
"github.com/kairos-io/kairos/pkg/utils" "github.com/kairos-io/kairos/pkg/utils"
"github.com/kairos-io/kairos/sdk/state" "github.com/kairos-io/kairos/sdk/state"
"github.com/rs/zerolog" "github.com/mudler/go-kdetect"
"github.com/rs/zerolog/log"
"github.com/spectrocloud-labs/herd" "github.com/spectrocloud-labs/herd"
"golang.org/x/sys/unix"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"syscall"
"time" "time"
) )
@@ -43,7 +44,7 @@ func (s *State) MountRootDagStep(g *herd.Graph) error {
), ),
) )
if err != nil { if err != nil {
s.Logger.Err(err).Send() internalUtils.Log.Err(err).Send()
} }
// 2 - mount the image as a loop device // 2 - mount the image as a loop device
@@ -53,7 +54,7 @@ func (s *State) MountRootDagStep(g *herd.Graph) error {
func(ctx context.Context) error { func(ctx context.Context) error {
// Check if loop device is mounted already // Check if loop device is mounted already
if internalUtils.IsMounted(s.TargetDevice) { if internalUtils.IsMounted(s.TargetDevice) {
log.Logger.Debug().Str("targetImage", s.TargetImage).Str("path", s.Rootdir).Str("TargetDevice", s.TargetDevice).Msg("Not mounting loop, already mounted") internalUtils.Log.Debug().Str("targetImage", s.TargetImage).Str("path", s.Rootdir).Str("TargetDevice", s.TargetDevice).Msg("Not mounting loop, already mounted")
return nil return nil
} }
_ = internalUtils.Fsck(s.path("/run/initramfs/cos-state", s.TargetImage)) _ = internalUtils.Fsck(s.path("/run/initramfs/cos-state", s.TargetImage))
@@ -65,13 +66,13 @@ func (s *State) MountRootDagStep(g *herd.Graph) error {
// But on other it seems like it won't trigger which causes the sysroot to not be mounted as we cant find // But on other it seems like it won't trigger which causes the sysroot to not be mounted as we cant find
// the block device by the target label. Make sure we run this after mounting so we refresh the devices. // the block device by the target label. Make sure we run this after mounting so we refresh the devices.
sh, _ := utils.SH("udevadm trigger") sh, _ := utils.SH("udevadm trigger")
s.Logger.Debug().Str("output", sh).Msg("udevadm trigger") internalUtils.Log.Debug().Str("output", sh).Msg("udevadm trigger")
log.Logger.Debug().Str("targetImage", s.TargetImage).Str("path", s.Rootdir).Str("TargetDevice", s.TargetDevice).Msg("mount done") internalUtils.Log.Debug().Str("targetImage", s.TargetImage).Str("path", s.Rootdir).Str("TargetDevice", s.TargetDevice).Msg("mount done")
return err return err
}, },
)) ))
if err != nil { if err != nil {
s.Logger.Err(err).Send() internalUtils.Log.Err(err).Send()
} }
// 3 - Mount the labels as Rootdir // 3 - Mount the labels as Rootdir
@@ -94,7 +95,7 @@ func (s *State) MountRootDagStep(g *herd.Graph) error {
), ),
) )
if err != nil { if err != nil {
s.Logger.Err(err).Send() internalUtils.Log.Err(err).Send()
} }
return err return err
} }
@@ -104,19 +105,23 @@ func (s *State) RootfsStageDagStep(g *herd.Graph, deps ...string) error {
return g.Add(cnst.OpRootfsHook, herd.WithDeps(deps...), herd.WithCallback(s.RunStageOp("rootfs"))) return g.Add(cnst.OpRootfsHook, herd.WithDeps(deps...), herd.WithCallback(s.RunStageOp("rootfs")))
} }
// InitramfsStageDagStep will add the rootfs stage.
func (s *State) InitramfsStageDagStep(g *herd.Graph, deps ...string) error {
return g.Add(cnst.OpInitramfsHook, herd.WithDeps(deps...), herd.WeakDeps, herd.WithCallback(s.RunStageOp("initramfs")))
}
// LoadEnvLayoutDagStep will add the stage to load from cos-layout.env and fill the proper CustomMounts, OverlayDirs and BindMounts // LoadEnvLayoutDagStep will add the stage to load from cos-layout.env and fill the proper CustomMounts, OverlayDirs and BindMounts
func (s *State) LoadEnvLayoutDagStep(g *herd.Graph) error { func (s *State) LoadEnvLayoutDagStep(g *herd.Graph, deps ...string) error {
return g.Add(cnst.OpLoadConfig, return g.Add(cnst.OpLoadConfig,
herd.WithDeps(cnst.OpRootfsHook), herd.WithDeps(deps...),
herd.WithCallback(func(ctx context.Context) error { herd.WithCallback(func(ctx context.Context) error {
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).With().Logger()
if s.CustomMounts == nil { if s.CustomMounts == nil {
s.CustomMounts = map[string]string{} s.CustomMounts = map[string]string{}
} }
env, err := internalUtils.ReadEnv("/run/cos/cos-layout.env") env, err := internalUtils.ReadEnv("/run/cos/cos-layout.env")
if err != nil { if err != nil {
log.Logger.Err(err).Msg("Reading env") internalUtils.Log.Err(err).Msg("Reading env")
return err return err
} }
// populate from env here // populate from env here
@@ -162,7 +167,7 @@ func (s *State) LoadEnvLayoutDagStep(g *herd.Graph) error {
func (s *State) MountOemDagStep(g *herd.Graph, deps ...string) error { func (s *State) MountOemDagStep(g *herd.Graph, deps ...string) error {
runtime, err := state.NewRuntime() runtime, err := state.NewRuntime()
if err != nil { if err != nil {
s.Logger.Debug().Err(err).Msg("runtime") internalUtils.Log.Debug().Err(err).Msg("runtime")
} }
return g.Add(cnst.OpMountOEM, return g.Add(cnst.OpMountOEM,
herd.WithDeps(deps...), herd.WithDeps(deps...),
@@ -220,13 +225,13 @@ func (s *State) MountCustomOverlayDagStep(g *herd.Graph) error {
herd.WithCallback( herd.WithCallback(
func(ctx context.Context) error { func(ctx context.Context) error {
var multierr *multierror.Error var multierr *multierror.Error
s.Logger.Debug().Strs("dirs", s.OverlayDirs).Msg("Mounting overlays") internalUtils.Log.Debug().Strs("dirs", s.OverlayDirs).Msg("Mounting overlays")
for _, p := range s.OverlayDirs { for _, p := range s.OverlayDirs {
op := mountWithBaseOverlay(p, s.Rootdir, "/run/overlay") op := mountWithBaseOverlay(p, s.Rootdir, "/run/overlay")
err := op.run() err := op.run()
// Append to errors only if it's not an already mounted error // Append to errors only if it's not an already mounted error
if err != nil && !errors.Is(err, cnst.ErrAlreadyMounted) { if err != nil && !errors.Is(err, cnst.ErrAlreadyMounted) {
log.Logger.Err(err).Msg("overlay mount") internalUtils.Log.Err(err).Msg("overlay mount")
multierr = multierror.Append(multierr, err) multierr = multierror.Append(multierr, err)
continue continue
} }
@@ -249,20 +254,25 @@ func (s *State) MountCustomMountsDagStep(g *herd.Graph) error {
// TODO: scan for the custom mount disk to know the underlying fs and set it proper // TODO: scan for the custom mount disk to know the underlying fs and set it proper
fstype := "ext4" fstype := "ext4"
mountOptions := []string{"ro"} mountOptions := []string{"ro"}
// TODO: Are custom mounts always rw?ro?depends? Clarify.
// Persistent needs to be RW // Persistent needs to be RW
if strings.Contains(what, "COS_PERSISTENT") { if strings.Contains(what, "COS_PERSISTENT") {
mountOptions = []string{"rw"} mountOptions = []string{"rw"}
} }
err = multierror.Append(err, s.MountOP( err2 := s.MountOP(
what, what,
s.path(where), s.path(where),
fstype, fstype,
mountOptions, mountOptions,
10*time.Second, 3*time.Second,
)(ctx)) )(ctx)
// If its COS_OEM and it fails then we can safely ignore, as it's not mandatory to have COS_OEM
if err2 != nil && !strings.Contains(what, "COS_OEM") {
err = multierror.Append(err, err2)
}
} }
s.Logger.Err(err.ErrorOrNil()).Send() internalUtils.Log.Err(err.ErrorOrNil()).Send()
return err.ErrorOrNil() return err.ErrorOrNil()
}), }),
@@ -277,7 +287,7 @@ func (s *State) MountCustomBindsDagStep(g *herd.Graph) error {
herd.WithCallback( herd.WithCallback(
func(ctx context.Context) error { func(ctx context.Context) error {
var err *multierror.Error var err *multierror.Error
s.Logger.Debug().Strs("mounts", s.BindMounts).Msg("Mounting binds") internalUtils.Log.Debug().Strs("mounts", s.BindMounts).Msg("Mounting binds")
for _, p := range s.BindMounts { for _, p := range s.BindMounts {
op := mountBind(p, s.Rootdir, s.StateDir) op := mountBind(p, s.Rootdir, s.StateDir)
@@ -288,11 +298,11 @@ func (s *State) MountCustomBindsDagStep(g *herd.Graph) error {
} }
// Append to errors only if it's not an already mounted error // Append to errors only if it's not an already mounted error
if err2 != nil && !errors.Is(err2, cnst.ErrAlreadyMounted) { if err2 != nil && !errors.Is(err2, cnst.ErrAlreadyMounted) {
log.Logger.Err(err2).Send() internalUtils.Log.Err(err2).Send()
err = multierror.Append(err, err2) err = multierror.Append(err, err2)
} }
} }
log.Logger.Err(err.ErrorOrNil()).Send() internalUtils.Log.Err(err.ErrorOrNil()).Send()
return err.ErrorOrNil() return err.ErrorOrNil()
}, },
), ),
@@ -345,11 +355,111 @@ func (s *State) WriteSentinelDagStep(g *herd.Graph) error {
sentinel = "live_mode" sentinel = "live_mode"
} }
s.Logger.Info().Str("to", sentinel).Msg("Setting sentinel file") internalUtils.Log.Info().Str("to", sentinel).Msg("Setting sentinel file")
err = os.WriteFile(filepath.Join("/run/cos/", sentinel), []byte("1"), os.ModePerm) err = os.WriteFile(filepath.Join("/run/cos/", sentinel), []byte("1"), os.ModePerm)
if err != nil { if err != nil {
return err return err
} }
// Lets add a uki sentinel as well!
if strings.Contains(cmdlineS, "rd.immucore.uki") {
err = os.WriteFile("/run/cos/uki_mode", []byte("1"), os.ModePerm)
if err != nil {
return err
}
}
return nil return nil
})) }))
} }
// UKIBootInitDagStep tries to launch /sbin/init in root and pass over the system
// booting to the real init process
// Drops to emergency if not able to. Panic if it cant even launch emergency
func (s *State) UKIBootInitDagStep(g *herd.Graph, deps ...string) error {
return g.Add(cnst.OpUkiInit,
herd.WithDeps(deps...),
herd.WeakDeps,
herd.WithCallback(func(ctx context.Context) error {
// Print dag before exit, otherwise its never printed as we never exit the program
internalUtils.Log.Info().Msg(s.WriteDAG(g))
internalUtils.Log.Debug().Msg("Executing init callback!")
internalUtils.CloseLogFiles()
if err := unix.Exec("/sbin/init", []string{"/sbin/init", "--system"}, os.Environ()); err != nil {
internalUtils.Log.Err(err).Msg("running init")
// drop to emergency shell
if err := unix.Exec("/bin/bash", []string{"/bin/bash"}, os.Environ()); err != nil {
internalUtils.Log.Fatal().Msg("Could not drop to emergency shell")
}
}
return nil
}))
}
// UKIRemountRootRODagStep remount root read only
func (s *State) UKIRemountRootRODagStep(g *herd.Graph, deps ...string) error {
return g.Add(cnst.OpRemountRootRO,
herd.WithDeps(deps...),
herd.WithCallback(func(ctx context.Context) error {
return syscall.Mount("/", "/", "rootfs", syscall.MS_REMOUNT|syscall.MS_RDONLY, "")
}),
)
}
// UKIUdevDaemon launches the udevd daemon and triggers+settles in order to discover devices
// Needed if we expect to find devices by label...
func (s *State) UKIUdevDaemon(g *herd.Graph) error {
return g.Add(cnst.OpUkiUdev,
herd.WithCallback(func(ctx context.Context) error {
// Should probably figure out other udevd binaries....
var udevBin string
if _, err := os.Stat("/usr/lib/systemd/systemd-udevd"); !os.IsNotExist(err) {
udevBin = "/usr/lib/systemd/systemd-udevd"
}
cmd := fmt.Sprintf("%s --daemon", udevBin)
out, err := internalUtils.CommandWithPath(cmd)
internalUtils.Log.Debug().Str("out", out).Str("cmd", cmd).Msg("Udev daemon")
if err != nil {
internalUtils.Log.Err(err).Msg("Udev daemon")
return err
}
out, err = internalUtils.CommandWithPath("udevadm trigger")
internalUtils.Log.Debug().Str("out", out).Msg("Udev trigger")
if err != nil {
internalUtils.Log.Err(err).Msg("Udev trigger")
return err
}
out, err = internalUtils.CommandWithPath("udevadm settle")
internalUtils.Log.Debug().Str("out", out).Msg("Udev settle")
if err != nil {
internalUtils.Log.Err(err).Msg("Udev settle")
return err
}
return nil
}),
)
}
// LoadKernelModules loads kernel modules needed during uki boot to load the disks for.
// Mainly block devices and net devices
// probably others down the line
func (s *State) LoadKernelModules(g *herd.Graph) error {
return g.Add("kernel-modules",
herd.WithCallback(func(ctx context.Context) error {
drivers, err := kdetect.ProbeKernelModules("")
if err != nil {
internalUtils.Log.Err(err).Msg("Detecting needed modules")
}
internalUtils.Log.Debug().Strs("drivers", drivers).Msg("Detecting needed modules")
for _, driver := range drivers {
cmd := fmt.Sprintf("modprobe %s", driver)
out, err := internalUtils.CommandWithPath(cmd)
if err != nil {
internalUtils.Log.Err(err).Str("out", out).Msg("modprobe")
}
}
return nil
}),
)
}

52
pkg/mount/dag_uki_boot.go Normal file
View File

@@ -0,0 +1,52 @@
package mount
import (
cnst "github.com/kairos-io/immucore/internal/constants"
"github.com/spectrocloud-labs/herd"
)
// RegisterUKI registers the dag for booting from UKI
func (s *State) RegisterUKI(g *herd.Graph) error {
// Write sentinel
s.LogIfError(s.WriteSentinelDagStep(g), "sentinel")
s.LogIfError(s.LoadKernelModules(g), "kernel modules")
// Udev for devices discovery
s.LogIfError(s.UKIUdevDaemon(g), "udev")
// Run rootfs stage
s.LogIfError(s.RootfsStageDagStep(g, cnst.OpSentinel), "uki rootfs")
// Remount root RO
s.LogIfError(s.UKIRemountRootRODagStep(g, cnst.OpRootfsHook), "remount root")
// Mount base overlay under /run/overlay
s.LogIfError(s.MountBaseOverlayDagStep(g), "base overlay")
// Populate state bind mounts, overlay mounts, custom-mounts from /run/cos/cos-layout.env
// Requires stage rootfs to have run, which usually creates the cos-layout.env file
s.LogIfError(s.LoadEnvLayoutDagStep(g, cnst.OpRootfsHook), "loading cos-layout.env")
// Mount custom overlays loaded from the /run/cos/cos-layout.env file
s.LogIfError(s.MountCustomOverlayDagStep(g), "custom overlays mount")
// Mount custom mounts loaded from the /run/cos/cos-layout.env file
s.LogIfError(s.MountCustomMountsDagStep(g), "custom mounts mount")
// Mount custom binds loaded from the /run/cos/cos-layout.env file
// Depends on mount binds as that usually mounts COS_PERSISTENT
s.LogIfError(s.MountCustomBindsDagStep(g), "custom binds mount")
// run initramfs stage
s.LogIfError(s.InitramfsStageDagStep(g, cnst.OpMountBind), "uki initramfs")
s.LogIfError(g.Add(cnst.OpWriteFstab,
herd.WithDeps(cnst.OpLoadConfig, cnst.OpCustomMounts, cnst.OpMountBind, cnst.OpOverlayMount),
herd.WeakDeps,
herd.WithCallback(s.WriteFstab(s.path("/etc/fstab")))), "fstab")
// Handover to /sbin/init
_ = s.UKIBootInitDagStep(g, cnst.OpRemountRootRO, cnst.OpRootfsHook, cnst.OpInitramfsHook)
return nil
}

View File

@@ -62,14 +62,13 @@ func mountBind(mountpoint, root, stateTarget string) mountOperation {
Type: "overlay", Type: "overlay",
Source: stateDir, Source: stateDir,
Options: []string{ Options: []string{
//"defaults",
"bind", "bind",
}, },
} }
internalUtils.Log.Debug().Str("mountpoint", mountpoint).Str("root", root).Str("bindMountPath", bindMountPath).Msg("BIND")
tmpFstab := internalUtils.MountToFstab(tmpMount) tmpFstab := internalUtils.MountToFstab(tmpMount)
tmpFstab.File = internalUtils.CleanSysrootForFstab(fmt.Sprintf("/%s", mountpoint)) tmpFstab.File = internalUtils.CleanSysrootForFstab(fmt.Sprintf("/%s", mountpoint))
tmpFstab.Spec = strings.ReplaceAll(tmpFstab.Spec, root, "") tmpFstab.Spec = internalUtils.CleanSysrootForFstab(tmpFstab.Spec)
return mountOperation{ return mountOperation{
MountOption: tmpMount, MountOption: tmpMount,
FstabEntry: *tmpFstab, FstabEntry: *tmpFstab,

View File

@@ -4,10 +4,8 @@ import (
"github.com/containerd/containerd/mount" "github.com/containerd/containerd/mount"
"github.com/deniswernert/go-fstab" "github.com/deniswernert/go-fstab"
"github.com/kairos-io/immucore/internal/constants" "github.com/kairos-io/immucore/internal/constants"
internalUtils "github.com/kairos-io/immucore/internal/utils"
"github.com/moby/sys/mountinfo" "github.com/moby/sys/mountinfo"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"os"
) )
type mountOperation struct { type mountOperation struct {
@@ -18,23 +16,23 @@ type mountOperation struct {
} }
func (m mountOperation) run() error { func (m mountOperation) run() error {
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).With().Str("what", m.MountOption.Source).Str("where", m.Target).Str("type", m.MountOption.Type).Strs("options", m.MountOption.Options).Logger() internalUtils.Log.With().Str("what", m.MountOption.Source).Str("where", m.Target).Str("type", m.MountOption.Type).Strs("options", m.MountOption.Options).Logger()
if m.PrepareCallback != nil { if m.PrepareCallback != nil {
if err := m.PrepareCallback(); err != nil { if err := m.PrepareCallback(); err != nil {
log.Logger.Err(err).Msg("executing mount callback") internalUtils.Log.Err(err).Msg("executing mount callback")
return err return err
} }
} }
//TODO: not only check if mounted but also if the type,options and source are the same? //TODO: not only check if mounted but also if the type,options and source are the same?
mounted, err := mountinfo.Mounted(m.Target) mounted, err := mountinfo.Mounted(m.Target)
if err != nil { if err != nil {
log.Logger.Err(err).Msg("checking mount status") internalUtils.Log.Err(err).Msg("checking mount status")
return err return err
} }
if mounted { if mounted {
log.Logger.Debug().Msg("Already mounted") internalUtils.Log.Debug().Msg("Already mounted")
return constants.ErrAlreadyMounted return constants.ErrAlreadyMounted
} }
log.Logger.Debug().Msg("mount ready") internalUtils.Log.Debug().Msg("mount ready")
return mount.All([]mount.Mount{m.MountOption}, m.Target) return mount.All([]mount.Mount{m.MountOption}, m.Target)
} }

View File

@@ -12,14 +12,11 @@ import (
"github.com/deniswernert/go-fstab" "github.com/deniswernert/go-fstab"
"github.com/kairos-io/immucore/internal/constants" "github.com/kairos-io/immucore/internal/constants"
internalUtils "github.com/kairos-io/immucore/internal/utils" internalUtils "github.com/kairos-io/immucore/internal/utils"
"github.com/kairos-io/kairos/pkg/utils"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/spectrocloud-labs/herd" "github.com/spectrocloud-labs/herd"
) )
type State struct { type State struct {
Logger zerolog.Logger
Rootdir string // where to mount the root partition e.g. /sysroot inside initrd with pivot, / with nopivot 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 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 TargetDevice string // e.g. /dev/disk/by-label/COS_ACTIVE
@@ -39,7 +36,6 @@ func (s *State) path(p ...string) string {
} }
func (s *State) WriteFstab(fstabFile string) func(context.Context) error { func (s *State) WriteFstab(fstabFile string) func(context.Context) error {
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).With().Logger()
return func(ctx context.Context) error { return func(ctx context.Context) error {
for _, fst := range s.fstabs { for _, fst := range s.fstabs {
select { select {
@@ -62,37 +58,38 @@ func (s *State) WriteFstab(fstabFile string) func(context.Context) error {
} }
// RunStageOp runs elemental run-stage stage. If its rootfs its special as it needs som symlinks // RunStageOp runs elemental run-stage stage. If its rootfs its special as it needs som symlinks
// If its uki we don't symlink as we already have everything in the sysroot
func (s *State) RunStageOp(stage string) func(context.Context) error { func (s *State) RunStageOp(stage string) func(context.Context) error {
return func(ctx context.Context) error { return func(ctx context.Context) error {
if stage == "rootfs" { if stage == "rootfs" && !internalUtils.IsUKI() {
if _, err := os.Stat("/system"); os.IsNotExist(err) { if _, err := os.Stat("/system"); os.IsNotExist(err) {
err = os.Symlink("/sysroot/system", "/system") err = os.Symlink("/sysroot/system", "/system")
if err != nil { if err != nil {
s.Logger.Err(err).Msg("creating symlink") internalUtils.Log.Err(err).Msg("creating symlink")
} }
} }
if _, err := os.Stat("/oem"); os.IsNotExist(err) { if _, err := os.Stat("/oem"); os.IsNotExist(err) {
err = os.Symlink("/sysroot/oem", "/oem") err = os.Symlink("/sysroot/oem", "/oem")
if err != nil { if err != nil {
s.Logger.Err(err).Msg("creating symlink") internalUtils.Log.Err(err).Msg("creating symlink")
} }
} }
} }
cmd := fmt.Sprintf("elemental run-stage %s", stage) cmd := fmt.Sprintf("/usr/bin/elemental run-stage %s", stage)
// If we set the level to debug, also call elemental with debug // If we set the level to debug, also call elemental with debug
if s.Logger.GetLevel() == zerolog.DebugLevel { if internalUtils.Log.GetLevel() == zerolog.DebugLevel {
cmd = fmt.Sprintf("%s --debug", cmd) cmd = fmt.Sprintf("%s --debug", cmd)
} }
output, err := utils.SH(cmd) output, err := internalUtils.CommandWithPath(cmd)
s.Logger.Debug().Msg(output) internalUtils.Log.Debug().Msg(output)
return err return err
} }
} }
// MountOP creates and executes a mount operation // MountOP creates and executes a mount operation
func (s *State) MountOP(what, where, t string, options []string, timeout time.Duration) func(context.Context) error { func (s *State) MountOP(what, where, t string, options []string, timeout time.Duration) func(context.Context) error {
log.Logger.With().Str("what", what).Str("where", where).Str("type", t).Strs("options", options).Logger() internalUtils.Log.With().Str("what", what).Str("where", where).Str("type", t).Strs("options", options).Logger()
return func(c context.Context) error { return func(c context.Context) error {
cc := time.After(timeout) cc := time.After(timeout)
@@ -101,7 +98,7 @@ func (s *State) MountOP(what, where, t string, options []string, timeout time.Du
default: default:
err := internalUtils.CreateIfNotExists(where) err := internalUtils.CreateIfNotExists(where)
if err != nil { if err != nil {
log.Logger.Err(err).Msg("Creating dir") internalUtils.Log.Err(err).Msg("Creating dir")
continue continue
} }
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
@@ -130,18 +127,18 @@ func (s *State) MountOP(what, where, t string, options []string, timeout time.Du
// only continue the loop if it's an error and not an already mounted error // only continue the loop if it's an error and not an already mounted error
if err != nil && !errors.Is(err, constants.ErrAlreadyMounted) { if err != nil && !errors.Is(err, constants.ErrAlreadyMounted) {
s.Logger.Err(err).Send() internalUtils.Log.Err(err).Send()
continue continue
} }
log.Logger.Debug().Msg("mount done") internalUtils.Log.Debug().Msg("mount done")
return nil return nil
case <-c.Done(): case <-c.Done():
e := fmt.Errorf("context canceled") e := fmt.Errorf("context canceled")
log.Logger.Err(e).Msg("mount canceled") internalUtils.Log.Err(e).Msg("mount canceled")
return e return e
case <-cc: case <-cc:
e := fmt.Errorf("timeout exhausted") e := fmt.Errorf("timeout exhausted")
log.Logger.Err(e).Msg("Mount timeout") internalUtils.Log.Err(e).Msg("Mount timeout")
return e return e
} }
} }
@@ -167,7 +164,7 @@ func (s *State) WriteDAG(g *herd.Graph) (out string) {
// Context can be empty // Context can be empty
func (s *State) LogIfError(e error, msgContext string) { func (s *State) LogIfError(e error, msgContext string) {
if e != nil { if e != nil {
s.Logger.Err(e).Msg(msgContext) internalUtils.Log.Err(e).Msg(msgContext)
} }
} }
@@ -176,7 +173,7 @@ func (s *State) LogIfError(e error, msgContext string) {
// Will also return the error // Will also return the error
func (s *State) LogIfErrorAndReturn(e error, msgContext string) error { func (s *State) LogIfErrorAndReturn(e error, msgContext string) error {
if e != nil { if e != nil {
s.Logger.Err(e).Msg(msgContext) internalUtils.Log.Err(e).Msg(msgContext)
} }
return e return e
} }
@@ -186,7 +183,7 @@ func (s *State) LogIfErrorAndReturn(e error, msgContext string) error {
// Will also panic // Will also panic
func (s *State) LogIfErrorAndPanic(e error, msgContext string) { func (s *State) LogIfErrorAndPanic(e error, msgContext string) {
if e != nil { if e != nil {
s.Logger.Err(e).Msg(msgContext) internalUtils.Log.Err(e).Msg(msgContext)
s.Logger.Fatal().Msg(e.Error()) internalUtils.Log.Fatal().Msg(e.Error())
} }
} }

View File

@@ -2,9 +2,6 @@ package mount_test
import ( import (
"context" "context"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"os"
"time" "time"
"github.com/kairos-io/immucore/pkg/mount" "github.com/kairos-io/immucore/pkg/mount"
@@ -24,7 +21,6 @@ var _ = Describe("mounting immutable setup", func() {
Context("simple invocation", func() { Context("simple invocation", func() {
It("generates normal dag", func() { It("generates normal dag", func() {
s := &mount.State{ s := &mount.State{
Logger: log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).With().Logger(),
Rootdir: "/", Rootdir: "/",
TargetImage: "/cOS/myimage.img", TargetImage: "/cOS/myimage.img",
TargetDevice: "/dev/disk/by-label/COS_LABEL", TargetDevice: "/dev/disk/by-label/COS_LABEL",
@@ -73,17 +69,10 @@ var _ = Describe("mounting immutable setup", func() {
func checkLiveCDDag(dag [][]herd.GraphEntry, actualDag string) { func checkLiveCDDag(dag [][]herd.GraphEntry, actualDag string) {
Expect(len(dag)).To(Equal(2), actualDag) Expect(len(dag)).To(Equal(2), actualDag)
Expect(len(dag[0])).To(Equal(1), actualDag) Expect(len(dag[0])).To(Equal(1), actualDag)
Expect(len(dag[1])).To(Equal(2), actualDag) Expect(len(dag[1])).To(Equal(1), actualDag)
Expect(dag[0][0].Name).To(Equal("init")) Expect(dag[0][0].Name).To(Equal("init"))
Expect(dag[1][0].Name).To(Or( Expect(dag[1][0].Name).To(Equal("create-sentinel"))
Equal("mount-tmpfs"),
Equal("create-sentinel"),
), actualDag)
Expect(dag[1][1].Name).To(Or(
Equal("mount-tmpfs"),
Equal("create-sentinel"),
), actualDag)
} }
func checkDag(dag [][]herd.GraphEntry, actualDag string) { func checkDag(dag [][]herd.GraphEntry, actualDag string) {