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>
This commit is contained in:
parent
1fe0e49d20
commit
e401b88e59
8 changed files with 104 additions and 1 deletions
|
@ -2721,6 +2721,10 @@ definitions:
|
||||||
- "default"
|
- "default"
|
||||||
- "process"
|
- "process"
|
||||||
- "hyperv"
|
- "hyperv"
|
||||||
|
Init:
|
||||||
|
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."
|
||||||
|
type: "boolean"
|
||||||
|
x-nullable: true
|
||||||
NetworkAttachmentSpec:
|
NetworkAttachmentSpec:
|
||||||
description: |
|
description: |
|
||||||
Read-only spec type for non-swarm containers attached to swarm overlay
|
Read-only spec type for non-swarm containers attached to swarm overlay
|
||||||
|
|
|
@ -55,6 +55,7 @@ type ContainerSpec struct {
|
||||||
User string `json:",omitempty"`
|
User string `json:",omitempty"`
|
||||||
Groups []string `json:",omitempty"`
|
Groups []string `json:",omitempty"`
|
||||||
Privileges *Privileges `json:",omitempty"`
|
Privileges *Privileges `json:",omitempty"`
|
||||||
|
Init *bool `json:",omitempty"`
|
||||||
StopSignal string `json:",omitempty"`
|
StopSignal string `json:",omitempty"`
|
||||||
TTY bool `json:",omitempty"`
|
TTY bool `json:",omitempty"`
|
||||||
OpenStdin bool `json:",omitempty"`
|
OpenStdin bool `json:",omitempty"`
|
||||||
|
|
|
@ -35,6 +35,7 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) *types.ContainerSpec {
|
||||||
Secrets: secretReferencesFromGRPC(c.Secrets),
|
Secrets: secretReferencesFromGRPC(c.Secrets),
|
||||||
Configs: configReferencesFromGRPC(c.Configs),
|
Configs: configReferencesFromGRPC(c.Configs),
|
||||||
Isolation: IsolationFromGRPC(c.Isolation),
|
Isolation: IsolationFromGRPC(c.Isolation),
|
||||||
|
Init: initFromGRPC(c.Init),
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.DNSConfig != nil {
|
if c.DNSConfig != nil {
|
||||||
|
@ -119,6 +120,21 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) *types.ContainerSpec {
|
||||||
return containerSpec
|
return containerSpec
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func initFromGRPC(v *gogotypes.BoolValue) *bool {
|
||||||
|
if v == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
value := v.GetValue()
|
||||||
|
return &value
|
||||||
|
}
|
||||||
|
|
||||||
|
func initToGRPC(v *bool) *gogotypes.BoolValue {
|
||||||
|
if v == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &gogotypes.BoolValue{Value: *v}
|
||||||
|
}
|
||||||
|
|
||||||
func secretReferencesToGRPC(sr []*types.SecretReference) []*swarmapi.SecretReference {
|
func secretReferencesToGRPC(sr []*types.SecretReference) []*swarmapi.SecretReference {
|
||||||
refs := make([]*swarmapi.SecretReference, 0, len(sr))
|
refs := make([]*swarmapi.SecretReference, 0, len(sr))
|
||||||
for _, s := range sr {
|
for _, s := range sr {
|
||||||
|
@ -234,6 +250,7 @@ func containerToGRPC(c *types.ContainerSpec) (*swarmapi.ContainerSpec, error) {
|
||||||
Secrets: secretReferencesToGRPC(c.Secrets),
|
Secrets: secretReferencesToGRPC(c.Secrets),
|
||||||
Configs: configReferencesToGRPC(c.Configs),
|
Configs: configReferencesToGRPC(c.Configs),
|
||||||
Isolation: isolationToGRPC(c.Isolation),
|
Isolation: isolationToGRPC(c.Isolation),
|
||||||
|
Init: initToGRPC(c.Init),
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.DNSConfig != nil {
|
if c.DNSConfig != nil {
|
||||||
|
|
|
@ -172,6 +172,14 @@ func (c *containerConfig) isolation() enginecontainer.Isolation {
|
||||||
return convert.IsolationFromGRPC(c.spec().Isolation)
|
return convert.IsolationFromGRPC(c.spec().Isolation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *containerConfig) init() *bool {
|
||||||
|
if c.spec().Init == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
init := c.spec().Init.GetValue()
|
||||||
|
return &init
|
||||||
|
}
|
||||||
|
|
||||||
func (c *containerConfig) exposedPorts() map[nat.Port]struct{} {
|
func (c *containerConfig) exposedPorts() map[nat.Port]struct{} {
|
||||||
exposedPorts := make(map[nat.Port]struct{})
|
exposedPorts := make(map[nat.Port]struct{})
|
||||||
if c.task.Endpoint == nil {
|
if c.task.Endpoint == nil {
|
||||||
|
@ -355,6 +363,7 @@ func (c *containerConfig) hostConfig() *enginecontainer.HostConfig {
|
||||||
Mounts: c.mounts(),
|
Mounts: c.mounts(),
|
||||||
ReadonlyRootfs: c.spec().ReadOnly,
|
ReadonlyRootfs: c.spec().ReadOnly,
|
||||||
Isolation: c.isolation(),
|
Isolation: c.isolation(),
|
||||||
|
Init: c.init(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.spec().DNSConfig != nil {
|
if c.spec().DNSConfig != nil {
|
||||||
|
|
|
@ -86,6 +86,14 @@ func defaultServiceSpec() swarmtypes.ServiceSpec {
|
||||||
return spec
|
return spec
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ServiceWithInit sets whether the service should use init or not
|
||||||
|
func ServiceWithInit(b *bool) func(*swarmtypes.ServiceSpec) {
|
||||||
|
return func(spec *swarmtypes.ServiceSpec) {
|
||||||
|
ensureContainerSpec(spec)
|
||||||
|
spec.TaskTemplate.ContainerSpec.Init = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ServiceWithImage sets the image to use for the service
|
// ServiceWithImage sets the image to use for the service
|
||||||
func ServiceWithImage(image string) func(*swarmtypes.ServiceSpec) {
|
func ServiceWithImage(image string) func(*swarmtypes.ServiceSpec) {
|
||||||
return func(spec *swarmtypes.ServiceSpec) {
|
return func(spec *swarmtypes.ServiceSpec) {
|
||||||
|
|
|
@ -2,6 +2,7 @@ package service // import "github.com/docker/docker/integration/service"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -11,11 +12,64 @@ import (
|
||||||
swarmtypes "github.com/docker/docker/api/types/swarm"
|
swarmtypes "github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
"github.com/docker/docker/integration/internal/swarm"
|
"github.com/docker/docker/integration/internal/swarm"
|
||||||
|
"github.com/docker/docker/internal/test/daemon"
|
||||||
"github.com/gotestyourself/gotestyourself/assert"
|
"github.com/gotestyourself/gotestyourself/assert"
|
||||||
is "github.com/gotestyourself/gotestyourself/assert/cmp"
|
is "github.com/gotestyourself/gotestyourself/assert/cmp"
|
||||||
"github.com/gotestyourself/gotestyourself/poll"
|
"github.com/gotestyourself/gotestyourself/poll"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestServiceCreateInit(t *testing.T) {
|
||||||
|
defer setupTest(t)()
|
||||||
|
t.Run("daemonInitDisabled", testServiceCreateInit(false))
|
||||||
|
t.Run("daemonInitEnabled", testServiceCreateInit(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
func testServiceCreateInit(daemonEnabled bool) func(t *testing.T) {
|
||||||
|
return func(t *testing.T) {
|
||||||
|
var ops = []func(*daemon.Daemon){}
|
||||||
|
|
||||||
|
if daemonEnabled {
|
||||||
|
ops = append(ops, daemon.WithInit)
|
||||||
|
}
|
||||||
|
d := swarm.NewSwarm(t, testEnv, ops...)
|
||||||
|
defer d.Stop(t)
|
||||||
|
client := d.NewClientT(t)
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
booleanTrue := true
|
||||||
|
booleanFalse := false
|
||||||
|
|
||||||
|
serviceID := swarm.CreateService(t, d)
|
||||||
|
poll.WaitOn(t, serviceRunningTasksCount(client, serviceID, 1), swarm.ServicePoll)
|
||||||
|
i := inspectServiceContainer(t, client, serviceID)
|
||||||
|
// HostConfig.Init == nil means that it delegates to daemon configuration
|
||||||
|
assert.Check(t, i.HostConfig.Init == nil)
|
||||||
|
|
||||||
|
serviceID = swarm.CreateService(t, d, swarm.ServiceWithInit(&booleanTrue))
|
||||||
|
poll.WaitOn(t, serviceRunningTasksCount(client, serviceID, 1), swarm.ServicePoll)
|
||||||
|
i = inspectServiceContainer(t, client, serviceID)
|
||||||
|
assert.Check(t, is.Equal(true, *i.HostConfig.Init))
|
||||||
|
|
||||||
|
serviceID = swarm.CreateService(t, d, swarm.ServiceWithInit(&booleanFalse))
|
||||||
|
poll.WaitOn(t, serviceRunningTasksCount(client, serviceID, 1), swarm.ServicePoll)
|
||||||
|
i = inspectServiceContainer(t, client, serviceID)
|
||||||
|
assert.Check(t, is.Equal(false, *i.HostConfig.Init))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func inspectServiceContainer(t *testing.T, client client.APIClient, serviceID string) types.ContainerJSON {
|
||||||
|
t.Helper()
|
||||||
|
filter := filters.NewArgs()
|
||||||
|
filter.Add("label", fmt.Sprintf("com.docker.swarm.service.id=%s", serviceID))
|
||||||
|
containers, err := client.ContainerList(context.Background(), types.ContainerListOptions{Filters: filter})
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.Check(t, is.Len(containers, 1))
|
||||||
|
|
||||||
|
i, err := client.ContainerInspect(context.Background(), containers[0].ID)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
func TestCreateServiceMultipleTimes(t *testing.T) {
|
func TestCreateServiceMultipleTimes(t *testing.T) {
|
||||||
defer setupTest(t)()
|
defer setupTest(t)()
|
||||||
d := swarm.NewSwarm(t, testEnv)
|
d := swarm.NewSwarm(t, testEnv)
|
||||||
|
|
|
@ -66,6 +66,7 @@ type Daemon struct {
|
||||||
userlandProxy bool
|
userlandProxy bool
|
||||||
execRoot string
|
execRoot string
|
||||||
experimental bool
|
experimental bool
|
||||||
|
init bool
|
||||||
dockerdBinary string
|
dockerdBinary string
|
||||||
log logT
|
log logT
|
||||||
|
|
||||||
|
@ -229,7 +230,10 @@ func (d *Daemon) StartWithLogFile(out *os.File, providedArgs ...string) error {
|
||||||
fmt.Sprintf("--userland-proxy=%t", d.userlandProxy),
|
fmt.Sprintf("--userland-proxy=%t", d.userlandProxy),
|
||||||
)
|
)
|
||||||
if d.experimental {
|
if d.experimental {
|
||||||
args = append(args, "--experimental", "--init")
|
args = append(args, "--experimental")
|
||||||
|
}
|
||||||
|
if d.init {
|
||||||
|
args = append(args, "--init")
|
||||||
}
|
}
|
||||||
if !(d.UseDefaultHost || d.UseDefaultTLSHost) {
|
if !(d.UseDefaultHost || d.UseDefaultTLSHost) {
|
||||||
args = append(args, []string{"--host", d.Sock()}...)
|
args = append(args, []string{"--host", d.Sock()}...)
|
||||||
|
|
|
@ -5,6 +5,12 @@ import "github.com/docker/docker/internal/test/environment"
|
||||||
// WithExperimental sets the daemon in experimental mode
|
// WithExperimental sets the daemon in experimental mode
|
||||||
func WithExperimental(d *Daemon) {
|
func WithExperimental(d *Daemon) {
|
||||||
d.experimental = true
|
d.experimental = true
|
||||||
|
d.init = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithInit sets the daemon init
|
||||||
|
func WithInit(d *Daemon) {
|
||||||
|
d.init = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithDockerdBinary sets the dockerd binary to the specified one
|
// WithDockerdBinary sets the dockerd binary to the specified one
|
||||||
|
|
Loading…
Reference in a new issue