-
Notifications
You must be signed in to change notification settings - Fork 62
Add Volumes in Mount spec #1937
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,45 +17,66 @@ limitations under the License. | |
| package action | ||
|
|
||
| import ( | ||
| "bufio" | ||
| "fmt" | ||
| "path/filepath" | ||
| "regexp" | ||
| "sort" | ||
| "strings" | ||
|
|
||
| "github.com/hashicorp/go-multierror" | ||
| "github.com/rancher/elemental-toolkit/pkg/constants" | ||
| "github.com/rancher/elemental-toolkit/pkg/elemental" | ||
| v1 "github.com/rancher/elemental-toolkit/pkg/types/v1" | ||
| "github.com/rancher/elemental-toolkit/pkg/utils" | ||
| ) | ||
|
|
||
| const overlaySuffix = ".overlay" | ||
| const labelPref = "LABEL=" | ||
| const partLabelPref = "PARTLABEL=" | ||
| const uuidPref = "UUID=" | ||
| const devPref = "/dev/" | ||
| const diskBy = "/dev/disk/by-" | ||
| const diskByLabel = diskBy + "label" | ||
| const diskByPartLabel = diskBy + "partlabel" | ||
| const diskByUUID = diskBy + "uuid" | ||
| const runPath = "/run" | ||
|
|
||
| func RunMount(cfg *v1.RunConfig, spec *v1.MountSpec) error { | ||
| var fstabData string | ||
| var err error | ||
|
|
||
| cfg.Logger.Info("Running mount command") | ||
|
|
||
| cfg.Logger.Debug("Mounting elemental partitions") | ||
| if err := elemental.MountPartitions(cfg.Config, spec.Partitions.PartitionsByMountPoint(false)); err != nil { | ||
| cfg.Logger.Errorf("Error mounting elemental partitions: %s", err.Error()) | ||
| if spec.WriteFstab { | ||
| cfg.Logger.Debug("Generating inital sysroot fstab lines") | ||
| fstabData, err = InitialFstabData(cfg.Runner, spec.Sysroot) | ||
| if err != nil { | ||
| cfg.Logger.Errorf("Error mounting volumes: %s", err.Error()) | ||
| return err | ||
| } | ||
|
|
||
| } | ||
|
|
||
| cfg.Logger.Debug("Mounting volumes") | ||
| if err = MountVolumes(cfg, spec); err != nil { | ||
| cfg.Logger.Errorf("Error mounting volumes: %s", err.Error()) | ||
| return err | ||
| } | ||
|
|
||
| cfg.Logger.Debugf("Mounting ephemeral directories") | ||
| if err := MountEphemeral(cfg, spec.Sysroot, spec.Ephemeral); err != nil { | ||
| if err = MountEphemeral(cfg, spec.Sysroot, spec.Ephemeral); err != nil { | ||
| cfg.Logger.Errorf("Error mounting overlays: %s", err.Error()) | ||
| return err | ||
| } | ||
|
|
||
| if ok, _ := elemental.IsMounted(cfg.Config, spec.Partitions.Persistent); ok { | ||
| cfg.Logger.Debugf("Mounting persistent directories") | ||
| if err := MountPersistent(cfg, spec.Sysroot, spec.Persistent); err != nil { | ||
| cfg.Logger.Errorf("Error mounting persistent overlays: %s", err.Error()) | ||
| return err | ||
| } | ||
| } else { | ||
| cfg.Logger.Warn("No persistent partition defined or mounted, omitting any persistent paths configuration") | ||
| cfg.Logger.Debugf("Mounting persistent directories") | ||
| if err = MountPersistent(cfg, spec.Sysroot, spec.Persistent); err != nil { | ||
| cfg.Logger.Errorf("Error mounting persistent overlays: %s", err.Error()) | ||
| return err | ||
| } | ||
|
|
||
| cfg.Logger.Debugf("Writing fstab") | ||
| if err := WriteFstab(cfg, spec); err != nil { | ||
| if err = WriteFstab(cfg, spec, fstabData); err != nil { | ||
| cfg.Logger.Errorf("Error writing new fstab: %s", err.Error()) | ||
| return err | ||
| } | ||
|
|
@@ -64,6 +85,66 @@ func RunMount(cfg *v1.RunConfig, spec *v1.MountSpec) error { | |
| return nil | ||
| } | ||
|
|
||
| func MountVolumes(cfg *v1.RunConfig, spec *v1.MountSpec) error { | ||
| var errs error | ||
|
|
||
| volumes := map[string]*v1.VolumeMount{} | ||
| keys := []string{} | ||
| if spec.HasPersistent() { | ||
| volumes[spec.Persistent.Volume.Mountpoint] = &spec.Persistent.Volume | ||
| keys = append(keys, spec.Persistent.Volume.Mountpoint) | ||
| } | ||
|
|
||
| for _, v := range spec.Volumes { | ||
| volumes[v.Mountpoint] = v | ||
| keys = append(keys, v.Mountpoint) | ||
| } | ||
|
|
||
| sort.Strings(keys) | ||
|
|
||
| for _, k := range keys { | ||
| var dev string | ||
| switch { | ||
| case strings.HasPrefix(volumes[k].Device, labelPref): | ||
| dev = filepath.Join(diskByLabel, strings.TrimPrefix(volumes[k].Device, labelPref)) | ||
| case strings.HasPrefix(volumes[k].Device, partLabelPref): | ||
| dev = filepath.Join(diskByPartLabel, strings.TrimPrefix(volumes[k].Device, partLabelPref)) | ||
| case strings.HasPrefix(volumes[k].Device, uuidPref): | ||
| dev = filepath.Join(diskByUUID, strings.TrimPrefix(volumes[k].Device, uuidPref)) | ||
| case strings.HasPrefix(volumes[k].Device, devPref): | ||
| dev = volumes[k].Device | ||
| default: | ||
| cfg.Logger.Errorf("Unknown device reference, it should be LABEL, PARTLABEL, UUID or a /dev/* path") | ||
| errs = multierror.Append(errs, fmt.Errorf("Unkown device reference: %s", volumes[k].Device)) | ||
| continue | ||
| } | ||
| mountpoint := volumes[k].Mountpoint | ||
| if !strings.HasPrefix(mountpoint, runPath) { | ||
| mountpoint = filepath.Join(spec.Sysroot, mountpoint) | ||
| } | ||
|
|
||
| err := utils.MkdirAll(cfg.Fs, mountpoint, constants.DirPerm) | ||
| if err != nil { | ||
| cfg.Logger.Errorf("failed creating mountpoint %s", mountpoint) | ||
| errs = multierror.Append(errs, err) | ||
| continue | ||
| } | ||
|
|
||
| fstype := volumes[k].FSType | ||
| if fstype == "" { | ||
| fstype = "auto" | ||
| } | ||
|
|
||
| cfg.Logger.Debugf("Mounting %s to %s", dev, mountpoint) | ||
| err = cfg.Mounter.Mount(dev, mountpoint, fstype, volumes[k].Options) | ||
| if err != nil { | ||
| cfg.Logger.Errorf("failed mounting device %s to %s", dev, mountpoint) | ||
| errs = multierror.Append(errs, err) | ||
| } | ||
| } | ||
| return errs | ||
| } | ||
|
|
||
| func MountEphemeral(cfg *v1.RunConfig, sysroot string, overlay v1.EphemeralMounts) error { | ||
| if err := utils.MkdirAll(cfg.Config.Fs, constants.OverlayDir, constants.DirPerm); err != nil { | ||
| cfg.Logger.Errorf("Error creating directory %s: %s", constants.OverlayDir, err.Error()) | ||
|
|
@@ -111,10 +192,16 @@ func MountPersistent(cfg *v1.RunConfig, sysroot string, persistent v1.Persistent | |
| mountFunc = MountBindPath | ||
| } | ||
|
|
||
| if persistent.Volume.Device == "" || persistent.Volume.Mountpoint == "" { | ||
| cfg.Logger.Debug("No persistent device defined, omitting persistent paths mounts") | ||
| return nil | ||
| } | ||
|
|
||
| for _, path := range persistent.Paths { | ||
| cfg.Logger.Debugf("Mounting path %s into %s", path, sysroot) | ||
|
|
||
| if err := mountFunc(cfg, sysroot, constants.PersistentStateDir, path); err != nil { | ||
| target := filepath.Join(persistent.Volume.Mountpoint, constants.PersistentStateDir) | ||
| if err := mountFunc(cfg, sysroot, target, path); err != nil { | ||
| cfg.Logger.Errorf("Error mounting path %s: %s", path, err.Error()) | ||
| return err | ||
| } | ||
|
|
@@ -192,68 +279,110 @@ func MountOverlayPath(cfg *v1.RunConfig, sysroot, overlayDir, path string) error | |
| return nil | ||
| } | ||
|
|
||
| func findmnt(runner v1.Runner, mountpoint string) (string, error) { | ||
frelon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| output, err := runner.Run("findmnt", "-fno", "SOURCE", mountpoint) | ||
| return strings.TrimSuffix(string(output), "\n"), err | ||
| } | ||
| func WriteFstab(cfg *v1.RunConfig, spec *v1.MountSpec, data string) error { | ||
| var errs error | ||
|
|
||
| func WriteFstab(cfg *v1.RunConfig, spec *v1.MountSpec) error { | ||
| if !spec.WriteFstab { | ||
| cfg.Logger.Debug("Skipping writing fstab") | ||
| return nil | ||
| } | ||
|
|
||
| loop, err := findmnt(cfg.Runner, spec.Sysroot) | ||
| if err != nil { | ||
| return err | ||
| for _, vol := range spec.Volumes { | ||
| data += fstab(vol.Device, vol.Mountpoint, vol.FSType, vol.Options) | ||
| } | ||
|
|
||
| data := fstab(loop, "/", "ext2", []string{"ro", "relatime"}) | ||
| data = data + fstab("tmpfs", constants.OverlayDir, "tmpfs", []string{"defaults", fmt.Sprintf("size=%s", spec.Ephemeral.Size)}) | ||
| if spec.HasPersistent() { | ||
| pVol := spec.Persistent.Volume | ||
| data += fstab(pVol.Device, pVol.Mountpoint, pVol.FSType, pVol.Options) | ||
|
|
||
| for _, part := range spec.Partitions.PartitionsByMountPoint(false) { | ||
| if part.Path == "" { | ||
| cfg.Logger.Warnf("Partition '%s' has undefined device, can't be included in fstab", part.Name) | ||
| continue | ||
| } | ||
|
|
||
| data = data + fstab(part.Path, part.MountPoint, "auto", part.Flags) | ||
| } | ||
|
|
||
| for _, rw := range spec.Ephemeral.Paths { | ||
| data += overlayLine(rw, constants.OverlayDir, constants.OverlayDir) | ||
| } | ||
|
|
||
| if ok, _ := elemental.IsMounted(cfg.Config, spec.Partitions.Persistent); ok { | ||
| for _, path := range spec.Persistent.Paths { | ||
| if spec.Persistent.Mode == constants.OverlayMode { | ||
| data += overlayLine(path, constants.PersistentStateDir, constants.PersistentDir) | ||
| data += overlayLine(path, filepath.Join(pVol.Mountpoint, constants.PersistentStateDir), constants.PersistentDir) | ||
| continue | ||
| } | ||
|
|
||
| if spec.Persistent.Mode == constants.BindMode { | ||
| trimmed := strings.TrimPrefix(path, "/") | ||
| pathName := strings.ReplaceAll(trimmed, "/", "-") + ".bind" | ||
| stateDir := fmt.Sprintf("%s/%s", constants.PersistentStateDir, pathName) | ||
| stateDir := filepath.Join(pVol.Mountpoint, constants.PersistentStateDir, pathName) | ||
|
|
||
| data = data + fstab(stateDir, path, "none", []string{"defaults", "bind"}) | ||
| continue | ||
| } | ||
|
|
||
| return fmt.Errorf("Unknown persistent mode '%s'", spec.Persistent.Mode) | ||
| errs = multierror.Append(errs, fmt.Errorf("Unknown persistent mode '%s'", spec.Persistent.Mode)) | ||
| } | ||
| } | ||
|
|
||
| data += fstab("tmpfs", constants.OverlayDir, "tmpfs", []string{"defaults", fmt.Sprintf("size=%s", spec.Ephemeral.Size)}) | ||
| for _, rw := range spec.Ephemeral.Paths { | ||
| data += overlayLine(rw, constants.OverlayDir, constants.OverlayDir) | ||
| } | ||
|
|
||
| return cfg.Config.Fs.WriteFile(filepath.Join(spec.Sysroot, "/etc/fstab"), []byte(data), 0644) | ||
| } | ||
|
|
||
| func InitialFstabData(runner v1.Runner, sysroot string) (string, error) { | ||
| var data string | ||
|
|
||
| mounts, err := findmnt(runner, "/") | ||
| if err != nil { | ||
| return "", err | ||
| } | ||
| for _, mnt := range mounts { | ||
| if mnt.Mountpoint == sysroot { | ||
| data += fstab(mnt.Device, "/", "auto", mnt.Options) | ||
| } else if strings.HasPrefix(mnt.Mountpoint, sysroot) { | ||
| data += fstab(mnt.Device, strings.TrimPrefix(mnt.Mountpoint, sysroot), mnt.FSType, mnt.Options) | ||
| } else if strings.HasPrefix(mnt.Mountpoint, constants.RunElementalDir) { | ||
| data += fstab(mnt.Device, mnt.Mountpoint, mnt.FSType, mnt.Options) | ||
| } else if mnt.Mountpoint == constants.RunningStateDir { | ||
| data += fstab(mnt.Device, mnt.Mountpoint, mnt.FSType, mnt.Options) | ||
| } | ||
| } | ||
|
|
||
| return data, nil | ||
| } | ||
|
|
||
| func fstab(device, path, fstype string, flags []string) string { | ||
| if len(flags) == 0 { | ||
| flags = []string{"defaults"} | ||
| } | ||
|
|
||
| if fstype == "" { | ||
| fstype = "auto" | ||
| } | ||
| return fmt.Sprintf("%s\t%s\t%s\t%s\t0\t0\n", device, path, fstype, strings.Join(flags, ",")) | ||
| } | ||
|
|
||
| func findmnt(runner v1.Runner, mountpoint string) ([]*v1.VolumeMount, error) { | ||
| mounts := []*v1.VolumeMount{} | ||
| output, err := runner.Run("findmnt", "-Rrfno", "SOURCE,TARGET,FSTYPE,OPTIONS", mountpoint) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| scanner := bufio.NewScanner(strings.NewReader(strings.TrimSpace(string(output)))) | ||
| for scanner.Scan() { | ||
| lineFields := strings.Fields(scanner.Text()) | ||
| if len(lineFields) != 4 { | ||
| continue | ||
| } | ||
| if lineFields[2] == "btrfs" { | ||
| r := regexp.MustCompile(`(/.+)\[.*\]`) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On btrfs mounted subvolume added in brackets something like |
||
| if r.MatchString(lineFields[0]) { | ||
| match := r.FindStringSubmatch(lineFields[0]) | ||
| lineFields[0] = match[1] | ||
| } | ||
| } | ||
| mounts = append(mounts, &v1.VolumeMount{ | ||
| Device: lineFields[0], | ||
| Mountpoint: lineFields[1], | ||
| Options: strings.Split(lineFields[3], ","), | ||
| }) | ||
| } | ||
| return mounts, nil | ||
| } | ||
|
|
||
| func overlayLine(path, upperPath, requriedMount string) string { | ||
| trimmed := strings.TrimPrefix(path, "/") | ||
| pathName := strings.ReplaceAll(trimmed, "/", "-") + overlaySuffix | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In case you are wondering this sorting keys logic to allow nested mountpoints such as having a volume mounted at
/run/elementaland persistent mount point at/run/elemental/persistent. With this logic we make sure/run/elementalis mounted first.