Skip to content
This repository was archived by the owner on Jul 18, 2025. It is now read-only.

Commit 56b0613

Browse files
author
Vincent Demeester
authored
Merge pull request #465 from gitlawr/add_stop_grace_period
add 'stop_grace_period'
2 parents 1a928db + 81e0786 commit 56b0613

File tree

7 files changed

+106
-60
lines changed

7 files changed

+106
-60
lines changed

cli/command/command.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,6 @@ func RestartCommand(factory app.ProjectFactory) cli.Command {
228228
cli.IntFlag{
229229
Name: "timeout,t",
230230
Usage: "Specify a shutdown timeout in seconds.",
231-
Value: 10,
232231
},
233232
},
234233
}
@@ -244,7 +243,6 @@ func StopCommand(factory app.ProjectFactory) cli.Command {
244243
cli.IntFlag{
245244
Name: "timeout,t",
246245
Usage: "Specify a shutdown timeout in seconds.",
247-
Value: 10,
248246
},
249247
},
250248
}
@@ -283,7 +281,6 @@ func ScaleCommand(factory app.ProjectFactory) cli.Command {
283281
cli.IntFlag{
284282
Name: "timeout,t",
285283
Usage: "Specify a shutdown timeout in seconds.",
286-
Value: 10,
287284
},
288285
},
289286
}

