Add systemd resource budgets with TOML profiles
This commit is contained in:
commit
be8749a384
1 changed files with 2591 additions and 0 deletions
119
internal/budget/app.go
Normal file
119
internal/budget/app.go
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
package budget
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type application struct {
|
||||
config config
|
||||
paths paths
|
||||
ctl unitCtl
|
||||
run func(...string) error
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
func (a application) apply() error {
|
||||
files, err := a.config.render()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeFiles(a.paths, files); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := a.run("daemon-reload"); err != nil {
|
||||
return err
|
||||
}
|
||||
units, err := a.ctl.Units()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, unit := range units {
|
||||
name := a.config.unitProfile(unit.Name)
|
||||
if name == "" || unit.Active != "active" {
|
||||
continue
|
||||
}
|
||||
values, err := a.config.properties(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := a.ctl.Set(unit.Name, values); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := a.run("enable", "budget.service"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := a.run("restart", "budget.service"); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = fmt.Fprintf(a.out, "Applied %d resource configuration files\n", len(files))
|
||||
return err
|
||||
}
|
||||
|
||||
func selectUnits(c config, units []unit, target string) []string {
|
||||
selected := map[string]bool{}
|
||||
if strings.HasSuffix(target, ".slice") || strings.HasSuffix(target, ".scope") || strings.HasSuffix(target, ".service") {
|
||||
selected[target] = true
|
||||
} else {
|
||||
for _, unit := range units {
|
||||
if unit.Active == "active" && appMatches(target, unit.Name) {
|
||||
selected[unit.Name] = true
|
||||
}
|
||||
}
|
||||
if _, shared := c.Shared[target]; shared {
|
||||
selected[c.sharedSlice()] = true
|
||||
}
|
||||
}
|
||||
return keys(selected)
|
||||
}
|
||||
|
||||
func (a application) override(name, target string) error {
|
||||
values, err := a.config.properties(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
units, err := a.ctl.Units()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
selected := selectUnits(a.config, units, target)
|
||||
if len(selected) == 0 {
|
||||
return fmt.Errorf("no running unit matches %s", target)
|
||||
}
|
||||
all := map[string]bool{}
|
||||
for _, name := range selected {
|
||||
all[name] = true
|
||||
}
|
||||
for _, unit := range selected {
|
||||
if !strings.HasSuffix(unit, ".slice") {
|
||||
continue
|
||||
}
|
||||
group, err := a.ctl.Group(unit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, child := range units {
|
||||
if child.Active != "active" || all[child.Name] || (!strings.HasSuffix(child.Name, ".scope") && !strings.HasSuffix(child.Name, ".service")) {
|
||||
continue
|
||||
}
|
||||
childGroup, err := a.ctl.Group(child.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.HasPrefix(childGroup, group+"/") {
|
||||
all[child.Name] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, unit := range keys(all) {
|
||||
if err := a.ctl.Set(unit, values); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := fmt.Fprintf(a.out, "%s: %s\n", unit, name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Loading…
Reference in a new issue