Skip to content

Commit

Permalink
add support for config credentialspecs to compose
Browse files Browse the repository at this point in the history
Signed-off-by: Drew Erny <drew.erny@docker.com>
  • Loading branch information
dperny committed Apr 1, 2019
1 parent 23b54b8 commit 56ea44b
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 16 deletions.
74 changes: 62 additions & 12 deletions cli/compose/convert/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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 == "" {
Expand Down Expand Up @@ -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
Expand All @@ -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
}
39 changes: 36 additions & 3 deletions cli/compose/convert/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ func TestConvertCredentialSpec(t *testing.T) {
name string
in composetypes.CredentialSpecConfig
out *swarm.CredentialSpec
configs []*swarm.ConfigReference
expectedErr string
}{
{
Expand All @@ -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",
Expand All @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion cli/compose/schema/data/config_schema_v3.8.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@
"target": {"type": "string"},
"uid": {"type": "string"},
"gid": {"type": "string"},
"mode": {"type": "number"}
"mode": {"type": "number"},
"runtime": {"type": "boolean"}
}
}
]
Expand Down
5 changes: 5 additions & 0 deletions cli/compose/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 56ea44b

Please sign in to comment.