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:
Vincent Demeester 2018-02-21 18:31:52 +01:00
parent 939938b976
commit cf86a4d922
No known key found for this signature in database
GPG Key ID: 083CC6FD6EB699A3
11 changed files with 60 additions and 70 deletions

View File

@ -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
} }

View File

@ -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
} }

View File

@ -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)

View File

@ -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{} {

View File

@ -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
} }

View File

@ -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(),

View File

@ -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

View File

@ -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",

View File

@ -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",

View File

@ -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)

View File

@ -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