// installNewMountUnit creates and installs new mount unit in default // systemd location (/usr/lib/systemd/system) in pod stage1 filesystem. // root is a stage1 relative to pod filesystem path like /var/lib/uuid/rootfs/ // (from Pod.Root). // beforeAndrequiredBy creates systemd unit dependency (can be space separated // for multi). func installNewMountUnit(root, what, where, fsType, options, beforeAndrequiredBy, unitsDir string) error { opts := []*unit.UnitOption{ unit.NewUnitOption("Unit", "Description", fmt.Sprintf("Mount unit for %s", where)), unit.NewUnitOption("Unit", "DefaultDependencies", "false"), unit.NewUnitOption("Unit", "Before", beforeAndrequiredBy), unit.NewUnitOption("Mount", "What", what), unit.NewUnitOption("Mount", "Where", where), unit.NewUnitOption("Mount", "Type", fsType), unit.NewUnitOption("Mount", "Options", options), unit.NewUnitOption("Install", "RequiredBy", beforeAndrequiredBy), } unitsPath := filepath.Join(root, unitsDir) unitName := unit.UnitNamePathEscape(where + ".mount") unitBytes, err := ioutil.ReadAll(unit.Serialize(opts)) if err != nil { return fmt.Errorf("failed to serialize mount unit file to bytes %q: %v", unitName, err) } err = ioutil.WriteFile(filepath.Join(unitsPath, unitName), unitBytes, 0644) if err != nil { return fmt.Errorf("failed to create mount unit file %q: %v", unitName, err) } log.Printf("mount unit created: %q in %q (what=%q, where=%q)", unitName, unitsPath, what, where) return nil }
// installNewMountUnit creates and installs a new mount unit in the default // systemd location (/usr/lib/systemd/system) inside the pod stage1 filesystem. // root is pod's absolute stage1 path (from Pod.Root). // beforeAndrequiredBy creates a systemd unit dependency (can be space separated // for multi). // It returns the name of the generated unit. func installNewMountUnit(root, what, where, fsType, options, beforeAndrequiredBy, unitsDir string) (string, error) { opts := []*unit.UnitOption{ unit.NewUnitOption("Unit", "Description", fmt.Sprintf("Mount unit for %s", where)), unit.NewUnitOption("Unit", "DefaultDependencies", "false"), unit.NewUnitOption("Unit", "Before", beforeAndrequiredBy), unit.NewUnitOption("Mount", "What", what), unit.NewUnitOption("Mount", "Where", where), unit.NewUnitOption("Mount", "Type", fsType), unit.NewUnitOption("Mount", "Options", options), unit.NewUnitOption("Install", "RequiredBy", beforeAndrequiredBy), } unitsPath := filepath.Join(root, unitsDir) unitName := unit.UnitNamePathEscape(where + ".mount") if err := writeUnit(opts, filepath.Join(unitsPath, unitName)); err != nil { return "", err } log.Printf("mount unit created: %q in %q (what=%q, where=%q)", unitName, unitsPath, what, where) return unitName, nil }
// AppToSystemdMountUnits prepare bind mount unit for empty or host kind mounting // between stage1 rootfs and chrooted filesystem for application func AppToSystemdMountUnits(root string, appName types.ACName, volumes []types.Volume, ra *schema.RuntimeApp, unitsDir string) error { app := ra.App vols := make(map[types.ACName]types.Volume) for _, v := range volumes { vols[v.Name] = v } mounts, err := initcommon.GenerateMounts(ra, vols) if err != nil { return err } for _, m := range mounts { vol := vols[m.Volume] // source relative to stage1 rootfs to relative pod root whatPath := filepath.Join(stage1MntDir, vol.Name.String()) whatFullPath := filepath.Join(root, whatPath) // destination relative to stage1 rootfs and relative to pod root wherePath := filepath.Join(common.RelAppRootfsPath(appName), m.Path) whereFullPath := filepath.Join(root, wherePath) // assertion to make sure that "what" exists (created earlier by PodToSystemdHostMountUnits) log.Printf("checking required source path: %q", whatFullPath) if _, err := os.Stat(whatFullPath); os.IsNotExist(err) { return fmt.Errorf("bug: missing source for volume %v", vol.Name) } // optionally prepare app directory log.Printf("optionally preparing destination path: %q", whereFullPath) err := os.MkdirAll(whereFullPath, 0700) if err != nil { return fmt.Errorf("failed to prepare dir for mount %v: %v", m.Volume, err) } // install new mount unit for bind mount /mnt/volumeName -> /opt/stage2/{app-id}/rootfs/{{mountPoint.Path}} mu, err := installNewMountUnit( root, // where put a mount unit whatPath, // what - stage1 rootfs /mnt/VolumeName wherePath, // where - inside chroot app filesystem "bind", // fstype "bind", // options serviceUnitName(appName), unitsDir, ) if err != nil { return fmt.Errorf("cannot install new mount unit for app %q: %v", appName.String(), err) } // TODO(iaguis) when we update util-linux to 2.27, this code can go // away and we can bind-mount RO with one unit file. // http://ftp.kernel.org/pub/linux/utils/util-linux/v2.27/v2.27-ReleaseNotes if initcommon.IsMountReadOnly(vol, app.MountPoints) { opts := []*unit.UnitOption{ unit.NewUnitOption("Unit", "Description", fmt.Sprintf("Remount read-only unit for %s", wherePath)), unit.NewUnitOption("Unit", "DefaultDependencies", "false"), unit.NewUnitOption("Unit", "After", mu), unit.NewUnitOption("Unit", "Wants", mu), unit.NewUnitOption("Service", "ExecStart", fmt.Sprintf("/usr/bin/mount -o remount,ro %s", wherePath)), unit.NewUnitOption("Install", "RequiredBy", mu), } remountUnitPath := filepath.Join(root, unitsDir, unit.UnitNamePathEscape(wherePath+"-remount.service")) if err := writeUnit(opts, remountUnitPath); err != nil { return err } } } return nil }
// InstantiatedPrepareAppUnitName returns the systemd service unit name for prepare-app // instantiated for the given root. func InstantiatedPrepareAppUnitName(appName types.ACName) string { // Naming respecting escaping rules, see systemd.unit(5) and systemd-escape(1) escapedRoot := unit.UnitNamePathEscape(common.RelAppRootfsPath(appName)) return "prepare-app@" + escapedRoot + ".service" }