mirror of
https://github.com/kairos-io/immucore.git
synced 2025-04-27 11:12:30 +00:00
Add tests and fix some issues that arised from testing (#74)
* Add tests and fix some issues that arised from testing Mainly around the cmdargs and how many items it returns. Also drop the iso target and jobs as its not necessary now Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com> * Lint Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com> --------- Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>
This commit is contained in:
parent
b0b326313b
commit
fecfbf8e92
40
.github/workflows/build-iso.yaml
vendored
40
.github/workflows/build-iso.yaml
vendored
@ -1,40 +0,0 @@
|
||||
name: Iso build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
concurrency:
|
||||
group: iso-build-${{ github.head_ref || github.ref }}-${{ github.repository }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
iso-build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- flavor: "core-opensuse-leap"
|
||||
steps:
|
||||
- name: Release space from worker
|
||||
run: |
|
||||
sudo rm -rf /usr/local/lib/android # will release about 10 GB if you don't need Android
|
||||
sudo rm -rf /usr/share/dotnet # will release about 20GB if you don't need .NET
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '^1.18'
|
||||
- name: Build iso
|
||||
run: |
|
||||
./earthly.sh +iso --FLAVOR=${{ matrix.flavor }}
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ matrix.flavor }}-immucore.iso.zip
|
||||
path: |
|
||||
build/*.iso
|
||||
build/*.sha256
|
10
.github/workflows/unit-tests.yaml
vendored
10
.github/workflows/unit-tests.yaml
vendored
@ -6,6 +6,16 @@ on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Run Lint checks
|
||||
run: |
|
||||
./earthly.sh +lint
|
||||
unit-tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
20
.golangci.yml
Normal file
20
.golangci.yml
Normal file
@ -0,0 +1,20 @@
|
||||
run:
|
||||
timeout: 5m
|
||||
tests: false
|
||||
linters:
|
||||
enable:
|
||||
- revive # replacement for golint
|
||||
- dupl # check duplicated code
|
||||
- goconst # check strings that can turn into constants
|
||||
- gofmt # check fmt
|
||||
- goheader # Check license headers, only checks files in current year
|
||||
- goimports # check imports
|
||||
- gocyclo # check complexity
|
||||
- govet
|
||||
- gosimple
|
||||
- ineffassign
|
||||
- unused
|
||||
- staticcheck
|
||||
- typecheck
|
||||
- godot
|
||||
- misspell
|
61
Earthfile
61
Earthfile
@ -79,63 +79,4 @@ dracut-artifacts:
|
||||
SAVE ARTIFACT 28immucore 28immucore
|
||||
SAVE ARTIFACT 02-kairos-setup-initramfs.conf 02-kairos-setup-initramfs.conf
|
||||
SAVE ARTIFACT 10-immucore.conf 10-immucore.conf
|
||||
SAVE ARTIFACT 50-kairos-initrd.conf 50-kairos-initrd.conf
|
||||
|
||||
build-dracut:
|
||||
FROM $BASE_IMAGE
|
||||
COPY +version/VERSION ./
|
||||
ARG VERSION=$(cat VERSION)
|
||||
ARG REMOVE_COS_MODULE
|
||||
COPY +build-immucore/immucore /usr/bin/immucore
|
||||
COPY --dir dracut/28immucore /usr/lib/dracut/modules.d/
|
||||
COPY dracut/*.conf /etc/dracut.conf.d/
|
||||
RUN ls -ltra /etc/dracut.conf.d/
|
||||
# (START) Remove cos-immutable-rootfs module
|
||||
RUN rm -Rf /usr/lib/dracut/modules.d/30cos-immutable-rootfs/
|
||||
RUN rm /etc/dracut.conf.d/02-cos-immutable-rootfs.conf
|
||||
RUN rm /etc/dracut.conf.d/02-cos-setup-initramfs.conf
|
||||
RUN rm /etc/dracut.conf.d/50-cos-initrd.conf
|
||||
# (END) Remove cos-immutable-rootfs module
|
||||
RUN kernel=$(ls /lib/modules | head -n1) && \
|
||||
dracut -f "/boot/initrd-${kernel}" "${kernel}" && \
|
||||
ln -sf "initrd-${kernel}" /boot/initrd
|
||||
ARG INITRD=$(readlink -f /boot/initrd)
|
||||
SAVE ARTIFACT $INITRD Initrd AS LOCAL build/initrd-$VERSION
|
||||
|
||||
elemental:
|
||||
FROM $OSBUILDER_IMAGE
|
||||
SAVE ARTIFACT --keep-own /usr/bin/elemental elemental
|
||||
|
||||
image:
|
||||
FROM $BASE_IMAGE
|
||||
COPY +version/VERSION ./
|
||||
ARG VERSION=$(cat VERSION)
|
||||
ARG INITRD=$(readlink -f /boot/initrd)
|
||||
COPY +build-dracut/Initrd $INITRD
|
||||
# For initrd use
|
||||
COPY +build-immucore/immucore /usr/bin/immucore
|
||||
COPY +elemental/elemental /usr/bin/elemental
|
||||
RUN ln -s /usr/lib/systemd/systemd /init
|
||||
SAVE IMAGE $FLAVOR-immucore:$VERSION
|
||||
|
||||
image-rootfs:
|
||||
FROM +image
|
||||
SAVE ARTIFACT --keep-own /. rootfs
|
||||
|
||||
grub-files:
|
||||
FROM alpine
|
||||
RUN apk add wget
|
||||
RUN wget https://raw.githubusercontent.com/c3os-io/c3os/master/overlay/files-iso/boot/grub2/grub.cfg -O grub.cfg
|
||||
SAVE ARTIFACT --keep-own grub.cfg grub.cfg
|
||||
|
||||
iso:
|
||||
FROM $OSBUILDER_IMAGE
|
||||
ARG ISO_NAME
|
||||
COPY +version/VERSION ./
|
||||
ARG VERSION=$(cat VERSION)
|
||||
WORKDIR /build
|
||||
COPY --keep-own +grub-files/grub.cfg /build/files-iso/boot/grub2/grub.cfg
|
||||
COPY --keep-own +image-rootfs/rootfs /build/rootfs
|
||||
RUN /entrypoint.sh --name $ISO_NAME --debug build-iso --squash-no-compression --date=false --local --overlay-iso /build/files-iso --output /build/ dir:/build/rootfs
|
||||
SAVE ARTIFACT /build/$ISO_NAME.iso iso AS LOCAL build/$ISO_NAME-$VERSION.iso
|
||||
SAVE ARTIFACT /build/$ISO_NAME.iso.sha256 sha256 AS LOCAL build/$ISO_NAME-$VERSION.iso.sha256
|
||||
SAVE ARTIFACT 50-kairos-initrd.conf 50-kairos-initrd.conf
|
1
go.mod
1
go.mod
@ -14,6 +14,7 @@ require (
|
||||
github.com/onsi/gomega v1.27.2
|
||||
github.com/rs/zerolog v1.29.0
|
||||
github.com/spectrocloud-labs/herd v0.4.2
|
||||
github.com/twpayne/go-vfs v1.7.2
|
||||
github.com/urfave/cli/v2 v2.24.4
|
||||
golang.org/x/sys v0.5.0
|
||||
)
|
||||
|
3
go.sum
3
go.sum
@ -723,6 +723,8 @@ github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG
|
||||
github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/twpayne/go-vfs v1.7.2 h1:ZNYMAXcu2Av8c109USrSGYm8dIIIV0xPlG19I2088Kw=
|
||||
github.com/twpayne/go-vfs v1.7.2/go.mod h1:1eni2ntkiiAHZG27xfLOO4CYvMR4Kw8V7rYiLeeolsQ=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
@ -936,6 +938,7 @@ golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -3,7 +3,7 @@ package constants
|
||||
import "errors"
|
||||
|
||||
func DefaultRWPaths() []string {
|
||||
// Default RW_PATHS to mount if not overriden by the cos-layout.env file
|
||||
// Default RW_PATHS to mount if not override by the cos-layout.env file
|
||||
return []string{"/etc", "/root", "/home", "/opt", "/srv", "/usr/local", "/var"}
|
||||
}
|
||||
|
||||
|
@ -1,16 +1,18 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/kairos-io/kairos/sdk/state"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/kairos-io/kairos/sdk/state"
|
||||
)
|
||||
|
||||
// BootStateToLabelDevice lets us know the device we need to mount sysroot on based on labels
|
||||
// BootStateToLabelDevice lets us know the device we need to mount sysroot on based on labels.
|
||||
func BootStateToLabelDevice() string {
|
||||
runtime, err := state.NewRuntime()
|
||||
if err != nil {
|
||||
@ -29,9 +31,9 @@ func BootStateToLabelDevice() string {
|
||||
}
|
||||
|
||||
// GetRootDir returns the proper dir to mount all the stuff
|
||||
// Useful if we want to move to a no-pivot boot
|
||||
// Useful if we want to move to a no-pivot boot.
|
||||
func GetRootDir() string {
|
||||
cmdline, _ := os.ReadFile("/proc/cmdline")
|
||||
cmdline, _ := os.ReadFile(GetHostProcCmdline())
|
||||
switch {
|
||||
case strings.Contains(string(cmdline), "rd.immucore.uki"):
|
||||
return "/"
|
||||
@ -54,7 +56,7 @@ func UniqueSlice(slice []string) []string {
|
||||
return list
|
||||
}
|
||||
|
||||
// ReadEnv will reaed an env file (key=value) and return a nice map
|
||||
// ReadEnv will read an env file (key=value) and return a nice map.
|
||||
func ReadEnv(file string) (map[string]string, error) {
|
||||
var envMap map[string]string
|
||||
var err error
|
||||
@ -75,7 +77,7 @@ func ReadEnv(file string) (map[string]string, error) {
|
||||
return envMap, err
|
||||
}
|
||||
|
||||
// CreateIfNotExists will check if a path exists and create it if needed
|
||||
// CreateIfNotExists will check if a path exists and create it if needed.
|
||||
func CreateIfNotExists(path string) error {
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return os.MkdirAll(path, os.ModePerm)
|
||||
@ -86,7 +88,7 @@ func CreateIfNotExists(path string) error {
|
||||
|
||||
// CleanupSlice will clean a slice of strings of empty items
|
||||
// Typos can be made on writing the cos-layout.env file and that could introduce empty items
|
||||
// In the lists that we need to go over, which causes bad stuff
|
||||
// In the lists that we need to go over, which causes bad stuff.
|
||||
func CleanupSlice(slice []string) []string {
|
||||
var cleanSlice []string
|
||||
for _, item := range slice {
|
||||
@ -98,37 +100,37 @@ func CleanupSlice(slice []string) []string {
|
||||
return cleanSlice
|
||||
}
|
||||
|
||||
// GetTarget gets the target image and device to mount in /sysroot
|
||||
func GetTarget(dryRun bool) (string, string) {
|
||||
var label string
|
||||
|
||||
label = BootStateToLabelDevice()
|
||||
// GetTarget gets the target image and device to mount in /sysroot.
|
||||
func GetTarget(dryRun bool) (string, string, error) {
|
||||
label := BootStateToLabelDevice()
|
||||
|
||||
// If dry run, or we are disabled return whatever values, we won't go much further
|
||||
if dryRun || DisableImmucore() {
|
||||
return "fake", label
|
||||
return "fake", label, nil
|
||||
}
|
||||
|
||||
imgs := ReadCMDLineArg("cos-img/filename=")
|
||||
imgs := CleanupSlice(ReadCMDLineArg("cos-img/filename="))
|
||||
|
||||
// If no image just panic here, we cannot longer continue
|
||||
if len(imgs) == 0 {
|
||||
if IsUKI() {
|
||||
imgs = []string{""}
|
||||
} else {
|
||||
Log.Fatal().Msg("could not get the image name from cmdline (i.e. cos-img/filename=/cOS/active.img)")
|
||||
msg := "could not get the image name from cmdline (i.e. cos-img/filename=/cOS/active.img)"
|
||||
Log.Error().Msg(msg)
|
||||
return "", "", errors.New(msg)
|
||||
}
|
||||
}
|
||||
|
||||
Log.Debug().Str("what", imgs[0]).Msg("Target device")
|
||||
Log.Debug().Str("what", label).Msg("Target label")
|
||||
return imgs[0], label
|
||||
return imgs[0], label, nil
|
||||
}
|
||||
|
||||
// DisableImmucore identifies if we need to be disabled
|
||||
// We disable if we boot from CD, netboot, squashfs recovery or have the rd.cos.disable stanza in cmdline
|
||||
// We disable if we boot from CD, netboot, squashfs recovery or have the rd.cos.disable stanza in cmdline.
|
||||
func DisableImmucore() bool {
|
||||
cmdline, _ := os.ReadFile("/proc/cmdline")
|
||||
cmdline, _ := os.ReadFile(GetHostProcCmdline())
|
||||
cmdlineS := string(cmdline)
|
||||
|
||||
return strings.Contains(cmdlineS, "live:LABEL") || strings.Contains(cmdlineS, "live:CDLABEL") ||
|
||||
@ -136,7 +138,7 @@ func DisableImmucore() bool {
|
||||
strings.Contains(cmdlineS, "rd.immucore.disable")
|
||||
}
|
||||
|
||||
// RootRW tells us if the mode to mount root
|
||||
// RootRW tells us if the mode to mount root.
|
||||
func RootRW() string {
|
||||
if len(ReadCMDLineArg("rd.cos.debugrw")) > 0 || len(ReadCMDLineArg("rd.immucore.debugrw")) > 0 {
|
||||
Log.Warn().Msg("Mounting root as RW")
|
||||
@ -146,7 +148,7 @@ func RootRW() string {
|
||||
}
|
||||
|
||||
// GetState returns the disk-by-label of the state partition to mount
|
||||
// This is only valid for either active/passive or normal recovery
|
||||
// This is only valid for either active/passive or normal recovery.
|
||||
func GetState() string {
|
||||
var label string
|
||||
runtime, err := state.NewRuntime()
|
||||
@ -164,14 +166,11 @@ func GetState() string {
|
||||
}
|
||||
|
||||
func IsUKI() bool {
|
||||
if len(ReadCMDLineArg("rd.immucore.uki")) > 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return len(ReadCMDLineArg("rd.immucore.uki")) > 0
|
||||
}
|
||||
|
||||
// CommandWithPath runs a command adding the usual PATH to environment
|
||||
// Useful under UKI as there is nothing setting the PATH
|
||||
// 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()
|
||||
@ -188,3 +187,13 @@ func CommandWithPath(c string) (string, error) {
|
||||
o, err := cmd.CombinedOutput()
|
||||
return string(o), err
|
||||
}
|
||||
|
||||
// GetHostProcCmdline returns the path to /proc/cmdline
|
||||
// Mainly used to override the cmdline during testing.
|
||||
func GetHostProcCmdline() string {
|
||||
proc := os.Getenv("HOST_PROC_CMDLINE")
|
||||
if proc == "" {
|
||||
return "/proc/cmdline"
|
||||
}
|
||||
return proc
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/rs/zerolog"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
var Log zerolog.Logger
|
||||
|
@ -2,20 +2,21 @@ package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/containerd/containerd/mount"
|
||||
"github.com/deniswernert/go-fstab"
|
||||
"github.com/kairos-io/kairos/sdk/state"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/containerd/containerd/mount"
|
||||
"github.com/deniswernert/go-fstab"
|
||||
"github.com/kairos-io/kairos/sdk/state"
|
||||
)
|
||||
|
||||
// https://github.com/kairos-io/packages/blob/7c3581a8ba6371e5ce10c3a98bae54fde6a505af/packages/system/dracut/immutable-rootfs/30cos-immutable-rootfs/cos-mount-layout.sh#L58
|
||||
|
||||
// ParseMount will return a proper full disk path based on UUID or LABEL given
|
||||
// input: LABEL=FOO:/mount
|
||||
// output: /dev/disk...:/mount
|
||||
// output: /dev/disk...:/mount .
|
||||
func ParseMount(s string) string {
|
||||
switch {
|
||||
case strings.Contains(s, "UUID="):
|
||||
@ -30,8 +31,9 @@ func ParseMount(s string) string {
|
||||
}
|
||||
|
||||
// ReadCMDLineArg will return the pair of arg=value for a given arg if it was passed on the cmdline
|
||||
// TODO: Split this into GetBool and GetValue to return decent defaults.
|
||||
func ReadCMDLineArg(arg string) []string {
|
||||
cmdLine, err := os.ReadFile("/proc/cmdline")
|
||||
cmdLine, err := os.ReadFile(GetHostProcCmdline())
|
||||
if err != nil {
|
||||
return []string{}
|
||||
}
|
||||
@ -43,7 +45,7 @@ func ReadCMDLineArg(arg string) []string {
|
||||
// For stanzas that have no value, we should return something better than an empty value
|
||||
// Otherwise anything can easily clean the value
|
||||
if dat[1] == "" {
|
||||
res = append(res, "true")
|
||||
res = append(res, "")
|
||||
} else {
|
||||
res = append(res, dat[1])
|
||||
}
|
||||
@ -52,7 +54,7 @@ func ReadCMDLineArg(arg string) []string {
|
||||
return res
|
||||
}
|
||||
|
||||
// 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 {
|
||||
_, err := CommandWithPath(fmt.Sprintf("findmnt %s", dev))
|
||||
return err == nil
|
||||
@ -60,7 +62,7 @@ func IsMounted(dev string) bool {
|
||||
|
||||
// DiskFSType will return the FS type for a given disk
|
||||
// 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 {
|
||||
out, e := CommandWithPath(fmt.Sprintf("blkid %s -s TYPE -o value", s))
|
||||
if e != nil {
|
||||
@ -85,7 +87,7 @@ func AppendSlash(path string) string {
|
||||
return path
|
||||
}
|
||||
|
||||
// MountToFstab transforms a mount.Mount into a fstab.Mount so we can transform existing mounts into the fstab format
|
||||
// MountToFstab transforms a mount.Mount into a fstab.Mount so we can transform existing mounts into the fstab format.
|
||||
func MountToFstab(m mount.Mount) *fstab.Mount {
|
||||
opts := map[string]string{}
|
||||
for _, o := range m.Options {
|
||||
@ -112,7 +114,7 @@ func MountToFstab(m mount.Mount) *fstab.Mount {
|
||||
// As we mount on /sysroot during initramfs but the fstab file is for the real init process, we need to remove
|
||||
// Any mentions to /sysroot from the fstab lines, otherwise they won't work
|
||||
// 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 {
|
||||
if IsUKI() {
|
||||
return path
|
||||
@ -126,13 +128,13 @@ func CleanSysrootForFstab(path string) string {
|
||||
|
||||
// Fsck will run fsck over the device
|
||||
// options are set on cmdline, but they are for systemd-fsck,
|
||||
// so we need to interpret ourselves
|
||||
// so we need to interpret ourselves.
|
||||
func Fsck(device string) error {
|
||||
if device == "tmpfs" {
|
||||
return nil
|
||||
}
|
||||
mode := ReadCMDLineArg("fsck.mode=")
|
||||
repair := ReadCMDLineArg("fsck.repair=")
|
||||
mode := CleanupSlice(ReadCMDLineArg("fsck.mode="))
|
||||
repair := CleanupSlice(ReadCMDLineArg("fsck.repair="))
|
||||
// Be safe with defaults
|
||||
if len(mode) == 0 {
|
||||
mode = []string{"auto"}
|
||||
@ -176,7 +178,7 @@ func Fsck(device string) error {
|
||||
|
||||
// MountProc will mount /proc
|
||||
// For now 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 skip this
|
||||
// in normal modes this should already be done by the initramfs process, so we can skip this.
|
||||
func MountProc() {
|
||||
_ = os.MkdirAll("/proc", 0755)
|
||||
if !IsMounted("/proc") {
|
||||
@ -185,13 +187,13 @@ func MountProc() {
|
||||
|
||||
}
|
||||
|
||||
// GetOemTimeout parses the cmdline to get the oem timeout to use. Defaults to 5 (converted into seconds afterwards)
|
||||
// GetOemTimeout parses the cmdline to get the oem timeout to use. Defaults to 5 (converted into seconds afterwards).
|
||||
func GetOemTimeout() int {
|
||||
var time []string
|
||||
|
||||
// Pick both stanzas until we deprecate the cos ones
|
||||
timeCos := ReadCMDLineArg("rd.cos.oemtimeout=")
|
||||
timeImmucore := ReadCMDLineArg("rd.immucore.oemtimeout=")
|
||||
timeCos := CleanupSlice(ReadCMDLineArg("rd.cos.oemtimeout="))
|
||||
timeImmucore := CleanupSlice(ReadCMDLineArg("rd.immucore.oemtimeout="))
|
||||
|
||||
if len(timeCos) != 0 {
|
||||
time = timeCos
|
||||
@ -203,7 +205,7 @@ func GetOemTimeout() int {
|
||||
if len(time) == 0 {
|
||||
return 5
|
||||
}
|
||||
converted, err := strconv.Atoi(time[1])
|
||||
converted, err := strconv.Atoi(time[0])
|
||||
if err != nil {
|
||||
return 5
|
||||
}
|
||||
@ -212,13 +214,14 @@ func GetOemTimeout() int {
|
||||
|
||||
// GetOverlayBase parses the cdmline and gets the overlay config
|
||||
// Format is rd.cos.overlay=tmpfs:20% or rd.cos.overlay=LABEL=$LABEL or rd.cos.overlay=UUID=$UUID
|
||||
// Notice that this can be later override by the config coming from cos-layout.env
|
||||
// Notice that this can be later override by the config coming from cos-layout.env .
|
||||
func GetOverlayBase() string {
|
||||
var overlayConfig []string
|
||||
|
||||
// Pick both stanzas until we deprecate the cos ones
|
||||
overlayConfigCos := ReadCMDLineArg("rd.cos.overlay=")
|
||||
overlayConfigImmucore := ReadCMDLineArg("rd.immucore.overlay=")
|
||||
// Clean up the slice in case the values are empty
|
||||
overlayConfigCos := CleanupSlice(ReadCMDLineArg("rd.cos.overlay="))
|
||||
overlayConfigImmucore := CleanupSlice(ReadCMDLineArg("rd.immucore.overlay="))
|
||||
|
||||
if len(overlayConfigCos) != 0 {
|
||||
overlayConfig = overlayConfigCos
|
||||
@ -231,22 +234,22 @@ func GetOverlayBase() string {
|
||||
return "tmpfs:20%"
|
||||
}
|
||||
|
||||
return overlayConfig[1]
|
||||
return overlayConfig[0]
|
||||
|
||||
}
|
||||
|
||||
// GetOemLabel will ge the oem label to mount, first from the cmdline and if that fails, from the runtime
|
||||
// This way users can override the oem label
|
||||
// This way users can override the oem label.
|
||||
func GetOemLabel() string {
|
||||
var oemLabel string
|
||||
// Pick both stanzas until we deprecate the cos ones
|
||||
oemLabelCos := ReadCMDLineArg("rd.cos.oemlabel=")
|
||||
oemLabelImmucore := ReadCMDLineArg("rd.immucore.oemlabel=")
|
||||
oemLabelCos := CleanupSlice(ReadCMDLineArg("rd.cos.oemlabel="))
|
||||
oemLabelImmucore := CleanupSlice(ReadCMDLineArg("rd.immucore.oemlabel="))
|
||||
if len(oemLabelCos) != 0 {
|
||||
oemLabel = oemLabelCos[1]
|
||||
oemLabel = oemLabelCos[0]
|
||||
}
|
||||
if len(oemLabelImmucore) != 0 {
|
||||
oemLabel = oemLabelImmucore[1]
|
||||
oemLabel = oemLabelImmucore[0]
|
||||
}
|
||||
|
||||
if oemLabel != "" {
|
||||
|
@ -4,7 +4,7 @@ import "runtime"
|
||||
|
||||
var (
|
||||
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 = "none"
|
||||
)
|
||||
|
||||
@ -22,7 +22,7 @@ type BuildInfo struct {
|
||||
GoVersion string `json:"go_version,omitempty"`
|
||||
}
|
||||
|
||||
// Get returns build info
|
||||
// Get returns build info.
|
||||
func Get() BuildInfo {
|
||||
v := BuildInfo{
|
||||
Version: GetVersion(),
|
||||
|
10
main.go
10
main.go
@ -3,12 +3,13 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/kairos-io/immucore/internal/utils"
|
||||
"github.com/kairos-io/immucore/internal/version"
|
||||
"github.com/kairos-io/immucore/pkg/mount"
|
||||
"github.com/spectrocloud-labs/herd"
|
||||
"github.com/urfave/cli/v2"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Apply Immutability profiles.
|
||||
@ -27,12 +28,15 @@ func main() {
|
||||
v := version.Get()
|
||||
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(utils.GetHostProcCmdline())
|
||||
utils.Log.Debug().Str("content", string(cmdline)).Msg("cmdline")
|
||||
g := herd.DAG(herd.EnableInit)
|
||||
|
||||
// Get targets and state
|
||||
targetImage, targetDevice = utils.GetTarget(c.Bool("dry-run"))
|
||||
targetImage, targetDevice, err = utils.GetTarget(c.Bool("dry-run"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
state = &mount.State{
|
||||
Rootdir: utils.GetRootDir(),
|
||||
|
@ -5,10 +5,9 @@ import (
|
||||
)
|
||||
|
||||
// RegisterLiveMedia registers the dag for booting from live media/netboot
|
||||
// This sets the sentinel
|
||||
// This sets the sentinel.
|
||||
func (s *State) RegisterLiveMedia(g *herd.Graph) error {
|
||||
var err error
|
||||
// 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")
|
||||
return err
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
// RegisterNormalBoot registers a dag for a normal boot, where we want to mount all the pieces that make up the
|
||||
// final system. This mounts root, oem, runs rootfs, loads the cos-layout.env file and mounts custom stuff from that file
|
||||
// and finally writes the fstab.
|
||||
// This is all done on initramfs, very early, and ends up pivoting to the final system, usually under /sysroot
|
||||
// This is all done on initramfs, very early, and ends up pivoting to the final system, usually under /sysroot.
|
||||
func (s *State) RegisterNormalBoot(g *herd.Graph) error {
|
||||
var err error
|
||||
|
||||
|
@ -4,6 +4,12 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
cnst "github.com/kairos-io/immucore/internal/constants"
|
||||
internalUtils "github.com/kairos-io/immucore/internal/utils"
|
||||
@ -12,14 +18,9 @@ import (
|
||||
"github.com/mudler/go-kdetect"
|
||||
"github.com/spectrocloud-labs/herd"
|
||||
"golang.org/x/sys/unix"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MountTmpfsDagStep adds the step to mount /tmp
|
||||
// MountTmpfsDagStep adds the step to mount /tmp .
|
||||
func (s *State) MountTmpfsDagStep(g *herd.Graph) error {
|
||||
return g.Add(cnst.OpMountTmpfs, herd.WithCallback(s.MountOP("tmpfs", "/tmp", "tmpfs", []string{"rw"}, 10*time.Second)))
|
||||
}
|
||||
@ -27,7 +28,7 @@ func (s *State) MountTmpfsDagStep(g *herd.Graph) error {
|
||||
// MountRootDagStep will add the step to mount the Rootdir for the system
|
||||
// 1 - mount the state partition to find the images (active/passive/recovery)
|
||||
// 2 - mount the image as a loop device
|
||||
// 3 - Mount the labels as /sysroot
|
||||
// 3 - Mount the labels as /sysroot .
|
||||
func (s *State) MountRootDagStep(g *herd.Graph) error {
|
||||
var err error
|
||||
|
||||
@ -82,7 +83,7 @@ func (s *State) MountRootDagStep(g *herd.Graph) error {
|
||||
s.MountOP(
|
||||
s.TargetDevice,
|
||||
s.Rootdir,
|
||||
"ext4", // TODO: Get this just in time? Currently if using DiskFSType is run immediately which is bad becuase its not mounted
|
||||
"ext4", // TODO: Get this just in time? Currently if using DiskFSType is run immediately which is bad because its not mounted
|
||||
[]string{
|
||||
s.RootMountMode,
|
||||
"suid",
|
||||
@ -110,7 +111,7 @@ 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, deps ...string) error {
|
||||
return g.Add(cnst.OpLoadConfig,
|
||||
herd.WithDeps(deps...),
|
||||
@ -163,8 +164,8 @@ func (s *State) LoadEnvLayoutDagStep(g *herd.Graph, deps ...string) error {
|
||||
// Parse custom mounts also from cmdline (rd.immucore.mount=)
|
||||
// Parse custom mounts also from env file (VOLUMES)
|
||||
var mounts []string
|
||||
mounts = internalUtils.ReadCMDLineArg("rd.cos.mount=")
|
||||
mounts = append(mounts, internalUtils.ReadCMDLineArg("rd.immucore.mount=")...)
|
||||
mounts = internalUtils.CleanupSlice(internalUtils.ReadCMDLineArg("rd.cos.mount="))
|
||||
mounts = append(mounts, internalUtils.CleanupSlice(internalUtils.ReadCMDLineArg("rd.immucore.mount="))...)
|
||||
mounts = append(mounts, env["VOLUMES"])
|
||||
for _, v := range mounts {
|
||||
addLine(internalUtils.ParseMount(v))
|
||||
@ -174,7 +175,7 @@ func (s *State) LoadEnvLayoutDagStep(g *herd.Graph, deps ...string) error {
|
||||
}))
|
||||
}
|
||||
|
||||
// MountOemDagStep will add mounting COS_OEM partition under s.Rootdir + /oem
|
||||
// MountOemDagStep will add mounting COS_OEM partition under s.Rootdir + /oem .
|
||||
func (s *State) MountOemDagStep(g *herd.Graph, deps ...string) error {
|
||||
return g.Add(cnst.OpMountOEM,
|
||||
herd.WithDeps(deps...),
|
||||
@ -195,7 +196,7 @@ func (s *State) MountOemDagStep(g *herd.Graph, deps ...string) error {
|
||||
}
|
||||
|
||||
// MountBaseOverlayDagStep will add mounting /run/overlay as an overlay dir
|
||||
// Requires the config-load step because some parameters can come from there
|
||||
// Requires the config-load step because some parameters can come from there.
|
||||
func (s *State) MountBaseOverlayDagStep(g *herd.Graph) error {
|
||||
return g.Add(cnst.OpMountBaseOverlay,
|
||||
herd.WithDeps(cnst.OpLoadConfig),
|
||||
@ -225,7 +226,7 @@ func (s *State) MountBaseOverlayDagStep(g *herd.Graph) error {
|
||||
)
|
||||
}
|
||||
|
||||
// MountCustomOverlayDagStep will add mounting s.OverlayDirs under /run/overlay
|
||||
// MountCustomOverlayDagStep will add mounting s.OverlayDirs under /run/overlay .
|
||||
func (s *State) MountCustomOverlayDagStep(g *herd.Graph) error {
|
||||
return g.Add(cnst.OpOverlayMount,
|
||||
herd.WithDeps(cnst.OpLoadConfig, cnst.OpMountBaseOverlay),
|
||||
@ -250,7 +251,7 @@ func (s *State) MountCustomOverlayDagStep(g *herd.Graph) error {
|
||||
)
|
||||
}
|
||||
|
||||
// MountCustomMountsDagStep will add mounting s.CustomMounts
|
||||
// MountCustomMountsDagStep will add mounting s.CustomMounts .
|
||||
func (s *State) MountCustomMountsDagStep(g *herd.Graph) error {
|
||||
return g.Add(cnst.OpCustomMounts,
|
||||
herd.WithDeps(cnst.OpLoadConfig),
|
||||
@ -287,7 +288,7 @@ func (s *State) MountCustomMountsDagStep(g *herd.Graph) error {
|
||||
}
|
||||
|
||||
// MountCustomBindsDagStep will add mounting s.BindMounts
|
||||
// mount state is defined over a custom mount (/usr/local/.state for instance, needs to be mounted over a device)
|
||||
// mount state is defined over a custom mount (/usr/local/.state for instance, needs to be mounted over a device).
|
||||
func (s *State) MountCustomBindsDagStep(g *herd.Graph) error {
|
||||
return g.Add(cnst.OpMountBind,
|
||||
herd.WithDeps(cnst.OpCustomMounts, cnst.OpLoadConfig),
|
||||
@ -317,7 +318,7 @@ func (s *State) MountCustomBindsDagStep(g *herd.Graph) error {
|
||||
}
|
||||
|
||||
// WriteFstabDagStep will add writing the final fstab file with all the mounts
|
||||
// Depends on everything but weak, so it will still try to write
|
||||
// Depends on everything but weak, so it will still try to write.
|
||||
func (s *State) WriteFstabDagStep(g *herd.Graph) error {
|
||||
return g.Add(cnst.OpWriteFstab,
|
||||
herd.WithDeps(cnst.OpMountRoot, cnst.OpDiscoverState, cnst.OpLoadConfig, cnst.OpMountOEM, cnst.OpCustomMounts, cnst.OpMountBind, cnst.OpOverlayMount),
|
||||
@ -326,7 +327,7 @@ func (s *State) WriteFstabDagStep(g *herd.Graph) error {
|
||||
}
|
||||
|
||||
// WriteSentinelDagStep sets the sentinel file to identify the boot mode.
|
||||
// This is used by several things to know in which state they are, for example cloud configs
|
||||
// This is used by several things to know in which state they are, for example cloud configs.
|
||||
func (s *State) WriteSentinelDagStep(g *herd.Graph) error {
|
||||
return g.Add(cnst.OpSentinel,
|
||||
herd.WithCallback(func(ctx context.Context) error {
|
||||
@ -356,7 +357,7 @@ func (s *State) WriteSentinelDagStep(g *herd.Graph) error {
|
||||
|
||||
// Workaround for runtime not detecting netboot/rd.cos.disable/rd.immucore.disable as live_mode
|
||||
// TODO: drop once the netboot/rd.cos.disable detection change is on the kairos sdk
|
||||
cmdline, err := os.ReadFile("/proc/cmdline")
|
||||
cmdline, _ := os.ReadFile(internalUtils.GetHostProcCmdline())
|
||||
cmdlineS := string(cmdline)
|
||||
if strings.Contains(cmdlineS, "netboot") || len(internalUtils.ReadCMDLineArg("rd.cos.disable")) > 0 || len(internalUtils.ReadCMDLineArg("rd.immucore.disable")) > 0 {
|
||||
sentinel = "live_mode"
|
||||
@ -382,7 +383,7 @@ func (s *State) WriteSentinelDagStep(g *herd.Graph) error {
|
||||
|
||||
// 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
|
||||
// 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...),
|
||||
@ -403,7 +404,7 @@ func (s *State) UKIBootInitDagStep(g *herd.Graph, deps ...string) error {
|
||||
}))
|
||||
}
|
||||
|
||||
// UKIRemountRootRODagStep remount root read only
|
||||
// UKIRemountRootRODagStep remount root read only.
|
||||
func (s *State) UKIRemountRootRODagStep(g *herd.Graph, deps ...string) error {
|
||||
return g.Add(cnst.OpRemountRootRO,
|
||||
herd.WithDeps(deps...),
|
||||
@ -450,7 +451,7 @@ func (s *State) UKIUdevDaemon(g *herd.Graph) error {
|
||||
|
||||
// LoadKernelModules loads kernel modules needed during uki boot to load the disks for.
|
||||
// Mainly block devices and net devices
|
||||
// probably others down the line
|
||||
// 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 {
|
||||
|
@ -5,7 +5,7 @@ import (
|
||||
"github.com/spectrocloud-labs/herd"
|
||||
)
|
||||
|
||||
// RegisterUKI registers the dag for booting from UKI
|
||||
// RegisterUKI registers the dag for booting from UKI.
|
||||
func (s *State) RegisterUKI(g *herd.Graph) error {
|
||||
// Write sentinel
|
||||
s.LogIfError(s.WriteSentinelDagStep(g), "sentinel")
|
||||
|
@ -1,23 +0,0 @@
|
||||
package mount
|
||||
|
||||
import (
|
||||
"github.com/kairos-io/immucore/internal/utils"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("mount utils", func() {
|
||||
BeforeEach(func() {
|
||||
})
|
||||
|
||||
Context("ReadCMDLineArg", func() {
|
||||
It("splits arguments from cmdline", func() {
|
||||
Skip("No way of overriding the cmdline yet")
|
||||
Expect(len(utils.ReadCMDLineArg("testvalue/key="))).To(Equal(1))
|
||||
})
|
||||
It("returns properly for stanzas without value", func() {
|
||||
Skip("No way of overriding the cmdline yet")
|
||||
Expect(len(utils.ReadCMDLineArg("singlevalue"))).To(Equal(1))
|
||||
})
|
||||
})
|
||||
})
|
@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/kairos-io/kairos/pkg/utils"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
@ -13,6 +12,7 @@ import (
|
||||
"github.com/deniswernert/go-fstab"
|
||||
"github.com/kairos-io/immucore/internal/constants"
|
||||
internalUtils "github.com/kairos-io/immucore/internal/utils"
|
||||
"github.com/kairos-io/kairos/pkg/utils"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/spectrocloud-labs/herd"
|
||||
)
|
||||
@ -62,7 +62,7 @@ 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
|
||||
// If its uki we don't symlink as we already have everything in the sysroot
|
||||
// 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 {
|
||||
return func(ctx context.Context) error {
|
||||
if stage == "rootfs" && !internalUtils.IsUKI() {
|
||||
@ -91,7 +91,7 @@ func (s *State) RunStageOp(stage string) func(context.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
internalUtils.Log.With().Str("what", what).Str("where", where).Str("type", t).Strs("options", options).Logger()
|
||||
|
||||
@ -149,7 +149,7 @@ func (s *State) MountOP(what, where, t string, options []string, timeout time.Du
|
||||
}
|
||||
}
|
||||
|
||||
// WriteDAG writes the dag
|
||||
// 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)
|
||||
@ -165,7 +165,7 @@ func (s *State) WriteDAG(g *herd.Graph) (out string) {
|
||||
}
|
||||
|
||||
// LogIfError will log if there is an error with the given context as message
|
||||
// Context can be empty
|
||||
// Context can be empty.
|
||||
func (s *State) LogIfError(e error, msgContext string) {
|
||||
if e != nil {
|
||||
internalUtils.Log.Err(e).Msg(msgContext)
|
||||
@ -174,7 +174,7 @@ func (s *State) LogIfError(e error, msgContext string) {
|
||||
|
||||
// LogIfErrorAndReturn will log if there is an error with the given context as message
|
||||
// Context can be empty
|
||||
// Will also return the error
|
||||
// Will also return the error.
|
||||
func (s *State) LogIfErrorAndReturn(e error, msgContext string) error {
|
||||
if e != nil {
|
||||
internalUtils.Log.Err(e).Msg(msgContext)
|
||||
@ -184,7 +184,7 @@ func (s *State) LogIfErrorAndReturn(e error, msgContext string) error {
|
||||
|
||||
// LogIfErrorAndPanic will log if there is an error with the given context as message
|
||||
// Context can be empty
|
||||
// Will also panic
|
||||
// Will also panic.
|
||||
func (s *State) LogIfErrorAndPanic(e error, msgContext string) {
|
||||
if e != nil {
|
||||
internalUtils.Log.Err(e).Msg(msgContext)
|
||||
|
173
tests/mocks/ghw_mock.go
Normal file
173
tests/mocks/ghw_mock.go
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
Copyright © 2022 SUSE LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/jaypipes/ghw/pkg/block"
|
||||
"github.com/jaypipes/ghw/pkg/context"
|
||||
"github.com/jaypipes/ghw/pkg/linuxpath"
|
||||
)
|
||||
|
||||
// GhwMock is used to construct a fake disk to present to ghw when scanning block devices
|
||||
// The way this works is ghw will use the existing files in the system to determine the different disks, partitions and
|
||||
// mountpoints. It uses /sys/block, /proc/self/mounts and /run/udev/data to gather everything
|
||||
// It also has an entrypoint to overwrite the root dir from which the paths are constructed so that allows us to override
|
||||
// it easily and make it read from a different location.
|
||||
// This mock is used to construct a fake FS with all its needed files on a different chroot and just add a Disk with its
|
||||
// partitions and let the struct do its thing creating files and mountpoints and such
|
||||
// You can even just pass no disks to simulate a system in which there is no disk/no cos partitions.
|
||||
type GhwMock struct {
|
||||
chroot string
|
||||
paths *linuxpath.Paths
|
||||
disks []block.Disk
|
||||
mounts []string
|
||||
}
|
||||
|
||||
// AddDisk adds a disk to GhwMock.
|
||||
func (g *GhwMock) AddDisk(disk block.Disk) {
|
||||
g.disks = append(g.disks, disk)
|
||||
}
|
||||
|
||||
// AddPartitionToDisk will add a partition to the given disk and call Clean+CreateDevices, so we recreate all files
|
||||
// It makes no effort checking if the disk exists.
|
||||
func (g *GhwMock) AddPartitionToDisk(diskName string, partition *block.Partition) {
|
||||
for _, disk := range g.disks {
|
||||
if disk.Name == diskName {
|
||||
disk.Partitions = append(disk.Partitions, partition)
|
||||
g.Clean()
|
||||
g.CreateDevices()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CreateDevices will create a new context and paths for ghw using the Chroot value as base, then set the env var GHW_ROOT so the
|
||||
// ghw library picks that up and then iterate over the disks and partitions and create the necessary files.
|
||||
func (g *GhwMock) CreateDevices() {
|
||||
d, _ := os.MkdirTemp("", "ghwmock")
|
||||
g.chroot = d
|
||||
ctx := context.New()
|
||||
ctx.Chroot = d
|
||||
g.paths = linuxpath.New(ctx)
|
||||
_ = os.Setenv("GHW_CHROOT", g.chroot)
|
||||
// Create the /sys/block dir
|
||||
_ = os.MkdirAll(g.paths.SysBlock, 0755)
|
||||
// Create the /run/udev/data dir
|
||||
_ = os.MkdirAll(g.paths.RunUdevData, 0755)
|
||||
// Create only the /proc/self dir, we add the mounts file afterwards
|
||||
procDir, _ := filepath.Split(g.paths.ProcMounts)
|
||||
_ = os.MkdirAll(procDir, 0755)
|
||||
|
||||
for indexDisk, disk := range g.disks {
|
||||
// For each dir we create the /sys/block/DISK_NAME
|
||||
diskPath := filepath.Join(g.paths.SysBlock, disk.Name)
|
||||
_ = os.Mkdir(diskPath, 0755)
|
||||
for indexPart, partition := range disk.Partitions {
|
||||
// For each partition we create the /sys/block/DISK_NAME/PARTITION_NAME
|
||||
_ = os.Mkdir(filepath.Join(diskPath, partition.Name), 0755)
|
||||
// Create the /sys/block/DISK_NAME/PARTITION_NAME/dev file which contains the major:minor of the partition
|
||||
_ = os.WriteFile(filepath.Join(diskPath, partition.Name, "dev"), []byte(fmt.Sprintf("%d:6%d\n", indexDisk, indexPart)), 0644)
|
||||
// Create the /run/udev/data/bMAJOR:MINOR file with the data inside to mimic the udev database
|
||||
data := []string{fmt.Sprintf("E:ID_FS_LABEL=%s\n", partition.Label)}
|
||||
if partition.Type != "" {
|
||||
data = append(data, fmt.Sprintf("E:ID_FS_TYPE=%s\n", partition.Type))
|
||||
}
|
||||
_ = os.WriteFile(filepath.Join(g.paths.RunUdevData, fmt.Sprintf("b%d:6%d", indexDisk, indexPart)), []byte(strings.Join(data, "")), 0644)
|
||||
// If we got a mountpoint, add it to our fake /proc/self/mounts
|
||||
if partition.MountPoint != "" {
|
||||
// Check if the partition has a fs, otherwise default to ext4
|
||||
if partition.Type == "" {
|
||||
partition.Type = "ext4"
|
||||
}
|
||||
// Prepare the g.mounts with all the mount lines
|
||||
g.mounts = append(
|
||||
g.mounts,
|
||||
fmt.Sprintf("%s %s %s ro,relatime 0 0\n", filepath.Join("/dev", partition.Name), partition.MountPoint, partition.Type))
|
||||
}
|
||||
}
|
||||
}
|
||||
// Finally, write all the mounts
|
||||
_ = os.WriteFile(g.paths.ProcMounts, []byte(strings.Join(g.mounts, "")), 0644)
|
||||
}
|
||||
|
||||
// RemoveDisk will remove the files for a disk. It makes no effort to check if the disk exists or not.
|
||||
func (g *GhwMock) RemoveDisk(disk string) {
|
||||
// This could be simpler I think, just removing the /sys/block/DEVICE should make ghw not find anything and not search
|
||||
// for partitions, but just in case do it properly
|
||||
var newMounts []string
|
||||
diskPath := filepath.Join(g.paths.SysBlock, disk)
|
||||
_ = os.RemoveAll(diskPath)
|
||||
|
||||
// Try to find any mounts that match the disk given and remove them from the mounts
|
||||
for _, mount := range g.mounts {
|
||||
fields := strings.Fields(mount)
|
||||
// If first field does not contain the /dev/DEVICE, add it to the newmounts
|
||||
if !strings.Contains(fields[0], filepath.Join("/dev", disk)) {
|
||||
newMounts = append(newMounts, mount)
|
||||
}
|
||||
}
|
||||
g.mounts = newMounts
|
||||
// Write the mounts again
|
||||
_ = os.WriteFile(g.paths.ProcMounts, []byte(strings.Join(g.mounts, "")), 0644)
|
||||
}
|
||||
|
||||
// RemovePartitionFromDisk will remove the files for a partition
|
||||
// It makes no effort checking if the disk/partition/files exist.
|
||||
func (g *GhwMock) RemovePartitionFromDisk(diskName string, partitionName string) {
|
||||
var newMounts []string
|
||||
diskPath := filepath.Join(g.paths.SysBlock, diskName)
|
||||
// Read the dev major:minor
|
||||
devName, _ := os.ReadFile(filepath.Join(diskPath, partitionName, "dev"))
|
||||
// Remove the MAJOR:MINOR file from the udev database
|
||||
_ = os.RemoveAll(filepath.Join(g.paths.RunUdevData, fmt.Sprintf("b%s", devName)))
|
||||
// Remove the /sys/block/DISK/PARTITION dir
|
||||
_ = os.RemoveAll(filepath.Join(diskPath, partitionName))
|
||||
|
||||
// Try to find any mounts that match the partition given and remove them from the mounts
|
||||
for _, mount := range g.mounts {
|
||||
fields := strings.Fields(mount)
|
||||
// If first field does not contain the /dev/PARTITION, add it to the newmounts
|
||||
if !strings.Contains(fields[0], filepath.Join("/dev", partitionName)) {
|
||||
newMounts = append(newMounts, mount)
|
||||
}
|
||||
}
|
||||
g.mounts = newMounts
|
||||
// Write the mounts again
|
||||
_ = os.WriteFile(g.paths.ProcMounts, []byte(strings.Join(g.mounts, "")), 0644)
|
||||
// Remove it from the partitions list
|
||||
for index, disk := range g.disks {
|
||||
if disk.Name == diskName {
|
||||
var newPartitions []*block.Partition
|
||||
for _, partition := range disk.Partitions {
|
||||
if partition.Name != partitionName {
|
||||
newPartitions = append(newPartitions, partition)
|
||||
}
|
||||
}
|
||||
g.disks[index].Partitions = newPartitions
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean will remove the chroot dir and unset the env var.
|
||||
func (g *GhwMock) Clean() {
|
||||
_ = os.Unsetenv("GHW_CHROOT")
|
||||
_ = os.RemoveAll(g.chroot)
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package mount_test
|
||||
package tests
|
||||
|
||||
import (
|
||||
"context"
|
@ -1,4 +1,4 @@
|
||||
package mount_test
|
||||
package tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@ -9,5 +9,5 @@ import (
|
||||
|
||||
func TestSuite(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "mount test Suite")
|
||||
RunSpecs(t, "Test Suite")
|
||||
}
|
351
tests/utils_test.go
Normal file
351
tests/utils_test.go
Normal file
@ -0,0 +1,351 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"github.com/containerd/containerd/mount"
|
||||
"github.com/jaypipes/ghw/pkg/block"
|
||||
"github.com/kairos-io/immucore/internal/utils"
|
||||
"github.com/kairos-io/immucore/tests/mocks"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/twpayne/go-vfs"
|
||||
"github.com/twpayne/go-vfs/vfst"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
var _ = Describe("mount utils", func() {
|
||||
var fs vfs.FS
|
||||
var cleanup func()
|
||||
|
||||
BeforeEach(func() {
|
||||
fs, cleanup, _ = vfst.NewTestFS(map[string]interface{}{
|
||||
"/proc/cmdline": "",
|
||||
})
|
||||
_, err := fs.Stat("/proc/cmdline")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
fakeCmdline, _ := fs.RawPath("/proc/cmdline")
|
||||
err = os.Setenv("HOST_PROC_CMDLINE", fakeCmdline)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
AfterEach(func() {
|
||||
cleanup()
|
||||
})
|
||||
|
||||
Context("ReadCMDLineArg", func() {
|
||||
BeforeEach(func() {
|
||||
err := fs.WriteFile("/proc/cmdline", []byte("test/key=value1 rd.immucore.debug rd.immucore.uki rd.cos.oemlabel=FAKE_LABEL empty=\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
It("splits arguments from cmdline", func() {
|
||||
value := utils.ReadCMDLineArg("test/key=")
|
||||
Expect(len(value)).To(Equal(1))
|
||||
Expect(value[0]).To(Equal("value1"))
|
||||
value = utils.ReadCMDLineArg("rd.cos.oemlabel=")
|
||||
Expect(len(value)).To(Equal(1))
|
||||
Expect(value[0]).To(Equal("FAKE_LABEL"))
|
||||
// This is mostly wrong, it should return and empty value, not a []string of 1 empty value
|
||||
// Requires refactoring
|
||||
value = utils.ReadCMDLineArg("empty=")
|
||||
Expect(len(value)).To(Equal(1))
|
||||
Expect(value[0]).To(Equal(""))
|
||||
|
||||
})
|
||||
It("returns properly for stanzas without value", func() {
|
||||
Expect(len(utils.ReadCMDLineArg("rd.immucore.debug"))).To(Equal(1))
|
||||
Expect(len(utils.ReadCMDLineArg("rd.immucore.uki"))).To(Equal(1))
|
||||
})
|
||||
})
|
||||
Context("GetRootDir", func() {
|
||||
It("Returns / for uki", func() {
|
||||
err := fs.WriteFile("/proc/cmdline", []byte("rd.immucore.uki"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(utils.GetRootDir()).To(Equal("/"))
|
||||
})
|
||||
It("Returns /sysroot", func() {
|
||||
Expect(utils.GetRootDir()).To(Equal("/sysroot"))
|
||||
})
|
||||
})
|
||||
Context("UniqueSlice", func() {
|
||||
It("Removes duplicates", func() {
|
||||
dups := []string{"a", "b", "c", "d", "b", "a"}
|
||||
dupsRemoved := utils.UniqueSlice(dups)
|
||||
Expect(len(dupsRemoved)).To(Equal(4))
|
||||
})
|
||||
})
|
||||
Context("ReadEnv", func() {
|
||||
It("Parses correctly an env file", func() {
|
||||
tmpDir, err := os.MkdirTemp("", "")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer os.RemoveAll(tmpDir)
|
||||
err = os.WriteFile(filepath.Join(tmpDir, "layout.env"), []byte("OVERLAY=\"tmpfs:25%\"\nPERSISTENT_STATE_BIND=\"true\"\nPERSISTENT_STATE_PATHS=\"/home /opt /root\"\nRW_PATHS=\"/var /etc /srv\"\nVOLUMES=\"LABEL=COS_OEM:/oem LABEL=COS_PERSISTENT:/usr/local\""), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
env, err := utils.ReadEnv(filepath.Join(tmpDir, "layout.env"))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(env).To(HaveKey("OVERLAY"))
|
||||
Expect(env).To(HaveKey("PERSISTENT_STATE_BIND"))
|
||||
Expect(env).To(HaveKey("PERSISTENT_STATE_PATHS"))
|
||||
Expect(env).To(HaveKey("RW_PATHS"))
|
||||
Expect(env).To(HaveKey("VOLUMES"))
|
||||
Expect(env["OVERLAY"]).To(Equal("tmpfs:25%"))
|
||||
Expect(env["PERSISTENT_STATE_BIND"]).To(Equal("true"))
|
||||
Expect(env["PERSISTENT_STATE_PATHS"]).To(Equal("/home /opt /root"))
|
||||
Expect(env["RW_PATHS"]).To(Equal("/var /etc /srv"))
|
||||
Expect(env["VOLUMES"]).To(Equal("LABEL=COS_OEM:/oem LABEL=COS_PERSISTENT:/usr/local"))
|
||||
})
|
||||
})
|
||||
Context("CleanupSlice", func() {
|
||||
It("Cleans up the slice of empty values", func() {
|
||||
slice := []string{"", " "}
|
||||
sliceCleaned := utils.CleanupSlice(slice)
|
||||
Expect(len(sliceCleaned)).To(Equal(0))
|
||||
})
|
||||
})
|
||||
Context("GetTarget", func() {
|
||||
It("Returns a fake target if called with dry run", func() {
|
||||
target, label, err := utils.GetTarget(true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(target).To(Equal("fake"))
|
||||
// We cant manipulate runtime, so it will return an empty label as it cant identify where are we
|
||||
Expect(label).To(Equal(""))
|
||||
})
|
||||
It("Returns a fake target if immucore is disabled", func() {
|
||||
err := fs.WriteFile("/proc/cmdline", []byte("rd.immucore.disabled\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
target, label, err := utils.GetTarget(false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(target).To(Equal("fake"))
|
||||
// We cant manipulate runtime, so it will return an empty label as it cant identify where are we
|
||||
Expect(label).To(Equal(""))
|
||||
})
|
||||
It("Returns the proper target from cmdline", func() {
|
||||
err := fs.WriteFile("/proc/cmdline", []byte("cos-img/filename=active.img\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
target, label, err := utils.GetTarget(false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(target).To(Equal("active.img"))
|
||||
// We cant manipulate runtime, so it will return an empty label as it cant identify where are we
|
||||
Expect(label).To(Equal(""))
|
||||
})
|
||||
It("Returns an empty target if we are on UKI", func() {
|
||||
err := fs.WriteFile("/proc/cmdline", []byte("rd.immucore.uki\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
target, label, err := utils.GetTarget(false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(target).To(Equal(""))
|
||||
// We cant manipulate runtime, so it will return an empty label as it cant identify where are we
|
||||
Expect(label).To(Equal(""))
|
||||
})
|
||||
It("Returns an error if we dont have the target in the cmdline", func() {
|
||||
target, label, err := utils.GetTarget(false)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(target).To(Equal(""))
|
||||
Expect(label).To(Equal(""))
|
||||
})
|
||||
})
|
||||
Context("DisableImmucore", func() {
|
||||
It("Disables immucore if cmdline contains live:LABEL", func() {
|
||||
err := fs.WriteFile("/proc/cmdline", []byte("root=live:LABEL=COS_LIVE\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(utils.DisableImmucore()).To(BeTrue())
|
||||
})
|
||||
It("Disables immucore if cmdline contains live:CDLABEL", func() {
|
||||
err := fs.WriteFile("/proc/cmdline", []byte("root=live:CDLABEL=COS_LIVE\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(utils.DisableImmucore()).To(BeTrue())
|
||||
})
|
||||
It("Disables immucore if cmdline contains netboot", func() {
|
||||
err := fs.WriteFile("/proc/cmdline", []byte("netboot\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(utils.DisableImmucore()).To(BeTrue())
|
||||
})
|
||||
It("Disables immucore if cmdline contains rd.cos.disable", func() {
|
||||
err := fs.WriteFile("/proc/cmdline", []byte("rd.cos.disable\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(utils.DisableImmucore()).To(BeTrue())
|
||||
})
|
||||
It("Disables immucore if cmdline contains rd.immucore.disable", func() {
|
||||
err := fs.WriteFile("/proc/cmdline", []byte("rd.immucore.disable\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(utils.DisableImmucore()).To(BeTrue())
|
||||
})
|
||||
It("Enables immucore by default", func() {
|
||||
Expect(utils.DisableImmucore()).To(BeFalse())
|
||||
})
|
||||
})
|
||||
Context("RootRW", func() {
|
||||
It("Defaults to RO", func() {
|
||||
Expect(utils.RootRW()).To(Equal("ro"))
|
||||
})
|
||||
It("Sets RW if set via cmdline with rd.cos.debugrw", func() {
|
||||
err := fs.WriteFile("/proc/cmdline", []byte("rd.cos.debugrw\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(utils.RootRW()).To(Equal("rw"))
|
||||
})
|
||||
It("Sets RW if set via cmdline with rd.immucore.debugrw", func() {
|
||||
err := fs.WriteFile("/proc/cmdline", []byte("rd.immucore.debugrw\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(utils.RootRW()).To(Equal("rw"))
|
||||
})
|
||||
It("Sets RW if set via cmdline with both rd.cos.debugrw and rd.immucore.debugrw at the same time", func() {
|
||||
err := fs.WriteFile("/proc/cmdline", []byte("rd.cos.debugrw rd.immucore.debugrw\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(utils.RootRW()).To(Equal("rw"))
|
||||
})
|
||||
})
|
||||
Context("IsUKI", func() {
|
||||
It("Returns false in a normal boot", func() {
|
||||
Expect(utils.IsUKI()).To(BeFalse())
|
||||
})
|
||||
It("Returns true if set via cmdline with rd.immucore.uki", func() {
|
||||
err := fs.WriteFile("/proc/cmdline", []byte("rd.immucore.uki\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(utils.IsUKI()).To(BeTrue())
|
||||
})
|
||||
})
|
||||
Context("ParseMount", func() {
|
||||
It("Returns disk path by LABEL", func() {
|
||||
Expect(utils.ParseMount("LABEL=MY_LABEL")).To(Equal("/dev/disk/by-label/MY_LABEL"))
|
||||
})
|
||||
It("Returns disk path by UUID", func() {
|
||||
Expect(utils.ParseMount("UUID=9999")).To(Equal("/dev/disk/by-uuid/9999"))
|
||||
})
|
||||
})
|
||||
Context("AppendSlash", func() {
|
||||
It("Appends a slash if it doesnt have one", func() {
|
||||
noSlash := "/noslash"
|
||||
Expect(utils.AppendSlash(noSlash)).To(Equal("/noslash/"))
|
||||
})
|
||||
It("Does not append a slash if it already has one", func() {
|
||||
slash := "/yesslash/"
|
||||
Expect(utils.AppendSlash(slash)).To(Equal("/yesslash/"))
|
||||
})
|
||||
})
|
||||
Context("MountToFstab", func() {
|
||||
It("Generates teh proper fstab config", func() {
|
||||
m := mount.Mount{
|
||||
Type: "fakefs",
|
||||
Source: "/dev/fake",
|
||||
Options: []string{"option1", "option=2"},
|
||||
}
|
||||
fstab := utils.MountToFstab(m)
|
||||
fstab.File = "/mnt/fake"
|
||||
// Options can be shown in whatever order, so regexp that
|
||||
Expect(fstab.String()).To(MatchRegexp("/dev/fake /mnt/fake fakefs (option1|option=2),(option=2|option1) 0 0"))
|
||||
Expect(fstab.Spec).To(Equal("/dev/fake"))
|
||||
Expect(fstab.VfsType).To(Equal("fakefs"))
|
||||
Expect(fstab.MntOps).To(HaveKey("option1"))
|
||||
Expect(fstab.MntOps).To(HaveKey("option"))
|
||||
Expect(fstab.MntOps["option1"]).To(Equal(""))
|
||||
Expect(fstab.MntOps["option"]).To(Equal("2"))
|
||||
})
|
||||
})
|
||||
Context("CleanSysrootForFstab", func() {
|
||||
It("Removes /sysroot", func() {
|
||||
Expect(utils.CleanSysrootForFstab("/sysroot/dev")).To(Equal("/dev"))
|
||||
Expect(utils.CleanSysrootForFstab("/sysroot/sysroot/dev")).To(Equal("/dev"))
|
||||
Expect(utils.CleanSysrootForFstab("sysroot/dev")).To(Equal("sysroot/dev"))
|
||||
Expect(utils.CleanSysrootForFstab("/dev/sysroot/dev")).To(Equal("/dev/dev"))
|
||||
Expect(utils.CleanSysrootForFstab("/dev/")).To(Equal("/dev/"))
|
||||
Expect(utils.CleanSysrootForFstab("/dev")).To(Equal("/dev"))
|
||||
Expect(utils.CleanSysrootForFstab("//sysroot/dev")).To(Equal("//dev"))
|
||||
Expect(utils.CleanSysrootForFstab("/sysroot//dev")).To(Equal("//dev"))
|
||||
})
|
||||
})
|
||||
Context("GetOemTimeout", func() {
|
||||
It("Gets timeout from rd.cos.oemtimeout", func() {
|
||||
err := fs.WriteFile("/proc/cmdline", []byte("rd.cos.oemtimeout=100\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(utils.GetOemTimeout()).To(Equal(100))
|
||||
})
|
||||
It("Gets timeout from rd.immucore.oemtimeout", func() {
|
||||
err := fs.WriteFile("/proc/cmdline", []byte("rd.immucore.oemtimeout=200\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(utils.GetOemTimeout()).To(Equal(200))
|
||||
})
|
||||
It("Gets timeout from both rd.cos.oemtimeout and rd.immucore.oemtimeout(immucore has precedence)", func() {
|
||||
err := fs.WriteFile("/proc/cmdline", []byte("rd.cos.oemtimeout=100 rd.immucore.oemtimeout=200\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(utils.GetOemTimeout()).To(Equal(200))
|
||||
})
|
||||
It("Fails to parse from cmdline and gets default", func() {
|
||||
err := fs.WriteFile("/proc/cmdline", []byte("rd.immucore.oemtimeout=really\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(utils.GetOemTimeout()).To(Equal(5))
|
||||
err = fs.WriteFile("/proc/cmdline", []byte("rd.immucore.oemtimeout=\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(utils.GetOemTimeout()).To(Equal(5))
|
||||
})
|
||||
It("Gets default timeout", func() {
|
||||
Expect(utils.GetOemTimeout()).To(Equal(5))
|
||||
})
|
||||
})
|
||||
Context("GetOverlayBase", func() {
|
||||
It("Gets overlay from rd.cos.overlay", func() {
|
||||
err := fs.WriteFile("/proc/cmdline", []byte("rd.cos.overlay=tmpfs:100%\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(utils.GetOverlayBase()).To(Equal("tmpfs:100%"))
|
||||
})
|
||||
It("Gets overlay from rd.immucore.overlay", func() {
|
||||
err := fs.WriteFile("/proc/cmdline", []byte("rd.immucore.overlay=tmpfs:200%\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(utils.GetOverlayBase()).To(Equal("tmpfs:200%"))
|
||||
})
|
||||
It("Gets overlay from both rd.cos.overlay and rd.immucore.overlay(immucore has precedence)", func() {
|
||||
err := fs.WriteFile("/proc/cmdline", []byte("rd.cos.overlay=tmpfs:100% rd.immucore.overlay=tmpfs:200%\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(utils.GetOverlayBase()).To(Equal("tmpfs:200%"))
|
||||
})
|
||||
It("Fails to parse from cmdline and gets default", func() {
|
||||
err := fs.WriteFile("/proc/cmdline", []byte("rd.immucore.overlay=\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(utils.GetOverlayBase()).To(Equal("tmpfs:20%"))
|
||||
err = fs.WriteFile("/proc/cmdline", []byte("rd.immucore.overlay=\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(utils.GetOverlayBase()).To(Equal("tmpfs:20%"))
|
||||
})
|
||||
It("Gets default overlay", func() {
|
||||
Expect(utils.GetOverlayBase()).To(Equal("tmpfs:20%"))
|
||||
})
|
||||
})
|
||||
Context("GetOemLabel", func() {
|
||||
It("Gets label from rd.cos.oemlabel", func() {
|
||||
err := fs.WriteFile("/proc/cmdline", []byte("rd.cos.oemlabel=COS_LABEL\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(utils.GetOemLabel()).To(Equal("COS_LABEL"))
|
||||
})
|
||||
It("Gets label from rd.immucore.oemlabel", func() {
|
||||
err := fs.WriteFile("/proc/cmdline", []byte("rd.immucore.oemlabel=IMMUCORE_LABEL\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(utils.GetOemLabel()).To(Equal("IMMUCORE_LABEL"))
|
||||
})
|
||||
It("Gets label from both rd.cos.oemlabel and rd.immucore.oemlabel(immucore has precedence)", func() {
|
||||
err := fs.WriteFile("/proc/cmdline", []byte("rd.cos.oemlabel=COS_LABEL rd.immucore.oemlabel=IMMUCORE_LABEL\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(utils.GetOemLabel()).To(Equal("IMMUCORE_LABEL"))
|
||||
})
|
||||
It("Fails to parse from cmdline and gets default from runtime", func() {
|
||||
mainDisk := block.Disk{
|
||||
Name: "device",
|
||||
Partitions: []*block.Partition{
|
||||
{
|
||||
Name: "device2",
|
||||
Label: "COS_OEM",
|
||||
Type: "ext4",
|
||||
MountPoint: "/oem",
|
||||
},
|
||||
},
|
||||
}
|
||||
ghwTest := mocks.GhwMock{}
|
||||
ghwTest.AddDisk(mainDisk)
|
||||
ghwTest.CreateDevices()
|
||||
defer ghwTest.Clean()
|
||||
|
||||
err := fs.WriteFile("/proc/cmdline", []byte("rd.cos.oemlabel=\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(utils.GetOemLabel()).To(Equal("COS_OEM"))
|
||||
err = fs.WriteFile("/proc/cmdline", []byte("rd.immucore.oemlabel=\n"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(utils.GetOemLabel()).To(Equal("COS_OEM"))
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue
Block a user