mirror of https://github.com/docker/cli.git
Simplify the marshaling of compose types.Config
- Add `Version` to `types.Config` - Add a new `Services` types (that is just `[]ServiceConfig`) and add `MarshalYAML` method on it. - Clean other top-level custom marshaling as `Services` is the only one required. Signed-off-by: Vincent Demeester <vincent@sbr.pm>
This commit is contained in:
parent
939938b976
commit
cf86a4d922
|
@ -22,11 +22,11 @@ func RunDeploy(dockerCli *KubeCli, opts options.Deploy) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the compose file
|
// Parse the compose file
|
||||||
cfg, version, err := loader.LoadComposefile(dockerCli, opts)
|
cfg, err := loader.LoadComposefile(dockerCli, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
stack, err := LoadStack(opts.Namespace, version, *cfg)
|
stack, err := LoadStack(opts.Namespace, *cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,32 +7,9 @@ import (
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type versionedConfig struct {
|
|
||||||
composetypes.Config
|
|
||||||
version string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c versionedConfig) MarshalYAML() (interface{}, error) {
|
|
||||||
services := map[string]composetypes.ServiceConfig{}
|
|
||||||
for _, service := range c.Services {
|
|
||||||
services[service.Name] = service
|
|
||||||
}
|
|
||||||
return map[string]interface{}{
|
|
||||||
"services": services,
|
|
||||||
"networks": c.Networks,
|
|
||||||
"volumes": c.Volumes,
|
|
||||||
"secrets": c.Secrets,
|
|
||||||
"configs": c.Configs,
|
|
||||||
"version": c.version,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadStack loads a stack from a Compose config, with a given name.
|
// LoadStack loads a stack from a Compose config, with a given name.
|
||||||
func LoadStack(name, version string, cfg composetypes.Config) (*apiv1beta1.Stack, error) {
|
func LoadStack(name string, cfg composetypes.Config) (*apiv1beta1.Stack, error) {
|
||||||
res, err := yaml.Marshal(versionedConfig{
|
res, err := yaml.Marshal(cfg)
|
||||||
version: version,
|
|
||||||
Config: cfg,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLoadStack(t *testing.T) {
|
func TestLoadStack(t *testing.T) {
|
||||||
s, err := LoadStack("foo", "3.1", composetypes.Config{
|
s, err := LoadStack("foo", composetypes.Config{
|
||||||
|
Version: "3.1",
|
||||||
Filename: "banana",
|
Filename: "banana",
|
||||||
Services: []composetypes.ServiceConfig{
|
Services: []composetypes.ServiceConfig{
|
||||||
{
|
{
|
||||||
|
@ -29,16 +30,16 @@ func TestLoadStack(t *testing.T) {
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
},
|
},
|
||||||
Spec: apiv1beta1.StackSpec{
|
Spec: apiv1beta1.StackSpec{
|
||||||
ComposeFile: string(`configs: {}
|
ComposeFile: string(`version: "3.1"
|
||||||
networks: {}
|
|
||||||
secrets: {}
|
|
||||||
services:
|
services:
|
||||||
bar:
|
bar:
|
||||||
image: bar
|
image: bar
|
||||||
foo:
|
foo:
|
||||||
image: foo
|
image: foo
|
||||||
version: "3.1"
|
networks: {}
|
||||||
volumes: {}
|
volumes: {}
|
||||||
|
secrets: {}
|
||||||
|
configs: {}
|
||||||
`),
|
`),
|
||||||
},
|
},
|
||||||
}, s)
|
}, s)
|
||||||
|
|
|
@ -18,21 +18,21 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// LoadComposefile parse the composefile specified in the cli and returns its Config and version.
|
// LoadComposefile parse the composefile specified in the cli and returns its Config and version.
|
||||||
func LoadComposefile(dockerCli command.Cli, opts options.Deploy) (*composetypes.Config, string, error) {
|
func LoadComposefile(dockerCli command.Cli, opts options.Deploy) (*composetypes.Config, error) {
|
||||||
configDetails, err := getConfigDetails(opts.Composefiles, dockerCli.In())
|
configDetails, err := getConfigDetails(opts.Composefiles, dockerCli.In())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
dicts := getDictsFrom(configDetails.ConfigFiles)
|
dicts := getDictsFrom(configDetails.ConfigFiles)
|
||||||
config, err := loader.Load(configDetails)
|
config, err := loader.Load(configDetails)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if fpe, ok := err.(*loader.ForbiddenPropertiesError); ok {
|
if fpe, ok := err.(*loader.ForbiddenPropertiesError); ok {
|
||||||
return nil, "", errors.Errorf("Compose file contains unsupported options:\n\n%s\n",
|
return nil, errors.Errorf("Compose file contains unsupported options:\n\n%s\n",
|
||||||
propertyWarnings(fpe.Properties))
|
propertyWarnings(fpe.Properties))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
unsupportedProperties := loader.GetUnsupportedProperties(dicts...)
|
unsupportedProperties := loader.GetUnsupportedProperties(dicts...)
|
||||||
|
@ -46,7 +46,7 @@ func LoadComposefile(dockerCli command.Cli, opts options.Deploy) (*composetypes.
|
||||||
fmt.Fprintf(dockerCli.Err(), "Ignoring deprecated options:\n\n%s\n\n",
|
fmt.Fprintf(dockerCli.Err(), "Ignoring deprecated options:\n\n%s\n\n",
|
||||||
propertyWarnings(deprecatedProperties))
|
propertyWarnings(deprecatedProperties))
|
||||||
}
|
}
|
||||||
return config, configDetails.Version, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDictsFrom(configFiles []composetypes.ConfigFile) []map[string]interface{} {
|
func getDictsFrom(configFiles []composetypes.ConfigFile) []map[string]interface{} {
|
||||||
|
|
|
@ -18,7 +18,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func deployCompose(ctx context.Context, dockerCli command.Cli, opts options.Deploy) error {
|
func deployCompose(ctx context.Context, dockerCli command.Cli, opts options.Deploy) error {
|
||||||
config, _, err := loader.LoadComposefile(dockerCli, opts)
|
config, err := loader.LoadComposefile(dockerCli, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
func fullExampleConfig(workingDir, homeDir string) *types.Config {
|
func fullExampleConfig(workingDir, homeDir string) *types.Config {
|
||||||
return &types.Config{
|
return &types.Config{
|
||||||
|
Version: "3.6",
|
||||||
Services: services(workingDir, homeDir),
|
Services: services(workingDir, homeDir),
|
||||||
Networks: networks(),
|
Networks: networks(),
|
||||||
Volumes: volumes(),
|
Volumes: volumes(),
|
||||||
|
|
|
@ -98,7 +98,9 @@ func validateForbidden(configDict map[string]interface{}) error {
|
||||||
|
|
||||||
func loadSections(config map[string]interface{}, configDetails types.ConfigDetails) (*types.Config, error) {
|
func loadSections(config map[string]interface{}, configDetails types.ConfigDetails) (*types.Config, error) {
|
||||||
var err error
|
var err error
|
||||||
cfg := types.Config{}
|
cfg := types.Config{
|
||||||
|
Version: schema.Version(config),
|
||||||
|
}
|
||||||
|
|
||||||
var loaders = []struct {
|
var loaders = []struct {
|
||||||
key string
|
key string
|
||||||
|
|
|
@ -119,6 +119,7 @@ func strPtr(val string) *string {
|
||||||
}
|
}
|
||||||
|
|
||||||
var sampleConfig = types.Config{
|
var sampleConfig = types.Config{
|
||||||
|
Version: "3.0",
|
||||||
Services: []types.ServiceConfig{
|
Services: []types.ServiceConfig{
|
||||||
{
|
{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
|
@ -174,6 +175,7 @@ func TestParseYAML(t *testing.T) {
|
||||||
func TestLoad(t *testing.T) {
|
func TestLoad(t *testing.T) {
|
||||||
actual, err := Load(buildConfigDetails(sampleDict, nil))
|
actual, err := Load(buildConfigDetails(sampleDict, nil))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, sampleConfig.Version, actual.Version)
|
||||||
assert.Equal(t, serviceSort(sampleConfig.Services), serviceSort(actual.Services))
|
assert.Equal(t, serviceSort(sampleConfig.Services), serviceSort(actual.Services))
|
||||||
assert.Equal(t, sampleConfig.Networks, actual.Networks)
|
assert.Equal(t, sampleConfig.Networks, actual.Networks)
|
||||||
assert.Equal(t, sampleConfig.Volumes, actual.Volumes)
|
assert.Equal(t, sampleConfig.Volumes, actual.Volumes)
|
||||||
|
@ -573,6 +575,7 @@ networks:
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
expected := &types.Config{
|
expected := &types.Config{
|
||||||
Filename: "filename.yml",
|
Filename: "filename.yml",
|
||||||
|
Version: "3.4",
|
||||||
Services: []types.ServiceConfig{
|
Services: []types.ServiceConfig{
|
||||||
{
|
{
|
||||||
Name: "web",
|
Name: "web",
|
||||||
|
|
|
@ -207,6 +207,7 @@ func TestLoadLogging(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, &types.Config{
|
require.Equal(t, &types.Config{
|
||||||
Filename: "base.yml",
|
Filename: "base.yml",
|
||||||
|
Version: "3.4",
|
||||||
Services: []types.ServiceConfig{
|
Services: []types.ServiceConfig{
|
||||||
{
|
{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
|
@ -325,6 +326,7 @@ func TestLoadMultipleServicePorts(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, &types.Config{
|
require.Equal(t, &types.Config{
|
||||||
Filename: "base.yml",
|
Filename: "base.yml",
|
||||||
|
Version: "3.4",
|
||||||
Services: []types.ServiceConfig{
|
Services: []types.ServiceConfig{
|
||||||
{
|
{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
|
@ -450,6 +452,7 @@ func TestLoadMultipleSecretsConfig(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, &types.Config{
|
require.Equal(t, &types.Config{
|
||||||
Filename: "base.yml",
|
Filename: "base.yml",
|
||||||
|
Version: "3.4",
|
||||||
Services: []types.ServiceConfig{
|
Services: []types.ServiceConfig{
|
||||||
{
|
{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
|
@ -575,6 +578,7 @@ func TestLoadMultipleConfigobjsConfig(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, &types.Config{
|
require.Equal(t, &types.Config{
|
||||||
Filename: "base.yml",
|
Filename: "base.yml",
|
||||||
|
Version: "3.4",
|
||||||
Services: []types.ServiceConfig{
|
Services: []types.ServiceConfig{
|
||||||
{
|
{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
|
@ -690,6 +694,7 @@ func TestLoadMultipleUlimits(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, &types.Config{
|
require.Equal(t, &types.Config{
|
||||||
Filename: "base.yml",
|
Filename: "base.yml",
|
||||||
|
Version: "3.4",
|
||||||
Services: []types.ServiceConfig{
|
Services: []types.ServiceConfig{
|
||||||
{
|
{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
|
@ -808,6 +813,7 @@ func TestLoadMultipleNetworks(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, &types.Config{
|
require.Equal(t, &types.Config{
|
||||||
Filename: "base.yml",
|
Filename: "base.yml",
|
||||||
|
Version: "3.4",
|
||||||
Services: []types.ServiceConfig{
|
Services: []types.ServiceConfig{
|
||||||
{
|
{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
|
@ -895,6 +901,7 @@ func TestLoadMultipleConfigs(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, &types.Config{
|
require.Equal(t, &types.Config{
|
||||||
Filename: "base.yml",
|
Filename: "base.yml",
|
||||||
|
Version: "3.4",
|
||||||
Services: []types.ServiceConfig{
|
Services: []types.ServiceConfig{
|
||||||
{
|
{
|
||||||
Name: "bar",
|
Name: "bar",
|
||||||
|
|
|
@ -9,26 +9,7 @@ import (
|
||||||
|
|
||||||
func TestMarshallConfig(t *testing.T) {
|
func TestMarshallConfig(t *testing.T) {
|
||||||
cfg := fullExampleConfig("/foo", "/bar")
|
cfg := fullExampleConfig("/foo", "/bar")
|
||||||
expected := `configs: {}
|
expected := `version: "3.6"
|
||||||
networks:
|
|
||||||
external-network:
|
|
||||||
name: external-network
|
|
||||||
external: true
|
|
||||||
other-external-network:
|
|
||||||
name: my-cool-network
|
|
||||||
external: true
|
|
||||||
other-network:
|
|
||||||
driver: overlay
|
|
||||||
driver_opts:
|
|
||||||
baz: "1"
|
|
||||||
foo: bar
|
|
||||||
ipam:
|
|
||||||
driver: overlay
|
|
||||||
config:
|
|
||||||
- subnet: 172.16.238.0/24
|
|
||||||
- subnet: 2001:3984:3989::/64
|
|
||||||
some-network: {}
|
|
||||||
secrets: {}
|
|
||||||
services:
|
services:
|
||||||
foo:
|
foo:
|
||||||
build:
|
build:
|
||||||
|
@ -292,6 +273,24 @@ services:
|
||||||
tmpfs:
|
tmpfs:
|
||||||
size: 10000
|
size: 10000
|
||||||
working_dir: /code
|
working_dir: /code
|
||||||
|
networks:
|
||||||
|
external-network:
|
||||||
|
name: external-network
|
||||||
|
external: true
|
||||||
|
other-external-network:
|
||||||
|
name: my-cool-network
|
||||||
|
external: true
|
||||||
|
other-network:
|
||||||
|
driver: overlay
|
||||||
|
driver_opts:
|
||||||
|
baz: "1"
|
||||||
|
foo: bar
|
||||||
|
ipam:
|
||||||
|
driver: overlay
|
||||||
|
config:
|
||||||
|
- subnet: 172.16.238.0/24
|
||||||
|
- subnet: 2001:3984:3989::/64
|
||||||
|
some-network: {}
|
||||||
volumes:
|
volumes:
|
||||||
another-volume:
|
another-volume:
|
||||||
name: user_specified_name
|
name: user_specified_name
|
||||||
|
@ -314,6 +313,8 @@ volumes:
|
||||||
baz: "1"
|
baz: "1"
|
||||||
foo: bar
|
foo: bar
|
||||||
some-volume: {}
|
some-volume: {}
|
||||||
|
secrets: {}
|
||||||
|
configs: {}
|
||||||
`
|
`
|
||||||
|
|
||||||
actual, err := yaml.Marshal(cfg)
|
actual, err := yaml.Marshal(cfg)
|
||||||
|
|
|
@ -71,26 +71,24 @@ func (cd ConfigDetails) LookupEnv(key string) (string, bool) {
|
||||||
// Config is a full compose file configuration
|
// Config is a full compose file configuration
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Filename string `yaml:"-"`
|
Filename string `yaml:"-"`
|
||||||
Services []ServiceConfig
|
Version string
|
||||||
|
Services Services
|
||||||
Networks map[string]NetworkConfig
|
Networks map[string]NetworkConfig
|
||||||
Volumes map[string]VolumeConfig
|
Volumes map[string]VolumeConfig
|
||||||
Secrets map[string]SecretConfig
|
Secrets map[string]SecretConfig
|
||||||
Configs map[string]ConfigObjConfig
|
Configs map[string]ConfigObjConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalYAML makes Config implement yaml.Marshaller
|
// Services is a list of ServiceConfig
|
||||||
func (c *Config) MarshalYAML() (interface{}, error) {
|
type Services []ServiceConfig
|
||||||
|
|
||||||
|
// MarshalYAML makes Services implement yaml.Marshaller
|
||||||
|
func (s Services) MarshalYAML() (interface{}, error) {
|
||||||
services := map[string]ServiceConfig{}
|
services := map[string]ServiceConfig{}
|
||||||
for _, service := range c.Services {
|
for _, service := range s {
|
||||||
services[service.Name] = service
|
services[service.Name] = service
|
||||||
}
|
}
|
||||||
return map[string]interface{}{
|
return services, nil
|
||||||
"services": services,
|
|
||||||
"networks": c.Networks,
|
|
||||||
"volumes": c.Volumes,
|
|
||||||
"secrets": c.Secrets,
|
|
||||||
"configs": c.Configs,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServiceConfig is the configuration of one service
|
// ServiceConfig is the configuration of one service
|
||||||
|
|
Loading…
Reference in New Issue