From 2386b2eca3d8189e80c6d6aed006e8eb9a387c5b Mon Sep 17 00:00:00 2001 From: Simon Ferquel Date: Thu, 16 Nov 2017 10:39:18 +0100 Subject: [PATCH] Add isolation mode on service update/create and compose files Signed-off-by: Simon Ferquel --- cli/command/service/opts.go | 6 + cli/command/service/update.go | 11 + cli/command/service/update_test.go | 29 + cli/compose/convert/service.go | 1 + cli/compose/convert/service_test.go | 9 + cli/compose/loader/loader_test.go | 33 ++ cli/compose/schema/bindata.go | 23 + .../schema/data/config_schema_v3.5.json | 544 ++++++++++++++++++ cli/compose/schema/schema_test.go | 13 + cli/compose/types/types.go | 1 + contrib/completion/bash/docker | 5 + contrib/completion/zsh/_docker | 1 + docs/reference/commandline/service_create.md | 17 + docs/reference/commandline/service_update.md | 7 + 14 files changed, 700 insertions(+) create mode 100644 cli/compose/schema/data/config_schema_v3.5.json diff --git a/cli/command/service/opts.go b/cli/command/service/opts.go index 24f9da3776e2..071998607ea3 100644 --- a/cli/command/service/opts.go +++ b/cli/command/service/opts.go @@ -505,6 +505,8 @@ type serviceOptions struct { healthcheck healthCheckOptions secrets opts.SecretOpt configs opts.ConfigOpt + + isolation string } func newServiceOptions() *serviceOptions { @@ -614,6 +616,7 @@ func (options *serviceOptions) ToService(ctx context.Context, apiClient client.N Hosts: convertExtraHostsToSwarmHosts(options.hosts.GetAll()), StopGracePeriod: options.ToStopGracePeriod(flags), Healthcheck: healthConfig, + Isolation: container.Isolation(options.isolation), }, Networks: networks, Resources: options.resources.ToResourceRequirements(), @@ -784,6 +787,8 @@ func addServiceFlags(flags *pflag.FlagSet, opts *serviceOptions, defaultFlagValu flags.StringVar(&opts.stopSignal, flagStopSignal, "", "Signal to stop the container") flags.SetAnnotation(flagStopSignal, "version", []string{"1.28"}) + flags.StringVar(&opts.isolation, flagIsolation, "", "Service container isolation mode") + flags.SetAnnotation(flagIsolation, "version", []string{"1.35"}) } const ( @@ -879,4 +884,5 @@ const ( flagConfig = "config" flagConfigAdd = "config-add" flagConfigRemove = "config-rm" + flagIsolation = "isolation" ) diff --git a/cli/command/service/update.go b/cli/command/service/update.go index bb9568955432..a7c82ec0c1b9 100644 --- a/cli/command/service/update.go +++ b/cli/command/service/update.go @@ -269,6 +269,14 @@ func updateService(ctx context.Context, apiClient client.NetworkAPIClient, flags } } + updateIsolation := func(flag string, field *container.Isolation) error { + if flags.Changed(flag) { + val, _ := flags.GetString(flag) + *field = container.Isolation(val) + } + return nil + } + cspec := spec.TaskTemplate.ContainerSpec task := &spec.TaskTemplate @@ -288,6 +296,9 @@ func updateService(ctx context.Context, apiClient client.NetworkAPIClient, flags updateString(flagWorkdir, &cspec.Dir) updateString(flagUser, &cspec.User) updateString(flagHostname, &cspec.Hostname) + if err := updateIsolation(flagIsolation, &cspec.Isolation); err != nil { + return err + } if err := updateMounts(flags, &cspec.Mounts); err != nil { return err } diff --git a/cli/command/service/update_test.go b/cli/command/service/update_test.go index 32484d021759..92b4d6bc59f3 100644 --- a/cli/command/service/update_test.go +++ b/cli/command/service/update_test.go @@ -518,3 +518,32 @@ func TestUpdateStopSignal(t *testing.T) { updateService(nil, nil, flags, spec) assert.Equal(t, "SIGWINCH", cspec.StopSignal) } + +func TestUpdateIsolationValid(t *testing.T) { + flags := newUpdateCommand(nil).Flags() + err := flags.Set("isolation", "process") + require.NoError(t, err) + spec := swarm.ServiceSpec{ + TaskTemplate: swarm.TaskSpec{ + ContainerSpec: &swarm.ContainerSpec{}, + }, + } + err = updateService(context.Background(), nil, flags, &spec) + require.NoError(t, err) + assert.Equal(t, container.IsolationProcess, spec.TaskTemplate.ContainerSpec.Isolation) +} + +func TestUpdateIsolationInvalid(t *testing.T) { + // validation depends on daemon os / version so validation should be done on the daemon side + flags := newUpdateCommand(nil).Flags() + err := flags.Set("isolation", "test") + require.NoError(t, err) + spec := swarm.ServiceSpec{ + TaskTemplate: swarm.TaskSpec{ + ContainerSpec: &swarm.ContainerSpec{}, + }, + } + err = updateService(context.Background(), nil, flags, &spec) + require.NoError(t, err) + assert.Equal(t, container.Isolation("test"), spec.TaskTemplate.ContainerSpec.Isolation) +} diff --git a/cli/compose/convert/service.go b/cli/compose/convert/service.go index d69244895b93..e180307047dd 100644 --- a/cli/compose/convert/service.go +++ b/cli/compose/convert/service.go @@ -149,6 +149,7 @@ func Service( Configs: configs, ReadOnly: service.ReadOnly, Privileges: &privileges, + Isolation: container.Isolation(service.Isolation), }, LogDriver: logDriver, Resources: resources, diff --git a/cli/compose/convert/service_test.go b/cli/compose/convert/service_test.go index c9534bcae4dd..ebcce495c603 100644 --- a/cli/compose/convert/service_test.go +++ b/cli/compose/convert/service_test.go @@ -424,3 +424,12 @@ func TestConvertFileObjectDefaults(t *testing.T) { } assert.Equal(t, expected, swarmRef) } + +func TestServiceConvertsIsolation(t *testing.T) { + src := composetypes.ServiceConfig{ + Isolation: "hyperv", + } + result, err := Service("1.35", Namespace{name: "foo"}, src, nil, nil, nil, nil) + require.NoError(t, err) + assert.Equal(t, container.IsolationHyperV, result.TaskTemplate.ContainerSpec.Isolation) +} diff --git a/cli/compose/loader/loader_test.go b/cli/compose/loader/loader_test.go index 8ad65d2f9d22..84c4ae411381 100644 --- a/cli/compose/loader/loader_test.go +++ b/cli/compose/loader/loader_test.go @@ -10,6 +10,7 @@ import ( "time" "github.com/docker/cli/cli/compose/types" + "github.com/docker/docker/api/types/container" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -1454,5 +1455,37 @@ func TestLoadVolumesWarnOnDeprecatedExternalNameVersion33(t *testing.T) { } assert.Equal(t, expected, volumes) assert.Equal(t, "", buf.String()) +} +func TestLoadV35(t *testing.T) { + actual, err := loadYAML(` +version: "3.5" +services: + foo: + image: busybox + isolation: process +configs: + super: + external: true +`) + require.NoError(t, err) + require.Len(t, actual.Services, 1) + assert.Equal(t, container.IsolationProcess, actual.Services[0].Isolation) +} + +func TestLoadV35InvalidIsolation(t *testing.T) { + // validation should be done only on the daemon side + actual, err := loadYAML(` +version: "3.5" +services: + foo: + image: busybox + isolation: invalid +configs: + super: + external: true +`) + require.NoError(t, err) + require.Len(t, actual.Services, 1) + assert.Equal(t, container.Isolation("invalid"), actual.Services[0].Isolation) } diff --git a/cli/compose/schema/bindata.go b/cli/compose/schema/bindata.go index f295006ed54f..5f150e13e7c7 100644 --- a/cli/compose/schema/bindata.go +++ b/cli/compose/schema/bindata.go @@ -5,6 +5,7 @@ // data/config_schema_v3.2.json // data/config_schema_v3.3.json // data/config_schema_v3.4.json +// data/config_schema_v3.5.json // DO NOT EDIT! package schema @@ -172,6 +173,26 @@ func dataConfig_schema_v34Json() (*asset, error) { return a, nil } +var _dataConfig_schema_v35Json = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x5b\xcf\x72\xdb\xbc\x11\xbf\xeb\x29\x38\xfc\xbe\x5b\x24\x3b\x33\x4d\x3b\xd3\xdc\x7a\xec\xa9\x3d\xd7\xa3\x70\x20\x70\x25\x21\x06\x01\x64\x01\xca\x56\x32\x7e\xf7\x0e\xff\x0a\x24\x41\x02\x94\xe8\xd8\x69\x73\xb2\x45\xee\x2e\xb0\x8b\xdd\x1f\x76\x17\xe0\x8f\x55\x14\xc5\x7f\x6a\x7a\x84\x8c\xc4\x9f\xa3\xf8\x68\x8c\xfa\x7c\x7f\xff\x55\x4b\xb1\xa9\x9e\xde\x49\x3c\xdc\xa7\x48\xf6\x66\xf3\xf1\xd3\x7d\xf5\xec\x8f\x78\x5d\xf0\xb1\xb4\x60\xa1\x52\xec\xd9\x21\xa9\xde\x24\xa7\xbf\xdc\xfd\xf5\xae\x60\xaf\x48\xcc\x59\x41\x41\x24\x77\x5f\x81\x9a\xea\x19\xc2\xb7\x9c\x21\x14\xcc\x0f\xf1\x09\x50\x33\x29\xe2\xed\x7a\x55\xbc\x53\x28\x15\xa0\x61\xa0\xe3\xcf\x51\x31\xb9\x28\x6a\x49\x9a\x07\x96\x58\x6d\x90\x89\x43\x5c\x3e\x7e\x29\x25\x44\x51\xac\x01\x4f\x8c\x5a\x12\xda\xa9\xfe\x71\x7f\x91\x7f\xdf\x92\xad\xfb\x52\xad\xc9\x96\xcf\x15\x31\x06\x50\xfc\x7b\x38\xb7\xf2\xf5\x97\x07\xb2\xf9\xfe\x8f\xcd\x7f\x3e\x6e\xfe\x7e\x97\x6c\xb6\x1f\xfe\xec\xbc\x2e\xec\x8b\xb0\xaf\x86\x4f\x61\xcf\x04\x33\x4c\x8a\x76\xfc\xb8\xa5\x7c\xa9\xff\x7b\x69\x07\x26\x69\x5a\x12\x13\xde\x19\x7b\x4f\xb8\x86\xae\xce\x02\xcc\x93\xc4\x47\x9f\xce\x2d\xd9\x1b\xe9\x5c\x8f\xef\xd0\xb9\xab\xce\x49\xf2\x3c\xf3\xae\x60\x43\xf5\x46\xca\x54\xc3\x2f\xb3\x7e\x1a\x28\x82\xf1\xbb\x6c\x45\xf5\x66\x1e\x5b\x0c\xbf\x8c\xc2\x15\x6a\xf8\x14\x6e\xa8\xde\x48\xe1\x6a\xf8\xdb\x14\x5e\x35\x4a\xbb\xe7\x18\x7f\x79\xde\x14\x7f\x5f\x4a\x99\x93\xf2\x2a\x29\xd6\xfc\x4a\x25\x3a\x98\xe7\x32\xa7\x0b\x73\xc6\xed\xd9\x1a\x74\xc4\x92\x29\x28\x2e\xcf\xe5\xcc\xdd\x36\xab\x08\x32\x10\x26\x6e\xcd\x14\x45\xf1\x2e\x67\x3c\xed\x5b\x5d\x0a\xf8\x57\x21\xe2\xc1\x7a\x18\x45\x3f\xfa\xf0\x6e\xc9\x29\xdf\x77\x7e\x8d\x3b\x45\xfb\x7e\x44\x97\xf6\x3d\x95\xc2\xc0\xb3\x29\x95\x9a\x1e\xba\x32\x81\xa4\x8f\x80\x7b\xc6\x21\x94\x83\x60\xe5\xe9\x23\x26\xe3\x4c\x9b\x44\x62\x92\x32\x6a\x9c\xfc\x9c\xec\x80\xdf\x24\x81\x12\x7a\x84\x64\x8f\x32\xf3\x4a\xd9\x27\x95\x26\xda\x29\xa8\x41\xf0\x40\xcd\x0d\xc1\x03\xb8\x2d\xdb\x23\x1e\x70\xfb\x63\xab\x65\xb5\x7e\x6d\x57\x0e\x81\x31\x25\x2a\x21\x69\xda\x99\x07\x41\x24\xe7\x78\x1d\xc5\xcc\x40\xa6\xdd\x0a\x45\x71\x2e\xd8\xb7\x1c\xfe\x59\x93\x18\xcc\xa1\x2f\x37\x45\xa9\x96\x17\x7c\x40\x99\xab\x44\x11\x2c\x02\x69\xda\xd8\x31\x95\x59\x46\xc4\x52\xd1\x35\x47\x8f\x00\xcb\x0f\x70\x3e\xb2\x43\xb6\x1e\xc3\x7e\xd5\x8e\xd6\x99\xd6\x88\x36\x7e\x7d\x86\x78\xe1\x47\x0c\x3f\x66\x14\x90\x2b\x73\xa4\xa1\x10\x30\x1d\x0a\x4e\xfa\x9c\xa5\xe1\xc4\x87\x39\xc4\x99\x4c\xbb\xf3\x16\x79\xb6\x03\x1c\x84\x64\x37\xb2\x86\xbf\xb7\x2b\xd7\x9b\xde\xea\x1b\xc2\x04\x60\x22\x48\xe6\xb3\x55\x4c\x11\x52\x10\x86\x11\x9e\x68\x05\xb4\x43\xde\xac\xd4\xc4\xca\xc4\x41\x90\x1c\x23\x1c\x98\x36\x78\x9e\x06\xa5\x17\x7b\x62\x29\x28\x10\xa9\x4e\xaa\x22\x64\x3e\x7a\xc6\x29\xb4\x15\xc9\xa2\x30\x91\x8a\xa9\x5d\xa1\x12\x53\xec\x0b\xc5\xdc\xe2\x1e\x63\xa2\x81\x20\x3d\x5e\xc9\x2f\x33\xc2\x44\xc8\xa2\x82\x30\x78\x56\x92\x55\x30\xf6\xee\xf0\x09\xc4\x29\x69\xfd\x66\xb6\x19\x40\x9c\x18\x4a\x91\x35\x20\x1d\xb6\x3b\x5b\xfc\xcf\x4a\x6a\xb8\x1d\x1c\x6b\x8e\x87\x46\xf1\x75\x1b\xd3\xdb\xae\xf5\xe2\xbd\xc4\x8c\x14\x93\x6d\xc6\xb6\x63\xb8\x33\xd4\xd0\xf3\x6c\x03\xda\x3a\x14\x59\x2d\xe1\x09\x67\xe2\x71\x79\x17\x87\x67\x83\x24\x39\x4a\x6d\xae\x49\x80\xe2\x23\x10\x6e\x8e\xf4\x08\xf4\x71\x82\xdd\xa6\xea\x70\x4b\x6d\x42\x9c\x9c\x65\xe4\xe0\x27\x52\xd4\x4b\xa2\x25\x27\xa6\x6e\x76\x4c\x11\x5e\x9d\x11\xc6\x8b\xae\x92\x25\x56\x1e\x0e\x05\xe9\x98\x6b\x0e\x2a\x8c\xfa\xb5\x2f\x37\x4f\x91\x9d\x00\x43\xd3\x4d\xa9\x2e\x85\x51\xff\x65\xc8\xb6\xef\xad\x24\x3b\xa4\x5f\xee\xaa\x42\x72\x22\xfc\xca\xff\x38\x8f\xb7\xc3\xbd\x75\xb8\xbb\xf6\x9f\xf4\x34\x0c\x4b\x88\x3b\xab\x92\x11\x5a\xe4\xbd\x08\x7a\x64\x5d\x2f\xa4\x75\x62\x9f\x0c\x92\x83\x0b\xed\x80\x78\xb0\x03\x8f\x41\xfa\x55\xf5\xc6\xfc\x3a\x2f\x68\xe9\xbc\xcd\x00\x8f\x36\x63\xd3\x0b\x9d\xe6\x65\xba\x7e\x17\x2b\xe9\x08\x67\x44\x83\x3f\xd8\x27\x0b\xb7\x56\x1a\x53\xa7\x4f\x81\x3e\xe1\xe2\xfd\xdb\x24\xef\x08\xeb\xa8\xcc\xf0\x1a\xcf\x23\xca\xce\x65\x39\x77\x4e\x64\xeb\xcf\x6e\x5f\xb3\x04\x55\xdd\x0c\xbd\x8b\x15\x25\x42\xd8\x01\xa6\x24\x9a\x9f\x52\x34\x5d\x70\xea\x92\x19\x54\x83\x0f\xeb\xa8\xfe\x72\x07\x31\xbd\x4e\xf1\x35\x81\x52\x0e\x6a\x47\xe9\xc5\x84\x81\x43\x51\xf3\xb8\x37\x81\x7c\xc7\x99\x3e\x42\x3a\x87\x07\xa5\x91\x54\xf2\xb0\xc0\x70\xb6\x89\xc2\x83\x61\xa2\x10\xbb\x2a\x89\x53\xc8\x4e\x8c\xc3\xa1\xa7\xf1\x4e\x4a\x0e\x44\x74\x36\x0a\x04\x92\x26\x52\xf0\x73\x00\xa5\x36\x04\xbd\xed\x0b\x0d\x34\x47\x66\xce\x89\x54\x66\xf1\xf4\x51\x1f\xb3\x44\xb3\xef\xd0\x8d\xbd\x8b\xd7\xd7\x82\xb6\xbd\x09\xf5\x9a\xf1\xd1\xef\x9e\xc5\xff\x4d\xcf\x42\x9f\x35\x35\xd7\xe5\xd6\xda\xa4\x4c\x24\x52\x81\xf0\xc6\x86\x36\x52\x25\x07\x24\x14\x12\x05\xc8\xa4\xd3\x14\x1d\x80\x4d\x73\xac\x4a\x83\x81\x18\xcd\x0e\x82\xb8\x71\xc7\x22\x35\x99\xda\x5f\xd9\x2d\x30\xc6\x1f\xec\x39\x67\x19\x1b\x0f\x1a\x87\xd7\x06\xe4\x6b\x55\xae\xe6\x4e\xd1\x26\xd2\xb3\x20\xc8\x9e\xa8\x10\xa6\x0b\x84\x80\xca\xe0\x48\x70\xc6\xd6\x51\x06\xe6\x7e\x64\x7f\x72\xd5\x0d\xce\x79\x75\x8e\xd5\x4b\x79\xeb\x7a\x22\x5b\x27\xfd\xac\xd4\xab\x3f\x8d\xed\x68\xf6\xe3\x0e\xaa\x5c\x7b\x8b\xb8\x92\x46\xe8\xa9\x02\xa4\x25\x1d\x9e\x0f\x47\xbf\x04\x42\x77\xd6\xa8\x24\x77\xac\x4d\x00\x8e\xd7\x23\x05\x62\xe7\x6b\xa3\x7e\x70\x46\x60\xf1\x50\x29\x34\xd3\x06\x04\x75\x37\x62\x9d\x4c\x3b\x36\x38\xe5\x18\x1a\x65\xba\xee\x0a\xab\xba\x4a\x2a\x72\x18\x6f\xc5\xb8\x6b\x93\xe0\x58\xad\xaf\x0e\xfc\x14\x55\x84\xa4\x52\x8d\x2c\x4d\xb8\x1a\x73\xb7\xd9\x5e\xeb\x62\x22\x0f\x1d\x83\x8c\x27\x89\x8f\xc5\x86\x94\x32\x37\x72\xac\x7a\x2c\x33\x2e\x1f\xf4\x9a\x82\x8d\x00\xd7\x89\xb9\x4d\xea\xbd\x85\x30\x7d\x7a\x5f\x13\x8d\x9e\xac\x33\x4d\x76\xbd\x03\x0c\xd7\x46\x5b\xec\x0c\x78\x72\xef\xf7\xfe\x84\x01\xc1\x20\xeb\x9d\x44\x34\xa9\x94\xbd\xe3\x83\x7e\x9f\xfd\x7a\xc3\x32\x90\xb9\x13\x9c\x42\xb2\x25\x82\x66\x7e\xbe\xb5\xb2\xdd\xb4\x96\x17\x5b\xf7\x1c\x3c\x2e\x64\x51\xf6\x3d\xe8\xc1\x3a\xd7\xaa\xba\x00\x5e\x37\x09\xd9\x1e\x41\xa4\xe5\x89\x4b\xd0\x5e\x8a\xa0\x38\xa3\x44\xfb\xf2\x95\x1b\x7a\xce\xb9\x4a\x89\x81\xa4\xbe\x4e\x33\x27\x43\x9c\x48\x0d\x15\x41\xc2\x39\x70\xa6\xb3\x90\x54\x2b\x4e\x81\x13\xe7\x5e\xe3\xf5\x9b\x92\x7d\x4f\x18\xcf\x11\x12\x42\x03\xfa\xf3\xf5\x4a\x09\x66\xa4\x13\xbc\xc2\x86\xcc\xc8\x73\xd2\x0c\x5b\x92\x78\xa2\xb6\x64\x92\x98\xba\x53\xad\x75\xe1\x17\x79\xe6\x48\x76\xaa\xb8\xd8\xec\x19\x6a\x53\xd5\xc4\x52\xd5\xbf\xba\xa0\xfe\x32\xda\x67\x08\x6d\x4d\x5b\x5e\x57\x65\x25\xf3\x0a\x86\x09\x77\xb8\x94\x1f\x23\xde\xd9\x8c\x38\xb0\x18\x82\x2e\x00\xb5\x3d\x39\xf0\xf2\x2f\x6a\x85\x0a\x92\x24\x67\x55\x22\xb4\x84\x29\xa8\x14\xd5\x3c\x42\xbc\xf4\xc6\xb0\x28\x7c\xb4\x28\xe3\x32\x65\xbc\x08\x52\x32\x3c\x31\x91\xca\xa7\xf9\xe8\xbb\x80\xb5\x15\x27\x14\x7a\x88\x7d\xab\xa1\xb5\x41\xc2\x84\x99\x7d\xa2\xd6\x37\x8b\x42\xd8\x03\x82\x18\x46\x44\x34\x5d\xd9\x44\xe3\xd5\x8d\x4f\x37\xbf\x86\x35\x85\x56\x45\x8a\xff\x06\x8d\xcd\x5b\x17\xff\x86\x1c\xb1\x0d\x77\xcf\xee\xde\xd2\x79\xb3\xc3\xb1\x1d\x9d\xaa\xdc\x7b\x3a\x97\x41\x26\xa7\xaf\xac\xdc\x70\x6b\xdc\xa7\x62\x43\xb6\x40\xf6\x12\x74\x9c\x5b\x53\x25\x52\x2d\xdf\x4f\xf2\x1f\xd9\x6e\xfd\xdd\x0c\xa6\x48\xb6\x14\x86\x04\x1f\x70\xc7\xce\xf4\x29\x7a\x07\xe8\x90\xef\x44\xd8\x1d\xcf\x77\x86\x0e\x76\xc6\x5c\x5f\x62\x19\x59\xd5\x87\xb6\x12\x5b\xb7\xb6\xda\x06\x2f\xf1\xe8\x0d\x92\xe5\xe6\x5f\x16\x85\xfd\x26\xb0\xab\x7a\x24\xc6\x10\x7a\x0c\x2a\x34\x67\xe6\xfb\x37\xe0\xd0\xa0\x1d\xe2\x84\xa1\x9a\x6a\x01\x14\x0a\xb9\xd2\xf3\xbf\x81\x54\xbf\xba\x5f\xff\x3c\x1f\xac\x3f\x6e\xf1\x7e\x40\x51\x52\x5d\xbd\xd7\x07\x5c\x51\x7d\x07\x6b\xf6\xc6\x4b\x31\xd8\xe8\x9c\x4b\x51\x53\xfd\x5e\x8a\x57\x8d\x8a\xee\xa1\xa0\xb5\x24\xc3\x6e\xe0\x94\x25\x83\x6f\x2e\xd5\x1c\xdb\xee\x34\xfa\x64\x8e\x4f\x41\xbb\xb9\xcf\xd4\x95\x81\x86\x64\xa4\x27\xdd\x1b\xb4\x36\xe2\xb4\xe6\x0b\xe2\xfe\xdd\x87\x89\x0c\x6f\xea\x86\xe1\x2b\xa5\x46\x0b\x5c\xc7\x70\xaf\x69\xaf\x78\x6e\xac\x3b\xfc\x92\x6d\x3c\xfe\x1b\xfe\xc1\x77\x6d\x85\x9e\xe2\x3c\xe8\x56\xff\xe8\x1e\xc0\x55\xdf\xa4\x6d\x3b\xf6\xe9\x91\x54\x17\x7b\xad\x8d\x76\x6b\xf7\x13\x46\xbf\x44\x70\x7d\xed\xd6\x3f\xfe\x6b\xbe\x3a\x1b\xb9\x91\xb0\xb2\xff\x96\x5f\x11\xae\x5e\x56\xff\x0d\x00\x00\xff\xff\xaa\xc2\x0c\x76\xaf\x3d\x00\x00") + +func dataConfig_schema_v35JsonBytes() ([]byte, error) { + return bindataRead( + _dataConfig_schema_v35Json, + "data/config_schema_v3.5.json", + ) +} + +func dataConfig_schema_v35Json() (*asset, error) { + bytes, err := dataConfig_schema_v35JsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "data/config_schema_v3.5.json", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + // Asset loads and returns the asset for the given name. // It returns an error if the asset could not be found or // could not be loaded. @@ -229,6 +250,7 @@ var _bindata = map[string]func() (*asset, error){ "data/config_schema_v3.2.json": dataConfig_schema_v32Json, "data/config_schema_v3.3.json": dataConfig_schema_v33Json, "data/config_schema_v3.4.json": dataConfig_schema_v34Json, + "data/config_schema_v3.5.json": dataConfig_schema_v35Json, } // AssetDir returns the file names below a certain @@ -277,6 +299,7 @@ var _bintree = &bintree{nil, map[string]*bintree{ "config_schema_v3.2.json": &bintree{dataConfig_schema_v32Json, map[string]*bintree{}}, "config_schema_v3.3.json": &bintree{dataConfig_schema_v33Json, map[string]*bintree{}}, "config_schema_v3.4.json": &bintree{dataConfig_schema_v34Json, map[string]*bintree{}}, + "config_schema_v3.5.json": &bintree{dataConfig_schema_v35Json, map[string]*bintree{}}, }}, }} diff --git a/cli/compose/schema/data/config_schema_v3.5.json b/cli/compose/schema/data/config_schema_v3.5.json new file mode 100644 index 000000000000..1151bb06424f --- /dev/null +++ b/cli/compose/schema/data/config_schema_v3.5.json @@ -0,0 +1,544 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "config_schema_v3.5.json", + "type": "object", + "required": ["version"], + + "properties": { + "version": { + "type": "string" + }, + + "services": { + "id": "#/properties/services", + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9._-]+$": { + "$ref": "#/definitions/service" + } + }, + "additionalProperties": false + }, + + "networks": { + "id": "#/properties/networks", + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9._-]+$": { + "$ref": "#/definitions/network" + } + } + }, + + "volumes": { + "id": "#/properties/volumes", + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9._-]+$": { + "$ref": "#/definitions/volume" + } + }, + "additionalProperties": false + }, + + "secrets": { + "id": "#/properties/secrets", + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9._-]+$": { + "$ref": "#/definitions/secret" + } + }, + "additionalProperties": false + }, + + "configs": { + "id": "#/properties/configs", + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9._-]+$": { + "$ref": "#/definitions/config" + } + }, + "additionalProperties": false + } + }, + + "patternProperties": {"^x-": {}}, + "additionalProperties": false, + + "definitions": { + + "service": { + "id": "#/definitions/service", + "type": "object", + + "properties": { + "deploy": {"$ref": "#/definitions/deployment"}, + "build": { + "oneOf": [ + {"type": "string"}, + { + "type": "object", + "properties": { + "context": {"type": "string"}, + "dockerfile": {"type": "string"}, + "args": {"$ref": "#/definitions/list_or_dict"}, + "labels": {"$ref": "#/definitions/list_or_dict"}, + "cache_from": {"$ref": "#/definitions/list_of_strings"}, + "network": {"type": "string"}, + "target": {"type": "string"} + }, + "additionalProperties": false + } + ] + }, + "cap_add": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, + "cap_drop": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, + "cgroup_parent": {"type": "string"}, + "command": { + "oneOf": [ + {"type": "string"}, + {"type": "array", "items": {"type": "string"}} + ] + }, + "configs": { + "type": "array", + "items": { + "oneOf": [ + {"type": "string"}, + { + "type": "object", + "properties": { + "source": {"type": "string"}, + "target": {"type": "string"}, + "uid": {"type": "string"}, + "gid": {"type": "string"}, + "mode": {"type": "number"} + } + } + ] + } + }, + "container_name": {"type": "string"}, + "credential_spec": {"type": "object", "properties": { + "file": {"type": "string"}, + "registry": {"type": "string"} + }}, + "depends_on": {"$ref": "#/definitions/list_of_strings"}, + "devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, + "dns": {"$ref": "#/definitions/string_or_list"}, + "dns_search": {"$ref": "#/definitions/string_or_list"}, + "domainname": {"type": "string"}, + "entrypoint": { + "oneOf": [ + {"type": "string"}, + {"type": "array", "items": {"type": "string"}} + ] + }, + "env_file": {"$ref": "#/definitions/string_or_list"}, + "environment": {"$ref": "#/definitions/list_or_dict"}, + + "expose": { + "type": "array", + "items": { + "type": ["string", "number"], + "format": "expose" + }, + "uniqueItems": true + }, + + "external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, + "extra_hosts": {"$ref": "#/definitions/list_or_dict"}, + "healthcheck": {"$ref": "#/definitions/healthcheck"}, + "hostname": {"type": "string"}, + "image": {"type": "string"}, + "ipc": {"type": "string"}, + "isolation": {"type": "string"}, + "labels": {"$ref": "#/definitions/list_or_dict"}, + "links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, + + "logging": { + "type": "object", + + "properties": { + "driver": {"type": "string"}, + "options": { + "type": "object", + "patternProperties": { + "^.+$": {"type": ["string", "number", "null"]} + } + } + }, + "additionalProperties": false + }, + + "mac_address": {"type": "string"}, + "network_mode": {"type": "string"}, + + "networks": { + "oneOf": [ + {"$ref": "#/definitions/list_of_strings"}, + { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9._-]+$": { + "oneOf": [ + { + "type": "object", + "properties": { + "aliases": {"$ref": "#/definitions/list_of_strings"}, + "ipv4_address": {"type": "string"}, + "ipv6_address": {"type": "string"} + }, + "additionalProperties": false + }, + {"type": "null"} + ] + } + }, + "additionalProperties": false + } + ] + }, + "pid": {"type": ["string", "null"]}, + + "ports": { + "type": "array", + "items": { + "oneOf": [ + {"type": "number", "format": "ports"}, + {"type": "string", "format": "ports"}, + { + "type": "object", + "properties": { + "mode": {"type": "string"}, + "target": {"type": "integer"}, + "published": {"type": "integer"}, + "protocol": {"type": "string"} + }, + "additionalProperties": false + } + ] + }, + "uniqueItems": true + }, + + "privileged": {"type": "boolean"}, + "read_only": {"type": "boolean"}, + "restart": {"type": "string"}, + "security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, + "shm_size": {"type": ["number", "string"]}, + "secrets": { + "type": "array", + "items": { + "oneOf": [ + {"type": "string"}, + { + "type": "object", + "properties": { + "source": {"type": "string"}, + "target": {"type": "string"}, + "uid": {"type": "string"}, + "gid": {"type": "string"}, + "mode": {"type": "number"} + } + } + ] + } + }, + "sysctls": {"$ref": "#/definitions/list_or_dict"}, + "stdin_open": {"type": "boolean"}, + "stop_grace_period": {"type": "string", "format": "duration"}, + "stop_signal": {"type": "string"}, + "tmpfs": {"$ref": "#/definitions/string_or_list"}, + "tty": {"type": "boolean"}, + "ulimits": { + "type": "object", + "patternProperties": { + "^[a-z]+$": { + "oneOf": [ + {"type": "integer"}, + { + "type":"object", + "properties": { + "hard": {"type": "integer"}, + "soft": {"type": "integer"} + }, + "required": ["soft", "hard"], + "additionalProperties": false + } + ] + } + } + }, + "user": {"type": "string"}, + "userns_mode": {"type": "string"}, + "volumes": { + "type": "array", + "items": { + "oneOf": [ + {"type": "string"}, + { + "type": "object", + "required": ["type"], + "properties": { + "type": {"type": "string"}, + "source": {"type": "string"}, + "target": {"type": "string"}, + "read_only": {"type": "boolean"}, + "consistency": {"type": "string"}, + "bind": { + "type": "object", + "properties": { + "propagation": {"type": "string"} + } + }, + "volume": { + "type": "object", + "properties": { + "nocopy": {"type": "boolean"} + } + } + } + } + ], + "uniqueItems": true + } + }, + "working_dir": {"type": "string"} + }, + "additionalProperties": false + }, + + "healthcheck": { + "id": "#/definitions/healthcheck", + "type": "object", + "additionalProperties": false, + "properties": { + "disable": {"type": "boolean"}, + "interval": {"type": "string", "format": "duration"}, + "retries": {"type": "number"}, + "test": { + "oneOf": [ + {"type": "string"}, + {"type": "array", "items": {"type": "string"}} + ] + }, + "timeout": {"type": "string", "format": "duration"}, + "start_period": {"type": "string", "format": "duration"} + } + }, + "deployment": { + "id": "#/definitions/deployment", + "type": ["object", "null"], + "properties": { + "mode": {"type": "string"}, + "endpoint_mode": {"type": "string"}, + "replicas": {"type": "integer"}, + "labels": {"$ref": "#/definitions/list_or_dict"}, + "update_config": { + "type": "object", + "properties": { + "parallelism": {"type": "integer"}, + "delay": {"type": "string", "format": "duration"}, + "failure_action": {"type": "string"}, + "monitor": {"type": "string", "format": "duration"}, + "max_failure_ratio": {"type": "number"}, + "order": {"type": "string", "enum": [ + "start-first", "stop-first" + ]} + }, + "additionalProperties": false + }, + "resources": { + "type": "object", + "properties": { + "limits": {"$ref": "#/definitions/resource"}, + "reservations": {"$ref": "#/definitions/resource"} + }, + "additionalProperties": false + }, + "restart_policy": { + "type": "object", + "properties": { + "condition": {"type": "string"}, + "delay": {"type": "string", "format": "duration"}, + "max_attempts": {"type": "integer"}, + "window": {"type": "string", "format": "duration"} + }, + "additionalProperties": false + }, + "placement": { + "type": "object", + "properties": { + "constraints": {"type": "array", "items": {"type": "string"}}, + "preferences": { + "type": "array", + "items": { + "type": "object", + "properties": { + "spread": {"type": "string"} + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + + "resource": { + "id": "#/definitions/resource", + "type": "object", + "properties": { + "cpus": {"type": "string"}, + "memory": {"type": "string"} + }, + "additionalProperties": false + }, + + "network": { + "id": "#/definitions/network", + "type": ["object", "null"], + "properties": { + "driver": {"type": "string"}, + "driver_opts": { + "type": "object", + "patternProperties": { + "^.+$": {"type": ["string", "number"]} + } + }, + "ipam": { + "type": "object", + "properties": { + "driver": {"type": "string"}, + "config": { + "type": "array", + "items": { + "type": "object", + "properties": { + "subnet": {"type": "string"} + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + }, + "external": { + "type": ["boolean", "object"], + "properties": { + "name": {"type": "string"} + }, + "additionalProperties": false + }, + "internal": {"type": "boolean"}, + "attachable": {"type": "boolean"}, + "labels": {"$ref": "#/definitions/list_or_dict"} + }, + "additionalProperties": false + }, + + "volume": { + "id": "#/definitions/volume", + "type": ["object", "null"], + "properties": { + "name": {"type": "string"}, + "driver": {"type": "string"}, + "driver_opts": { + "type": "object", + "patternProperties": { + "^.+$": {"type": ["string", "number"]} + } + }, + "external": { + "type": ["boolean", "object"], + "properties": { + "name": {"type": "string"} + }, + "additionalProperties": false + }, + "labels": {"$ref": "#/definitions/list_or_dict"} + }, + "additionalProperties": false + }, + + "secret": { + "id": "#/definitions/secret", + "type": "object", + "properties": { + "file": {"type": "string"}, + "external": { + "type": ["boolean", "object"], + "properties": { + "name": {"type": "string"} + } + }, + "labels": {"$ref": "#/definitions/list_or_dict"} + }, + "additionalProperties": false + }, + + "config": { + "id": "#/definitions/config", + "type": "object", + "properties": { + "file": {"type": "string"}, + "external": { + "type": ["boolean", "object"], + "properties": { + "name": {"type": "string"} + } + }, + "labels": {"$ref": "#/definitions/list_or_dict"} + }, + "additionalProperties": false + }, + + "string_or_list": { + "oneOf": [ + {"type": "string"}, + {"$ref": "#/definitions/list_of_strings"} + ] + }, + + "list_of_strings": { + "type": "array", + "items": {"type": "string"}, + "uniqueItems": true + }, + + "list_or_dict": { + "oneOf": [ + { + "type": "object", + "patternProperties": { + ".+": { + "type": ["string", "number", "null"] + } + }, + "additionalProperties": false + }, + {"type": "array", "items": {"type": "string"}, "uniqueItems": true} + ] + }, + + "constraints": { + "service": { + "id": "#/definitions/constraints/service", + "anyOf": [ + {"required": ["build"]}, + {"required": ["image"]} + ], + "properties": { + "build": { + "required": ["context"] + } + } + } + } + } +} diff --git a/cli/compose/schema/schema_test.go b/cli/compose/schema/schema_test.go index 6a761fddef1d..3461b5949b46 100644 --- a/cli/compose/schema/schema_test.go +++ b/cli/compose/schema/schema_test.go @@ -84,3 +84,16 @@ func TestValidatePlacement(t *testing.T) { assert.NoError(t, Validate(config, "3.3")) } + +func TestValidateIsolation(t *testing.T) { + config := dict{ + "version": "3.5", + "services": dict{ + "foo": dict{ + "image": "busybox", + "isolation": "some-isolation-value", + }, + }, + } + assert.NoError(t, Validate(config, "3.5")) +} diff --git a/cli/compose/types/types.go b/cli/compose/types/types.go index 99c536887a64..296e2595756d 100644 --- a/cli/compose/types/types.go +++ b/cli/compose/types/types.go @@ -126,6 +126,7 @@ type ServiceConfig struct { User string Volumes []ServiceVolumeConfig WorkingDir string `mapstructure:"working_dir"` + Isolation string `mapstructure:"isolation"` } // BuildConfig is a type for build diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker index 4a835239bacb..990934b610c7 100644 --- a/contrib/completion/bash/docker +++ b/contrib/completion/bash/docker @@ -3305,6 +3305,7 @@ _docker_service_update_and_create() { --health-start-period --health-timeout --hostname + --isolation --label -l --limit-cpu --limit-memory @@ -3493,6 +3494,10 @@ _docker_service_update_and_create() { __docker_nospace return ;; + --isolation) + __docker_complete_isolation + return + ;; --log-driver) __docker_complete_log_drivers return diff --git a/contrib/completion/zsh/_docker b/contrib/completion/zsh/_docker index 5d6edd880140..b25dc7b1970c 100644 --- a/contrib/completion/zsh/_docker +++ b/contrib/completion/zsh/_docker @@ -1955,6 +1955,7 @@ __docker_service_subcommand() { "($help)--health-retries=[Consecutive failures needed to report unhealthy]:retries:(1 2 3 4 5)" "($help)--health-timeout=[Maximum time to allow one check to run]:time: " "($help)--hostname=[Service container hostname]:hostname: " \ + "($help)--isolation=[Service container isolation mode]:isolation:(default process hyperv)" \ "($help)*--label=[Service labels]:label: " "($help)--limit-cpu=[Limit CPUs]:value: " "($help)--limit-memory=[Limit Memory]:value: " diff --git a/docs/reference/commandline/service_create.md b/docs/reference/commandline/service_create.md index bce70248cdc4..e7a65afd8c5c 100644 --- a/docs/reference/commandline/service_create.md +++ b/docs/reference/commandline/service_create.md @@ -42,6 +42,7 @@ Options: --help Print usage --host list Set one or more custom host-to-IP mappings (host:ip) --hostname string Container hostname + --isolation isolation Service container isolation mode -l, --label list Service labels --limit-cpu decimal Limit CPUs --limit-memory bytes Limit Memory @@ -875,6 +876,22 @@ $ docker inspect --format="{{.Config.Hostname}}" 2e7a8a9c4da2-wo41w8hg8qanxwjwsg x3ti0erg11rjpg64m75kej2mz-hosttempl ``` +### Specify isolation mode (Windows) + +By default, tasks scheduled on Windows nodes are run using the default isolation mode +configured for this particular node. To force a specific isolation mode, you can use +the `--isolation` flag: + +```bash +$ docker service create --name myservice --isolation=process microsoft/nanoserver +``` + +Supported isolation modes on Windows are: +- `default`: use default settings specified on the node running the task +- `process`: use process isolation (Windows server only) +- `hyperv`: use Hyper-V isolation + + ## Related commands * [service inspect](service_inspect.md) diff --git a/docs/reference/commandline/service_update.md b/docs/reference/commandline/service_update.md index 7f27f84f4c44..a8c733ff6e11 100644 --- a/docs/reference/commandline/service_update.md +++ b/docs/reference/commandline/service_update.md @@ -53,6 +53,7 @@ Options: --host-rm list Remove a custom host-to-IP mapping (host:ip) --hostname string Container hostname --image string Service image tag + --isolation isolation Service container isolation mode --label-add list Add or update a service label --label-rm list Remove a label by its key --limit-cpu decimal Limit CPUs @@ -258,6 +259,12 @@ $ docker service update \ Some flags of `service update` support the use of templating. See [`service create`](./service_create.md#templating) for the reference. + +### Specify isolation mode (Windows) + +`service update` supports the same `--isolation` flag as `service create` +See [`service create`](./service_create.md) for the reference. + ## Related commands * [service create](service_create.md)