diff --git a/Earthfile b/Earthfile index f99182d..c3445a3 100644 --- a/Earthfile +++ b/Earthfile @@ -55,14 +55,16 @@ lint: build-immucore: FROM +golang-image - COPY +version/VERSION ./ - ARG VERSION=$(cat VERSION) WORKDIR /work COPY go.mod go.sum /work COPY main.go /work COPY --dir internal /work COPY --dir pkg /work - RUN CGO_ENABLED=0 go build -o immucore -ldflags "-X internal/version.Version=$VERSION" + COPY +version/VERSION ./ + ARG VERSION=$(cat VERSION) + ARG LDFLAGS="-s -w -X github.com/kairos-io/immucore/internal/version.Version=$VERSION" + RUN echo ${LDFLAGS} + RUN CGO_ENABLED=0 go build -o immucore -ldflags "${LDFLAGS}" SAVE ARTIFACT /work/immucore AS LOCAL build/immucore-$VERSION build-dracut: @@ -78,7 +80,7 @@ build-dracut: RUN rm /etc/dracut.conf.d/02-cos-immutable-rootfs.conf END RUN kernel=$(ls /lib/modules | head -n1) && \ - dracut -f "/boot/initrd-${kernel}" "${kernel}" && \ + dracut -v -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 diff --git a/dracut/28immucore/generator.sh b/dracut/28immucore/generator.sh old mode 100644 new mode 100755 index e1b086f..5afca84 --- a/dracut/28immucore/generator.sh +++ b/dracut/28immucore/generator.sh @@ -1,46 +1,32 @@ #!/bin/bash +set +x + type getarg >/dev/null 2>&1 || . /lib/dracut-lib.sh GENERATOR_DIR="$2" - [ -z "$GENERATOR_DIR" ] && exit 1 [ -d "$GENERATOR_DIR" ] || mkdir "$GENERATOR_DIR" -oem_label=$(getarg rd.cos.oemlabel=) -# See https://github.com/kairos-io/packages/blob/d12b12b043a71d8471454f7b4fc84c3181d2bf60/packages/system/dracut/immutable-rootfs/30cos-immutable-rootfs/cos-generator.sh#L29 +## GENERATE SYSROOT +cos_img=$(getarg cos-img/filename=) +[ -z "${cos_img}" ] && exit 0 + +# This is necessary because otherwise systemd-fstab-generator will se the cmdline with the root=LABEL=X stanza and +# say, hey this is the ROOT where we need to boot! so it auto creates a sysroot.mount with the content of the value +# passed in the cmdline. But because we usually pass the label of the img (COS_ACTIVE) it will create the wrong mount +# service and be stuck in there forever. +# by generating it ourselves we get the sysroot.mount intot he generators.early dir, which tells systemd to not generate it +# as it already exists and the rest is history { echo "[Unit]" + echo "Before=initrd-root-fs.target" echo "DefaultDependencies=no" - echo "Before=initrd-fs.target" - echo "Conflicts=initrd-switch-root.target" - echo "Requires=initrd-root-fs.target" - if getargbool 0 rd.neednet; then - echo "Wants=network-online.target" - echo "After=network-online.target" - echo "Description=immucore online mount" - else - echo "Description=immucore mount" - fi - # Itxaka: oem is mounted by immucore? - # OEM is special as immucore plugins might need that in order to unlock other partitions and plugins can reside in /oem as well and immucore needs to find them - #if [ -n "${oem_label}" ]; then - # echo "After=oem.mount" - #fi - echo "After=initrd-root-fs.target cos-setup-rootfs.service" - echo "[Service]" - echo "Type=oneshot" - echo "RemainAfterExit=yes" - echo "ExecStart=/usr/bin/immucore start" + echo "[Mount]" + echo "Where=/sysroot" + echo "What=/run/initramfs/cos-state/${cos_img#/}" + echo "Options=ro,suid,dev,exec,auto,nouser,async" +} > "$GENERATOR_DIR"/sysroot.mount - echo "[Install]" - echo "RequiredBy=initrd-fs.target" -} > "$GENERATOR_DIR"/immucore.service - - -if [ ! -e "$GENERATOR_DIR/initrd-fs.target.requires/immucore.service" ]; then - mkdir -p "$GENERATOR_DIR"/initrd-fs.target.requires - ln -s "$GENERATOR_DIR"/immucore.service \ - "$GENERATOR_DIR"/initrd-fs.target.requires/immucore.service -fi \ No newline at end of file +## END GENERATE SYSROOT \ No newline at end of file diff --git a/dracut/28immucore/immucore.service b/dracut/28immucore/immucore.service new file mode 100644 index 0000000..00b70d0 --- /dev/null +++ b/dracut/28immucore/immucore.service @@ -0,0 +1,14 @@ +[Unit] +Description=immucore +DefaultDependencies=no +After=systemd-udev-settle.service +Requires=systemd-udev-settle.service +Before=dracut-initqueue.service +Conflicts=initrd-switch-root.target + +[Service] +Type=oneshot +RemainAfterExit=yes +StandardOutput=journal+console +ExecStartPre=-/usr/bin/systemctl stop oem.mount +ExecStart=/usr/bin/immucore start \ No newline at end of file diff --git a/dracut/28immucore/module-setup.sh b/dracut/28immucore/module-setup.sh index d25ecaa..0fd3d73 100755 --- a/dracut/28immucore/module-setup.sh +++ b/dracut/28immucore/module-setup.sh @@ -23,18 +23,16 @@ install() { declare moddir=${moddir} declare systemdutildir=${systemdutildir} declare systemdsystemunitdir=${systemdsystemunitdir} - declare initdir="${initdir}" + declare initdir=${initdir} # Add missing elemental binary, drop once we get yip lib inside immucore as its only needed to run the stages - inst_multiple \ - immucore elemental - - inst_script "${moddir}/generator.sh" \ - "${systemdutildir}/system-generators/immucore-generator" - + inst_multiple immucore elemental + # add utils used by elemental or stages + inst_multiple partprobe sync udevadm parted mkfs.ext2 mkfs.ext3 mkfs.ext4 mkfs.vfat mkfs.fat blkid e2fsck resize2fs mount umount sgdisk + # missing mkfs.xfs xfs_growfs in image? + inst_script "${moddir}/generator.sh" "${systemdutildir}/system-generators/immucore-generator" + inst_simple "${moddir}/immucore.service" "${systemdsystemunitdir}/immucore.service" mkdir -p "${initdir}/${systemdsystemunitdir}/initrd-fs.target.requires" - ln_r "../immucore.service" \ - "${systemdsystemunitdir}/initrd-fs.target.requires/immucore.service" - + ln_r "../immucore.service" "${systemdsystemunitdir}/initrd-fs.target.requires/immucore.service" dracut_need_initqueue } \ No newline at end of file diff --git a/dracut/dracut.conf b/dracut/dracut.conf index 6f1a85b..bbe6458 100644 --- a/dracut/dracut.conf +++ b/dracut/dracut.conf @@ -1,6 +1,8 @@ -# This services should be run from immucore directly, rootfs at start of immucore and initramfs once we mount everything, -# so at the end, just before we let init do the switch_root +# This services should be run from immucore directly, rootfs once we mount /sysroot and initramfs once we mount everything, +# so at the end, just before we let init do the switch_root. Notice that we run really early in the boot with immucore so +# maybe by this time some things are not ready... # Note that initramfs run with RootDirectory=/sysroot install_items+=" /etc/systemd/system/cos-setup-initramfs.service /etc/systemd/system/initrd.target.requires/cos-setup-initramfs.service " -install_items+=" /etc/systemd/system/cos-setup-rootfs.service /etc/systemd/system/initrd-fs.target.requires/cos-setup-rootfs.service " +# RUN BY IMMUCORE BUT FAILING +#install_items+=" /etc/systemd/system/cos-setup-rootfs.service /etc/systemd/system/initrd-fs.target.requires/cos-setup-rootfs.service " add_dracutmodules+=" immucore " \ No newline at end of file diff --git a/pkg/mount/mount.go b/pkg/mount/mount.go index 8587463..ae7ed4f 100644 --- a/pkg/mount/mount.go +++ b/pkg/mount/mount.go @@ -84,12 +84,21 @@ func (s *State) WriteFstab(fstabFile string) func(context.Context) error { } } -// ln -sf -t / /sysroot/system func (s *State) RunStageOp(stage string) func(context.Context) error { return func(ctx context.Context) error { + log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).With().Caller().Logger() + if stage == "rootfs" { + err := os.Symlink("/sysroot/system", "/system") + if err != nil { + s.Logger.Err(err).Msg("creating symlink") + return err + } + } + cmd := fmt.Sprintf("elemental run-stage %s", stage) - s.Logger.Debug().Str("cmd", cmd) - _, err := utils.SH(cmd) + log.Logger.Debug().Str("cmd", cmd).Msg("") + output, err := utils.SH(cmd) + log.Logger.Debug().Str("output", output).Msg("") return err } } @@ -183,8 +192,10 @@ func (s *State) Register(g *herd.Graph) error { runtime, err := state.NewRuntime() if err != nil { + s.Logger.Debug().Err(err).Msg("") return err } + s.Logger.Debug().Str("state", litter.Sdump(runtime)).Msg("Register") // TODO: add hooks, fstab (might have missed some), systemd compat // TODO: We should also set tmpfs here (not -related) @@ -250,7 +261,7 @@ func (s *State) Register(g *herd.Graph) error { // "auto", //"nouser", "async", - }, 60*time.Second), + }, 10*time.Second), ), ) if err != nil { @@ -263,24 +274,26 @@ func (s *State) Register(g *herd.Graph) error { // This is building the mountRoot dependendency if it was enabled mountRootCondition := herd.ConditionalOption(func() bool { return s.MountRoot }, herd.WithDeps(opMountRoot)) - // TODO: this needs to be run after state is discovered - // TODO: add symlink if Rootdir != "" - // TODO: chroot? + // TODO: this needs to be run after sysroot so we can link to /sysroot/system/oem and after /oem mounted s.Logger.Debug().Str("what", opRootfsHook).Msg("Add operation") - err = g.Add(opRootfsHook, mountRootCondition, herd.WithDeps(opMountOEM), herd.WithCallback(s.RunStageOp("rootfs"))) + err = g.Add(opRootfsHook, mountRootCondition, herd.WithDeps(opMountRoot, opMountOEM), herd.WithCallback(s.RunStageOp("rootfs"))) if err != nil { - s.Logger.Err(err) + s.Logger.Err(err).Msg("running rootfs stage") } - // /run/cos-layout.env + // /run/cos/cos-layout.env // populate state bindmounts, overlaymounts, custommounts s.Logger.Debug().Str("what", opLoadConfig).Msg("Add operation") err = g.Add(opLoadConfig, herd.WithDeps(opRootfsHook), herd.WithCallback(func(ctx context.Context) error { log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).With().Caller().Logger() - env, err := readEnv("/run/cos-layout.env") + if s.CustomMounts == nil { + s.CustomMounts = map[string]string{} + } + env, err := readEnv("/run/cos/cos-layout.env") if err != nil { + log.Logger.Err(err).Msg("Reading env") return err } log.Logger.Debug().Str("envfile", litter.Sdump(env)).Msg("loading cos layout") @@ -389,7 +402,7 @@ func (s *State) Register(g *herd.Graph) error { []string{ "ro", // or rw }, - 60*time.Second, + 10*time.Second, )(ctx)) } @@ -435,7 +448,7 @@ func (s *State) Register(g *herd.Graph) error { mountRootCondition, herd.WithCallback( s.MountOP( - runtime.OEM.Label, + runtime.OEM.Name, s.path("/oem"), runtime.OEM.Type, []string{ @@ -443,8 +456,8 @@ func (s *State) Register(g *herd.Graph) error { "suid", "dev", "exec", - "noauto", - "nouser", + //"noauto", + //"nouser", "async", }, 10*time.Second), ), diff --git a/pkg/mount/operation.go b/pkg/mount/operation.go index 9982bfe..176b2f8 100644 --- a/pkg/mount/operation.go +++ b/pkg/mount/operation.go @@ -3,6 +3,10 @@ package mount import ( "github.com/containerd/containerd/mount" "github.com/deniswernert/go-fstab" + "github.com/moby/sys/mountinfo" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "os" ) type mountOperation struct { @@ -13,11 +17,22 @@ type mountOperation struct { } func (m mountOperation) run() error { + log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).With().Caller().Logger() if m.PrepareCallback != nil { if err := m.PrepareCallback(); err != nil { + log.Logger.Err(err).Str("what", m.MountOption.Source).Str("where", m.Target).Str("type", m.MountOption.Type).Msg("executing mount callback") return err } } - + //TODO: not only check if mounted but also if the type,options and source are the same? + mounted, err := mountinfo.Mounted(m.Target) + if err != nil { + log.Logger.Err(err).Str("what", m.MountOption.Source).Str("where", m.Target).Str("type", m.MountOption.Type).Msg("checking mount status") + return err + } + if mounted { + log.Logger.Debug().Str("what", m.MountOption.Source).Str("where", m.Target).Str("type", m.MountOption.Type).Msg("Already mounted") + return nil + } return mount.All([]mount.Mount{m.MountOption}, m.Target) }