Beispiel #1
0
// WaitOnDevices waits for the devices named in devs to be plugged before returning.
func WaitOnDevices(devs []string, stage string) error {
	conn, err := dbus.NewSystemdConnection()
	if err != nil {
		return err
	}

	results := map[string]chan string{}
	for _, dev := range devs {
		unitName := unit.UnitNamePathEscape(dev + ".device")
		results[unitName] = make(chan string)

		if _, err = conn.StartUnit(unitName, "replace", results[unitName]); err != nil {
			return fmt.Errorf("failed starting device unit %s: %v", unitName, err)
		}
	}

	for unitName, result := range results {
		s := <-result

		if s != "done" {
			return fmt.Errorf("device unit %s %s", unitName, s)
		}
	}

	return nil
}
Beispiel #2
0
// 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
}
Beispiel #3
0
// 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 := GenerateMounts(ra, vols)
	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)

		if vol.Kind == "empty" {
			log.Printf("creating an empty volume folder for sharing: %q", whatFullPath)
			err := os.MkdirAll(whatFullPath, 0700)
			if err != nil {
				return err
			}
		}

		// 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 errwrap.Wrap(fmt.Errorf("failed to prepare dir for mount %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 errwrap.Wrap(fmt.Errorf("cannot install new mount unit for app %q", 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 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
}
Beispiel #4
0
func devToUnitName(dev string) string {
	return "torus-" + unit.UnitNamePathEscape(dev) + ".service"
}
Beispiel #5
0
// 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"
}