Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Use compose-go to load and parse compose file #5657

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions cli/command/stack/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ package stack

import (
"fmt"
"path/filepath"

specLoader "github.com/compose-spec/compose-go/v2/loader"
compose "github.com/compose-spec/compose-go/v2/types"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
"github.com/docker/cli/cli/command/stack/loader"
"github.com/docker/cli/cli/command/stack/options"
composeLoader "github.com/docker/cli/cli/compose/loader"
composetypes "github.com/docker/cli/cli/compose/types"
"github.com/spf13/cobra"
yaml "gopkg.in/yaml.v2"
)
Expand Down Expand Up @@ -45,9 +47,17 @@ func newConfigCommand(dockerCli command.Cli) *cobra.Command {
}

// outputConfig returns the merged and interpolated config file
func outputConfig(configFiles composetypes.ConfigDetails, skipInterpolation bool) (string, error) {
optsFunc := func(opts *composeLoader.Options) {
func outputConfig(configFiles compose.ConfigDetails, skipInterpolation bool) (string, error) {
var dir string
for _, f := range configFiles.ConfigFiles {
if f.Filename != "" {
dir = filepath.Base(f.Filename)
break
}
}
optsFunc := func(opts *specLoader.Options) {
opts.SkipInterpolation = skipInterpolation
opts.SetProjectName(specLoader.NormalizeProjectName(dir), true)
}
config, err := composeLoader.Load(configFiles, optsFunc)
if err != nil {
Expand Down
27 changes: 16 additions & 11 deletions cli/command/stack/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import (
"io"
"testing"

"github.com/docker/cli/cli/compose/loader"
composetypes "github.com/docker/cli/cli/compose/types"
composetypes "github.com/compose-spec/compose-go/v2/types"

"github.com/docker/cli/internal/test"
"gotest.tools/v3/assert"
)
Expand Down Expand Up @@ -41,13 +41,18 @@ services:
image: busybox:${VERSION}
command: cat file2.txt
`,
expected: `version: "3.7"
expected: `name: firstconfig
services:
foo:
command:
- cat
- file2.txt
image: busybox:1.0
networks:
default: null
networks:
default:
name: firstconfig_default
`,
},
{
Expand All @@ -65,28 +70,28 @@ services:
image: busybox:${VERSION}
command: cat file2.txt
`,
expected: `version: "3.7"
expected: `name: firstconfig
services:
foo:
command:
- cat
- file2.txt
image: busybox:${VERSION}
networks:
default: null
networks:
default:
name: firstconfig_default
`,
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
firstConfigData, err := loader.ParseYAML([]byte(tc.fileOne))
assert.Check(t, err)
secondConfigData, err := loader.ParseYAML([]byte(tc.fileTwo))
assert.Check(t, err)

actual, err := outputConfig(composetypes.ConfigDetails{
ConfigFiles: []composetypes.ConfigFile{
{Config: firstConfigData, Filename: "firstConfig"},
{Config: secondConfigData, Filename: "secondConfig"},
{Content: []byte(tc.fileOne), Filename: "firstConfig"},
{Content: []byte(tc.fileTwo), Filename: "secondConfig"},
},
Environment: map[string]string{
"VERSION": "1.0",
Expand Down
4 changes: 2 additions & 2 deletions cli/command/stack/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ func newDeployCommand(dockerCli command.Cli) *cobra.Command {
if err := validateStackName(opts.Namespace); err != nil {
return err
}
config, err := loader.LoadComposefile(dockerCli, opts)
project, err := loader.LoadComposefile(dockerCli, opts)
if err != nil {
return err
}
return swarm.RunDeploy(cmd.Context(), dockerCli, cmd.Flags(), &opts, config)
return swarm.RunDeploy(cmd.Context(), dockerCli, cmd.Flags(), &opts, project)
},
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return completeNames(dockerCli)(cmd, args, toComplete)
Expand Down
29 changes: 11 additions & 18 deletions cli/command/stack/loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,26 @@ import (
"sort"
"strings"

specloader "github.com/compose-spec/compose-go/v2/loader"
composetypes "github.com/compose-spec/compose-go/v2/types"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/stack/options"
"github.com/docker/cli/cli/compose/loader"
"github.com/docker/cli/cli/compose/schema"
composetypes "github.com/docker/cli/cli/compose/types"
"github.com/pkg/errors"
)

// LoadComposefile parse the composefile specified in the cli and returns its Config and version.
func LoadComposefile(dockerCli command.Cli, opts options.Deploy) (*composetypes.Config, error) {
func LoadComposefile(dockerCli command.Cli, opts options.Deploy) (*composetypes.Project, error) {
configDetails, err := GetConfigDetails(opts.Composefiles, dockerCli.In())
if err != nil {
return nil, err
}

dicts := getDictsFrom(configDetails.ConfigFiles)
config, err := loader.Load(configDetails)
projectNameFunc := func(o *specloader.Options) {
o.SetProjectName(opts.Namespace, true)
}
project, err := loader.Load(configDetails, projectNameFunc)
if err != nil {
if fpe, ok := err.(*loader.ForbiddenPropertiesError); ok {
// this error is intentionally formatted multi-line
Expand All @@ -38,28 +41,18 @@ func LoadComposefile(dockerCli command.Cli, opts options.Deploy) (*composetypes.
return nil, err
}

unsupportedProperties := loader.GetUnsupportedProperties(dicts...)
unsupportedProperties := loader.GetUnsupportedProperties(project.Services)
if len(unsupportedProperties) > 0 {
fmt.Fprintf(dockerCli.Err(), "Ignoring unsupported options: %s\n\n",
strings.Join(unsupportedProperties, ", "))
}

deprecatedProperties := loader.GetDeprecatedProperties(dicts...)
deprecatedProperties := loader.GetDeprecatedProperties(project.Services)
if len(deprecatedProperties) > 0 {
fmt.Fprintf(dockerCli.Err(), "Ignoring deprecated options:\n\n%s\n\n",
propertyWarnings(deprecatedProperties))
}
return config, nil
}

func getDictsFrom(configFiles []composetypes.ConfigFile) []map[string]any {
dicts := []map[string]any{}

for _, configFile := range configFiles {
dicts = append(dicts, configFile.Config)
}

return dicts
return project, nil
}

func propertyWarnings(properties map[string]string) string {
Expand Down Expand Up @@ -157,7 +150,7 @@ func loadConfigFile(filename string, stdin io.Reader) (*composetypes.ConfigFile,
return nil, err
}

config, err := loader.ParseYAML(bytes)
config, err := specloader.ParseYAML(bytes)
if err != nil {
return nil, err
}
Expand Down
6 changes: 3 additions & 3 deletions cli/command/stack/swarm/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import (
"context"
"fmt"

composetypes "github.com/compose-spec/compose-go/v2/types"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/stack/options"
"github.com/docker/cli/cli/compose/convert"
composetypes "github.com/docker/cli/cli/compose/types"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/api/types/versions"
"github.com/pkg/errors"
Expand All @@ -23,7 +23,7 @@ const (
)

// RunDeploy is the swarm implementation of docker stack deploy
func RunDeploy(ctx context.Context, dockerCli command.Cli, flags *pflag.FlagSet, opts *options.Deploy, cfg *composetypes.Config) error {
func RunDeploy(ctx context.Context, dockerCli command.Cli, flags *pflag.FlagSet, opts *options.Deploy, project *composetypes.Project) error {
if err := validateResolveImageFlag(opts); err != nil {
return err
}
Expand All @@ -38,7 +38,7 @@ func RunDeploy(ctx context.Context, dockerCli command.Cli, flags *pflag.FlagSet,
"In a future release, --detach=false will become the default.")
}

return deployCompose(ctx, dockerCli, opts, cfg)
return deployCompose(ctx, dockerCli, opts, project)
}

// validateResolveImageFlag validates the opts.resolveImage command line option
Expand Down
18 changes: 9 additions & 9 deletions cli/command/stack/swarm/deploy_composefile.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import (
"errors"
"fmt"

composetypes "github.com/compose-spec/compose-go/v2/types"
"github.com/docker/cli/cli/command"
servicecli "github.com/docker/cli/cli/command/service"
"github.com/docker/cli/cli/command/stack/options"
"github.com/docker/cli/cli/compose/convert"
composetypes "github.com/docker/cli/cli/compose/types"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
Expand All @@ -18,7 +18,7 @@ import (
"github.com/docker/docker/errdefs"
)

func deployCompose(ctx context.Context, dockerCli command.Cli, opts *options.Deploy, config *composetypes.Config) error {
func deployCompose(ctx context.Context, dockerCli command.Cli, opts *options.Deploy, project *composetypes.Project) error {
if err := checkDaemonIsSwarmManager(ctx, dockerCli); err != nil {
return err
}
Expand All @@ -27,38 +27,38 @@ func deployCompose(ctx context.Context, dockerCli command.Cli, opts *options.Dep

if opts.Prune {
services := map[string]struct{}{}
for _, service := range config.Services {
for _, service := range project.Services {
services[service.Name] = struct{}{}
}
pruneServices(ctx, dockerCli, namespace, services)
}

serviceNetworks := getServicesDeclaredNetworks(config.Services)
networks, externalNetworks := convert.Networks(namespace, config.Networks, serviceNetworks)
serviceNetworks := getServicesDeclaredNetworks(project.Services)
networks, externalNetworks := convert.Networks(namespace, project.Networks, serviceNetworks)
if err := validateExternalNetworks(ctx, dockerCli.Client(), externalNetworks); err != nil {
return err
}
if err := createNetworks(ctx, dockerCli, namespace, networks); err != nil {
return err
}

secrets, err := convert.Secrets(namespace, config.Secrets)
secrets, err := convert.Secrets(namespace, project.Secrets)
if err != nil {
return err
}
if err := createSecrets(ctx, dockerCli, secrets); err != nil {
return err
}

configs, err := convert.Configs(namespace, config.Configs)
configs, err := convert.Configs(namespace, project.Configs)
if err != nil {
return err
}
if err := createConfigs(ctx, dockerCli, configs); err != nil {
return err
}

services, err := convert.Services(ctx, namespace, config, dockerCli.Client())
services, err := convert.Services(ctx, namespace, project, dockerCli.Client())
if err != nil {
return err
}
Expand All @@ -75,7 +75,7 @@ func deployCompose(ctx context.Context, dockerCli command.Cli, opts *options.Dep
return waitOnServices(ctx, dockerCli, serviceIDs, opts.Quiet)
}

func getServicesDeclaredNetworks(serviceConfigs []composetypes.ServiceConfig) map[string]struct{} {
func getServicesDeclaredNetworks(serviceConfigs composetypes.Services) map[string]struct{} {
serviceNetworks := map[string]struct{}{}
for _, serviceConfig := range serviceConfigs {
if len(serviceConfig.Networks) == 0 {
Expand Down
46 changes: 23 additions & 23 deletions cli/compose/convert/compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import (
"os"
"strings"

composetypes "github.com/docker/cli/cli/compose/types"
"github.com/docker/docker/api/types/network"
composetypes "github.com/compose-spec/compose-go/v2/types"
networktypes "github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/swarm"
)

Expand Down Expand Up @@ -51,45 +51,45 @@ func AddStackLabel(namespace Namespace, labels map[string]string) map[string]str
type networkMap map[string]composetypes.NetworkConfig

// Networks from the compose-file type to the engine API type
func Networks(namespace Namespace, networks networkMap, servicesNetworks map[string]struct{}) (map[string]network.CreateOptions, []string) {
func Networks(namespace Namespace, networks composetypes.Networks, servicesNetworks map[string]struct{}) (map[string]networktypes.CreateOptions, []string) {
if networks == nil {
networks = make(map[string]composetypes.NetworkConfig)
}

externalNetworks := []string{}
result := make(map[string]network.CreateOptions)
result := make(map[string]networktypes.CreateOptions)
for internalName := range servicesNetworks {
nw := networks[internalName]
if nw.External.External {
externalNetworks = append(externalNetworks, nw.Name)
network := networks[internalName]
if network.External {
externalNetworks = append(externalNetworks, network.Name)
continue
}

createOpts := network.CreateOptions{
Labels: AddStackLabel(namespace, nw.Labels),
Driver: nw.Driver,
Options: nw.DriverOpts,
Internal: nw.Internal,
Attachable: nw.Attachable,
createOpts := networktypes.CreateOptions{
Labels: AddStackLabel(namespace, network.Labels),
Driver: network.Driver,
Options: network.DriverOpts,
Internal: network.Internal,
Attachable: network.Attachable,
}

if nw.Ipam.Driver != "" || len(nw.Ipam.Config) > 0 {
createOpts.IPAM = &network.IPAM{}
if network.Ipam.Driver != "" || len(network.Ipam.Config) > 0 {
createOpts.IPAM = &networktypes.IPAM{}
}

if nw.Ipam.Driver != "" {
createOpts.IPAM.Driver = nw.Ipam.Driver
if network.Ipam.Driver != "" {
createOpts.IPAM.Driver = network.Ipam.Driver
}
for _, ipamConfig := range nw.Ipam.Config {
config := network.IPAMConfig{
for _, ipamConfig := range network.Ipam.Config {
config := networktypes.IPAMConfig{
Subnet: ipamConfig.Subnet,
}
createOpts.IPAM.Config = append(createOpts.IPAM.Config, config)
}

networkName := namespace.Scope(internalName)
if nw.Name != "" {
networkName = nw.Name
if network.Name != "" {
networkName = network.Name
}
result[networkName] = createOpts
}
Expand All @@ -101,7 +101,7 @@ func Networks(namespace Namespace, networks networkMap, servicesNetworks map[str
func Secrets(namespace Namespace, secrets map[string]composetypes.SecretConfig) ([]swarm.SecretSpec, error) {
result := []swarm.SecretSpec{}
for name, secret := range secrets {
if secret.External.External {
if secret.External {
continue
}

Expand Down Expand Up @@ -136,7 +136,7 @@ func Secrets(namespace Namespace, secrets map[string]composetypes.SecretConfig)
func Configs(namespace Namespace, configs map[string]composetypes.ConfigObjConfig) ([]swarm.ConfigSpec, error) {
result := []swarm.ConfigSpec{}
for name, config := range configs {
if config.External.External {
if config.External {
continue
}

Expand Down
Loading