From b33f303325515035c05b5849880f12cd9f021814 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20L=C3=B6nnegren?= Date: Thu, 14 Dec 2023 11:35:02 +0100 Subject: [PATCH] Load mount-layout from .env-files (#1885) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Load mount-layout from .env-files Adds code to load environment variables OVERLAY, RW_PATHS, PERSISTENT_STATE_PATHS and PERSISTENT_STATE_BINDS during mounting. The variables are read from /run/cos/cos-layout.env and /run/elemental/mount-layout.env if they exist. Signed-off-by: Fredrik Lönnegren --- cmd/config/config.go | 74 +++++++++++++++++++ cmd/config/config_test.go | 29 +++++++- pkg/constants/constants.go | 6 +- .../system/oem/00_rootfs.yaml | 2 +- .../systemd/system/elemental-rootfs.service | 2 + .../system/elemental-setup-rootfs.service | 2 +- 6 files changed, 108 insertions(+), 7 deletions(-) diff --git a/cmd/config/config.go b/cmd/config/config.go index c8b43c3cedf..1142d677b89 100644 --- a/cmd/config/config.go +++ b/cmd/config/config.go @@ -270,6 +270,11 @@ func ReadMountSpec(r *v1.RunConfig, flags *pflag.FlagSet) (*v1.MountSpec, error) return mount, err } + if err := applyMountEnvVars(r, mount); err != nil { + r.Logger.Errorf("Error reading mount env-vars: %s", err.Error()) + return mount, err + } + if err := applyKernelCmdline(r, mount); err != nil { r.Logger.Errorf("Error reading kernel cmdline: %s", err.Error()) return mount, err @@ -312,6 +317,11 @@ func applyKernelCmdline(r *v1.RunConfig, mount *v1.MountSpec) error { r.Logger.Errorf("Error parsing cmdline %s", cmd) return errors.New("Unknown image path") } + case "elemental.overlay", "rd.cos.overlay": + err := applyMountOverlay(mount, val) + if err != nil { + return err + } case "elemental.oemlabel", "rd.cos.oemlabel": mount.Partitions.OEM.FilesystemLabel = val } @@ -320,6 +330,70 @@ func applyKernelCmdline(r *v1.RunConfig, mount *v1.MountSpec) error { return nil } +func applyMountEnvVars(r *v1.RunConfig, mount *v1.MountSpec) error { + r.Logger.Debugf("Applying mount env-vars") + + overlay := os.Getenv("OVERLAY") + if overlay != "" { + r.Logger.Debugf("Setting ephemeral settings based on OVERLAY") + + err := applyMountOverlay(mount, overlay) + if err != nil { + return err + } + } + + rwPaths := os.Getenv("RW_PATHS") + if rwPaths != "" { + r.Logger.Debugf("Setting ephemeral paths based on RW_PATHS") + mount.Ephemeral.Paths = strings.Split(rwPaths, " ") + } + + persistentPaths := os.Getenv("PERSISTENT_STATE_PATHS") + if persistentPaths != "" { + r.Logger.Debugf("Setting persistent paths based on PERSISTENT_STATE_PATHS") + mount.Persistent.Paths = strings.Split(persistentPaths, " ") + } + + persistentBind := os.Getenv("PERSISTENT_STATE_BIND") + if persistentBind != "" { + r.Logger.Debugf("Setting persistent bind based on PERSISTENT_STATE_BIND") + mount.Persistent.Mode = constants.BindMode + } + + return nil +} + +func applyMountOverlay(mount *v1.MountSpec, overlay string) error { + split := strings.Split(overlay, ":") + + if len(split) == 2 && split[0] == constants.Tmpfs { + mount.Ephemeral.Device = "" + mount.Ephemeral.Type = split[0] + mount.Ephemeral.Size = split[1] + return nil + } + + mount.Ephemeral.Type = constants.Block + mount.Ephemeral.Size = "" + + blockSplit := strings.Split(overlay, "=") + if len(blockSplit) != 2 { + return fmt.Errorf("Unknown block overlay '%s'", overlay) + } + + switch blockSplit[0] { + case "LABEL": + mount.Ephemeral.Device = fmt.Sprintf("/dev/disk/by-label/%s", blockSplit[1]) + case "UUID": + mount.Ephemeral.Device = fmt.Sprintf("/dev/disk/by-uuid/%s", blockSplit[1]) + default: + return fmt.Errorf("Unknown block overlay '%s'", overlay) + } + + return nil +} + func ReadResetSpec(r *v1.RunConfig, flags *pflag.FlagSet) (*v1.ResetSpec, error) { reset, err := config.NewResetSpec(r.Config) if err != nil { diff --git a/cmd/config/config_test.go b/cmd/config/config_test.go index 5e1ce68f0df..e361fa2cef8 100644 --- a/cmd/config/config_test.go +++ b/cmd/config/config_test.go @@ -271,6 +271,11 @@ var _ = Describe("Config", Label("config"), func() { fs, cleanup, err = vfst.NewTestFS(map[string]interface{}{}) Expect(err).Should(BeNil()) + err = fs.Mkdir("/proc", constants.DirPerm) + Expect(err).Should(BeNil()) + err = fs.WriteFile("/proc/cmdline", []byte("root=LABEL=COS_STATE elemental.image=active elemental.overlay=tmpfs:30%"), 0444) + Expect(err).Should(BeNil()) + cfg, err = ReadConfigRun("fixtures/config/", nil, mounter) Expect(err).Should(BeNil()) @@ -449,6 +454,28 @@ var _ = Describe("Config", Label("config"), func() { Expect(spec.RecoveryUpgrade).To(BeTrue()) }) }) - + Describe("Read MountSpec", Label("mount"), func() { + It("inits a mount spec according to given configs", func() { + err := os.Setenv("ELEMENTAL_MOUNT_SYSROOT", "/newroot") + spec, err := ReadMountSpec(cfg, nil) + Expect(err).ShouldNot(HaveOccurred()) + Expect(spec.Mode).To(Equal("active")) + Expect(spec.Sysroot).To(Equal("/newroot")) + }) + It("picks kernel cmdline first then env-vars", func() { + err := os.Setenv("ELEMENTAL_MOUNT_IMAGE", "passive") + spec, err := ReadMountSpec(cfg, nil) + Expect(err).ShouldNot(HaveOccurred()) + // Set by kernel cmdline + Expect(spec.Mode).To(Equal("active")) + }) + It("picks kernel cmdline first then env-vars", func() { + err := os.Setenv("OVERLAY", "UUID=1234") + spec, err := ReadMountSpec(cfg, nil) + Expect(err).ShouldNot(HaveOccurred()) + Expect(spec.Ephemeral.Type).To(Equal("tmpfs")) + Expect(spec.Ephemeral.Size).To(Equal("30%")) + }) + }) }) }) diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 4a82628d209..6f9549fc276 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -263,10 +263,8 @@ func GetInitKeyEnvMap() map[string]string { // GetMountKeyEnvMap returns environment variable bindings to MountSpec data func GetMountKeyEnvMap() map[string]string { return map[string]string{ - "write-fstab": "WRITE_FSTAB", - "write-sentinel": "WRITE_SENTINEL", - "sysroot": "SYSROOT", - "read-kernel-cmdline": "READ_KERNEL_CMDLINE", + "write-fstab": "WRITE_FSTAB", + "sysroot": "SYSROOT", } } diff --git a/pkg/features/embedded/cloud-config-essentials/system/oem/00_rootfs.yaml b/pkg/features/embedded/cloud-config-essentials/system/oem/00_rootfs.yaml index fb498d1b506..93daa5b3fa9 100644 --- a/pkg/features/embedded/cloud-config-essentials/system/oem/00_rootfs.yaml +++ b/pkg/features/embedded/cloud-config-essentials/system/oem/00_rootfs.yaml @@ -13,7 +13,7 @@ stages: providers: ["aws", "gcp", "openstack", "cdrom"] path: "/oem" rootfs: - - if: '[ ! -f "/run/cos/recovery_mode" ]' + - if: '[ ! -f "/run/elemental/recovery_mode" ]' name: "Layout configuration" environment_file: /run/cos/cos-layout.env environment: diff --git a/pkg/features/embedded/elemental-rootfs/usr/lib/systemd/system/elemental-rootfs.service b/pkg/features/embedded/elemental-rootfs/usr/lib/systemd/system/elemental-rootfs.service index 2b02a585a90..9bdac8a4f70 100644 --- a/pkg/features/embedded/elemental-rootfs/usr/lib/systemd/system/elemental-rootfs.service +++ b/pkg/features/embedded/elemental-rootfs/usr/lib/systemd/system/elemental-rootfs.service @@ -9,6 +9,8 @@ Conflicts=initrd-switch-root.target [Service] Type=oneshot RemainAfterExit=yes +EnvironmentFile=-/run/cos/cos-layout.env +EnvironmentFile=-/run/elemental/mount-layout.env ExecStart=/usr/bin/elemental mount --debug [Install] diff --git a/pkg/features/embedded/elemental-setup/usr/lib/systemd/system/elemental-setup-rootfs.service b/pkg/features/embedded/elemental-setup/usr/lib/systemd/system/elemental-setup-rootfs.service index b5f38661d9a..6b452d12aaa 100644 --- a/pkg/features/embedded/elemental-setup/usr/lib/systemd/system/elemental-setup-rootfs.service +++ b/pkg/features/embedded/elemental-setup/usr/lib/systemd/system/elemental-setup-rootfs.service @@ -5,7 +5,7 @@ After=sysroot.mount Requires=sysroot.mount After=oem.mount Wants=oem.mount -Before=initrd-fs.target +Before=initrd-root-fs.target Conflicts=initrd-switch-root.target [Service]