Skip to content

Commit e401b88

Browse files
committed
Add support for init on services
It's already supported by `swarmkit`, and act the same as `HostConfig.Init` on container creation. Signed-off-by: Vincent Demeester <[email protected]>
1 parent 1fe0e49 commit e401b88

File tree

8 files changed

+104
-1
lines changed

8 files changed

+104
-1
lines changed

api/swagger.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -2721,6 +2721,10 @@ definitions:
27212721
- "default"
27222722
- "process"
27232723
- "hyperv"
2724+
Init:
2725+
description: "Run an init inside the container that forwards signals and reaps processes. This field is omitted if empty, and the default (as configured on the daemon) is used."
2726+
type: "boolean"
2727+
x-nullable: true
27242728
NetworkAttachmentSpec:
27252729
description: |
27262730
Read-only spec type for non-swarm containers attached to swarm overlay

api/types/swarm/container.go

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ type ContainerSpec struct {
5555
User string `json:",omitempty"`
5656
Groups []string `json:",omitempty"`
5757
Privileges *Privileges `json:",omitempty"`
58+
Init *bool `json:",omitempty"`
5859
StopSignal string `json:",omitempty"`
5960
TTY bool `json:",omitempty"`
6061
OpenStdin bool `json:",omitempty"`

daemon/cluster/convert/container.go

+17
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) *types.ContainerSpec {
3535
Secrets: secretReferencesFromGRPC(c.Secrets),
3636
Configs: configReferencesFromGRPC(c.Configs),
3737
Isolation: IsolationFromGRPC(c.Isolation),
38+
Init: initFromGRPC(c.Init),
3839
}
3940

4041
if c.DNSConfig != nil {
@@ -119,6 +120,21 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) *types.ContainerSpec {
119120
return containerSpec
120121
}
121122

