diff --git a/components/cli/cli/command/cli.go b/components/cli/cli/command/cli.go index c919cbbbe9..b17aaf238f 100644 --- a/components/cli/cli/command/cli.go +++ b/components/cli/cli/command/cli.go @@ -162,7 +162,12 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions) error { if err != nil { return err } - hasExperimental, err := isEnabled(cli.configFile.Experimental) + var experimentalValue string + // Environment variable always overrides configuration + if experimentalValue = os.Getenv("DOCKER_CLI_EXPERIMENTAL"); experimentalValue == "" { + experimentalValue = cli.configFile.Experimental + } + hasExperimental, err := isEnabled(experimentalValue) if err != nil { return errors.Wrap(err, "Experimental field") } diff --git a/components/cli/cli/compose/convert/service.go b/components/cli/cli/compose/convert/service.go index 5407a0114a..08865968e3 100644 --- a/components/cli/cli/compose/convert/service.go +++ b/components/cli/cli/compose/convert/service.go @@ -150,6 +150,7 @@ func Service( ReadOnly: service.ReadOnly, Privileges: &privileges, Isolation: container.Isolation(service.Isolation), + Init: service.Init, }, LogDriver: logDriver, Resources: resources, diff --git a/components/cli/cli/compose/loader/full-example.yml b/components/cli/cli/compose/loader/full-example.yml index 6da091b180..2dd5799a19 100644 --- a/components/cli/cli/compose/loader/full-example.yml +++ b/components/cli/cli/compose/loader/full-example.yml @@ -278,6 +278,8 @@ services: size: 10000 working_dir: /code + x-bar: baz + x-foo: bar networks: # Entries can be null, which specifies simply that a network @@ -318,6 +320,8 @@ networks: # can be referred to within this file as "other-external-network" external: name: my-cool-network + x-bar: baz + x-foo: bar volumes: # Entries can be null, which specifies simply that a volume @@ -361,6 +365,8 @@ volumes: # can be referred to within this file as "external-volume3" name: this-is-volume3 external: true + x-bar: baz + x-foo: bar configs: config1: @@ -374,6 +380,8 @@ configs: external: true config4: name: foo + x-bar: baz + x-foo: bar secrets: secret1: @@ -387,3 +395,10 @@ secrets: external: true secret4: name: bar + x-bar: baz + x-foo: bar +x-bar: baz +x-foo: bar +x-nested: + bar: baz + foo: bar diff --git a/components/cli/cli/compose/loader/full-struct_test.go b/components/cli/cli/compose/loader/full-struct_test.go index 3e8cfdb31f..4ee7798f42 100644 --- a/components/cli/cli/compose/loader/full-struct_test.go +++ b/components/cli/cli/compose/loader/full-struct_test.go @@ -14,8 +14,16 @@ func fullExampleConfig(workingDir, homeDir string) *types.Config { Services: services(workingDir, homeDir), Networks: networks(), Volumes: volumes(), - Configs: configs(), - Secrets: secrets(), + Configs: configs(workingDir), + Secrets: secrets(workingDir), + Extras: map[string]interface{}{ + "x-foo": "bar", + "x-bar": "baz", + "x-nested": map[string]interface{}{ + "foo": "bar", + "bar": "baz", + }, + }, } } @@ -136,6 +144,10 @@ func services(workingDir, homeDir string) []types.ServiceConfig { "somehost:162.242.195.82", "otherhost:50.31.209.229", }, + Extras: map[string]interface{}{ + "x-bar": "baz", + "x-foo": "bar", + }, HealthCheck: &types.HealthCheckConfig{ Test: types.HealthCheckTest([]string{"CMD-SHELL", "echo \"hello world\""}), Interval: durationPtr(10 * time.Second), @@ -392,6 +404,10 @@ func networks() map[string]types.NetworkConfig { "other-external-network": { Name: "my-cool-network", External: types.External{External: true}, + Extras: map[string]interface{}{ + "x-bar": "baz", + "x-foo": "bar", + }, }, } } @@ -428,14 +444,18 @@ func volumes() map[string]types.VolumeConfig { "external-volume3": { Name: "this-is-volume3", External: types.External{External: true}, + Extras: map[string]interface{}{ + "x-bar": "baz", + "x-foo": "bar", + }, }, } } -func configs() map[string]types.ConfigObjConfig { +func configs(workingDir string) map[string]types.ConfigObjConfig { return map[string]types.ConfigObjConfig{ "config1": { - File: "./config_data", + File: filepath.Join(workingDir, "config_data"), Labels: map[string]string{ "foo": "bar", }, @@ -445,18 +465,24 @@ func configs() map[string]types.ConfigObjConfig { External: types.External{External: true}, }, "config3": { + Name: "config3", External: types.External{External: true}, }, "config4": { Name: "foo", + File: workingDir, + Extras: map[string]interface{}{ + "x-bar": "baz", + "x-foo": "bar", + }, }, } } -func secrets() map[string]types.SecretConfig { +func secrets(workingDir string) map[string]types.SecretConfig { return map[string]types.SecretConfig{ "secret1": { - File: "./secret_data", + File: filepath.Join(workingDir, "secret_data"), Labels: map[string]string{ "foo": "bar", }, @@ -466,10 +492,16 @@ func secrets() map[string]types.SecretConfig { External: types.External{External: true}, }, "secret3": { + Name: "secret3", External: types.External{External: true}, }, "secret4": { Name: "bar", + File: workingDir, + Extras: map[string]interface{}{ + "x-bar": "baz", + "x-foo": "bar", + }, }, } } @@ -760,6 +792,8 @@ services: tmpfs: size: 10000 working_dir: /code + x-bar: baz + x-foo: bar networks: external-network: name: external-network @@ -767,6 +801,8 @@ networks: other-external-network: name: my-cool-network external: true + x-bar: baz + x-foo: bar other-network: driver: overlay driver_opts: @@ -793,6 +829,8 @@ volumes: external-volume3: name: this-is-volume3 external: true + x-bar: baz + x-foo: bar other-external-volume: name: my-cool-volume external: true @@ -806,27 +844,46 @@ volumes: some-volume: {} secrets: secret1: - file: ./secret_data + file: %s/secret_data labels: foo: bar secret2: name: my_secret external: true secret3: + name: secret3 external: true secret4: name: bar + file: %s + x-bar: baz + x-foo: bar configs: config1: - file: ./config_data + file: %s/config_data labels: foo: bar config2: name: my_config external: true config3: + name: config3 external: true config4: name: foo -`, filepath.Join(workingDir, "static"), filepath.Join(workingDir, "opt")) + file: %s + x-bar: baz + x-foo: bar +x-bar: baz +x-foo: bar +x-nested: + bar: baz + foo: bar +`, + filepath.Join(workingDir, "static"), + filepath.Join(workingDir, "opt"), + workingDir, + workingDir, + workingDir, + workingDir) } diff --git a/components/cli/cli/compose/loader/loader.go b/components/cli/cli/compose/loader/loader.go index 505e4c78fe..3c138d59ec 100644 --- a/components/cli/cli/compose/loader/loader.go +++ b/components/cli/cli/compose/loader/loader.go @@ -147,6 +147,7 @@ func loadSections(config map[string]interface{}, configDetails types.ConfigDetai return nil, err } } + cfg.Extras = getExtras(config) return &cfg, nil } @@ -364,9 +365,32 @@ func LoadService(name string, serviceDict map[string]interface{}, workingDir str if err := resolveVolumePaths(serviceConfig.Volumes, workingDir, lookupEnv); err != nil { return nil, err } + + serviceConfig.Extras = getExtras(serviceDict) + return serviceConfig, nil } +func loadExtras(name string, source map[string]interface{}) map[string]interface{} { + if dict, ok := source[name].(map[string]interface{}); ok { + return getExtras(dict) + } + return nil +} + +func getExtras(dict map[string]interface{}) map[string]interface{} { + extras := map[string]interface{}{} + for key, value := range dict { + if strings.HasPrefix(key, "x-") { + extras[key] = value + } + } + if len(extras) == 0 { + return nil + } + return extras +} + func updateEnvironment(environment map[string]*string, vars map[string]*string, lookupEnv template.Mapping) { for k, v := range vars { interpolatedV, ok := lookupEnv(k) @@ -479,6 +503,7 @@ func LoadNetworks(source map[string]interface{}, version string) (map[string]typ case network.Name == "": network.Name = name } + network.Extras = loadExtras(name, source) networks[name] = network } return networks, nil @@ -521,6 +546,7 @@ func LoadVolumes(source map[string]interface{}, version string) (map[string]type case volume.Name == "": volume.Name = name } + volume.Extras = loadExtras(name, source) volumes[name] = volume } return volumes, nil @@ -538,7 +564,9 @@ func LoadSecrets(source map[string]interface{}, details types.ConfigDetails) (ma if err != nil { return nil, err } - secrets[name] = types.SecretConfig(obj) + secretConfig := types.SecretConfig(obj) + secretConfig.Extras = loadExtras(name, source) + secrets[name] = secretConfig } return secrets, nil } @@ -555,7 +583,9 @@ func LoadConfigObjs(source map[string]interface{}, details types.ConfigDetails) if err != nil { return nil, err } - configs[name] = types.ConfigObjConfig(obj) + configConfig := types.ConfigObjConfig(obj) + configConfig.Extras = loadExtras(name, source) + configs[name] = configConfig } return configs, nil } diff --git a/components/cli/cli/compose/loader/loader_test.go b/components/cli/cli/compose/loader/loader_test.go index a00a972eb8..7b72ad768c 100644 --- a/components/cli/cli/compose/loader/loader_test.go +++ b/components/cli/cli/compose/loader/loader_test.go @@ -182,6 +182,23 @@ func TestLoad(t *testing.T) { assert.Check(t, is.DeepEqual(sampleConfig.Volumes, actual.Volumes)) } +func TestLoadExtras(t *testing.T) { + actual, err := loadYAML(` +version: "3.7" +services: + foo: + image: busybox + x-foo: bar`) + assert.NilError(t, err) + assert.Check(t, is.Len(actual.Services, 1)) + service := actual.Services[0] + assert.Check(t, is.Equal("busybox", service.Image)) + extras := map[string]interface{}{ + "x-foo": "bar", + } + assert.Check(t, is.DeepEqual(extras, service.Extras)) +} + func TestLoadV31(t *testing.T) { actual, err := loadYAML(` version: "3.1" @@ -825,6 +842,9 @@ func TestFullExample(t *testing.T) { assert.Check(t, is.DeepEqual(expectedConfig.Services, config.Services)) assert.Check(t, is.DeepEqual(expectedConfig.Networks, config.Networks)) assert.Check(t, is.DeepEqual(expectedConfig.Volumes, config.Volumes)) + assert.Check(t, is.DeepEqual(expectedConfig.Secrets, config.Secrets)) + assert.Check(t, is.DeepEqual(expectedConfig.Configs, config.Configs)) + assert.Check(t, is.DeepEqual(expectedConfig.Extras, config.Extras)) } func TestLoadTmpfsVolume(t *testing.T) { @@ -1395,3 +1415,51 @@ networks: } assert.DeepEqual(t, config, expected, cmpopts.EquateEmpty()) } + +func TestLoadInit(t *testing.T) { + booleanTrue := true + booleanFalse := false + + var testcases = []struct { + doc string + yaml string + init *bool + }{ + { + doc: "no init defined", + yaml: ` +version: '3.7' +services: + foo: + image: alpine`, + }, + { + doc: "has true init", + yaml: ` +version: '3.7' +services: + foo: + image: alpine + init: true`, + init: &booleanTrue, + }, + { + doc: "has false init", + yaml: ` +version: '3.7' +services: + foo: + image: alpine + init: false`, + init: &booleanFalse, + }, + } + for _, testcase := range testcases { + t.Run(testcase.doc, func(t *testing.T) { + config, err := loadYAML(testcase.yaml) + assert.NilError(t, err) + assert.Check(t, is.Len(config.Services, 1)) + assert.Check(t, is.DeepEqual(config.Services[0].Init, testcase.init)) + }) + } +} diff --git a/components/cli/cli/compose/schema/bindata.go b/components/cli/cli/compose/schema/bindata.go index e56e243201..9ec4891fa3 100644 --- a/components/cli/cli/compose/schema/bindata.go +++ b/components/cli/cli/compose/schema/bindata.go @@ -467,7 +467,7 @@ DoTuq9lAU9Q4O1xV/59X/w8AAP//zRo7vm9CAAA= "/data/config_schema_v3.7.json": { local: "data/config_schema_v3.7.json", - size: 17740, + size: 17777, modtime: 1518458244, compressed: ` H4sIAAAAAAAC/+xcS4/bOBK++1cYmrlNPwLsYBeb2x73tHvehiPQVNnmNEVyipTTTuD/vtDTEkWKlK1O @@ -484,27 +484,27 @@ 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 `, }, diff --git a/components/cli/cli/compose/schema/data/config_schema_v3.7.json b/components/cli/cli/compose/schema/data/config_schema_v3.7.json index 05566cf819..f1597df001 100644 --- a/components/cli/cli/compose/schema/data/config_schema_v3.7.json +++ b/components/cli/cli/compose/schema/data/config_schema_v3.7.json @@ -154,6 +154,7 @@ "healthcheck": {"$ref": "#/definitions/healthcheck"}, "hostname": {"type": "string"}, "image": {"type": "string"}, + "init": {"type": "boolean"}, "ipc": {"type": "string"}, "isolation": {"type": "string"}, "labels": {"$ref": "#/definitions/list_or_dict"}, diff --git a/components/cli/cli/compose/types/types.go b/components/cli/cli/compose/types/types.go index 397bc79c1f..6c1a0dd758 100644 --- a/components/cli/cli/compose/types/types.go +++ b/components/cli/cli/compose/types/types.go @@ -77,6 +77,7 @@ type Config struct { Volumes map[string]VolumeConfig Secrets map[string]SecretConfig Configs map[string]ConfigObjConfig + Extras map[string]interface{} `yaml:",inline"` } // Services is a list of ServiceConfig @@ -118,7 +119,9 @@ type ServiceConfig struct { Hostname string `yaml:",omitempty"` HealthCheck *HealthCheckConfig `yaml:",omitempty"` Image string `yaml:",omitempty"` + Init *bool `yaml:",omitempty"` Ipc string `yaml:",omitempty"` + Isolation string `mapstructure:"isolation" yaml:"isolation,omitempty"` Labels Labels `yaml:",omitempty"` Links []string `yaml:",omitempty"` Logging *LoggingConfig `yaml:",omitempty"` @@ -141,7 +144,8 @@ type ServiceConfig struct { User string `yaml:",omitempty"` Volumes []ServiceVolumeConfig `yaml:",omitempty"` WorkingDir string `mapstructure:"working_dir" yaml:"working_dir,omitempty"` - Isolation string `mapstructure:"isolation" yaml:"isolation,omitempty"` + + Extras map[string]interface{} `yaml:",inline"` } // BuildConfig is a type for build @@ -354,14 +358,15 @@ func (u *UlimitsConfig) MarshalYAML() (interface{}, error) { // NetworkConfig for a network type NetworkConfig struct { - Name string `yaml:",omitempty"` - Driver string `yaml:",omitempty"` - DriverOpts map[string]string `mapstructure:"driver_opts" yaml:"driver_opts,omitempty"` - Ipam IPAMConfig `yaml:",omitempty"` - External External `yaml:",omitempty"` - Internal bool `yaml:",omitempty"` - Attachable bool `yaml:",omitempty"` - Labels Labels `yaml:",omitempty"` + Name string `yaml:",omitempty"` + Driver string `yaml:",omitempty"` + DriverOpts map[string]string `mapstructure:"driver_opts" yaml:"driver_opts,omitempty"` + Ipam IPAMConfig `yaml:",omitempty"` + External External `yaml:",omitempty"` + Internal bool `yaml:",omitempty"` + Attachable bool `yaml:",omitempty"` + Labels Labels `yaml:",omitempty"` + Extras map[string]interface{} `yaml:",inline"` } // IPAMConfig for a network @@ -377,11 +382,12 @@ type IPAMPool struct { // VolumeConfig for a volume type VolumeConfig struct { - Name string `yaml:",omitempty"` - Driver string `yaml:",omitempty"` - DriverOpts map[string]string `mapstructure:"driver_opts" yaml:"driver_opts,omitempty"` - External External `yaml:",omitempty"` - Labels Labels `yaml:",omitempty"` + Name string `yaml:",omitempty"` + Driver string `yaml:",omitempty"` + DriverOpts map[string]string `mapstructure:"driver_opts" yaml:"driver_opts,omitempty"` + External External `yaml:",omitempty"` + Labels Labels `yaml:",omitempty"` + Extras map[string]interface{} `yaml:",inline"` } // External identifies a Volume or Network as a reference to a resource that is @@ -408,10 +414,11 @@ type CredentialSpecConfig struct { // FileObjectConfig is a config type for a file used by a service type FileObjectConfig struct { - Name string `yaml:",omitempty"` - File string `yaml:",omitempty"` - External External `yaml:",omitempty"` - Labels Labels `yaml:",omitempty"` + Name string `yaml:",omitempty"` + File string `yaml:",omitempty"` + External External `yaml:",omitempty"` + Labels Labels `yaml:",omitempty"` + Extras map[string]interface{} `yaml:",inline"` } // SecretConfig for a secret diff --git a/components/cli/docs/reference/commandline/cli.md b/components/cli/docs/reference/commandline/cli.md index 81cd64f28b..eaf60cc60e 100644 --- a/components/cli/docs/reference/commandline/cli.md +++ b/components/cli/docs/reference/commandline/cli.md @@ -61,6 +61,7 @@ by the `docker` command line: * `DOCKER_API_VERSION` The API version to use (e.g. `1.19`) * `DOCKER_CONFIG` The location of your client configuration files. * `DOCKER_CERT_PATH` The location of your authentication keys. +* `DOCKER_CLI_EXPERIMENTAL` Enable experimental features for the cli (e.g. `enabled` or `disabled`) * `DOCKER_DRIVER` The graph driver to use. * `DOCKER_HOST` Daemon socket to connect to. * `DOCKER_NOWARN_KERNEL_VERSION` Prevent warnings that your Linux kernel is