Skip to content

Commit

Permalink
Added support of kill mode configuration (systemd feature)
Browse files Browse the repository at this point in the history
  • Loading branch information
andyone committed Jun 20, 2018
1 parent a631b86 commit 75306a7
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 28 deletions.
1 change: 1 addition & 0 deletions common/init-exporter.spec
Expand Up @@ -113,6 +113,7 @@ rm -rf %{buildroot}
%changelog
* Wed Jun 20 2018 Anton Novojilov <andyone@fun-box.ru> - 0.20.0-0
- Added support of resources usage limits configuration (systemd feature)
- Added support of kill mode configuration (systemd feature)

* Thu Apr 05 2018 Anton Novojilov <andyone@fun-box.ru> - 0.19.0-0
- Added pyenv support
Expand Down
5 changes: 4 additions & 1 deletion export/export_test.go
Expand Up @@ -355,6 +355,7 @@ func (s *ExportSuite) TestSystemdExport(c *C) {
"[Service]",
"Type=simple",
"",
"",
"KillSignal=SIGQUIT",
"TimeoutStopSec=10",
"Restart=on-failure",
Expand Down Expand Up @@ -388,6 +389,7 @@ func (s *ExportSuite) TestSystemdExport(c *C) {
"[Service]",
"Type=simple",
"",
"",
"KillSignal=SIGQUIT",
"TimeoutStopSec=10",
"Restart=on-failure",
Expand Down Expand Up @@ -422,6 +424,7 @@ func (s *ExportSuite) TestSystemdExport(c *C) {
"Type=simple",
"",
"",
"",
"TimeoutStopSec=0",
"Restart=on-failure",
"",
Expand Down Expand Up @@ -534,7 +537,7 @@ func createTestApp(helperDir, targetDir string) *procfile.Application {
Resources: &procfile.Resources{
CPUWeight: 50,
StartupCPUWeight: 15,
CPUQuota: "35%",
CPUQuota: 35,
MemoryLow: "1G",
MemoryHigh: "4G",
MemoryMax: "8G",
Expand Down
5 changes: 3 additions & 2 deletions export/systemd.go
Expand Up @@ -72,6 +72,7 @@ PartOf={{.Application.Name}}.service
[Service]
Type=simple
{{ if .Service.Options.IsKillModeSet }}KillMode={{.Service.Options.KillMode}}{{ end }}
{{ if .Service.Options.IsKillSignalSet }}KillSignal={{.Service.Options.KillSignal}}{{ end }}
TimeoutStopSec={{.Service.Options.KillTimeout}}
{{ if .Service.Options.IsRespawnEnabled }}Restart=on-failure{{ end }}
Expand Down Expand Up @@ -135,8 +136,8 @@ func (sd *systemdServiceData) ResourcesAsString() string {
result += fmt.Sprintf("StartupCPUWeight=%d\n", resources.CPUWeight)
}

if resources.CPUQuota != "" {
result += fmt.Sprintf("CPUQuota=%s\n", resources.CPUQuota)
if resources.CPUQuota != 0 {
result += fmt.Sprintf("CPUQuota=%d%%\n", resources.CPUQuota)
}

if resources.MemoryLow != "" {
Expand Down
35 changes: 34 additions & 1 deletion procfile/procfile.go
Expand Up @@ -17,6 +17,7 @@ import (
"pkg.re/essentialkaos/ek.v9/fsutil"
"pkg.re/essentialkaos/ek.v9/log"
"pkg.re/essentialkaos/ek.v9/path"
"pkg.re/essentialkaos/ek.v9/sliceutil"
"pkg.re/essentialkaos/ek.v9/strutil"
)

Expand Down Expand Up @@ -61,6 +62,7 @@ type ServiceOptions struct {
LogFile string // Path to log file
KillTimeout int // Kill timeout in seconds
KillSignal string // Kill signal name
KillMode string // Kill mode (systemd only)
ReloadSignal string // Reload signal name
Count int // Exec count
RespawnInterval int // Respawn interval in seconds
Expand All @@ -74,7 +76,7 @@ type ServiceOptions struct {
type Resources struct {
CPUWeight int
StartupCPUWeight int
CPUQuota string
CPUQuota int
MemoryLow string
MemoryHigh string
MemoryMax string
Expand Down Expand Up @@ -218,6 +220,32 @@ func (so *ServiceOptions) Validate() []error {
errs.Add(fmt.Errorf("Property \"respawn:interval\" must be greater or equal 0"))
}

if so.KillMode != "" && !sliceutil.Contains([]string{"control-group", "process", "mixed", "none"}, so.KillMode) {
errs.Add(fmt.Errorf("Property \"kill_mode\" must contains 'control-group', 'process', 'mixed' or 'none'"))
}

if so.Resources != nil {
if so.Resources.CPUWeight < 0 || so.Resources.CPUWeight > 10000 {
errs.Add(fmt.Errorf("Property \"resources:cpu_weight\" must be greater or equal 0 and less or equal 10000"))
}

if so.Resources.StartupCPUWeight < 0 || so.Resources.StartupCPUWeight > 10000 {
errs.Add(fmt.Errorf("Property \"resources:startup_cpu_weight\" must be greater or equal 0 and less or equal 10000"))
}

if so.Resources.CPUQuota < 0 {
errs.Add(fmt.Errorf("Property \"resources:cpu_quota\" must be greater than 0"))
}

if so.Resources.IOWeight < 0 || so.Resources.IOWeight > 10000 {
errs.Add(fmt.Errorf("Property \"resources:io_weight\" must be greater or equal 0 and less or equal 10000"))
}

if so.Resources.StartupIOWeight < 0 || so.Resources.StartupIOWeight > 10000 {
errs.Add(fmt.Errorf("Property \"resources:startup_io_weight\" must be greater or equal 0 and less or equal 10000"))
}
}

return errs.All()
}

Expand Down Expand Up @@ -298,6 +326,11 @@ func (so *ServiceOptions) IsKillSignalSet() bool {
return so.KillSignal != ""
}

// IsKillModeSet return true if custom kill mode set
func (so *ServiceOptions) IsKillModeSet() bool {
return so.KillMode != ""
}

// IsReloadSignalSet return true if custom reload signal set
func (so *ServiceOptions) IsReloadSignalSet() bool {
return so.ReloadSignal != ""
Expand Down
3 changes: 2 additions & 1 deletion procfile/procfile_test.go
Expand Up @@ -119,6 +119,7 @@ func (s *ProcfileSuite) TestProcV2Parsing(c *C) {
c.Assert(service.Options.IsCustomLogEnabled(), Equals, false)
c.Assert(service.Options.KillTimeout, Equals, 60)
c.Assert(service.Options.KillSignal, Equals, "SIGQUIT")
c.Assert(service.Options.KillMode, Equals, "process")
c.Assert(service.Options.ReloadSignal, Equals, "SIGUSR2")
c.Assert(service.Options.RespawnCount, Equals, 7)
c.Assert(service.Options.RespawnInterval, Equals, 22)
Expand Down Expand Up @@ -151,7 +152,7 @@ func (s *ProcfileSuite) TestProcV2Parsing(c *C) {
c.Assert(service.Options.Resources, NotNil)
c.Assert(service.Options.Resources.CPUWeight, Equals, 50)
c.Assert(service.Options.Resources.StartupCPUWeight, Equals, 15)
c.Assert(service.Options.Resources.CPUQuota, Equals, "40%")
c.Assert(service.Options.Resources.CPUQuota, Equals, 40)
c.Assert(service.Options.Resources.MemoryLow, Equals, "1G")
c.Assert(service.Options.Resources.MemoryHigh, Equals, "4G")
c.Assert(service.Options.Resources.MemoryMax, Equals, "8G")
Expand Down
52 changes: 30 additions & 22 deletions procfile/procfile_v2.go
Expand Up @@ -197,6 +197,14 @@ func parseV2Options(options *ServiceOptions, yaml *simpleyaml.Yaml) error {
}
}

if yaml.IsExist("kill_mode") {
options.KillMode, err = yaml.Get("kill_mode").String()

if err != nil {
return formatPropError("kill_mode", err)
}
}

if yaml.IsExist("reload_signal") {
options.ReloadSignal, err = yaml.Get("reload_signal").String()

Expand Down Expand Up @@ -236,15 +244,15 @@ func parseV2Options(options *ServiceOptions, yaml *simpleyaml.Yaml) error {
options.RespawnCount, err = yaml.Get("respawn").Get("count").Int()

if err != nil {
return formatPropError("respawn.count", err)
return formatPropError("respawn:count", err)
}
}

if yaml.IsPathExist("respawn", "interval") {
options.RespawnInterval, err = yaml.Get("respawn").Get("interval").Int()

if err != nil {
return formatPropError("respawn.interval", err)
return formatPropError("respawn:interval", err)
}
}

Expand All @@ -261,15 +269,15 @@ func parseV2Options(options *ServiceOptions, yaml *simpleyaml.Yaml) error {
options.LimitFile, err = yaml.Get("limits").Get("nofile").Int()

if err != nil {
return formatPropError("limits.nofile", err)
return formatPropError("limits:nofile", err)
}
}

if yaml.IsPathExist("limits", "nproc") {
options.LimitProc, err = yaml.Get("limits").Get("nproc").Int()

if err != nil {
return formatPropError("limits.nproc", err)
return formatPropError("limits:nproc", err)
}
}
}
Expand All @@ -295,135 +303,135 @@ func parseV2Resources(yaml *simpleyaml.Yaml) (*Resources, error) {
resources.CPUWeight, err = yaml.Get("cpu_weight").Int()

if err != nil {
return nil, formatPropError("cpu_weight", err)
return nil, formatPropError("resources:cpu_weight", err)
}
}

if yaml.IsExist("startup_cpu_weight") {
resources.StartupCPUWeight, err = yaml.Get("startup_cpu_weight").Int()

if err != nil {
return nil, formatPropError("startup_cpu_weight", err)
return nil, formatPropError("resources:startup_cpu_weight", err)
}
}

if yaml.IsExist("cpu_quota") {
resources.CPUQuota, err = yaml.Get("cpu_quota").String()
resources.CPUQuota, err = yaml.Get("cpu_quota").Int()

if err != nil {
return nil, formatPropError("cpu_quota", err)
return nil, formatPropError("resources:cpu_quota", err)
}
}

if yaml.IsExist("memory_low") {
resources.MemoryLow, err = yaml.Get("memory_low").String()

if err != nil {
return nil, formatPropError("memory_low", err)
return nil, formatPropError("resources:memory_low", err)
}
}

if yaml.IsExist("memory_high") {
resources.MemoryHigh, err = yaml.Get("memory_high").String()

if err != nil {
return nil, formatPropError("memory_high", err)
return nil, formatPropError("resources:memory_high", err)
}
}

if yaml.IsExist("memory_max") {
resources.MemoryMax, err = yaml.Get("memory_max").String()

if err != nil {
return nil, formatPropError("memory_max", err)
return nil, formatPropError("resources:memory_max", err)
}
}

if yaml.IsExist("memory_swap_max") {
resources.MemorySwapMax, err = yaml.Get("memory_swap_max").String()

if err != nil {
return nil, formatPropError("memory_swap_max", err)
return nil, formatPropError("resources:memory_swap_max", err)
}
}

if yaml.IsExist("task_max") {
resources.TasksMax, err = yaml.Get("task_max").Int()

if err != nil {
return nil, formatPropError("task_max", err)
return nil, formatPropError("resources:task_max", err)
}
}

if yaml.IsExist("io_weight") {
resources.IOWeight, err = yaml.Get("io_weight").Int()

if err != nil {
return nil, formatPropError("io_weight", err)
return nil, formatPropError("resources:io_weight", err)
}
}

if yaml.IsExist("startup_io_weight") {
resources.StartupIOWeight, err = yaml.Get("startup_io_weight").Int()

if err != nil {
return nil, formatPropError("startup_io_weight", err)
return nil, formatPropError("resources:startup_io_weight", err)
}
}

if yaml.IsExist("io_device_weight") {
resources.IODeviceWeight, err = yaml.Get("io_device_weight").String()

if err != nil {
return nil, formatPropError("io_device_weight", err)
return nil, formatPropError("resources:io_device_weight", err)
}
}

if yaml.IsExist("io_read_bandwidth_max") {
resources.IOReadBandwidthMax, err = yaml.Get("io_read_bandwidth_max").String()

if err != nil {
return nil, formatPropError("io_read_bandwidth_max", err)
return nil, formatPropError("resources:io_read_bandwidth_max", err)
}
}

if yaml.IsExist("io_write_bandwidth_max") {
resources.IOWriteBandwidthMax, err = yaml.Get("io_write_bandwidth_max").String()

if err != nil {
return nil, formatPropError("io_write_bandwidth_max", err)
return nil, formatPropError("resources:io_write_bandwidth_max", err)
}
}

if yaml.IsExist("io_read_iops_max") {
resources.IOReadIOPSMax, err = yaml.Get("io_read_iops_max").String()

if err != nil {
return nil, formatPropError("io_read_iops_max", err)
return nil, formatPropError("resources:io_read_iops_max", err)
}
}

if yaml.IsExist("io_write_iops_max") {
resources.IOWriteIOPSMax, err = yaml.Get("io_write_iops_max").String()

if err != nil {
return nil, formatPropError("io_write_iops_max", err)
return nil, formatPropError("resources:io_write_iops_max", err)
}
}

if yaml.IsExist("ip_address_allow") {
resources.IPAddressAllow, err = yaml.Get("ip_address_allow").String()

if err != nil {
return nil, formatPropError("ip_address_allow", err)
return nil, formatPropError("resources:ip_address_allow", err)
}
}

if yaml.IsExist("ip_address_deny") {
resources.IPAddressDeny, err = yaml.Get("ip_address_deny").String()

if err != nil {
return nil, formatPropError("ip_address_deny", err)
return nil, formatPropError("resources:ip_address_deny", err)
}
}

Expand Down
3 changes: 2 additions & 1 deletion testdata/procfile_v2
Expand Up @@ -40,6 +40,7 @@ commands:
limits:
nofile: 8192
nproc: 8192
kill_mode: process
kill_timeout: 60
kill_signal: SIGQUIT
reload_signal: SIGUSR2
Expand All @@ -52,7 +53,7 @@ commands:
resources:
cpu_weight: 50
startup_cpu_weight: 15
cpu_quota: 40%
cpu_quota: 40
memory_low: 1G
memory_high: 4G
memory_max: 8G
Expand Down

0 comments on commit 75306a7

Please sign in to comment.