Browse code

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 <vincent@sbr.pm>

Vincent Demeester authored on 2018/06/01 19:47:38
Showing 8 changed files
... ...
@@ -2721,6 +2721,10 @@ definitions:
2721 2721
               - "default"
2722 2722
               - "process"
2723 2723
               - "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
2724 2728
       NetworkAttachmentSpec:
2725 2729
         description: |
2726 2730
           Read-only spec type for non-swarm containers attached to swarm overlay
... ...
@@ -55,6 +55,7 @@ type ContainerSpec struct {
55 55
 	User            string                  `json:",omitempty"`
56 56
 	Groups          []string                `json:",omitempty"`
57 57
 	Privileges      *Privileges             `json:",omitempty"`
58
+	Init            *bool                   `json:",omitempty"`
58 59
 	StopSignal      string                  `json:",omitempty"`
59 60
 	TTY             bool                    `json:",omitempty"`
60 61
 	OpenStdin       bool                    `json:",omitempty"`
... ...
@@ -35,6 +35,7 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) *types.ContainerSpec {
35 35
 		Secrets:    secretReferencesFromGRPC(c.Secrets),
36 36
 		Configs:    configReferencesFromGRPC(c.Configs),
37 37
 		Isolation:  IsolationFromGRPC(c.Isolation),
38
+		Init:       initFromGRPC(c.Init),
38 39
 	}
39 40
 
40 41
 	if c.DNSConfig != nil {
... ...
@@ -119,6 +120,21 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) *types.ContainerSpec {
119 119
 	return containerSpec
120 120
 }
121 121
 