config/schema.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,7 @@ var servicesSchemaDataV2 = `{
348348
"security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
349349
"shm_size": {"type": ["number", "string"]},
350350
"stdin_open": {"type": "boolean"},
351+
"stop_grace_period": {"type": "string"},
351352
"stop_signal": {"type": "string"},
352353
"tmpfs": {"$ref": "#/definitions/string_or_list"},
353354
"tty": {"type": "boolean"},

config/types.go

Lines changed: 58 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -85,63 +85,64 @@ type Log struct {
8585

8686
// ServiceConfig holds version 2 of libcompose service configuration
8787
type ServiceConfig struct {
88-
Build yaml.Build `yaml:"build,omitempty"`
89-
CapAdd []string `yaml:"cap_add,omitempty"`
90-
CapDrop []string `yaml:"cap_drop,omitempty"`
91-
CPUSet string `yaml:"cpuset,omitempty"`
92-
CPUShares yaml.StringorInt `yaml:"cpu_shares,omitempty"`
93-
CPUQuota yaml.StringorInt `yaml:"cpu_quota,omitempty"`
94-
Command yaml.Command `yaml:"command,flow,omitempty"`
95-
CgroupParent string `yaml:"cgroup_parent,omitempty"`
96-
ContainerName string `yaml:"container_name,omitempty"`
97-
Devices []string `yaml:"devices,omitempty"`
98-
DependsOn []string `yaml:"depends_on,omitempty"`
99-
DNS yaml.Stringorslice `yaml:"dns,omitempty"`
100-
DNSOpts []string `yaml:"dns_opt,omitempty"`
101-
DNSSearch yaml.Stringorslice `yaml:"dns_search,omitempty"`
102-
DomainName string `yaml:"domainname,omitempty"`
103-
Entrypoint yaml.Command `yaml:"entrypoint,flow,omitempty"`
104-
EnvFile yaml.Stringorslice `yaml:"env_file,omitempty"`
105-
Environment yaml.MaporEqualSlice `yaml:"environment,omitempty"`
106-
Expose []string `yaml:"expose,omitempty"`
107-
Extends yaml.MaporEqualSlice `yaml:"extends,omitempty"`
108-
ExternalLinks []string `yaml:"external_links,omitempty"`
109-
ExtraHosts []string `yaml:"extra_hosts,omitempty"`
110-
GroupAdd []string `yaml:"group_add,omitempty"`
111-
Image string `yaml:"image,omitempty"`
112-
Isolation string `yaml:"isolation,omitempty"`
113-
Hostname string `yaml:"hostname,omitempty"`
114-
Ipc string `yaml:"ipc,omitempty"`
115-
Labels yaml.SliceorMap `yaml:"labels,omitempty"`
116-
Links yaml.MaporColonSlice `yaml:"links,omitempty"`
117-
Logging Log `yaml:"logging,omitempty"`
118-
MacAddress string `yaml:"mac_address,omitempty"`
119-
MemLimit yaml.MemStringorInt `yaml:"mem_limit,omitempty"`
120-
MemReservation yaml.MemStringorInt `yaml:"mem_reservation,omitempty"`
121-
MemSwapLimit yaml.MemStringorInt `yaml:"memswap_limit,omitempty"`
122-
MemSwappiness yaml.MemStringorInt `yaml:"mem_swappiness,omitempty"`
123-
NetworkMode string `yaml:"network_mode,omitempty"`
124-
Networks *yaml.Networks `yaml:"networks,omitempty"`
125-
OomKillDisable bool `yaml:"oom_kill_disable,omitempty"`
126-
OomScoreAdj yaml.StringorInt `yaml:"oom_score_adj,omitempty"`
127-
Pid string `yaml:"pid,omitempty"`
128-
Ports []string `yaml:"ports,omitempty"`
129-
Privileged bool `yaml:"privileged,omitempty"`
130-
SecurityOpt []string `yaml:"security_opt,omitempty"`
131-
ShmSize yaml.MemStringorInt `yaml:"shm_size,omitempty"`
132-
StopSignal string `yaml:"stop_signal,omitempty"`
133-
Tmpfs yaml.Stringorslice `yaml:"tmpfs,omitempty"`
134-
VolumeDriver string `yaml:"volume_driver,omitempty"`
135-
Volumes *yaml.Volumes `yaml:"volumes,omitempty"`
136-
VolumesFrom []string `yaml:"volumes_from,omitempty"`
137-
Uts string `yaml:"uts,omitempty"`
138-
Restart string `yaml:"restart,omitempty"`
139-
ReadOnly bool `yaml:"read_only,omitempty"`
140-
StdinOpen bool `yaml:"stdin_open,omitempty"`
141-
Tty bool `yaml:"tty,omitempty"`
142-
User string `yaml:"user,omitempty"`
143-
WorkingDir string `yaml:"working_dir,omitempty"`
144-
Ulimits yaml.Ulimits `yaml:"ulimits,omitempty"`
88+
Build yaml.Build `yaml:"build,omitempty"`
89+
CapAdd []string `yaml:"cap_add,omitempty"`
90+
CapDrop []string `yaml:"cap_drop,omitempty"`
91+
CPUSet string `yaml:"cpuset,omitempty"`
92+
CPUShares yaml.StringorInt `yaml:"cpu_shares,omitempty"`
93+
CPUQuota yaml.StringorInt `yaml:"cpu_quota,omitempty"`
94+
Command yaml.Command `yaml:"command,flow,omitempty"`
95+
CgroupParent string `yaml:"cgroup_parent,omitempty"`
96+
ContainerName string `yaml:"container_name,omitempty"`
97+
Devices []string `yaml:"devices,omitempty"`
98+
DependsOn []string `yaml:"depends_on,omitempty"`
99+
DNS yaml.Stringorslice `yaml:"dns,omitempty"`
100+
DNSOpts []string `yaml:"dns_opt,omitempty"`
101+
DNSSearch yaml.Stringorslice `yaml:"dns_search,omitempty"`
102+
DomainName string `yaml:"domainname,omitempty"`
103+
Entrypoint yaml.Command `yaml:"entrypoint,flow,omitempty"`
104+
EnvFile yaml.Stringorslice `yaml:"env_file,omitempty"`
105+
Environment yaml.MaporEqualSlice `yaml:"environment,omitempty"`
106+
Expose []string `yaml:"expose,omitempty"`
107+
Extends yaml.MaporEqualSlice `yaml:"extends,omitempty"`
108+
ExternalLinks []string `yaml:"external_links,omitempty"`
109+
ExtraHosts []string `yaml:"extra_hosts,omitempty"`
110+
GroupAdd []string `yaml:"group_add,omitempty"`
111+
Image string `yaml:"image,omitempty"`
112+
Isolation string `yaml:"isolation,omitempty"`
113+
Hostname string `yaml:"hostname,omitempty"`
114+
Ipc string `yaml:"ipc,omitempty"`
115+
Labels yaml.SliceorMap `yaml:"labels,omitempty"`
116+
Links yaml.MaporColonSlice `yaml:"links,omitempty"`
117+
Logging Log `yaml:"logging,omitempty"`
118+
MacAddress string `yaml:"mac_address,omitempty"`
119+
MemLimit yaml.MemStringorInt `yaml:"mem_limit,omitempty"`
120+
MemReservation yaml.MemStringorInt `yaml:"mem_reservation,omitempty"`
121+
MemSwapLimit yaml.MemStringorInt `yaml:"memswap_limit,omitempty"`
122+
MemSwappiness yaml.MemStringorInt `yaml:"mem_swappiness,omitempty"`
123+
NetworkMode string `yaml:"network_mode,omitempty"`
124+
Networks *yaml.Networks `yaml:"networks,omitempty"`
125+
OomKillDisable bool `yaml:"oom_kill_disable,omitempty"`
126+
OomScoreAdj yaml.StringorInt `yaml:"oom_score_adj,omitempty"`
127+
Pid string `yaml:"pid,omitempty"`
128+
Ports []string `yaml:"ports,omitempty"`
129+
Privileged bool `yaml:"privileged,omitempty"`
130+
SecurityOpt []string `yaml:"security_opt,omitempty"`
131+
ShmSize yaml.MemStringorInt `yaml:"shm_size,omitempty"`
132+
StopGracePeriod string `yaml:"stop_grace_period,omitempty"`
133+
StopSignal string `yaml:"stop_signal,omitempty"`
134+
Tmpfs yaml.Stringorslice `yaml:"tmpfs,omitempty"`
135+
VolumeDriver string `yaml:"volume_driver,omitempty"`
136+
Volumes *yaml.Volumes `yaml:"volumes,omitempty"`
137+
VolumesFrom []string `yaml:"volumes_from,omitempty"`
138+
Uts string `yaml:"uts,omitempty"`
139+
Restart string `yaml:"restart,omitempty"`
140+
ReadOnly bool `yaml:"read_only,omitempty"`
141+
StdinOpen bool `yaml:"stdin_open,omitempty"`
142+
Tty bool `yaml:"tty,omitempty"`
143+
User string `yaml:"user,omitempty"`
144+
WorkingDir string `yaml:"working_dir,omitempty"`
145+
Ulimits yaml.Ulimits `yaml:"ulimits,omitempty"`
145146
}
146147

147148
// VolumeConfig holds v2 volume configuration

docker/service/convert.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ func Convert(c *config.ServiceConfig, ctx project.Context, clientFactory compose
166166
Volumes: toMap(Filter(vols, isVolume)),
167167
MacAddress: c.MacAddress,
168168
StopSignal: c.StopSignal,
169+
StopTimeout: utils.DurationStrToSecondsInt(c.StopGracePeriod),
169170
}
170171

171172
ulimits := []*units.Ulimit{}

docker/service/convert_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,17 @@ func TestOomScoreAdj(t *testing.T) {
171171
assert.Equal(t, 500, hostCfg.OomScoreAdj)
172172
}
173173

174+
func TestStopGracePeriod(t *testing.T) {
175+
ctx := &ctx.Context{}
176+
sc := &config.ServiceConfig{
177+
StopGracePeriod: "5s",
178+
}
179+
cfg, _, err := Convert(sc, ctx.Context, nil)
180+
assert.Nil(t, err)
181+
182+
assert.Equal(t, 5, *cfg.StopTimeout)
183+
}
184+
174185
func TestStopSignal(t *testing.T) {
175186
ctx := &ctx.Context{}
176187
sc := &config.ServiceConfig{

docker/service/service.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,13 +525,15 @@ func (s *Service) eachContainer(ctx context.Context, containers []*container.Con
525525

526526
// Stop implements Service.Stop. It stops any containers related to the service.
527527
func (s *Service) Stop(ctx context.Context, timeout int) error {
528+
timeout = s.stopTimeout(timeout)
528529
return s.collectContainersAndDo(ctx, func(c *container.Container) error {
529530
return c.Stop(ctx, timeout)
530531
})
531532
}
532533

533534
// Restart implements Service.Restart. It restarts any containers related to the service.
534535
func (s *Service) Restart(ctx context.Context, timeout int) error {
536+
timeout = s.stopTimeout(timeout)
535537
return s.collectContainersAndDo(ctx, func(c *container.Container) error {
536538
return c.Restart(ctx, timeout)
537539
})
@@ -587,6 +589,7 @@ func (s *Service) Scale(ctx context.Context, scale int, timeout int) error {
587589
for _, c := range containers {
588590
foundCount++
589591
if foundCount > scale {
592+
timeout = s.stopTimeout(timeout)
590593
if err := c.Stop(ctx, timeout); err != nil {
591594
return err
592595
}
@@ -728,3 +731,19 @@ func (s *Service) specificiesHostPort() bool {
728731

729732
return false
730733
}
734+
735+
//take in timeout flag from cli as parameter
736+
//return timeout if it is set,
737+
//else return stop_grace_period if it is set,
738+
//else return default 10s
739+
func (s *Service) stopTimeout(timeout int) int {
740+
DEFAULTTIMEOUT := 10
741+
if timeout != 0 {
742+
return timeout
743+
}
744+
configTimeout := utils.DurationStrToSecondsInt(s.Config().StopGracePeriod)
745+
if configTimeout != nil {
746+
return *configTimeout
747+
}
748+
return DEFAULTTIMEOUT
749+
}

utils/util.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package utils
33
import (
44
"encoding/json"
55
"sync"
6+
"time"
67

78
"github.com/Sirupsen/logrus"
89

@@ -160,3 +161,18 @@ func ConvertKeysToStrings(item interface{}) interface{} {
160161
return item
161162
}
162163
}
164+
165+
// DurationStrToSecondsInt converts duration string to *int in seconds
166+
func DurationStrToSecondsInt(s string) *int {
167+
if s == "" {
168+
return nil
169+
}
170+
duration, err := time.ParseDuration(s)
171+
if err != nil {
172+
logrus.Errorf("Failed to parse duration:%v", s)
173+
return nil
174+
}
175+
r := (int)(duration.Seconds())
176+
return &r
177+
178+
}

0 commit comments

Comments
 (0)