mirror of https://github.com/docker/cli.git
add support for config credentialspecs to compose
Signed-off-by: Drew Erny <drew.erny@docker.com>
This commit is contained in:
parent
4cacd1304a
commit
42ec51e1ae
|
@ -169,6 +169,9 @@ func setConfigs(apiClient client.ConfigAPIClient, service *swarm.ServiceSpec, op
|
||||||
for _, config := range configs {
|
for _, config := range configs {
|
||||||
if config.ConfigName == cs.Config {
|
if config.ConfigName == cs.Config {
|
||||||
service.TaskTemplate.ContainerSpec.Privileges.CredentialSpec.Config = config.ConfigID
|
service.TaskTemplate.ContainerSpec.Privileges.CredentialSpec.Config = config.ConfigID
|
||||||
|
// we've found the right config, no need to keep iterating
|
||||||
|
// through the rest of them.
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ func Services(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "service %s", service.Name)
|
return nil, errors.Wrapf(err, "service %s", service.Name)
|
||||||
}
|
}
|
||||||
configs, err := convertServiceConfigObjs(client, namespace, service.Configs, config.Configs)
|
configs, err := convertServiceConfigObjs(client, namespace, service, config.Configs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "service %s", service.Name)
|
return nil, errors.Wrapf(err, "service %s", service.Name)
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,9 @@ func Service(
|
||||||
}
|
}
|
||||||
|
|
||||||
var privileges swarm.Privileges
|
var privileges swarm.Privileges
|
||||||
privileges.CredentialSpec, err = convertCredentialSpec(service.CredentialSpec)
|
privileges.CredentialSpec, err = convertCredentialSpec(
|
||||||
|
namespace, service.CredentialSpec, configs,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return swarm.ServiceSpec{}, err
|
return swarm.ServiceSpec{}, err
|
||||||
}
|
}
|
||||||
|
@ -286,11 +288,17 @@ func convertServiceSecrets(
|
||||||
return secrs, err
|
return secrs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// convertServiceConfigObjs takes an API client, a namespace, a ServiceConfig,
|
||||||
|
// and a set of compose Config specs, and creates the swarm ConfigReferences
|
||||||
|
// required by the serivce. Unlike convertServiceSecrets, this takes the whole
|
||||||
|
// ServiceConfig, because some Configs may be needed as a result of other
|
||||||
|
// fields (like CredentialSpecs).
|
||||||
|
//
|
||||||
// TODO: fix configs API so that ConfigsAPIClient is not required here
|
// TODO: fix configs API so that ConfigsAPIClient is not required here
|
||||||
func convertServiceConfigObjs(
|
func convertServiceConfigObjs(
|
||||||
client client.ConfigAPIClient,
|
client client.ConfigAPIClient,
|
||||||
namespace Namespace,
|
namespace Namespace,
|
||||||
configs []composetypes.ServiceConfigObjConfig,
|
service composetypes.ServiceConfig,
|
||||||
configSpecs map[string]composetypes.ConfigObjConfig,
|
configSpecs map[string]composetypes.ConfigObjConfig,
|
||||||
) ([]*swarm.ConfigReference, error) {
|
) ([]*swarm.ConfigReference, error) {
|
||||||
refs := []*swarm.ConfigReference{}
|
refs := []*swarm.ConfigReference{}
|
||||||
|
@ -302,7 +310,7 @@ func convertServiceConfigObjs(
|
||||||
}
|
}
|
||||||
return composetypes.FileObjectConfig(configSpec), nil
|
return composetypes.FileObjectConfig(configSpec), nil
|
||||||
}
|
}
|
||||||
for _, config := range configs {
|
for _, config := range service.Configs {
|
||||||
obj, err := convertFileObject(namespace, composetypes.FileReferenceConfig(config), lookup)
|
obj, err := convertFileObject(namespace, composetypes.FileReferenceConfig(config), lookup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -315,6 +323,38 @@ func convertServiceConfigObjs(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// finally, after converting all of the file objects, create any
|
||||||
|
// Runtime-type configs that are needed. these are configs that are not
|
||||||
|
// mounted into the container, but are used in some other way by the
|
||||||
|
// container runtime. Currently, this only means CredentialSpecs, but in
|
||||||
|
// the future it may be used for other fields
|
||||||
|
|
||||||
|
// grab the CredentialSpec out of the Service
|
||||||
|
credSpec := service.CredentialSpec
|
||||||
|
// if the credSpec uses a config, then we should grab the config name, and
|
||||||
|
// create a config reference for it. A File or Registry-type CredentialSpec
|
||||||
|
// does not need this operation.
|
||||||
|
if credSpec.Config != "" {
|
||||||
|
// look up the config in the configSpecs.
|
||||||
|
obj, err := lookup(credSpec.Config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the actual correct name.
|
||||||
|
name := namespace.Scope(credSpec.Config)
|
||||||
|
if obj.Name != "" {
|
||||||
|
name = obj.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// now append a Runtime-type config.
|
||||||
|
refs = append(refs, &swarm.ConfigReference{
|
||||||
|
ConfigName: name,
|
||||||
|
Runtime: &swarm.ConfigReferenceRuntimeTarget{},
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
confs, err := servicecli.ParseConfigs(client, refs)
|
confs, err := servicecli.ParseConfigs(client, refs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -342,11 +382,6 @@ func convertFileObject(
|
||||||
config composetypes.FileReferenceConfig,
|
config composetypes.FileReferenceConfig,
|
||||||
lookup func(key string) (composetypes.FileObjectConfig, error),
|
lookup func(key string) (composetypes.FileObjectConfig, error),
|
||||||
) (swarmReferenceObject, error) {
|
) (swarmReferenceObject, error) {
|
||||||
target := config.Target
|
|
||||||
if target == "" {
|
|
||||||
target = config.Source
|
|
||||||
}
|
|
||||||
|
|
||||||
obj, err := lookup(config.Source)
|
obj, err := lookup(config.Source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return swarmReferenceObject{}, err
|
return swarmReferenceObject{}, err
|
||||||
|
@ -357,6 +392,11 @@ func convertFileObject(
|
||||||
source = obj.Name
|
source = obj.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
target := config.Target
|
||||||
|
if target == "" {
|
||||||
|
target = config.Source
|
||||||
|
}
|
||||||
|
|
||||||
uid := config.UID
|
uid := config.UID
|
||||||
gid := config.GID
|
gid := config.GID
|
||||||
if uid == "" {
|
if uid == "" {
|
||||||
|
@ -599,7 +639,7 @@ func convertDNSConfig(DNS []string, DNSSearch []string) (*swarm.DNSConfig, error
|
||||||
return nil, nil
|
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
|
var o []string
|
||||||
|
|
||||||
// Config was added in API v1.40
|
// Config was added in API v1.40
|
||||||
|
@ -622,5 +662,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])
|
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)
|
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
|
return &swarmCredSpec, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -318,6 +318,7 @@ func TestConvertCredentialSpec(t *testing.T) {
|
||||||
name string
|
name string
|
||||||
in composetypes.CredentialSpecConfig
|
in composetypes.CredentialSpecConfig
|
||||||
out *swarm.CredentialSpec
|
out *swarm.CredentialSpec
|
||||||
|
configs []*swarm.ConfigReference
|
||||||
expectedErr string
|
expectedErr string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
@ -343,10 +344,41 @@ func TestConvertCredentialSpec(t *testing.T) {
|
||||||
in: composetypes.CredentialSpecConfig{Config: "0bt9dmxjvjiqermk6xrop3ekq", File: "somefile.json", Registry: "testing"},
|
in: composetypes.CredentialSpecConfig{Config: "0bt9dmxjvjiqermk6xrop3ekq", File: "somefile.json", Registry: "testing"},
|
||||||
expectedErr: `invalid credential spec: cannot specify both "Config", "File", and "Registry"`,
|
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",
|
name: "config",
|
||||||
in: composetypes.CredentialSpecConfig{Config: "0bt9dmxjvjiqermk6xrop3ekq"},
|
in: composetypes.CredentialSpecConfig{Config: "someName"},
|
||||||
out: &swarm.CredentialSpec{Config: "0bt9dmxjvjiqermk6xrop3ekq"},
|
configs: []*swarm.ConfigReference{
|
||||||
|
{
|
||||||
|
ConfigName: "someOtherName",
|
||||||
|
ConfigID: "someOtherID",
|
||||||
|
}, {
|
||||||
|
ConfigName: "someName",
|
||||||
|
ConfigID: "someID",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
out: &swarm.CredentialSpec{Config: "someID"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "file",
|
name: "file",
|
||||||
|
@ -363,7 +395,8 @@ func TestConvertCredentialSpec(t *testing.T) {
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
tc := tc
|
tc := tc
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
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 != "" {
|
if tc.expectedErr != "" {
|
||||||
assert.Error(t, err, tc.expectedErr)
|
assert.Error(t, err, tc.expectedErr)
|
||||||
|
@ -502,9 +535,14 @@ func TestConvertServiceSecrets(t *testing.T) {
|
||||||
|
|
||||||
func TestConvertServiceConfigs(t *testing.T) {
|
func TestConvertServiceConfigs(t *testing.T) {
|
||||||
namespace := Namespace{name: "foo"}
|
namespace := Namespace{name: "foo"}
|
||||||
configs := []composetypes.ServiceConfigObjConfig{
|
service := composetypes.ServiceConfig{
|
||||||
|
Configs: []composetypes.ServiceConfigObjConfig{
|
||||||
{Source: "foo_config"},
|
{Source: "foo_config"},
|
||||||
{Source: "bar_config"},
|
{Source: "bar_config"},
|
||||||
|
},
|
||||||
|
CredentialSpec: composetypes.CredentialSpecConfig{
|
||||||
|
Config: "baz_config",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
configSpecs := map[string]composetypes.ConfigObjConfig{
|
configSpecs := map[string]composetypes.ConfigObjConfig{
|
||||||
"foo_config": {
|
"foo_config": {
|
||||||
|
@ -513,18 +551,23 @@ func TestConvertServiceConfigs(t *testing.T) {
|
||||||
"bar_config": {
|
"bar_config": {
|
||||||
Name: "bar_config",
|
Name: "bar_config",
|
||||||
},
|
},
|
||||||
|
"baz_config": {
|
||||||
|
Name: "baz_config",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
client := &fakeClient{
|
client := &fakeClient{
|
||||||
configListFunc: func(opts types.ConfigListOptions) ([]swarm.Config, error) {
|
configListFunc: func(opts types.ConfigListOptions) ([]swarm.Config, error) {
|
||||||
assert.Check(t, is.Contains(opts.Filters.Get("name"), "foo_config"))
|
assert.Check(t, is.Contains(opts.Filters.Get("name"), "foo_config"))
|
||||||
assert.Check(t, is.Contains(opts.Filters.Get("name"), "bar_config"))
|
assert.Check(t, is.Contains(opts.Filters.Get("name"), "bar_config"))
|
||||||
|
assert.Check(t, is.Contains(opts.Filters.Get("name"), "baz_config"))
|
||||||
return []swarm.Config{
|
return []swarm.Config{
|
||||||
{Spec: swarm.ConfigSpec{Annotations: swarm.Annotations{Name: "foo_config"}}},
|
{Spec: swarm.ConfigSpec{Annotations: swarm.Annotations{Name: "foo_config"}}},
|
||||||
{Spec: swarm.ConfigSpec{Annotations: swarm.Annotations{Name: "bar_config"}}},
|
{Spec: swarm.ConfigSpec{Annotations: swarm.Annotations{Name: "bar_config"}}},
|
||||||
|
{Spec: swarm.ConfigSpec{Annotations: swarm.Annotations{Name: "baz_config"}}},
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
refs, err := convertServiceConfigObjs(client, namespace, configs, configSpecs)
|
refs, err := convertServiceConfigObjs(client, namespace, service, configSpecs)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
expected := []*swarm.ConfigReference{
|
expected := []*swarm.ConfigReference{
|
||||||
{
|
{
|
||||||
|
@ -536,6 +579,10 @@ func TestConvertServiceConfigs(t *testing.T) {
|
||||||
Mode: 0444,
|
Mode: 0444,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
ConfigName: "baz_config",
|
||||||
|
Runtime: &swarm.ConfigReferenceRuntimeTarget{},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
ConfigName: "foo_config",
|
ConfigName: "foo_config",
|
||||||
File: &swarm.ConfigReferenceFileTarget{
|
File: &swarm.ConfigReferenceFileTarget{
|
||||||
|
|
Loading…
Reference in New Issue