diff --git a/cmd/install.go b/cmd/install.go index 729248e..244c4a2 100644 --- a/cmd/install.go +++ b/cmd/install.go @@ -31,7 +31,7 @@ func runInstall(cmd *cobra.Command, args []string) error { return err } - prefix, suffix := cfg.Prefix, cfg.Suffix + prefix, suffix, serviceUser := cfg.Prefix, cfg.Suffix, cfg.EffectiveServiceUser() installed := 0 skipped := 0 removed := 0 @@ -57,7 +57,7 @@ func runInstall(cmd *cobra.Command, args []string) error { } if dryRun { - content, err := systemd.Generate(u, prefix, suffix) + content, err := systemd.Generate(u, prefix, suffix, serviceUser) if err != nil { fmt.Printf(" ✗ %s: %v\n", u.Name, err) continue @@ -68,7 +68,7 @@ func runInstall(cmd *cobra.Command, args []string) error { continue } - if err := systemd.Install(u, prefix, suffix); err != nil { + if err := systemd.Install(u, prefix, suffix, serviceUser); err != nil { fmt.Printf(" ✗ failed: %s: %v\n", u.Name, err) } else { path, _ := systemd.UnitPath(u, prefix, suffix) diff --git a/config/config.go b/config/config.go index 6a19d92..08a7ec2 100644 --- a/config/config.go +++ b/config/config.go @@ -3,6 +3,7 @@ package config import ( "fmt" "os" + "os/user" "path/filepath" "gopkg.in/yaml.v3" @@ -24,9 +25,21 @@ type Unit struct { // 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" + 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" + ServiceUser string `yaml:"service_user,omitempty"` // User= in [Service] section; defaults to "unitdore" +} + +// EffectiveServiceUser returns the configured service user, or the current OS user if unset. +func (c *Config) EffectiveServiceUser() string { + if c.ServiceUser != "" { + return c.ServiceUser + } + if u, err := user.Current(); err == nil { + return u.Username + } + return "root" } // Load reads and parses the config file at path. diff --git a/systemd/generator.go b/systemd/generator.go index 10214af..cd40b5f 100644 --- a/systemd/generator.go +++ b/systemd/generator.go @@ -17,6 +17,7 @@ After=network.target [Service] Type=simple +User={{.ServiceUser}} ExecStart={{.ExecStart}} ExecStop={{.ExecStop}} Restart=on-failure @@ -49,6 +50,7 @@ type templateData struct { Unit config.Unit ExecStart string ExecStop string + ServiceUser string } // ServiceName returns the systemd service name for a unit, with optional prefix/suffix. @@ -57,7 +59,7 @@ func ServiceName(u config.Unit, prefix, suffix string) string { } // Generate produces the .service file content for a unit. -func Generate(u config.Unit, prefix, suffix string) (string, error) { +func Generate(u config.Unit, prefix, suffix, serviceUser string) (string, error) { execStart, execStop := buildExecCommands(u) data := templateData{ @@ -65,6 +67,7 @@ func Generate(u config.Unit, prefix, suffix string) (string, error) { Unit: u, ExecStart: execStart, ExecStop: execStop, + ServiceUser: serviceUser, } tmplStr := systemUnitTemplate diff --git a/systemd/generator_test.go b/systemd/generator_test.go index 05d1ec4..6f1268a 100644 --- a/systemd/generator_test.go +++ b/systemd/generator_test.go @@ -35,7 +35,7 @@ func TestGenerate_SystemUnit(t *testing.T) { Enabled: true, } - content, err := Generate(u, "", "") + content, err := Generate(u, "", "", "unitdore") if err != nil { t.Fatalf("Generate() error: %v", err) } @@ -70,7 +70,7 @@ func TestGenerate_UserUnit(t *testing.T) { Enabled: true, } - content, err := Generate(u, "", "") + content, err := Generate(u, "", "", "unitdore") if err != nil { t.Fatalf("Generate() error: %v", err) } @@ -101,7 +101,7 @@ func TestGenerate_CustomCommand(t *testing.T) { Enabled: true, } - content, err := Generate(u, "", "") + content, err := Generate(u, "", "", "unitdore") if err != nil { t.Fatalf("Generate() error: %v", err) } @@ -118,7 +118,7 @@ func TestGenerate_DockerRuntime(t *testing.T) { Enabled: true, } - content, err := Generate(u, "", "") + content, err := Generate(u, "", "", "unitdore") if err != nil { t.Fatalf("Generate() error: %v", err) } @@ -138,7 +138,7 @@ func TestGenerate_UnknownRuntime(t *testing.T) { Enabled: true, } - content, err := Generate(u, "", "") + content, err := Generate(u, "", "", "unitdore") if err != nil { t.Fatalf("Generate() should not error on unknown runtime: %v", err) } @@ -155,7 +155,7 @@ func TestGenerate_WithPrefixSuffix(t *testing.T) { Enabled: true, } - content, err := Generate(u, "prod-", "-web") + content, err := Generate(u, "prod-", "-web", "unitdore") if err != nil { t.Fatalf("Generate() error: %v", err) } diff --git a/systemd/manager.go b/systemd/manager.go index 4dccdfb..dff8ba3 100644 --- a/systemd/manager.go +++ b/systemd/manager.go @@ -27,8 +27,8 @@ func UnitPath(u config.Unit, prefix, suffix string) (string, error) { } // Install writes the .service file for a unit and reloads systemd. -func Install(u config.Unit, prefix, suffix string) error { - content, err := Generate(u, prefix, suffix) +func Install(u config.Unit, prefix, suffix, serviceUser string) error { + content, err := Generate(u, prefix, suffix, serviceUser) if err != nil { return err }