- config: add Prefix/Suffix fields to Config struct - systemd: ServiceName/Generate/UnitPath/Install/Uninstall/Enable/Disable/Status all accept prefix+suffix - runtime: fall back to short container ID (12 chars) when container has no name - cmd: active, status, install all thread prefix/suffix from config - systemd/generator_test.go: updated all calls + added TestGenerate_WithPrefixSuffix - docs/generated-units.md: full examples of every unit type + ordering + naming - README: updated config docs, prefix/suffix section, link to docs/
88 lines
2.4 KiB
Go
88 lines
2.4 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
const DefaultConfigPath = "/etc/unitdore/units.yaml"
|
|
|
|
// Unit represents a single managed container unit.
|
|
type Unit struct {
|
|
Name string `yaml:"name"`
|
|
Runtime string `yaml:"runtime"` // podman | docker | exec
|
|
User string `yaml:"user,omitempty"` // empty = root/system unit
|
|
Command string `yaml:"command,omitempty"` // override ExecStart
|
|
Order int `yaml:"order"`
|
|
Delay string `yaml:"delay,omitempty"` // e.g. "5s"
|
|
Enabled bool `yaml:"enabled"`
|
|
DisabledReason string `yaml:"disabled_reason,omitempty"`
|
|
}
|
|
|
|
// Config is the root config structure.
|
|
type Config struct {
|
|
Units []Unit `yaml:"units"`
|
|
Prefix string `yaml:"prefix,omitempty"` // prepended to generated service name, e.g. "prod-"
|
|
Suffix string `yaml:"suffix,omitempty"` // appended to generated service name, e.g. "-svc"
|
|
}
|
|
|
|
// Load reads and parses the config file at path.
|
|
func Load(path string) (*Config, error) {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("reading config: %w", err)
|
|
}
|
|
var cfg Config
|
|
if err := yaml.Unmarshal(data, &cfg); err != nil {
|
|
return nil, fmt.Errorf("parsing config: %w", err)
|
|
}
|
|
return &cfg, nil
|
|
}
|
|
|
|
// LoadOrEmpty loads the config file, or returns an empty config if it doesn't exist.
|
|
func LoadOrEmpty(path string) (*Config, error) {
|
|
_, err := os.Stat(path)
|
|
if os.IsNotExist(err) {
|
|
return &Config{}, nil
|
|
}
|
|
return Load(path)
|
|
}
|
|
|
|
// Save writes the config back to disk, creating parent directories if needed.
|
|
func (c *Config) Save(path string) error {
|
|
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
|
return fmt.Errorf("creating config dir: %w", err)
|
|
}
|
|
data, err := yaml.Marshal(c)
|
|
if err != nil {
|
|
return fmt.Errorf("marshalling config: %w", err)
|
|
}
|
|
if err := os.WriteFile(path, data, 0644); err != nil {
|
|
return fmt.Errorf("writing config: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// FindUnit returns a pointer to the unit with the given name, or nil.
|
|
func (c *Config) FindUnit(name string) *Unit {
|
|
for i := range c.Units {
|
|
if c.Units[i].Name == name {
|
|
return &c.Units[i]
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AddUnit appends a unit if one with that name doesn't already exist.
|
|
// Returns true if it was added.
|
|
func (c *Config) AddUnit(u Unit) bool {
|
|
if c.FindUnit(u.Name) != nil {
|
|
return false
|
|
}
|
|
c.Units = append(c.Units, u)
|
|
return true
|
|
}
|