diff --git a/cli/compose/convert/service.go b/cli/compose/convert/service.go index 0fe40bf3a7bc..25f8fee6b611 100644 --- a/cli/compose/convert/service.go +++ b/cli/compose/convert/service.go @@ -109,7 +109,9 @@ func Service( } var privileges swarm.Privileges - privileges.CredentialSpec, err = convertCredentialSpec(service.CredentialSpec) + privileges.CredentialSpec, err = convertCredentialSpec( + namespace, service.CredentialSpec, configs, + ) if err != nil { return swarm.ServiceSpec{}, err } @@ -308,11 +310,24 @@ func convertServiceConfigObjs( return nil, err } - file := swarm.ConfigReferenceFileTarget(obj.File) - refs = append(refs, &swarm.ConfigReference{ - File: &file, - ConfigName: obj.Name, - }) + // if the obj.File is identical to the zero-value of + // swarmReferenceTarget, that means this is a Runtime-type config. This + // code may have to be made more robust later, if Runtime targets start + // including data or other targets are created, but for now it works + // fine to do this check, and it's much easier than the alternatives + // (which involve altering the swarmReferenceObject type) + if (obj.File == swarmReferenceTarget{}) { + refs = append(refs, &swarm.ConfigReference{ + ConfigName: obj.Name, + Runtime: &swarm.ConfigReferenceRuntimeTarget{}, + }) + } else { + file := swarm.ConfigReferenceFileTarget(obj.File) + refs = append(refs, &swarm.ConfigReference{ + File: &file, + ConfigName: obj.Name, + }) + } } confs, err := servicecli.ParseConfigs(client, refs) @@ -342,11 +357,6 @@ func convertFileObject( config composetypes.FileReferenceConfig, lookup func(key string) (composetypes.FileObjectConfig, error), ) (swarmReferenceObject, error) { - target := config.Target - if target == "" { - target = config.Source - } - obj, err := lookup(config.Source) if err != nil { return swarmReferenceObject{}, err @@ -357,6 +367,28 @@ func convertFileObject( source = obj.Name } + // if the config is a Runtime config, that means we don't mount it as a + // file in the container. This is used for supporting CredentialSpec + // configs. + if config.Runtime { + if config.Target != "" || config.UID != "" || config.GID != "" || config.Mode != nil { + return swarmReferenceObject{}, errors.Errorf( + "config %v is a runtime config, but has file options set", + config.Source, + ) + } + + return swarmReferenceObject{ + Name: source, + // File is the zero-value for Runtime configs + }, nil + } + + target := config.Target + if target == "" { + target = config.Source + } + uid := config.UID gid := config.GID if uid == "" { @@ -599,7 +631,7 @@ func convertDNSConfig(DNS []string, DNSSearch []string) (*swarm.DNSConfig, error return nil, nil } -func convertCredentialSpec(spec composetypes.CredentialSpecConfig) (*swarm.CredentialSpec, error) { +func convertCredentialSpec(namespace Namespace, spec composetypes.CredentialSpecConfig, refs []*swarm.ConfigReference) (*swarm.CredentialSpec, error) { var o []string // Config was added in API v1.40 @@ -622,5 +654,23 @@ func convertCredentialSpec(spec composetypes.CredentialSpecConfig) (*swarm.Crede return nil, errors.Errorf("invalid credential spec: cannot specify both %s, and %s", strings.Join(o[:l-1], ", "), o[l-1]) } swarmCredSpec := swarm.CredentialSpec(spec) + // if we're using a swarm Config for the credential spec, over-write it + // here with the config ID + if swarmCredSpec.Config != "" { + for _, config := range refs { + if swarmCredSpec.Config == config.ConfigName { + swarmCredSpec.Config = config.ConfigID + return &swarmCredSpec, nil + } + } + // if none of the configs match, try namespacing + for _, config := range refs { + if namespace.Scope(swarmCredSpec.Config) == config.ConfigName { + swarmCredSpec.Config = config.ConfigID + return &swarmCredSpec, nil + } + } + return nil, errors.Errorf("invalid credential spec: spec specifies config %v, but no such config can be found", swarmCredSpec.Config) + } return &swarmCredSpec, nil } diff --git a/cli/compose/convert/service_test.go b/cli/compose/convert/service_test.go index f275fb874cad..77b008dca302 100644 --- a/cli/compose/convert/service_test.go +++ b/cli/compose/convert/service_test.go @@ -318,6 +318,7 @@ func TestConvertCredentialSpec(t *testing.T) { name string in composetypes.CredentialSpecConfig out *swarm.CredentialSpec + configs []*swarm.ConfigReference expectedErr string }{ { @@ -343,10 +344,41 @@ func TestConvertCredentialSpec(t *testing.T) { in: composetypes.CredentialSpecConfig{Config: "0bt9dmxjvjiqermk6xrop3ekq", File: "somefile.json", Registry: "testing"}, expectedErr: `invalid credential spec: cannot specify both "Config", "File", and "Registry"`, }, + { + name: "missing-config-reference", + in: composetypes.CredentialSpecConfig{Config: "missing"}, + expectedErr: "invalid credential spec: spec specifies config missing, but no such config can be found", + configs: []*swarm.ConfigReference{ + { + ConfigName: "someName", + ConfigID: "missing", + }, + }, + }, + { + name: "namespaced-config", + in: composetypes.CredentialSpecConfig{Config: "name"}, + configs: []*swarm.ConfigReference{ + { + ConfigName: "namespaced-config_name", + ConfigID: "someID", + }, + }, + out: &swarm.CredentialSpec{Config: "someID"}, + }, { name: "config", - in: composetypes.CredentialSpecConfig{Config: "0bt9dmxjvjiqermk6xrop3ekq"}, - out: &swarm.CredentialSpec{Config: "0bt9dmxjvjiqermk6xrop3ekq"}, + in: composetypes.CredentialSpecConfig{Config: "someName"}, + configs: []*swarm.ConfigReference{ + { + ConfigName: "someOtherName", + ConfigID: "someOtherID", + }, { + ConfigName: "someName", + ConfigID: "someID", + }, + }, + out: &swarm.CredentialSpec{Config: "someID"}, }, { name: "file", @@ -363,7 +395,8 @@ func TestConvertCredentialSpec(t *testing.T) { for _, tc := range tests { tc := tc t.Run(tc.name, func(t *testing.T) { - swarmSpec, err := convertCredentialSpec(tc.in) + namespace := NewNamespace(tc.name) + swarmSpec, err := convertCredentialSpec(namespace, tc.in, tc.configs) if tc.expectedErr != "" { assert.Error(t, err, tc.expectedErr) diff --git a/cli/compose/schema/bindata.go b/cli/compose/schema/bindata.go index 6651be946a46..3b33b163f14a 100644 --- a/cli/compose/schema/bindata.go +++ b/cli/compose/schema/bindata.go @@ -510,45 +510,45 @@ bnBpPlHfjORjkTRf1wyAwiYqMXd9/G6313QfoXs6/sbZ66r6e179PwAA//8ZL3SpvkUAAA== "/data/config_schema_v3.8.json": { local: "data/config_schema_v3.8.json", - size: 18048, + size: 18098, modtime: 1518458244, compressed: ` -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 -ek8n4jg2XlV/T6v/BgAA//8BShmMgEYAAA== +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 `, }, diff --git a/cli/compose/schema/data/config_schema_v3.8.json b/cli/compose/schema/data/config_schema_v3.8.json index 08a9719c785d..e9926108e730 100644 --- a/cli/compose/schema/data/config_schema_v3.8.json +++ b/cli/compose/schema/data/config_schema_v3.8.json @@ -115,7 +115,8 @@ "target": {"type": "string"}, "uid": {"type": "string"}, "gid": {"type": "string"}, - "mode": {"type": "number"} + "mode": {"type": "number"}, + "runtime": {"type": "boolean"} } } ] diff --git a/cli/compose/types/types.go b/cli/compose/types/types.go index c053d2ba21a7..f8b2e0ee0c19 100644 --- a/cli/compose/types/types.go +++ b/cli/compose/types/types.go @@ -408,6 +408,11 @@ type FileReferenceConfig struct { UID string `yaml:",omitempty" json:"uid,omitempty"` GID string `yaml:",omitempty" json:"gid,omitempty"` Mode *uint32 `yaml:",omitempty" json:"mode,omitempty"` + // Runtime is set `true` if reference isn't actually used as a File in + // the service. Specifically, this currently only indicates that the Config + // is used as a CredentialSpec. If this field is set `true`, then all other + // fields besides `Source` must be empty. + Runtime bool `yaml:",omitempty" json:"runtime,omitempty"` } // ServiceConfigObjConfig is the config obj configuration for a service