123+
func initFromGRPC(v *gogotypes.BoolValue) *bool {
124+
if v == nil {
125+
return nil
126+
}
127+
value := v.GetValue()
128+
return &value
129+
}
130+
131+
func initToGRPC(v *bool) *gogotypes.BoolValue {
132+
if v == nil {
133+
return nil
134+
}
135+
return &gogotypes.BoolValue{Value: *v}
136+
}
137+
122138
func secretReferencesToGRPC(sr []*types.SecretReference) []*swarmapi.SecretReference {
123139
refs := make([]*swarmapi.SecretReference, 0, len(sr))
124140
for _, s := range sr {
@@ -234,6 +250,7 @@ func containerToGRPC(c *types.ContainerSpec) (*swarmapi.ContainerSpec, error) {
234250
Secrets: secretReferencesToGRPC(c.Secrets),
235251
Configs: configReferencesToGRPC(c.Configs),
236252
Isolation: isolationToGRPC(c.Isolation),
253+
Init: initToGRPC(c.Init),
237254
}
238255

239256
if c.DNSConfig != nil {

daemon/cluster/executor/container/container.go

+9
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,14 @@ func (c *containerConfig) isolation() enginecontainer.Isolation {
172172
return convert.IsolationFromGRPC(c.spec().Isolation)
173173
}
174174

175+
func (c *containerConfig) init() *bool {
176+
if c.spec().Init == nil {
177+
return nil
178+
}
179+
init := c.spec().Init.GetValue()
180+
return &init
181+
}
182+
175183
func (c *containerConfig) exposedPorts() map[nat.Port]struct{} {
176184
exposedPorts := make(map[nat.Port]struct{})
177185
if c.task.Endpoint == nil {
@@ -355,6 +363,7 @@ func (c *containerConfig) hostConfig() *enginecontainer.HostConfig {
355363
Mounts: c.mounts(),
356364
ReadonlyRootfs: c.spec().ReadOnly,
357365
Isolation: c.isolation(),
366+
Init: c.init(),
358367
}
359368

360369
if c.spec().DNSConfig != nil {

integration/internal/swarm/service.go

+8
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,14 @@ func defaultServiceSpec() swarmtypes.ServiceSpec {
8686
return spec
8787
}
8888

89+
// ServiceWithInit sets whether the service should use init or not
90+
func ServiceWithInit(b *bool) func(*swarmtypes.ServiceSpec) {
91+
return func(spec *swarmtypes.ServiceSpec) {
92+
ensureContainerSpec(spec)
93+
spec.TaskTemplate.ContainerSpec.Init = b
94+
}
95+
}
96+
8997
// ServiceWithImage sets the image to use for the service
9098
func ServiceWithImage(image string) func(*swarmtypes.ServiceSpec) {
9199
return func(spec *swarmtypes.ServiceSpec) {

integration/service/create_test.go

+54
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package service // import "github.com/docker/docker/integration/service"
22

33
import (
44
"context"
5+
"fmt"
56
"io/ioutil"
67
"testing"
78
"time"
@@ -11,11 +12,64 @@ import (
1112
swarmtypes "github.com/docker/docker/api/types/swarm"
1213
"github.com/docker/docker/client"
1314
"github.com/docker/docker/integration/internal/swarm"
15+
"github.com/docker/docker/internal/test/daemon"
1416
"github.com/gotestyourself/gotestyourself/assert"
1517
is "github.com/gotestyourself/gotestyourself/assert/cmp"
1618
"github.com/gotestyourself/gotestyourself/poll"
1719
)
1820

21+
func TestServiceCreateInit(t *testing.T) {
22+
defer setupTest(t)()
23+
t.Run("daemonInitDisabled", testServiceCreateInit(false))
24+
t.Run("daemonInitEnabled", testServiceCreateInit(true))
25+
}
26+
27+
func testServiceCreateInit(daemonEnabled bool) func(t *testing.T) {
28+
return func(t *testing.T) {
29+
var ops = []func(*daemon.Daemon){}
30+
31+
if daemonEnabled {
32+
ops = append(ops, daemon.WithInit)
33+
}
34+
d := swarm.NewSwarm(t, testEnv, ops...)
35+
defer d.Stop(t)
36+
client := d.NewClientT(t)
37+
defer client.Close()
38+
39+
booleanTrue := true
40+
booleanFalse := false
41+
42+
serviceID := swarm.CreateService(t, d)
43+
poll.WaitOn(t, serviceRunningTasksCount(client, serviceID, 1), swarm.ServicePoll)
44+
i := inspectServiceContainer(t, client, serviceID)
45+
// HostConfig.Init == nil means that it delegates to daemon configuration
46+
assert.Check(t, i.HostConfig.Init == nil)
47+
48+
serviceID = swarm.CreateService(t, d, swarm.ServiceWithInit(&booleanTrue))
49+
poll.WaitOn(t, serviceRunningTasksCount(client, serviceID, 1), swarm.ServicePoll)
50+
i = inspectServiceContainer(t, client, serviceID)
51+
assert.Check(t, is.Equal(true, *i.HostConfig.Init))
52+
53+
serviceID = swarm.CreateService(t, d, swarm.ServiceWithInit(&booleanFalse))
54+
poll.WaitOn(t, serviceRunningTasksCount(client, serviceID, 1), swarm.ServicePoll)
55+
i = inspectServiceContainer(t, client, serviceID)
56+
assert.Check(t, is.Equal(false, *i.HostConfig.Init))
57+
}
58+
}
59+
60+
func inspectServiceContainer(t *testing.T, client client.APIClient, serviceID string) types.ContainerJSON {
61+
t.Helper()
62+
filter := filters.NewArgs()
63+
filter.Add("label", fmt.Sprintf("com.docker.swarm.service.id=%s", serviceID))
64+
containers, err := client.ContainerList(context.Background(), types.ContainerListOptions{Filters: filter})
65+
assert.NilError(t, err)
66+
assert.Check(t, is.Len(containers, 1))
67+
68+
i, err := client.ContainerInspect(context.Background(), containers[0].ID)
69+
assert.NilError(t, err)
70+
return i
71+
}
72+
1973
func TestCreateServiceMultipleTimes(t *testing.T) {
2074
defer setupTest(t)()
2175
d := swarm.NewSwarm(t, testEnv)

internal/test/daemon/daemon.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ type Daemon struct {
6666
userlandProxy bool
6767
execRoot string
6868
experimental bool
69+
init bool
6970
dockerdBinary string
7071
log logT
7172

@@ -229,7 +230,10 @@ func (d *Daemon) StartWithLogFile(out *os.File, providedArgs ...string) error {
229230
fmt.Sprintf("--userland-proxy=%t", d.userlandProxy),
230231
)
231232
if d.experimental {
232-
args = append(args, "--experimental", "--init")
233+
args = append(args, "--experimental")
234+
}
235+
if d.init {
236+
args = append(args, "--init")
233237
}
234238
if !(d.UseDefaultHost || d.UseDefaultTLSHost) {
235239
args = append(args, []string{"--host", d.Sock()}...)

internal/test/daemon/ops.go

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ import "github.com/docker/docker/internal/test/environment"
55
// WithExperimental sets the daemon in experimental mode
66
func WithExperimental(d *Daemon) {
77
d.experimental = true
8+
d.init = true
9+
}
10+
11+
// WithInit sets the daemon init
12+
func WithInit(d *Daemon) {
13+
d.init = true
814
}
915

1016
// WithDockerdBinary sets the dockerd binary to the specified one

0 commit comments

Comments
 (0)