- install/uninstall/start/stop/enable take <name> or --all; *all kept as shortcuts - add enable (enable at boot + start); startall no longer enables - order/delay resolved by config.ResolveOrder and written into systemd, OpenRC and runit service files (After=/sleep), so they apply at boot - install --all stops and disables units before removing their files - update regenerates all installed, enabled units and removes disabled ones - edit creates the config dir; docs and man page updated - tests: fake Init for cmd, ResolveOrder, backend ordering output
68 lines
1.8 KiB
Go
68 lines
1.8 KiB
Go
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"sort"
|
|
"sync"
|
|
|
|
"github.com/warkanum/unitdore/config"
|
|
)
|
|
|
|
// sortUnits returns a copy of units sorted by order then name; reverse flips both.
|
|
func sortUnits(units []config.Unit, reverse bool) []config.Unit {
|
|
out := make([]config.Unit, len(units))
|
|
copy(out, units)
|
|
sort.SliceStable(out, func(i, j int) bool {
|
|
if out[i].Order != out[j].Order {
|
|
return (out[i].Order < out[j].Order) != reverse
|
|
}
|
|
return (out[i].Name < out[j].Name) != reverse
|
|
})
|
|
return out
|
|
}
|
|
|
|
// groupByOrder splits order-sorted units into groups sharing the same order.
|
|
func groupByOrder(sorted []config.Unit) [][]config.Unit {
|
|
var groups [][]config.Unit
|
|
for _, u := range sorted {
|
|
n := len(groups)
|
|
if n > 0 && groups[n-1][0].Order == u.Order {
|
|
groups[n-1] = append(groups[n-1], u)
|
|
continue
|
|
}
|
|
groups = append(groups, []config.Unit{u})
|
|
}
|
|
return groups
|
|
}
|
|
|
|
// runOrdered runs fn for every unit: groups of equal order one after another,
|
|
// units within a group in parallel. Start delays live in the generated service
|
|
// files, so a blocking start covers them. Output is printed per group in name
|
|
// order. Returns the number of units fn succeeded and failed for.
|
|
func runOrdered(units []config.Unit, w io.Writer, fn func(u config.Unit) (string, error)) (ok, failed int) {
|
|
for _, g := range groupByOrder(sortUnits(units, false)) {
|
|
msgs := make([]string, len(g))
|
|
errs := make([]error, len(g))
|
|
var wg sync.WaitGroup
|
|
for j, u := range g {
|
|
wg.Add(1)
|
|
go func(j int, u config.Unit) {
|
|
defer wg.Done()
|
|
msgs[j], errs[j] = fn(u)
|
|
}(j, u)
|
|
}
|
|
wg.Wait()
|
|
for j, u := range g {
|
|
if errs[j] != nil {
|
|
fmt.Fprintf(w, " ✗ failed: %s: %v\n", u.Name, errs[j])
|
|
failed++
|
|
continue
|
|
}
|
|
fmt.Fprintf(w, " ✓ %s\n", msgs[j])
|
|
ok++
|
|
}
|
|
}
|
|
return ok, failed
|
|
}
|