122
+func initFromGRPC(v *gogotypes.BoolValue) *bool {
123
+	if v == nil {
124
+		return nil
125
+	}
126
+	value := v.GetValue()
127
+	return &value
128
+}
129
+
130
+func initToGRPC(v *bool) *gogotypes.BoolValue {
131
+	if v == nil {
132
+		return nil
133
+	}
134
+	return &gogotypes.BoolValue{Value: *v}
135
+}
136
+
122 137
 func secretReferencesToGRPC(sr []*types.SecretReference) []*swarmapi.SecretReference {
123 138
 	refs := make([]*swarmapi.SecretReference, 0, len(sr))
124 139
 	for _, s := range sr {
... ...
@@ -234,6 +250,7 @@ func containerToGRPC(c *types.ContainerSpec) (*swarmapi.ContainerSpec, error) {
234 234
 		Secrets:    secretReferencesToGRPC(c.Secrets),
235 235
 		Configs:    configReferencesToGRPC(c.Configs),
236 236
 		Isolation:  isolationToGRPC(c.Isolation),
237
+		Init:       initToGRPC(c.Init),
237 238
 	}
238 239
 
239 240
 	if c.DNSConfig != nil {
... ...
@@ -172,6 +172,14 @@ func (c *containerConfig) isolation() enginecontainer.Isolation {
172 172
 	return convert.IsolationFromGRPC(c.spec().Isolation)
173 173
 }
174 174
 
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
+
175 183
 func (c *containerConfig) exposedPorts() map[nat.Port]struct{} {
176 184
 	exposedPorts := make(map[nat.Port]struct{})
177 185
 	if c.task.Endpoint == nil {
... ...
@@ -355,6 +363,7 @@ func (c *containerConfig) hostConfig() *enginecontainer.HostConfig {
355 355
 		Mounts:         c.mounts(),
356 356
 		ReadonlyRootfs: c.spec().ReadOnly,
357 357
 		Isolation:      c.isolation(),
358
+		Init:           c.init(),
358 359
 	}
359 360
 
360 361
 	if c.spec().DNSConfig != nil {
... ...
@@ -86,6 +86,14 @@ func defaultServiceSpec() swarmtypes.ServiceSpec {
86 86
 	return spec
87 87
 }
88 88
 
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
+
89 97
 // ServiceWithImage sets the image to use for the service
90 98
 func ServiceWithImage(image string) func(*swarmtypes.ServiceSpec) {
91 99
 	return func(spec *swarmtypes.ServiceSpec) {
... ...
@@ -2,6 +2,7 @@ package service // import "github.com/docker/docker/integration/service"
2 2
 
3 3
 import (
4 4
 	"context"
5
+	"fmt"
5 6
 	"io/ioutil"
6 7
 	"testing"
7 8
 	"time"
... ...
@@ -11,11 +12,64 @@ import (
11 11
 	swarmtypes "github.com/docker/docker/api/types/swarm"
12 12
 	"github.com/docker/docker/client"
13 13
 	"github.com/docker/docker/integration/internal/swarm"
14
+	"github.com/docker/docker/internal/test/daemon"
14 15
 	"github.com/gotestyourself/gotestyourself/assert"
15 16
 	is "github.com/gotestyourself/gotestyourself/assert/cmp"
16 17
 	"github.com/gotestyourself/gotestyourself/poll"
17 18
 )
18 19
 
20
+func TestServiceCreateInit(t *testing.T) {
21
+	defer setupTest(t)()
22
+	t.Run("daemonInitDisabled", testServiceCreateInit(false))
23
+	t.Run("daemonInitEnabled", testServiceCreateInit(true))
24
+}
25
+
26
+func testServiceCreateInit(daemonEnabled bool) func(t *testing.T) {
27
+	return func(t *testing.T) {
28
+		var ops = []func(*daemon.Daemon){}
29
+
30
+		if daemonEnabled {
31
+			ops = append(ops, daemon.WithInit)
32
+		}
33
+		d := swarm.NewSwarm(t, testEnv, ops...)
34
+		defer d.Stop(t)
35
+		client := d.NewClientT(t)
36
+		defer client.Close()
37
+
38
+		booleanTrue := true
39
+		booleanFalse := false
40
+
41
+		serviceID := swarm.CreateService(t, d)
42
+		poll.WaitOn(t, serviceRunningTasksCount(client, serviceID, 1), swarm.ServicePoll)
43
+		i := inspectServiceContainer(t, client, serviceID)
44
+		// HostConfig.Init == nil means that it delegates to daemon configuration
45
+		assert.Check(t, i.HostConfig.Init == nil)
46
+
47
+		serviceID = swarm.CreateService(t, d, swarm.ServiceWithInit(&booleanTrue))
48
+		poll.WaitOn(t, serviceRunningTasksCount(client, serviceID, 1), swarm.ServicePoll)
49
+		i = inspectServiceContainer(t, client, serviceID)
50
+		assert.Check(t, is.Equal(true, *i.HostConfig.Init))
51
+
52
+		serviceID = swarm.CreateService(t, d, swarm.ServiceWithInit(&booleanFalse))
53
+		poll.WaitOn(t, serviceRunningTasksCount(client, serviceID, 1), swarm.ServicePoll)
54
+		i = inspectServiceContainer(t, client, serviceID)
55
+		assert.Check(t, is.Equal(false, *i.HostConfig.Init))
56
+	}
57
+}
58
+
59
+func inspectServiceContainer(t *testing.T, client client.APIClient, serviceID string) types.ContainerJSON {
60
+	t.Helper()
61
+	filter := filters.NewArgs()
62
+	filter.Add("label", fmt.Sprintf("com.docker.swarm.service.id=%s", serviceID))
63
+	containers, err := client.ContainerList(context.Background(), types.ContainerListOptions{Filters: filter})
64
+	assert.NilError(t, err)
65
+	assert.Check(t, is.Len(containers, 1))
66
+
67
+	i, err := client.ContainerInspect(context.Background(), containers[0].ID)
68
+	assert.NilError(t, err)
69
+	return i
70
+}
71
+
19 72
 func TestCreateServiceMultipleTimes(t *testing.T) {
20 73
 	defer setupTest(t)()
21 74
 	d := swarm.NewSwarm(t, testEnv)
... ...
@@ -66,6 +66,7 @@ type Daemon struct {
66 66
 	userlandProxy bool
67 67
 	execRoot      string
68 68
 	experimental  bool
69
+	init          bool
69 70
 	dockerdBinary string
70 71
 	log           logT
71 72
 
... ...
@@ -229,7 +230,10 @@ func (d *Daemon) StartWithLogFile(out *os.File, providedArgs ...string) error {
229 229
 		fmt.Sprintf("--userland-proxy=%t", d.userlandProxy),
230 230
 	)
231 231
 	if d.experimental {
232
-		args = append(args, "--experimental", "--init")
232
+		args = append(args, "--experimental")
233
+	}
234
+	if d.init {
235
+		args = append(args, "--init")
233 236
 	}
234 237
 	if !(d.UseDefaultHost || d.UseDefaultTLSHost) {
235 238
 		args = append(args, []string{"--host", d.Sock()}...)
... ...
@@ -5,6 +5,12 @@ import "github.com/docker/docker/internal/test/environment"
5 5
 // WithExperimental sets the daemon in experimental mode
6 6
 func WithExperimental(d *Daemon) {
7 7
 	d.experimental = true
8
+	d.init = true
9
+}
10
+
11
+// WithInit sets the daemon init
12
+func WithInit(d *Daemon) {
13
+	d.init = true
8 14
 }
9 15
 
10 16
 // WithDockerdBinary sets the dockerd binary to the specified one