mirror of https://github.com/docker/cli.git
Merge pull request #671 from dnephin/remove-secret-config-duplication
Remove secret/config duplication in cli/compose
This commit is contained in:
commit
801ddee343
|
@ -101,18 +101,11 @@ func Secrets(namespace Namespace, secrets map[string]composetypes.SecretConfig)
|
|||
continue
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadFile(secret.File)
|
||||
obj, err := fileObjectConfig(namespace, name, composetypes.FileObjectConfig(secret))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result = append(result, swarm.SecretSpec{
|
||||
Annotations: swarm.Annotations{
|
||||
Name: namespace.Scope(name),
|
||||
Labels: AddStackLabel(namespace, secret.Labels),
|
||||
},
|
||||
Data: data,
|
||||
})
|
||||
result = append(result, swarm.SecretSpec{Annotations: obj.Annotations, Data: obj.Data})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
@ -125,18 +118,31 @@ func Configs(namespace Namespace, configs map[string]composetypes.ConfigObjConfi
|
|||
continue
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadFile(config.File)
|
||||
obj, err := fileObjectConfig(namespace, name, composetypes.FileObjectConfig(config))
|
||||
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,
|
||||
})
|
||||
result = append(result, swarm.ConfigSpec{Annotations: obj.Annotations, Data: obj.Data})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type swarmFileObject struct {
|
||||
Annotations swarm.Annotations
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func fileObjectConfig(namespace Namespace, name string, obj composetypes.FileObjectConfig) (swarmFileObject, error) {
|
||||
data, err := ioutil.ReadFile(obj.File)
|
||||
if err != nil {
|
||||
return swarmFileObject{}, err
|
||||
}
|
||||
|
||||
return swarmFileObject{
|
||||
Annotations: swarm.Annotations{
|
||||
Name: namespace.Scope(name),
|
||||
Labels: AddStackLabel(namespace, obj.Labels),
|
||||
},
|
||||
Data: data,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -255,43 +255,24 @@ func convertServiceSecrets(
|
|||
secretSpecs map[string]composetypes.SecretConfig,
|
||||
) ([]*swarm.SecretReference, error) {
|
||||
refs := []*swarm.SecretReference{}
|
||||
for _, secret := range secrets {
|
||||
target := secret.Target
|
||||
if target == "" {
|
||||
target = secret.Source
|
||||
}
|
||||
|
||||
secretSpec, exists := secretSpecs[secret.Source]
|
||||
lookup := func(key string) (composetypes.FileObjectConfig, error) {
|
||||
configSpec, exists := secretSpecs[key]
|
||||
if !exists {
|
||||
return nil, errors.Errorf("undefined secret %q", secret.Source)
|
||||
}
|
||||
|
||||
source := namespace.Scope(secret.Source)
|
||||
if secretSpec.External.External {
|
||||
source = secretSpec.External.Name
|
||||
}
|
||||
|
||||
uid := secret.UID
|
||||
gid := secret.GID
|
||||
if uid == "" {
|
||||
uid = "0"
|
||||
}
|
||||
if gid == "" {
|
||||
gid = "0"
|
||||
}
|
||||
mode := secret.Mode
|
||||
if mode == nil {
|
||||
mode = uint32Ptr(0444)
|
||||
return composetypes.FileObjectConfig{}, errors.Errorf("undefined secret %q", key)
|
||||
}
|
||||
return composetypes.FileObjectConfig(configSpec), nil
|
||||
}
|
||||
for _, config := range secrets {
|
||||
obj, err := convertFileObject(namespace, composetypes.FileReferenceConfig(config), lookup)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
file := swarm.SecretReferenceFileTarget(obj.File)
|
||||
refs = append(refs, &swarm.SecretReference{
|
||||
File: &swarm.SecretReferenceFileTarget{
|
||||
Name: target,
|
||||
UID: uid,
|
||||
GID: gid,
|
||||
Mode: os.FileMode(*mode),
|
||||
},
|
||||
SecretName: source,
|
||||
File: &file,
|
||||
SecretName: obj.Name,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -312,43 +293,24 @@ func convertServiceConfigObjs(
|
|||
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]
|
||||
lookup := func(key string) (composetypes.FileObjectConfig, error) {
|
||||
configSpec, exists := configSpecs[key]
|
||||
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)
|
||||
return composetypes.FileObjectConfig{}, errors.Errorf("undefined config %q", key)
|
||||
}
|
||||
return composetypes.FileObjectConfig(configSpec), nil
|
||||
}
|
||||
for _, config := range configs {
|
||||
obj, err := convertFileObject(namespace, composetypes.FileReferenceConfig(config), lookup)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
file := swarm.ConfigReferenceFileTarget(obj.File)
|
||||
refs = append(refs, &swarm.ConfigReference{
|
||||
File: &swarm.ConfigReferenceFileTarget{
|
||||
Name: target,
|
||||
UID: uid,
|
||||
GID: gid,
|
||||
Mode: os.FileMode(*mode),
|
||||
},
|
||||
ConfigName: source,
|
||||
File: &file,
|
||||
ConfigName: obj.Name,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -361,6 +323,63 @@ func convertServiceConfigObjs(
|
|||
return confs, err
|
||||
}
|
||||
|
||||
type swarmReferenceTarget struct {
|
||||
Name string
|
||||
UID string
|
||||
GID string
|
||||
Mode os.FileMode
|
||||
}
|
||||
|
||||
type swarmReferenceObject struct {
|
||||
File swarmReferenceTarget
|
||||
ID string
|
||||
Name string
|
||||
}
|
||||
|
||||
func convertFileObject(
|
||||
namespace Namespace,
|
||||
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
|
||||
}
|
||||
|
||||
source := namespace.Scope(config.Source)
|
||||
if obj.External.External {
|
||||
source = obj.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)
|
||||
}
|
||||
|
||||
return swarmReferenceObject{
|
||||
File: swarmReferenceTarget{
|
||||
Name: target,
|
||||
UID: uid,
|
||||
GID: gid,
|
||||
Mode: os.FileMode(*mode),
|
||||
},
|
||||
Name: source,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func uint32Ptr(value uint32) *uint32 {
|
||||
return &value
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package convert
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -9,7 +10,9 @@ import (
|
|||
composetypes "github.com/docker/cli/cli/compose/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestConvertRestartPolicyFromNone(t *testing.T) {
|
||||
|
@ -372,3 +375,52 @@ func TestConvertUpdateConfigOrder(t *testing.T) {
|
|||
})
|
||||
assert.Equal(t, updateConfig.Order, "stop-first")
|
||||
}
|
||||
|
||||
func TestConvertFileObject(t *testing.T) {
|
||||
namespace := NewNamespace("testing")
|
||||
config := composetypes.FileReferenceConfig{
|
||||
Source: "source",
|
||||
Target: "target",
|
||||
UID: "user",
|
||||
GID: "group",
|
||||
Mode: uint32Ptr(0644),
|
||||
}
|
||||
swarmRef, err := convertFileObject(namespace, config, lookupConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := swarmReferenceObject{
|
||||
Name: "testing_source",
|
||||
File: swarmReferenceTarget{
|
||||
Name: config.Target,
|
||||
UID: config.UID,
|
||||
GID: config.GID,
|
||||
Mode: os.FileMode(0644),
|
||||
},
|
||||
}
|
||||
assert.Equal(t, expected, swarmRef)
|
||||
}
|
||||
|
||||
func lookupConfig(key string) (composetypes.FileObjectConfig, error) {
|
||||
if key != "source" {
|
||||
return composetypes.FileObjectConfig{}, errors.New("bad key")
|
||||
}
|
||||
return composetypes.FileObjectConfig{}, nil
|
||||
}
|
||||
|
||||
func TestConvertFileObjectDefaults(t *testing.T) {
|
||||
namespace := NewNamespace("testing")
|
||||
config := composetypes.FileReferenceConfig{Source: "source"}
|
||||
swarmRef, err := convertFileObject(namespace, config, lookupConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := swarmReferenceObject{
|
||||
Name: "testing_source",
|
||||
File: swarmReferenceTarget{
|
||||
Name: config.Source,
|
||||
UID: "0",
|
||||
GID: "0",
|
||||
Mode: os.FileMode(0444),
|
||||
},
|
||||
}
|
||||
assert.Equal(t, expected, swarmRef)
|
||||
}
|
||||
|
|
|
@ -112,14 +112,14 @@ func loadSections(config map[string]interface{}, configDetails types.ConfigDetai
|
|||
{
|
||||
key: "secrets",
|
||||
fnc: func(config map[string]interface{}) error {
|
||||
cfg.Secrets, err = LoadSecrets(config, configDetails.WorkingDir)
|
||||
cfg.Secrets, err = LoadSecrets(config, configDetails)
|
||||
return err
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "configs",
|
||||
fnc: func(config map[string]interface{}) error {
|
||||
cfg.Configs, err = LoadConfigObjs(config, configDetails.WorkingDir)
|
||||
cfg.Configs, err = LoadConfigObjs(config, configDetails)
|
||||
return err
|
||||
},
|
||||
},
|
||||
|
@ -484,42 +484,48 @@ func LoadVolumes(source map[string]interface{}, version string) (map[string]type
|
|||
|
||||
// LoadSecrets produces a SecretConfig map from a compose file Dict
|
||||
// the source Dict is not validated if directly used. Use Load() to enable validation
|
||||
func LoadSecrets(source map[string]interface{}, workingDir string) (map[string]types.SecretConfig, error) {
|
||||
func LoadSecrets(source map[string]interface{}, details types.ConfigDetails) (map[string]types.SecretConfig, error) {
|
||||
secrets := make(map[string]types.SecretConfig)
|
||||
if err := transform(source, &secrets); err != nil {
|
||||
return secrets, err
|
||||
}
|
||||
for name, secret := range secrets {
|
||||
if secret.External.External && secret.External.Name == "" {
|
||||
secret.External.Name = name
|
||||
secrets[name] = secret
|
||||
}
|
||||
if secret.File != "" {
|
||||
secret.File = absPath(workingDir, secret.File)
|
||||
obj, err := loadFileObjectConfig(name, types.FileObjectConfig(secret), details)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
secrets[name] = types.SecretConfig(obj)
|
||||
}
|
||||
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) {
|
||||
func LoadConfigObjs(source map[string]interface{}, details types.ConfigDetails) (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)
|
||||
obj, err := loadFileObjectConfig(name, types.FileObjectConfig(config), details)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
configs[name] = types.ConfigObjConfig(obj)
|
||||
}
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
func loadFileObjectConfig(name string, obj types.FileObjectConfig, details types.ConfigDetails) (types.FileObjectConfig, error) {
|
||||
if obj.External.External && obj.External.Name == "" {
|
||||
obj.External.Name = name
|
||||
}
|
||||
if obj.File != "" {
|
||||
obj.File = absPath(details.WorkingDir, obj.File)
|
||||
}
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
func absPath(workingDir string, filePath string) string {
|
||||
if filepath.IsAbs(filePath) {
|
||||
return filePath
|
||||
|
|
|
@ -278,7 +278,8 @@ type ServiceVolumeVolume struct {
|
|||
NoCopy bool `mapstructure:"nocopy"`
|
||||
}
|
||||
|
||||
type fileReferenceConfig struct {
|
||||
// FileReferenceConfig for a reference to a swarm file object
|
||||
type FileReferenceConfig struct {
|
||||
Source string
|
||||
Target string
|
||||
UID string
|
||||
|
@ -287,10 +288,10 @@ type fileReferenceConfig struct {
|
|||
}
|
||||
|
||||
// ServiceConfigObjConfig is the config obj configuration for a service
|
||||
type ServiceConfigObjConfig fileReferenceConfig
|
||||
type ServiceConfigObjConfig FileReferenceConfig
|
||||
|
||||
// ServiceSecretConfig is the secret configuration for a service
|
||||
type ServiceSecretConfig fileReferenceConfig
|
||||
type ServiceSecretConfig FileReferenceConfig
|
||||
|
||||
// UlimitsConfig the ulimit configuration
|
||||
type UlimitsConfig struct {
|
||||
|
@ -344,14 +345,15 @@ type CredentialSpecConfig struct {
|
|||
Registry string
|
||||
}
|
||||
|
||||
type fileObjectConfig struct {
|
||||
// FileObjectConfig is a config type for a file used by a service
|
||||
type FileObjectConfig struct {
|
||||
File string
|
||||
External External
|
||||
Labels Labels
|
||||
}
|
||||
|
||||
// SecretConfig for a secret
|
||||
type SecretConfig fileObjectConfig
|
||||
type SecretConfig FileObjectConfig
|
||||
|
||||
// ConfigObjConfig is the config for the swarm "Config" object
|
||||
type ConfigObjConfig fileObjectConfig
|
||||
type ConfigObjConfig FileObjectConfig
|
||||
|
|
Loading…
Reference in New Issue