diff --git a/cli/compose/convert/volume.go b/cli/compose/convert/volume.go index e767b07ee988..8400f8d523e9 100644 --- a/cli/compose/convert/volume.go +++ b/cli/compose/convert/volume.go @@ -74,6 +74,10 @@ func convertVolumeToMount( return result, nil } + if stackVolume.Name != "" { + result.Source = stackVolume.Name + } + result.VolumeOptions.Labels = AddStackLabel(namespace, stackVolume.Labels) if stackVolume.Driver != "" || stackVolume.DriverOpts != nil { result.VolumeOptions.DriverConfig = &mount.Driver{ diff --git a/cli/compose/convert/volume_test.go b/cli/compose/convert/volume_test.go index c3d3c1d54e88..9e921701a0d7 100644 --- a/cli/compose/convert/volume_test.go +++ b/cli/compose/convert/volume_test.go @@ -98,6 +98,53 @@ func TestConvertVolumeToMountNamedVolume(t *testing.T) { assert.Equal(t, expected, mount) } +func TestConvertVolumeToMountNamedVolumeWithNameCustomizd(t *testing.T) { + stackVolumes := volumes{ + "normal": composetypes.VolumeConfig{ + Name: "user_specified_name", + Driver: "vsphere", + DriverOpts: map[string]string{ + "opt": "value", + }, + Labels: map[string]string{ + "something": "labeled", + }, + }, + } + namespace := NewNamespace("foo") + expected := mount.Mount{ + Type: mount.TypeVolume, + Source: "user_specified_name", + Target: "/foo", + ReadOnly: true, + VolumeOptions: &mount.VolumeOptions{ + Labels: map[string]string{ + LabelNamespace: "foo", + "something": "labeled", + }, + DriverConfig: &mount.Driver{ + Name: "vsphere", + Options: map[string]string{ + "opt": "value", + }, + }, + NoCopy: true, + }, + } + config := composetypes.ServiceVolumeConfig{ + Type: "volume", + Source: "normal", + Target: "/foo", + ReadOnly: true, + Volume: &composetypes.ServiceVolumeVolume{ + NoCopy: true, + }, + } + mount, err := convertVolumeToMount(config, stackVolumes, namespace) + assert.NoError(t, err) + assert.Equal(t, expected, mount) +} + func TestConvertVolumeToMountNamedVolumeExternal(t *testing.T) { stackVolumes := volumes{ "outside": composetypes.VolumeConfig{ diff --git a/cli/compose/loader/full-example.yml b/cli/compose/loader/full-example.yml index 76087c57ba68..347dbf5136fb 100644 --- a/cli/compose/loader/full-example.yml +++ b/cli/compose/loader/full-example.yml @@ -1,4 +1,4 @@ -version: "3.3" +version: "3.4" services: foo: @@ -280,6 +280,15 @@ volumes: foo: "bar" baz: 1 + another-volume: + name: "user_specified_name" + driver: vsphere + + driver_opts: + # Values can be strings or numbers + foo: "bar" + baz: 1 + external-volume: # Specifies that a pre-existing volume called "external-volume" # can be referred to within this file as "external-volume" @@ -288,5 +297,12 @@ volumes: other-external-volume: # Specifies that a pre-existing volume called "my-cool-volume" # can be referred to within this file as "other-external-volume" + # This example uses the deprecated "volume.external.name" (replaced by "volume.name") external: name: my-cool-volume + + external-volume3: + # Specifies that a pre-existing volume called "this-is-volume3" + # can be referred to within this file as "external-volume3" + name: this-is-volume3 + external: true diff --git a/cli/compose/loader/loader.go b/cli/compose/loader/loader.go index b456e1824dc1..2fb2630087c0 100644 --- a/cli/compose/loader/loader.go +++ b/cli/compose/loader/loader.go @@ -444,6 +444,12 @@ func LoadVolumes(source map[string]interface{}) (map[string]types.VolumeConfig, if volume.External.Name == "" { volume.External.Name = name volumes[name] = volume + } else { + logrus.Warnf("volume %s: volume.external.name is deprecated in favor of volume.name", name) + + if volume.Name != "" { + return nil, errors.Errorf("volume %s: volume.external.name and volume.name conflict; only use volume.name", name) + } } } } diff --git a/cli/compose/loader/loader_test.go b/cli/compose/loader/loader_test.go index 6a43e52121a3..231109a1e83f 100644 --- a/cli/compose/loader/loader_test.go +++ b/cli/compose/loader/loader_test.go @@ -628,6 +628,22 @@ volumes: assert.Contains(t, err.Error(), "external_volume") } +func TestInvalidExternalNameAndNameCombination(t *testing.T) { + _, err := loadYAML(` +version: "3.4" +volumes: + external_volume: + name: user_specified_name + external: + name: external_name +`) + + assert.Error(t, err) + fmt.Println(err) + assert.Contains(t, err.Error(), "volume.external.name and volume.name conflict; only use volume.name") + assert.Contains(t, err.Error(), "external_volume") +} + func durationPtr(value time.Duration) *time.Duration { return &value } @@ -983,6 +999,14 @@ func TestFullExample(t *testing.T) { "baz": "1", }, }, + "another-volume": { + Name: "user_specified_name", + Driver: "vsphere", + DriverOpts: map[string]string{ + "foo": "bar", + "baz": "1", + }, + }, "external-volume": { External: types.External{ Name: "external-volume", @@ -995,6 +1019,13 @@ func TestFullExample(t *testing.T) { External: true, }, }, + "external-volume3": { + Name: "this-is-volume3", + External: types.External{ + Name: "external-volume3", + External: true, + }, + }, } assert.Equal(t, expectedVolumeConfig, config.Volumes) diff --git a/cli/compose/schema/bindata.go b/cli/compose/schema/bindata.go index 06fcae3591b1..f5301bc5c9e0 100644 --- a/cli/compose/schema/bindata.go +++ b/cli/compose/schema/bindata.go @@ -4,6 +4,7 @@ // data/config_schema_v3.1.json // data/config_schema_v3.2.json // data/config_schema_v3.3.json +// data/config_schema_v3.4.json // DO NOT EDIT! package schema @@ -151,6 +152,26 @@ func dataConfig_schema_v33Json() (*asset, error) { return a, nil } +var _dataConfig_schema_v34Json = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x5b\x4b\x93\xdb\x2a\x16\xde\xfb\x57\xa8\x74\xef\x2e\xfd\x48\xd5\xa4\xa6\x6a\xb2\x9b\xe5\xac\x66\xd6\xd3\xe5\xa8\x30\x3a\xb6\x49\x23\x20\x80\xdc\xed\xa4\xfa\xbf\x4f\xe9\x69\x40\x20\xb0\xad\x4e\x77\xe6\x66\xd5\x6d\xe9\x3b\xc0\x79\x9f\x03\xe8\xc7\x2a\xcb\xf2\x3f\x15\xde\x43\x85\xf2\xcf\x59\xbe\xd7\x5a\x7c\xbe\xbf\xff\xaa\x38\xbb\xed\x9e\xde\x71\xb9\xbb\x2f\x25\xda\xea\xdb\x8f\x9f\xee\xbb\x67\x7f\xe4\x37\x0d\x1d\x29\x1b\x12\xcc\xd9\x96\xec\x8a\xee\x4d\x71\xf8\xdb\xdd\xa7\xbb\x86\xbc\x83\xe8\xa3\x80\x06\xc4\x37\x5f\x01\xeb\xee\x99\x84\x6f\x35\x91\xd0\x10\x3f\xe4\x07\x90\x8a\x70\x96\xaf\x6f\x56\xcd\x3b\x21\xb9\x00\xa9\x09\xa8\xfc\x73\xd6\x2c\x2e\xcb\x46\xc8\xf0\xc0\x18\x56\x69\x49\xd8\x2e\x6f\x1f\xbf\xb4\x23\x64\x59\xae\x40\x1e\x08\x36\x46\x18\x97\xfa\xc7\xfd\x69\xfc\xfb\x11\x76\xe3\x8e\x6a\x2c\xb6\x7d\x2e\x90\xd6\x20\xd9\x7f\xa6\x6b\x6b\x5f\x7f\x79\x40\xb7\xdf\xff\x79\xfb\xdf\x8f\xb7\xff\xb8\x2b\x6e\xd7\x1f\xfe\xb4\x5e\x37\xf2\x95\xb0\xed\xa6\x2f\x61\x4b\x18\xd1\x84\xb3\x71\xfe\x7c\x44\xbe\xf4\xff\xbd\x8c\x13\xa3\xb2\x6c\xc1\x88\x5a\x73\x6f\x11\x55\x60\xf3\xcc\x40\x3f\x71\xf9\x18\xe3\x79\x84\xbd\x11\xcf\xfd\xfc\x1e\x9e\x6d\x76\x0e\x9c\xd6\x55\x54\x83\x03\xea\x8d\x98\xe9\xa6\x5f\x46\x7f\x0a\xb0\x04\x1d\x37\xd9\x0e\xf5\x66\x16\xdb\x4c\xbf\x0c\xc3\x5d\xd4\x88\x31\x3c\xa0\xde\x88\xe1\x6e\xfa\xeb\x18\x5e\x0d\x4c\xcf\x62\x3b\x84\x31\x77\xbb\x40\x2b\x9e\xf9\x44\xe5\x8b\x27\x61\x59\x8d\xc2\x0a\x48\xa9\x04\x41\xf9\xb1\x79\x16\x90\x47\x07\xa8\x80\xe9\x7c\x14\x41\x96\xe5\x9b\x9a\xd0\xd2\x95\x28\x67\xf0\xef\x66\x88\x07\xe3\x61\x96\xfd\x70\x43\xb7\x31\x4e\xfb\xde\xfa\x15\x56\xf8\xf8\x3e\xc0\xcb\xf8\x1e\x73\xa6\xe1\x59\xb7\x4c\xcd\x4f\xdd\x89\x80\xe3\x47\x90\x5b\x42\x21\x95\x02\xc9\xce\x8a\x03\x22\xa3\x44\xe9\x82\xcb\xa2\x24\x58\x7b\xe9\x29\xda\x00\xbd\x6a\x04\x8c\xf0\x1e\x8a\xad\xe4\x55\x74\x94\x6d\xd1\x71\xa2\xf2\x17\x67\x9c\xc9\xc0\x71\xd3\x1e\x49\x8d\x5f\xeb\x95\x67\xc0\x1c\x23\x51\xa0\xb2\xb4\x44\x8a\xa4\x44\xc7\xfc\x26\xcb\x89\x86\x4a\xf9\xa5\x9d\xe5\x35\x23\xdf\x6a\xf8\x57\x0f\xd1\xb2\x06\x77\xdc\x52\x72\xb1\xfc\xc0\x3b\xc9\x6b\x51\x08\x24\x1b\x5b\x9f\xb7\x84\x1c\xf3\xaa\x42\x6c\x29\x07\x38\x87\x8f\x04\xc9\x4f\xc2\x6c\x66\x7a\x55\x3f\x87\xf9\x6a\x9c\xcd\x5a\x56\x80\x9b\x38\x3f\x53\x97\x8e\x3b\x75\xdc\xad\x9b\xa8\xc8\x6b\x89\x53\xbd\xb4\x99\x13\xc9\x1d\xa4\xc6\x81\x2c\xcb\x6b\x52\xa6\x83\x77\xe7\x80\x2b\x5e\xda\xeb\x66\x75\xb5\x01\x39\x71\x49\xdb\xb3\xa6\xbf\xd7\x2b\xdf\x1b\x47\xfb\x1a\x11\x06\xb2\x60\xa8\x8a\xc9\x2a\xc7\x12\x4a\x60\x9a\x20\x5a\x28\x01\xd8\x82\x0f\x9a\x9a\xd1\x4c\x9e\x14\x35\x73\x09\x3b\xa2\xb4\x3c\x7a\x91\x27\x2e\xcc\x85\x95\x20\x80\x95\xaa\xe8\x7a\x80\xd4\x00\x67\x0d\x30\x36\x04\x8b\x86\x89\x92\xcd\x05\xee\x6e\x98\x26\x74\x37\x6b\xcb\x1d\xc2\x42\x01\x92\x78\x7f\x21\x3d\xaf\x10\x61\x29\x4a\x05\xa6\xe5\x51\x70\xd2\x85\xb1\x77\x17\x9f\x80\x1d\x8a\xd1\x6e\xce\x16\x03\xb0\x03\x91\x9c\x55\x43\x90\x4e\x4b\xa0\x06\xfd\xb3\xe0\x0a\xae\x0f\x8e\x3d\xc5\xc3\xc0\xf8\xcd\xe8\xd3\x6b\x5b\x7a\xf9\x96\xcb\x0a\x35\x8b\x1d\xe6\x36\x7d\xd8\x9a\x6a\x6a\x79\xa6\x00\x4d\x1e\x9a\xc2\x17\xd1\x82\x12\xf6\xb8\xbc\x89\xc3\xb3\x96\xa8\xd8\x73\xa5\x2f\xa9\x51\xf2\x3d\x20\xaa\xf7\x78\x0f\xf8\x71\x86\xdc\x44\x59\xd4\x5c\xe9\x14\x23\x27\x15\xda\xc5\x41\x02\xc7\x20\x17\xd7\x62\xf9\xa2\xc2\x37\x86\xe5\xbb\x5d\x03\x0d\x59\xdc\xa4\xb6\xef\x5f\xc7\xaa\xe2\x52\x92\x03\xc8\xd4\x12\x97\x8b\x53\x4b\xe2\xbe\x4c\xc9\xe6\xd1\xfe\xcc\x82\x7e\xb9\xeb\xda\xb3\x19\xaf\x6a\xff\xa3\x34\x5f\x4f\x53\xe6\x34\x69\xba\x4f\x1c\x0e\xd3\xea\x5c\x4b\x2b\x15\xc2\x4d\x39\x2b\x41\x05\xf4\x7a\x82\xf6\x1b\x1e\xc5\x24\xe7\x9f\xb0\x13\xf0\x24\xb1\x86\x22\xf5\xd9\x89\x30\xbb\xa8\xc3\x4a\x52\x5d\xb4\xc5\x8e\x70\x13\x5a\x5e\xea\x32\x4f\xcb\x8d\x9b\x58\x8b\x43\x94\x20\x05\x71\x67\x0f\x0a\xd2\x1a\x8d\x88\xc3\xa7\x44\x9b\xf0\xd1\xfe\x7d\x96\x36\x40\x1a\x1c\x33\xbd\x75\x8b\x0c\x65\x96\xa8\x94\x7a\x17\xb2\x8e\x17\xad\xaf\xd9\x59\x0a\xbb\xf0\xb6\x63\x45\x1b\x21\x4c\x07\x13\x5c\xea\x9f\xd2\x0b\x9d\xe2\xd4\x29\xe1\x77\x93\x4f\xdb\x23\x57\xdd\x49\x44\xaf\xd3\x53\xcd\x44\x29\x0f\xda\xd3\x51\x11\xa6\x61\xd7\xb4\x32\xfe\x24\x50\x6f\x28\x51\x7b\x28\xcf\xa1\x91\x5c\x73\xcc\x69\x9a\x63\x78\x37\x68\xd2\x9d\x61\xa6\xbf\xba\xa8\x36\x13\x92\x1c\x08\x85\x9d\xc3\xf1\x86\x73\x0a\x88\x59\x89\x42\x02\x2a\x0b\xce\xe8\x31\x01\xa9\x34\x92\xd1\x5d\x09\x05\xb8\x96\x44\x1f\x0b\x2e\xf4\xe2\x55\xa1\xda\x57\x85\x22\xdf\xc1\xf6\xbd\x93\xd5\xf7\x03\xad\x9d\x05\x39\x5b\xdc\xd9\xef\xad\x88\xbf\xcc\x56\x84\x3a\x2a\xac\x2f\xab\xad\x95\x2e\x09\x2b\xb8\x00\x16\xf5\x0d\xa5\xb9\x28\x76\x12\x61\x28\x04\x48\xc2\xbd\xa2\xb0\x02\x6c\x59\x4b\xd4\xcc\x3f\x1d\x46\x91\x1d\x43\xfe\xb8\x63\x40\x75\x25\xb6\x17\x6e\x02\x68\x1d\x77\xf6\x9a\x92\x8a\x84\x9d\xc6\x63\xb5\x09\xf5\x5a\x57\xab\xf9\x4b\xb4\x99\xf2\x2c\x29\x64\xcf\x74\x08\xf3\x0d\x42\x42\x67\xb0\x47\xf2\x8c\xd4\xd1\x3a\xe6\x36\x90\x9f\x7c\x7d\x83\x77\x5d\xd6\x61\x75\x3b\xde\x4d\xbf\x90\xb5\x17\x7f\x56\xe9\xe5\x2e\x63\x1d\xac\x7e\xfc\x4e\x55\xab\x68\x13\xd7\x62\x98\x9a\x6b\x40\x46\xe8\xf4\xd4\x35\xfb\x25\x22\xb4\xa5\xa3\x16\xee\xd1\x4d\x42\x1c\xef\x67\x4a\x8c\x9d\xaf\x1d\xf5\x93\x2b\x02\x83\x06\x73\xa6\x88\xd2\xc0\xb0\x7f\x7f\xd5\x4b\xb4\x21\x93\xc3\x8b\xa9\x50\xe6\xfb\xae\xb4\xae\xab\x45\xa1\x5d\x17\x6f\x93\x1b\x9d\x74\x5f\xed\x0f\xe4\x7f\x0a\x2b\x8c\x63\x2e\x02\xaa\x49\x67\xe3\xdc\x34\xeb\x6c\x5d\xcc\xd4\xa1\xa1\x90\xf1\xc4\xe5\x63\x93\x90\x4a\xe2\x8f\x1c\x2b\x87\xe4\x8c\x23\x7d\x67\xaf\x6f\x18\xc0\x77\x56\x6d\x42\xa3\x67\xfb\xf3\xe7\xe6\x3d\x28\x78\xa6\x4d\x14\xda\x38\xe7\x12\xbe\x44\xdb\x64\x06\x79\x88\xe7\x7b\x09\x5a\x12\xe7\x28\x61\x28\x9a\xcc\xdc\x0e\xea\x7d\x6e\xb8\x6b\x52\x01\xaf\xfd\x61\x68\x65\x1a\x4e\x4f\x94\x1b\x67\xfe\x11\xa5\x1a\x48\x57\xa7\x0f\xc6\x01\x52\xd7\x97\x47\x15\x97\x92\xb0\x80\x95\xed\xd1\x46\x52\x76\x93\x20\x28\xc1\x48\xc5\x2a\x88\x2b\x76\x81\x6b\x51\x22\x0d\x45\x7f\x6d\xe4\x9c\x9a\x6d\xa6\x58\x13\x48\x22\x4a\x81\x12\x55\xa5\x14\x3f\x79\x09\x14\x79\xa3\x7f\xb4\xee\x6d\xc9\xb7\x88\xd0\x5a\x42\x81\x70\x30\x4c\x3b\x14\x15\x67\x44\x73\x6f\x38\x49\x9b\xb2\x42\xcf\xc5\x30\x6d\x0b\x89\xb5\x24\x76\x37\x9e\xba\x81\x6b\x58\x42\x97\xbb\xcf\x2b\xab\x67\x54\x74\x2a\xd2\x03\x16\x33\xcc\x38\x61\x5d\x82\x6a\xc2\xce\xb8\xbf\x1e\xa5\x8f\x06\xf8\x7e\x7b\xa0\x10\x9c\x92\xae\x0a\x58\x82\x43\xcc\x59\x27\xe4\x14\x83\xb8\xd2\x02\x1b\x73\x68\x7a\x98\x4a\xe8\xa8\xb3\xb6\x04\x4f\x84\x95\xfc\xe9\x8c\x09\x97\x33\x25\x41\x11\x06\x27\x38\x5e\x2b\x68\xa5\x25\x22\x4c\x9f\x7d\x9c\xe4\x8a\x45\x48\xd8\x82\x04\x36\x35\xf4\x6c\xbe\xac\xcf\xc2\xa5\x7d\x8c\xb7\x38\x87\x3d\x42\x89\xa6\xbe\x7d\x83\x5d\xbd\x6b\x95\x7f\x45\x81\x34\x7a\x71\x24\x91\x8e\xb8\x68\x69\x14\x4a\x9e\x58\xd4\xd1\xa3\xa9\x0a\x2a\x3e\x7f\x0d\xe3\x8a\x8b\xc8\x31\x16\x07\xd8\x02\x85\x42\xd2\x59\x66\x8f\x2a\xb8\x58\x7e\x33\x25\x7e\x5e\xb9\x8e\x87\x6d\x22\x50\xb5\x54\x0c\x49\x3e\xdd\xcd\xbd\x95\x4a\xf6\x0e\xa2\x43\xbd\x61\x81\x5e\xf9\x7d\x47\x07\xfb\xe6\x44\x7b\x31\x23\xa0\xd5\x87\xb1\x0d\xb9\x19\x65\xb5\x4e\x56\x71\xf0\x56\xc4\x72\xeb\x6f\x3b\x22\x77\x07\xd4\xd7\x3a\x21\xad\x11\xde\x27\x75\x59\x67\x96\xd6\x57\xc4\xa1\xc9\x5e\x80\x37\x0c\xf5\xa8\x05\xa2\x50\xca\x35\x95\xff\x8f\x48\xf5\xab\xdb\xf5\xcf\xb3\xc1\xfe\x7b\x89\xe8\xbd\xfd\x16\x75\x71\xae\x4f\xb8\x76\xf9\x0e\x74\xf6\xc6\xaa\x98\x24\x3a\xaf\x2a\x7a\xd4\x6f\x55\xbc\xaa\x57\xd8\x27\x62\x86\x4a\xa6\x1b\x64\x73\x92\x3c\xf7\x4b\x87\xb5\xbd\x0c\x17\xe6\xf9\xba\xd0\xae\x7d\xe6\xce\xcb\x07\x48\x60\x43\xd6\x99\xb4\x17\xe2\x3c\xe7\x0b\xc6\xfd\xbb\x0f\x33\x15\xde\xdc\xf5\xba\x57\x2a\x8d\x16\xb8\x8b\xe0\xd7\xa9\xd3\x3c\x0f\xd2\x9d\x7e\x40\x15\xf6\xff\x81\x7e\xf2\x39\x55\xc3\x27\x3b\x4e\x36\x70\x7f\xd8\xa7\x4f\xdd\xa7\x50\x6b\x4b\x3e\x0e\xa4\xbb\xac\x6a\x24\xda\xb5\xb9\x9f\x10\xbc\x5d\xef\xfb\xc8\xca\x3d\xfb\x1a\x3e\x76\x0a\x1c\xc7\xaf\xcc\xbf\xed\x87\x69\xab\x97\xd5\xff\x02\x00\x00\xff\xff\xa6\x9f\xe0\x94\x02\x3c\x00\x00") + +func dataConfig_schema_v34JsonBytes() ([]byte, error) { + return bindataRead( + _dataConfig_schema_v34Json, + "data/config_schema_v3.4.json", + ) +} + +func dataConfig_schema_v34Json() (*asset, error) { + bytes, err := dataConfig_schema_v34JsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "data/config_schema_v3.4.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. @@ -207,6 +228,7 @@ var _bindata = map[string]func() (*asset, error){ "data/config_schema_v3.1.json": dataConfig_schema_v31Json, "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, } // AssetDir returns the file names below a certain @@ -254,6 +276,7 @@ var _bintree = &bintree{nil, map[string]*bintree{ "config_schema_v3.1.json": &bintree{dataConfig_schema_v31Json, 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{}}, }}, }} diff --git a/cli/compose/schema/data/config_schema_v3.4.json b/cli/compose/schema/data/config_schema_v3.4.json new file mode 100644 index 000000000000..4cfbd060a2e2 --- /dev/null +++ b/cli/compose/schema/data/config_schema_v3.4.json @@ -0,0 +1,535 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "config_schema_v3.4.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 + } + }, + + "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"} + }, + "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"}, + "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"}, + "retries": {"type": "number"}, + "test": { + "oneOf": [ + {"type": "string"}, + {"type": "array", "items": {"type": "string"}} + ] + }, + "timeout": {"type": "string"} + } + }, + "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"} + }, + "additionalProperties": false + }, + "resources": { + "type": "object", + "properties": { + "limits": {"$ref": "#/definitions/resource"}, + "reservations": {"$ref": "#/definitions/resource"} + } + }, + "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/types/types.go b/cli/compose/types/types.go index 1eecc124f6b1..918c9bef0c67 100644 --- a/cli/compose/types/types.go +++ b/cli/compose/types/types.go @@ -305,6 +305,7 @@ type IPAMPool struct { // VolumeConfig for a volume type VolumeConfig struct { + Name string Driver string DriverOpts map[string]string `mapstructure:"driver_opts"` External External @@ -313,6 +314,7 @@ type VolumeConfig struct { // External identifies a Volume or Network as a reference to a resource that is // not managed, and should already exist. +// External.name is deprecated and replaced by Volume.name type External struct { Name string External bool