From e574286ba2b5ff602912410fddb7324c50c208ec Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Mon, 15 May 2017 11:19:32 -0400 Subject: [PATCH] Add support for configs to compose format Signed-off-by: Brian Goff --- cli/command/stack/deploy_composefile.go | 35 ++++++ cli/compose/convert/compose.go | 24 ++++ cli/compose/convert/compose_test.go | 31 +++++ cli/compose/convert/service.go | 59 ++++++++- cli/compose/loader/loader.go | 112 ++++++++++-------- cli/compose/loader/loader_test.go | 5 + cli/compose/schema/bindata.go | 2 +- .../schema/data/config_schema_v3.3.json | 45 +++++++ cli/compose/types/types.go | 36 ++++-- 9 files changed, 287 insertions(+), 62 deletions(-) diff --git a/cli/command/stack/deploy_composefile.go b/cli/command/stack/deploy_composefile.go index f574332646..abe0b0ea2c 100644 --- a/cli/command/stack/deploy_composefile.go +++ b/cli/command/stack/deploy_composefile.go @@ -79,6 +79,14 @@ func deployCompose(ctx context.Context, dockerCli command.Cli, opts deployOption return err } + configs, err := convert.Configs(namespace, config.Configs) + if err != nil { + return err + } + if err := createConfigs(ctx, dockerCli, namespace, configs); err != nil { + return err + } + services, err := convert.Services(namespace, config, dockerCli.Client()) if err != nil { return err @@ -208,6 +216,33 @@ func createSecrets( return nil } +func createConfigs( + ctx context.Context, + dockerCli command.Cli, + namespace convert.Namespace, + configs []swarm.ConfigSpec, +) error { + client := dockerCli.Client() + + for _, configSpec := range configs { + config, _, err := client.ConfigInspectWithRaw(ctx, configSpec.Name) + if err == nil { + // config already exists, then we update that + if err := client.ConfigUpdate(ctx, config.ID, config.Meta.Version, configSpec); err != nil { + return err + } + } else if apiclient.IsErrConfigNotFound(err) { + // config does not exist, then we create a new one. + if _, err := client.ConfigCreate(ctx, configSpec); err != nil { + return err + } + } else { + return err + } + } + return nil +} + func createNetworks( ctx context.Context, dockerCli command.Cli, diff --git a/cli/compose/convert/compose.go b/cli/compose/convert/compose.go index e0c6bbf81d..02b1dccb6a 100644 --- a/cli/compose/convert/compose.go +++ b/cli/compose/convert/compose.go @@ -116,3 +116,27 @@ func Secrets(namespace Namespace, secrets map[string]composetypes.SecretConfig) } return result, nil } + +// Configs converts config objects from the Compose type to the engine API type +func Configs(namespace Namespace, configs map[string]composetypes.ConfigObjConfig) ([]swarm.ConfigSpec, error) { + result := []swarm.ConfigSpec{} + for name, config := range configs { + if config.External.External { + continue + } + + data, err := ioutil.ReadFile(config.File) + if err != nil { + return nil, err + } + + result = append(result, swarm.ConfigSpec{ + Annotations: swarm.Annotations{ + Name: namespace.Scope(name), + Labels: AddStackLabel(namespace, config.Labels), + }, + Data: data, + }) + } + return result, nil +} diff --git a/cli/compose/convert/compose_test.go b/cli/compose/convert/compose_test.go index 36e48110c8..57849dd9bc 100644 --- a/cli/compose/convert/compose_test.go +++ b/cli/compose/convert/compose_test.go @@ -133,3 +133,34 @@ func TestSecrets(t *testing.T) { }, secret.Labels) assert.Equal(t, []byte(secretText), secret.Data) } + +func TestConfigs(t *testing.T) { + namespace := Namespace{name: "foo"} + + configText := "this is the first config" + configFile := tempfile.NewTempFile(t, "convert-configs", configText) + defer configFile.Remove() + + source := map[string]composetypes.ConfigObjConfig{ + "one": { + File: configFile.Name(), + Labels: map[string]string{"monster": "mash"}, + }, + "ext": { + External: composetypes.External{ + External: true, + }, + }, + } + + specs, err := Configs(namespace, source) + assert.NoError(t, err) + require.Len(t, specs, 1) + config := specs[0] + assert.Equal(t, "foo_one", config.Name) + assert.Equal(t, map[string]string{ + "monster": "mash", + LabelNamespace: "foo", + }, config.Labels) + assert.Equal(t, []byte(configText), config.Data) +} diff --git a/cli/compose/convert/service.go b/cli/compose/convert/service.go index a31f4dda1c..79c6555276 100644 --- a/cli/compose/convert/service.go +++ b/cli/compose/convert/service.go @@ -37,7 +37,12 @@ func Services( if err != nil { return nil, errors.Wrapf(err, "service %s", service.Name) } - serviceSpec, err := convertService(client.ClientVersion(), namespace, service, networks, volumes, secrets) + configs, err := convertServiceConfigObjs(client, namespace, service.Configs, config.Configs) + if err != nil { + return nil, errors.Wrapf(err, "service %s", service.Name) + } + + serviceSpec, err := convertService(client.ClientVersion(), namespace, service, networks, volumes, secrets, configs) if err != nil { return nil, errors.Wrapf(err, "service %s", service.Name) } @@ -54,6 +59,7 @@ func convertService( networkConfigs map[string]composetypes.NetworkConfig, volumes map[string]composetypes.VolumeConfig, secrets []*swarm.SecretReference, + configs []*swarm.ConfigReference, ) (swarm.ServiceSpec, error) { name := namespace.Scope(service.Name) @@ -277,6 +283,57 @@ func convertServiceSecrets( return servicecli.ParseSecrets(client, refs) } +// TODO: fix configs API so that ConfigsAPIClient is not required here +func convertServiceConfigObjs( + client client.ConfigAPIClient, + namespace Namespace, + configs []composetypes.ServiceConfigObjConfig, + configSpecs map[string]composetypes.ConfigObjConfig, +) ([]*swarm.ConfigReference, error) { + refs := []*swarm.ConfigReference{} + for _, config := range configs { + target := config.Target + if target == "" { + target = config.Source + } + + configSpec, exists := configSpecs[config.Source] + if !exists { + return nil, errors.Errorf("undefined config %q", config.Source) + } + + source := namespace.Scope(config.Source) + if configSpec.External.External { + source = configSpec.External.Name + } + + uid := config.UID + gid := config.GID + if uid == "" { + uid = "0" + } + if gid == "" { + gid = "0" + } + mode := config.Mode + if mode == nil { + mode = uint32Ptr(0444) + } + + refs = append(refs, &swarm.ConfigReference{ + File: &swarm.ConfigReferenceFileTarget{ + Name: target, + UID: uid, + GID: gid, + Mode: os.FileMode(*mode), + }, + ConfigName: source, + }) + } + + return servicecli.ParseConfigs(client, refs) +} + func uint32Ptr(value uint32) *uint32 { return &value } diff --git a/cli/compose/loader/loader.go b/cli/compose/loader/loader.go index ba292054d9..0ed8ce87d7 100644 --- a/cli/compose/loader/loader.go +++ b/cli/compose/loader/loader.go @@ -66,69 +66,58 @@ func Load(configDetails types.ConfigDetails) (*types.Config, error) { } cfg := types.Config{} - lookupEnv := func(k string) (string, bool) { - v, ok := configDetails.Environment[k] - return v, ok - } - if services, ok := configDict["services"]; ok { - servicesConfig, err := interpolation.Interpolate(services.(map[string]interface{}), "service", lookupEnv) - if err != nil { - return nil, err - } - servicesList, err := LoadServices(servicesConfig, configDetails.WorkingDir, lookupEnv) - if err != nil { - return nil, err - } - - cfg.Services = servicesList + config, err := interpolateConfig(configDict, configDetails.LookupEnv) + if err != nil { + return nil, err } - if networks, ok := configDict["networks"]; ok { - networksConfig, err := interpolation.Interpolate(networks.(map[string]interface{}), "network", lookupEnv) - if err != nil { - return nil, err - } - - networksMapping, err := LoadNetworks(networksConfig) - if err != nil { - return nil, err - } - - cfg.Networks = networksMapping + cfg.Services, err = LoadServices(config["services"], configDetails.WorkingDir, configDetails.LookupEnv) + if err != nil { + return nil, err } - if volumes, ok := configDict["volumes"]; ok { - volumesConfig, err := interpolation.Interpolate(volumes.(map[string]interface{}), "volume", lookupEnv) - if err != nil { - return nil, err - } - - volumesMapping, err := LoadVolumes(volumesConfig) - if err != nil { - return nil, err - } - - cfg.Volumes = volumesMapping + cfg.Networks, err = LoadNetworks(config["networks"]) + if err != nil { + return nil, err } - if secrets, ok := configDict["secrets"]; ok { - secretsConfig, err := interpolation.Interpolate(secrets.(map[string]interface{}), "secret", lookupEnv) - if err != nil { - return nil, err - } + cfg.Volumes, err = LoadVolumes(config["volumes"]) + if err != nil { + return nil, err + } - secretsMapping, err := LoadSecrets(secretsConfig, configDetails.WorkingDir) - if err != nil { - return nil, err - } + cfg.Secrets, err = LoadSecrets(config["secrets"], configDetails.WorkingDir) + if err != nil { + return nil, err + } - cfg.Secrets = secretsMapping + cfg.Configs, err = LoadConfigObjs(config["configs"], configDetails.WorkingDir) + if err != nil { + return nil, err } return &cfg, nil } +func interpolateConfig(configDict map[string]interface{}, lookupEnv template.Mapping) (map[string]map[string]interface{}, error) { + config := make(map[string]map[string]interface{}) + + for _, key := range []string{"services", "networks", "volumes", "secrets", "configs"} { + section, ok := configDict[key] + if !ok { + config[key] = make(map[string]interface{}) + continue + } + var err error + config[key], err = interpolation.Interpolate(section.(map[string]interface{}), key, lookupEnv) + if err != nil { + return nil, err + } + } + return config, nil +} + // GetUnsupportedProperties returns the list of any unsupported properties that are // used in the Compose files. func GetUnsupportedProperties(configDetails types.ConfigDetails) []string { @@ -241,7 +230,9 @@ func transformHook( case reflect.TypeOf([]types.ServicePortConfig{}): return transformServicePort(data) case reflect.TypeOf(types.ServiceSecretConfig{}): - return transformServiceSecret(data) + return transformStringSourceMap(data) + case reflect.TypeOf(types.ServiceConfigObjConfig{}): + return transformStringSourceMap(data) case reflect.TypeOf(types.StringOrNumberList{}): return transformStringOrNumberList(data) case reflect.TypeOf(map[string]*types.ServiceNetworkConfig{}): @@ -482,6 +473,25 @@ func LoadSecrets(source map[string]interface{}, workingDir string) (map[string]t return secrets, nil } +// LoadConfigObjs produces a ConfigObjConfig map from a compose file Dict +// the source Dict is not validated if directly used. Use Load() to enable validation +func LoadConfigObjs(source map[string]interface{}, workingDir string) (map[string]types.ConfigObjConfig, error) { + configs := make(map[string]types.ConfigObjConfig) + if err := transform(source, &configs); err != nil { + return configs, err + } + for name, config := range configs { + if config.External.External && config.External.Name == "" { + config.External.Name = name + configs[name] = config + } + if config.File != "" { + config.File = absPath(workingDir, config.File) + } + } + return configs, nil +} + func absPath(workingDir string, filepath string) string { if path.IsAbs(filepath) { return filepath @@ -544,7 +554,7 @@ func transformServicePort(data interface{}) (interface{}, error) { } } -func transformServiceSecret(data interface{}) (interface{}, error) { +func transformStringSourceMap(data interface{}) (interface{}, error) { switch value := data.(type) { case string: return map[string]interface{}{"source": value}, nil diff --git a/cli/compose/loader/loader_test.go b/cli/compose/loader/loader_test.go index 530e348423..4e604431d3 100644 --- a/cli/compose/loader/loader_test.go +++ b/cli/compose/loader/loader_test.go @@ -206,12 +206,17 @@ services: image: busybox credential_spec: File: "/foo" + configs: [super] +configs: + super: + external: true `) if !assert.NoError(t, err) { return } assert.Equal(t, len(actual.Services), 1) assert.Equal(t, actual.Services[0].CredentialSpec.File, "/foo") + assert.Equal(t, len(actual.Configs), 1) } func TestParseAndLoad(t *testing.T) { diff --git a/cli/compose/schema/bindata.go b/cli/compose/schema/bindata.go index dba99812e6..06fcae3591 100644 --- a/cli/compose/schema/bindata.go +++ b/cli/compose/schema/bindata.go @@ -131,7 +131,7 @@ func dataConfig_schema_v32Json() (*asset, error) { return a, nil } -var _dataConfig_schema_v33Json = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x5b\x4d\x73\xdc\x28\x13\xbe\xcf\xaf\x50\x29\xb9\xc5\x1f\xa9\x4a\xea\xad\x7a\x73\xdb\xe3\x9e\x76\xcf\xeb\x9a\xa8\x18\xd4\xa3\x21\x96\x80\x00\x1a\x7b\x92\xf2\x7f\xdf\xd2\xe7\x00\x02\x81\x3c\x72\xec\xdd\xda\x93\x3d\xd2\xd3\x0d\xdd\x74\x37\xdd\x34\xfa\xb9\x49\x92\xf4\xbd\xc4\x07\xa8\x50\xfa\x25\x49\x0f\x4a\xf1\x2f\xb7\xb7\xdf\x24\xa3\xd7\xdd\xd3\x1b\x26\x8a\xdb\x5c\xa0\xbd\xba\xfe\xf8\xf9\xb6\x7b\xf6\x2e\xbd\x6a\xe8\x48\xde\x90\x60\x46\xf7\xa4\xc8\xba\x37\xd9\xf1\xd3\xcd\xa7\x9b\x86\xbc\x83\xa8\x13\x87\x06\xc4\x76\xdf\x00\xab\xee\x99\x80\xef\x35\x11\xd0\x10\xdf\xa5\x47\x10\x92\x30\x9a\x6e\xaf\x36\xcd\x3b\x2e\x18\x07\xa1\x08\xc8\xf4\x4b\xd2\x4c\x2e\x49\x46\xc8\xf0\x40\x63\x2b\x95\x20\xb4\x48\xdb\xc7\x4f\x2d\x87\x24\x49\x25\x88\x23\xc1\x1a\x87\x71\xaa\xef\x6e\xcf\xfc\x6f\x47\xd8\x95\xcd\x55\x9b\x6c\xfb\x9c\x23\xa5\x40\xd0\x3f\xa7\x73\x6b\x5f\x7f\xbd\x43\xd7\x3f\x7e\xbb\xfe\xeb\xe3\xf5\xff\x6f\xb2\xeb\xed\x87\xf7\xc6\xeb\x46\xbf\x02\xf6\xdd\xf0\x39\xec\x09\x25\x8a\x30\x3a\x8e\x9f\x8e\xc8\xa7\xfe\xbf\xa7\x71\x60\x94\xe7\x2d\x18\x95\xc6\xd8\x7b\x54\x4a\x30\x65\xa6\xa0\x1e\x98\xb8\x0f\xc9\x3c\xc2\x5e\x49\xe6\x7e\x7c\x87\xcc\xa6\x38\x47\x56\xd6\x55\x70\x05\x07\xd4\x2b\x09\xd3\x0d\xbf\xce\xfa\x49\xc0\x02\x54\xd8\x64\x3b\xd4\xab\x59\x6c\x33\xfc\x65\x02\x6f\x06\xa1\x67\xb1\x1d\x42\x1b\xbb\x9d\xa0\xe1\xde\x2e\x55\xb9\xdc\xcb\xaf\xab\x51\x59\x1e\x2d\xe5\xc0\x4b\x76\x6a\x9e\x79\xf4\xd1\x01\x2a\xa0\x2a\x1d\x55\x90\x24\xe9\xae\x26\x65\x6e\x6b\x94\x51\xf8\xa3\x61\x71\xa7\x3d\x4c\x92\x9f\x76\x24\xd3\xf8\xb4\xef\x8d\x5f\xfe\x05\x1f\xdf\x7b\x64\x19\xdf\x63\x46\x15\x3c\xaa\x56\xa8\xf9\xa1\x3b\x15\x30\x7c\x0f\x62\x4f\x4a\x88\xa5\x40\xa2\x90\x33\x2a\x2b\x89\x54\x19\x13\x59\x4e\xb0\x72\xd2\x97\x68\x07\xe5\x45\x1c\x30\xc2\x07\xc8\xf6\x82\x55\x41\x2e\xfb\xac\x93\x44\xa6\x4f\x16\x9f\x09\xe3\xb0\x69\x8f\xa4\xda\xaf\xed\xc6\xc1\x30\xc5\x88\x67\x28\xcf\x0d\x95\x22\x21\xd0\x29\xbd\x4a\x52\xa2\xa0\x92\x6e\x6d\x27\x69\x4d\xc9\xf7\x1a\x7e\xef\x21\x4a\xd4\x60\xf3\xcd\x05\xe3\xeb\x33\x2e\x04\xab\x79\xc6\x91\x68\x6c\x7d\xde\x12\x52\xcc\xaa\x0a\xd1\xb5\x1c\x60\x89\x1c\x11\x9a\x67\x54\x21\x42\x41\x64\x14\x55\x21\x9b\x4e\xb1\x80\x1c\xa8\x22\xa8\xcc\x24\x07\x6c\xc0\x07\x07\x9c\x71\xb9\x34\xca\x6f\x52\x01\x05\x91\x4a\x9c\x9c\xc8\xb3\x14\xfa\xc4\x72\xe0\x40\x73\x99\x75\x49\x51\xac\x89\x1b\x0c\xc6\x0c\x69\x55\x43\xc9\xe9\x9c\xeb\x76\x6c\x1a\xe7\x6d\xe6\x96\x5a\x84\x99\x04\x24\xf0\xe1\x99\xf4\xac\x42\x84\xc6\x2c\x2a\x50\x25\x4e\x9c\x91\xce\x90\xdf\x9c\x85\x02\x3d\x66\xa3\xdd\x2c\x56\x03\xd0\x23\x11\x8c\x56\x83\x9b\xc6\x85\x50\x8d\xfe\x91\x33\x09\xb6\x62\x2c\x01\xf5\x57\xa3\xa8\x86\x4e\x06\x8a\xbb\x41\xf0\xab\x24\xa5\x75\xb5\x03\xd1\xe4\xf9\x06\x72\xcf\x44\x85\x9a\xc9\x0e\x63\x6b\xaf\x4d\x3f\x99\x5a\x9e\xae\x40\x5d\x86\x26\xf5\x41\x65\x56\x12\x7a\xbf\xbe\x89\xc3\xa3\x12\x28\x3b\x30\xa9\x9e\xb3\x4b\xa5\x07\x40\xa5\x3a\xe0\x03\xe0\xfb\x19\x72\x1d\x65\x50\x33\xa9\x62\x8c\x9c\x54\xa8\x08\x83\x38\x0e\x41\x9e\xbd\x1b\xa7\xab\x2a\x5f\x63\xcb\x8a\xa2\x81\xfa\x2c\x6e\x92\xdd\xf5\xaf\x43\x79\x51\x2e\xc8\x11\x44\x6c\x92\xc3\xf8\x39\x29\xb5\x5f\x86\x93\xb4\x24\x9c\xa1\x1b\xd0\xaf\x37\x5d\x82\x3e\xe3\x55\xed\x7f\x65\x99\x6e\xed\x3c\x26\xb1\x12\x12\xd7\x13\x4b\xc2\xb8\x4c\xc7\x58\x95\x0a\xe1\x26\xa1\x11\x20\x3d\xeb\x7a\x86\xf6\x15\x60\x56\xb1\xdc\x67\xa0\x13\xf0\x64\x63\xf5\x45\xea\xc5\x1b\x61\xf2\xac\x1c\x3b\x6a\xe9\x82\x45\x56\x40\x1a\xdf\xf4\x62\xa7\x79\x9e\x6e\xd8\xc4\x5a\x1c\x2a\x09\x92\x10\x76\x76\xaf\x22\x0d\x6e\x84\x1f\x3f\x47\xda\x84\x8b\xf6\x7f\xb3\xb4\x1e\x52\x2f\xcf\xf8\xe4\x3d\xc0\xea\x3c\x95\xd6\xdd\x5c\x13\xd9\x06\xbc\xed\x85\x6b\x0b\x4e\x72\x7f\xac\x68\x23\x84\xee\x60\x9c\x09\x35\xf1\xae\xe5\xdb\xbd\xcf\x82\x75\x75\x0d\x71\xea\xbc\xe1\x77\x83\x4f\xb4\x31\x59\xee\x28\xa2\xa9\xff\x05\xfd\x23\xec\x19\xe9\x4c\x94\x72\xa0\x15\x12\x05\x98\xf5\x11\xa1\x0a\x0a\x10\x1e\x02\x5e\xef\x4a\x22\x0f\x90\x2f\xa1\x11\x4c\x31\xcc\xca\x38\xc7\x70\x96\xe8\xf1\xce\x60\x32\xdc\x5e\x9c\x9b\x71\x41\x8e\xa4\x84\xc2\x92\x78\xc7\x58\x09\x88\x1a\x1b\x85\x00\x94\x67\x8c\x96\xa7\x08\xa4\x54\x48\x04\xeb\x52\x09\xb8\x16\x44\x9d\x32\xc6\xd5\xea\x59\xa1\x3c\x54\x99\x24\x3f\xc0\xf4\xbd\xb3\xd5\xf7\x8c\xb6\xd6\x84\xac\x33\xbf\xe4\xa5\xdc\xcf\x67\xb6\x2f\xe4\x36\x92\xd5\x02\x5f\xe6\x38\xb3\xf8\xda\x0c\x72\xf3\xe0\x62\x09\x78\xe2\xf0\xfd\x12\x86\x72\xa8\x59\x57\x71\x06\x6a\x79\x92\x58\x3d\x2f\xb7\x96\x2a\x27\x34\x63\x1c\x68\xd0\x37\xa4\x62\x3c\x2b\x04\xc2\x90\x71\x10\x84\x39\x55\x61\x04\xd8\xbc\x16\xa8\x19\x7f\xca\x46\x92\x82\x22\x77\xdc\xd1\xa0\xaa\xe2\xfb\x67\x1e\x02\x28\x15\x76\xf6\xba\x24\x15\xf1\x3b\x8d\xc3\x6a\x23\xf2\xb5\x2e\x57\x73\xa7\x68\x33\xe9\x59\x54\xc8\x9e\xa9\x10\xe6\x0b\x84\x88\xca\xe0\x80\xc4\x82\xad\xa3\x75\xcc\xbd\x67\x7f\x72\xd5\x0d\xce\x79\x19\xdd\xbb\x96\xdf\x55\x3f\x91\xad\x13\xbf\x28\xf5\xb2\xa7\xb1\xf5\x66\x3f\x6e\xa7\xaa\x65\xb0\x88\x6b\x31\x54\xce\x15\x20\x23\x74\xda\x86\x4a\xfe\x11\x11\xda\x58\xa3\x16\xee\x58\x9b\x88\x38\xde\x8f\x14\x19\x3b\x5f\x3a\xea\x47\x67\x04\x1a\x0d\x66\x54\x12\xa9\x80\x62\xf7\xf9\xaa\x93\x68\x47\x26\xc7\xd7\x53\xa5\xcc\xd7\x5d\x71\x55\x57\x8b\x42\x45\x17\x6f\xa3\x0b\x9d\x78\x5f\xed\x3b\x94\xbf\x44\x14\xca\x30\xe3\x9e\xa5\x89\x17\x63\xe9\x36\x6b\x1d\x5d\xcc\xe4\xa1\xbe\x90\xf1\xc0\xc4\x7d\xb3\x21\xe5\xc4\x1d\x39\x36\x16\xc9\x82\xa6\xae\x75\xd6\x37\x30\x70\x75\x2b\x75\x68\xb0\xbb\x3b\xdf\x39\xed\x41\xde\xae\x26\x91\x68\x67\xf5\x25\x5c\x1b\x6d\xb3\x33\x88\x63\x78\xbf\x17\xa0\x04\xb1\x5a\x09\x43\xd2\xa4\xef\xed\x20\xdf\xe6\x81\xbb\x22\x15\xb0\xda\x1d\x86\x36\xba\xe1\xf4\x44\xa9\xd6\xf5\x0d\x2c\xaa\x86\xb4\xd7\xf4\x4e\x6b\x20\x75\x75\x79\x70\xe1\x62\x36\x2c\xa0\x79\xdb\xda\x88\xda\xdd\x04\xf0\x92\x60\x24\x43\x19\xc4\x05\xa7\xc0\x35\xcf\x91\x82\xac\xbb\x22\xb4\x28\x67\x9b\x49\xd6\x38\x12\xa8\x2c\xa1\x24\xb2\x8a\x49\x7e\xd2\x1c\x4a\xe4\x8c\xfe\xc1\xbc\xb7\x25\xdf\x23\x52\xd6\x02\x32\x84\xbd\x61\xda\xa2\xa8\x18\x25\x8a\x39\xc3\x49\xdc\x90\x15\x7a\xcc\x86\x61\x5b\x48\xa8\x24\x31\xab\xf1\xd8\x03\x5c\xcd\x12\xba\xbd\x7b\x59\x5a\x3d\xb3\x44\xe7\x24\xdd\x63\x31\xc3\x88\x13\xd1\x05\xc8\x26\xec\x8c\xe7\xeb\x41\xfa\x60\x80\xef\x8f\x07\x32\xce\x4a\xd2\x65\x01\x6b\x48\x88\x19\xed\x94\x1c\x63\x10\x17\x5a\x60\x63\x0e\x4d\x0d\x53\x71\x15\x74\xd6\x96\xe0\x81\xd0\x9c\x3d\x2c\x18\x70\x3d\x53\xe2\x25\xc2\x60\x05\xc7\x4b\x15\x2d\x95\x40\x84\xaa\xc5\xed\x24\x5b\x2d\x5c\xc0\x1e\x04\xd0\xa9\xa1\x27\xf3\x69\x7d\xe2\x4f\xed\x43\xb2\x85\x25\xec\x11\x92\x37\xf9\xed\x2b\x9c\xea\x5d\xba\xf8\x17\x24\x48\xa3\x17\x07\x36\xd2\x11\x17\x4c\x8d\x7c\x9b\x27\xe6\x75\xb0\x35\x55\x41\xc5\xe6\xaf\x61\x5c\x70\x33\x33\x24\xe2\x00\x5b\x21\x51\x88\xea\x65\xf6\xa8\x8c\xf1\xf5\x0f\x53\xc2\xfd\xca\x6d\x38\x6c\x13\x8e\xaa\xb5\x62\x48\x74\x77\x37\x75\x66\x2a\xc9\x1b\x88\x0e\xf5\x8e\x7a\x6a\xe5\xb7\x1d\x1d\xcc\x9b\x13\xed\xc5\x0c\xcf\xaa\xde\x8d\x65\xc8\xd5\xa8\xab\x6d\xf4\x12\x7b\x6f\x45\xac\x37\xff\xb6\x22\xb2\x4f\x40\x5d\xa5\x13\x52\x0a\xe1\x43\x54\x95\xb5\x30\xb5\xbe\x20\x0e\x4d\xce\x02\x9c\x61\xa8\x47\xfd\x17\x85\xfe\x25\x36\xfb\xeb\xec\xab\xbf\x1c\x1e\xbc\x95\xdd\xa2\x9e\xbd\x8f\x47\x5c\xa9\x7c\x03\x6b\xf6\xda\x4b\x61\xb6\x58\xb4\x25\x99\x9e\xb8\xcc\x69\x72\xe9\xe5\xe9\xad\x39\x0d\x1b\xe6\xf8\x7e\xc7\xdc\x4c\xe7\x1a\xb0\x03\xc4\x73\xc2\x67\x0d\xda\x2b\x71\x5e\xf2\x15\x83\xcd\xcd\x87\x99\x94\x61\xee\xbe\xd6\x0b\xed\xb5\x2b\x34\xb7\xdd\x6b\x6a\x55\x63\x83\x76\xa7\xdf\x64\x78\xfc\x5f\xa3\x9f\x7c\xa1\xd1\xc8\x49\x4f\x93\x13\xc1\x9f\x66\x3b\xa3\xfb\xba\x62\x6b\xe8\xc7\x82\x74\xb7\x1f\xb5\xe8\xbe\xd5\x0b\x54\xef\x75\x6d\xd7\x77\x1b\x76\x33\x65\xf8\x7e\xc2\xd3\xdf\xdd\xe8\x7f\xdb\x6f\x5d\x36\x4f\x9b\xbf\x03\x00\x00\xff\xff\x39\x5d\x41\x2a\x64\x37\x00\x00") +var _dataConfig_schema_v33Json = []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\x55\x52\x53\x35\xd9\xcd\x72\x56\x33\xeb\xe9\x72\x54\x18\x1d\xdb\xa4\x11\x10\x40\xee\x76\x52\xfd\xdf\xa7\xf4\x34\x20\x10\xd8\x56\xa7\x7b\xe6\x66\xd5\x6d\xe9\x3b\xc0\x79\x9f\x03\xe8\xe7\x2a\xcb\xf2\x3f\x15\xde\x43\x85\xf2\x2f\x59\xbe\xd7\x5a\x7c\xb9\xbf\xff\xa6\x38\xbb\xed\x9e\xde\x71\xb9\xbb\x2f\x25\xda\xea\xdb\x8f\x9f\xef\xbb\x67\x7f\xe4\x37\x0d\x1d\x29\x1b\x12\xcc\xd9\x96\xec\x8a\xee\x4d\x71\xf8\x74\xf7\xe9\xae\x21\xef\x20\xfa\x28\xa0\x01\xf1\xcd\x37\xc0\xba\x7b\x26\xe1\x7b\x4d\x24\x34\xc4\x0f\xf9\x01\xa4\x22\x9c\xe5\xeb\x9b\x55\xf3\x4e\x48\x2e\x40\x6a\x02\x2a\xff\x92\x35\x8b\xcb\xb2\x11\x32\x3c\x30\x86\x55\x5a\x12\xb6\xcb\xdb\xc7\x2f\xed\x08\x59\x96\x2b\x90\x07\x82\x8d\x11\xc6\xa5\xfe\x71\x7f\x1a\xff\x7e\x84\xdd\xb8\xa3\x1a\x8b\x6d\x9f\x0b\xa4\x35\x48\xf6\xef\xe9\xda\xda\xd7\x5f\x1f\xd0\xed\x8f\x7f\xdc\xfe\xe7\xe3\xed\xdf\xef\x8a\xdb\xf5\x87\x3f\xad\xd7\x8d\x7c\x25\x6c\xbb\xe9\x4b\xd8\x12\x46\x34\xe1\x6c\x9c\x3f\x1f\x91\x2f\xfd\x7f\x2f\xe3\xc4\xa8\x2c\x5b\x30\xa2\xd6\xdc\x5b\x44\x15\xd8\x3c\x33\xd0\x4f\x5c\x3e\xc6\x78\x1e\x61\x6f\xc4\x73\x3f\xbf\x87\x67\x9b\x9d\x03\xa7\x75\x15\xd5\xe0\x80\x7a\x23\x66\xba\xe9\x97\xd1\x9f\x02\x2c\x41\xc7\x4d\xb6\x43\xbd\x99\xc5\x36\xd3\x2f\xc3\x70\x17\x35\x62\x0c\x0f\xa8\x37\x62\xb8\x9b\xfe\x3a\x86\x57\x03\xd3\xb3\xd8\x0e\x61\xcc\xdd\x2e\xd0\x8a\x67\x3e\x51\xf9\xe2\x49\x58\x56\xa3\xb0\x02\x52\x2a\x41\x50\x7e\x6c\x9e\x05\xe4\xd1\x01\x2a\x60\x3a\x1f\x45\x90\x65\xf9\xa6\x26\xb4\x74\x25\xca\x19\xfc\xab\x19\xe2\xc1\x78\x98\x65\x3f\xdd\xd0\x6d\x8c\xd3\xbe\xb7\x7e\x85\x15\x3e\xbe\x0f\xf0\x32\xbe\xc7\x9c\x69\x78\xd6\x2d\x53\xf3\x53\x77\x22\xe0\xf8\x11\xe4\x96\x50\x48\xa5\x40\xb2\xb3\xe2\x80\xc8\x28\x51\xba\xe0\xb2\x28\x09\xd6\x5e\x7a\x8a\x36\x40\xaf\x1a\x01\x23\xbc\x87\x62\x2b\x79\x15\x1d\x65\x5b\x74\x9c\xa8\xfc\xc5\x19\x67\x32\x70\xdc\xb4\x47\x52\xe3\xd7\x7a\xe5\x19\x30\xc7\x48\x14\xa8\x2c\x2d\x91\x22\x29\xd1\x31\xbf\xc9\x72\xa2\xa1\x52\x7e\x69\x67\x79\xcd\xc8\xf7\x1a\xfe\xd9\x43\xb4\xac\xc1\x1d\xb7\x94\x5c\x2c\x3f\xf0\x4e\xf2\x5a\x14\x02\xc9\xc6\xd6\xe7\x2d\x21\xc7\xbc\xaa\x10\x5b\xca\x01\xce\xe1\x23\x41\xf2\x93\x30\x9b\x99\x5e\xd5\xcf\x61\xbe\x1a\x67\xb3\x96\x15\xe0\x26\xce\xcf\xd4\xa5\xe3\x4e\x1d\x77\xeb\x26\x2a\xf2\x5a\xe2\x54\x2f\x6d\xe6\x44\x72\x07\xa9\x71\x20\xcb\xf2\x9a\x94\xe9\xe0\xdd\x39\xe0\x8a\x97\xf6\xba\x59\x5d\x6d\x40\x4e\x5c\xd2\xf6\xac\xe9\xef\xf5\xca\xf7\xc6\xd1\xbe\x46\x84\x81\x2c\x18\xaa\x62\xb2\xca\xb1\x84\x12\x98\x26\x88\x16\x4a\x00\xb6\xe0\x83\xa6\x66\x34\x93\x27\x45\xcd\x5c\xc2\x8e\x28\x2d\x8f\x5e\xe4\x89\x0b\x73\x61\x25\x08\x60\xa5\x2a\xba\x1e\x20\x35\xc0\x59\x03\x8c\x0d\xc1\xa2\x61\xa2\x64\x73\x81\xbb\x1b\xa6\x09\xdd\xcd\xda\x72\x87\xb0\x50\x80\x24\xde\x5f\x48\xcf\x2b\x44\x58\x8a\x52\x81\x69\x79\x14\x9c\x74\x61\xec\xdd\xc5\x27\x60\x87\x62\xb4\x9b\xb3\xc5\x00\xec\x40\x24\x67\xd5\x10\xa4\xd3\x12\xa8\x41\xff\x2c\xb8\x82\xeb\x83\x63\x4f\xf1\x30\x30\x7e\x33\xfa\xf4\xda\x96\x5e\xbe\xe5\xb2\x42\xcd\x62\x87\xb9\x4d\x1f\xb6\xa6\x9a\x5a\x9e\x29\x40\x93\x87\xa6\xf0\x45\xb4\xa0\x84\x3d\x2e\x6f\xe2\xf0\xac\x25\x2a\xf6\x5c\xe9\x4b\x6a\x94\x7c\x0f\x88\xea\x3d\xde\x03\x7e\x9c\x21\x37\x51\x16\x35\x57\x3a\xc5\xc8\x49\x85\x76\x71\x90\xc0\x31\xc8\xc5\xb5\x58\xbe\xa8\xf0\x8d\x61\xf9\x6e\xd7\x40\x43\x16\x37\xa9\xed\xfb\xd7\xb1\xaa\xb8\x94\xe4\x00\x32\xb5\xc4\xe5\xe2\xd4\x92\xb8\x2f\x53\xb2\x79\xb4\x3f\xb3\xa0\x5f\xef\xba\xf6\x6c\xc6\xab\xda\xff\x28\xcd\xd7\xd3\x94\x39\x4d\x9a\xee\x13\x87\xc3\xb4\x3a\xd7\xd2\x4a\x85\x70\x53\xce\x4a\x50\x01\xbd\x9e\xa0\xfd\x86\x47\x31\xc9\xf9\x27\xec\x04\x3c\x49\xac\xa1\x48\x7d\x76\x22\xcc\x2e\xea\xb0\x92\x54\x17\x6d\xb1\x23\xdc\x84\x96\x97\xba\xcc\xd3\x72\xe3\x26\xd6\xe2\x10\x25\x48\x41\xdc\xd9\x83\x82\xb4\x46\x23\xe2\xf0\x39\xd1\x26\x7c\xb4\x7f\x9b\xa5\x0d\x90\x06\xc7\x4c\x6f\xdd\x22\x43\x99\x25\x2a\xa5\xde\x85\xac\xe3\x45\xeb\x6b\x76\x96\xc2\x2e\xbc\xed\x58\xd1\x46\x08\xd3\xc1\x04\x97\xfa\x97\xf4\x42\xa7\x38\x75\x4a\xf8\xdd\xe4\xd3\xf6\xc8\x55\x77\x12\xd1\xeb\xf4\x54\x33\x51\xca\x83\xf6\x74\x54\x84\x69\xd8\x35\xad\x8c\x3f\x09\xd4\x1b\x4a\xd4\x1e\xca\x73\x68\x24\xd7\x1c\x73\x9a\xe6\x18\xde\x0d\x9a\x74\x67\x98\xe9\xaf\x2e\xaa\xcd\x84\x24\x07\x42\x61\xe7\x70\xbc\xe1\x9c\x02\x62\x56\xa2\x90\x80\xca\x82\x33\x7a\x4c\x40\x2a\x8d\x64\x74\x57\x42\x01\xae\x25\xd1\xc7\x82\x0b\xbd\x78\x55\xa8\xf6\x55\xa1\xc8\x0f\xb0\x7d\xef\x64\xf5\xfd\x40\x6b\x67\x41\xce\x16\x77\xf6\x7b\x2b\xe2\x2f\xb3\x15\xa1\x8e\x0a\xeb\xcb\x6a\x6b\xa5\x4b\xc2\x0a\x2e\x80\x45\x7d\x43\x69\x2e\x8a\x9d\x44\x18\x0a\x01\x92\x70\xaf\x28\xac\x00\x5b\xd6\x12\x35\xf3\x4f\x87\x51\x64\xc7\x90\x3f\xee\x18\x50\x5d\x89\xed\x85\x9b\x00\x5a\xc7\x9d\xbd\xa6\xa4\x22\x61\xa7\xf1\x58\x6d\x42\xbd\xd6\xd5\x6a\xfe\x12\x6d\xa6\x3c\x4b\x0a\xd9\x33\x1d\xc2\x7c\x83\x90\xd0\x19\xec\x91\x3c\x23\x75\xb4\x8e\xb9\x0d\xe4\x27\x5f\xdf\xe0\x5d\x97\x75\x58\xdd\x8e\x77\xd3\x2f\x64\xed\xc5\x9f\x55\x7a\xb9\xcb\x58\x07\xab\x1f\xbf\x53\xd5\x2a\xda\xc4\xb5\x18\xa6\xe6\x1a\x90\x11\x3a\x3d\x75\xcd\xfe\x27\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\x09\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\xdf\x51\xe8\xff\xc4\x66\x7f\x9d\x7d\xf5\xdf\x42\x44\xef\xe4\xb7\xa8\x8b\xf3\x78\xc2\x95\xca\x77\xa0\xb3\x37\x56\xc5\x24\x89\x79\x55\xd1\xa3\x7e\xab\xe2\x55\xbd\xc2\x3e\xed\x32\x54\x32\xdd\xfc\x9a\x93\xe4\xb9\x5f\x31\xac\xed\x65\xb8\x30\xcf\x97\x83\x76\x5d\x33\x77\x16\x3e\x40\x02\x9b\xad\xce\xa4\xbd\x10\xe7\x39\x5f\x30\xee\xdf\x7d\x98\xa9\xde\xe6\xae\xce\xbd\x52\xd9\xb3\xc0\x3d\x03\xbf\x4e\x9d\xc6\x78\x90\xee\xf4\xe3\xa8\xb0\xff\x0f\xf4\x93\x4f\xa5\x1a\x3e\xd9\x71\xb2\x39\xfb\xd3\x3e\x59\xea\x3e\x73\x5a\x5b\xf2\x71\x20\xdd\x45\x54\x23\xd1\xae\xcd\xbd\x82\xe0\xcd\x79\xdf\x07\x54\xee\xb9\xd6\xf0\x21\x53\xe0\xa8\x7d\x65\xfe\x6d\x3f\x3a\x5b\xbd\xac\xfe\x1b\x00\x00\xff\xff\x88\x06\xbb\x83\xde\x3b\x00\x00") func dataConfig_schema_v33JsonBytes() ([]byte, error) { return bindataRead( diff --git a/cli/compose/schema/data/config_schema_v3.3.json b/cli/compose/schema/data/config_schema_v3.3.json index 66efb14bb0..e69116c388 100644 --- a/cli/compose/schema/data/config_schema_v3.3.json +++ b/cli/compose/schema/data/config_schema_v3.3.json @@ -50,6 +50,17 @@ } }, "additionalProperties": false + }, + + "configs": { + "id": "#/properties/configs", + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9._-]+$": { + "$ref": "#/definitions/config" + } + }, + "additionalProperties": false } }, @@ -88,6 +99,24 @@ {"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"}, @@ -443,6 +472,22 @@ "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"}, diff --git a/cli/compose/types/types.go b/cli/compose/types/types.go index 225dd00ec8..1eecc124f6 100644 --- a/cli/compose/types/types.go +++ b/cli/compose/types/types.go @@ -60,12 +60,19 @@ type ConfigDetails struct { Environment map[string]string } +// LookupEnv provides a lookup function for environment variables +func (cd ConfigDetails) LookupEnv(key string) (string, bool) { + v, ok := cd.Environment[key] + return v, ok +} + // Config is a full compose file configuration type Config struct { Services []ServiceConfig Networks map[string]NetworkConfig Volumes map[string]VolumeConfig Secrets map[string]SecretConfig + Configs map[string]ConfigObjConfig } // ServiceConfig is the configuration of one service @@ -76,6 +83,7 @@ type ServiceConfig struct { CapDrop []string `mapstructure:"cap_drop"` CgroupParent string `mapstructure:"cgroup_parent"` Command ShellCommand + Configs []ServiceConfigObjConfig ContainerName string `mapstructure:"container_name"` CredentialSpec CredentialSpecConfig `mapstructure:"credential_spec"` DependsOn []string `mapstructure:"depends_on"` @@ -252,8 +260,7 @@ type ServiceVolumeVolume struct { NoCopy bool `mapstructure:"nocopy"` } -// ServiceSecretConfig is the secret configuration for a service -type ServiceSecretConfig struct { +type fileReferenceConfig struct { Source string Target string UID string @@ -261,6 +268,12 @@ type ServiceSecretConfig struct { Mode *uint32 } +// ServiceConfigObjConfig is the config obj configuration for a service +type ServiceConfigObjConfig fileReferenceConfig + +// ServiceSecretConfig is the secret configuration for a service +type ServiceSecretConfig fileReferenceConfig + // UlimitsConfig the ulimit configuration type UlimitsConfig struct { Single int @@ -305,15 +318,20 @@ type External struct { External bool } -// SecretConfig for a secret -type SecretConfig struct { - File string - External External - Labels Labels -} - // CredentialSpecConfig for credential spec on Windows type CredentialSpecConfig struct { File string Registry string } + +type fileObjectConfig struct { + File string + External External + Labels Labels +} + +// SecretConfig for a secret +type SecretConfig fileObjectConfig + +// ConfigObjConfig is the config for the swarm "Config" object +type ConfigObjConfig fileObjectConfig