2018-01-29 16:18:43 -05:00
|
|
|
package loader
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"sort"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"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.
|
2018-02-21 12:31:52 -05:00
|
|
|
func LoadComposefile(dockerCli command.Cli, opts options.Deploy) (*composetypes.Config, error) {
|
2018-01-29 16:18:43 -05:00
|
|
|
configDetails, err := getConfigDetails(opts.Composefiles, dockerCli.In())
|
|
|
|
if err != nil {
|
2018-02-21 12:31:52 -05:00
|
|
|
return nil, err
|
2018-01-29 16:18:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
dicts := getDictsFrom(configDetails.ConfigFiles)
|
|
|
|
config, err := loader.Load(configDetails)
|
|
|
|
if err != nil {
|
|
|
|
if fpe, ok := err.(*loader.ForbiddenPropertiesError); ok {
|
linting: fix incorrectly formatted errors (revive)
cli/compose/interpolation/interpolation.go:102:4: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
"invalid interpolation format for %s: %#v. You may need to escape any $ with another $.",
^
cli/command/stack/loader/loader.go:30:30: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return nil, errors.Errorf("Compose file contains unsupported options:\n\n%s\n",
^
cli/command/formatter/formatter.go:76:30: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return tmpl, errors.Errorf("Template parsing error: %v\n", err)
^
cli/command/formatter/formatter.go:97:24: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("Template parsing error: %v\n", err)
^
cli/command/image/build.go:257:25: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("error checking context: '%s'.", err)
^
cli/command/volume/create.go:35:27: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("Conflicting options: either specify --name or provide positional arg, not both\n")
^
cli/command/container/create.go:160:24: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err)
^
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-03-27 15:13:03 -04:00
|
|
|
//nolint:revive // ignore capitalization error; this error is intentionally formatted multi-line
|
2018-02-21 12:31:52 -05:00
|
|
|
return nil, errors.Errorf("Compose file contains unsupported options:\n\n%s\n",
|
2018-01-29 16:18:43 -05:00
|
|
|
propertyWarnings(fpe.Properties))
|
|
|
|
}
|
|
|
|
|
2018-02-21 12:31:52 -05:00
|
|
|
return nil, err
|
2018-01-29 16:18:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
unsupportedProperties := loader.GetUnsupportedProperties(dicts...)
|
|
|
|
if len(unsupportedProperties) > 0 {
|
|
|
|
fmt.Fprintf(dockerCli.Err(), "Ignoring unsupported options: %s\n\n",
|
|
|
|
strings.Join(unsupportedProperties, ", "))
|
|
|
|
}
|
|
|
|
|
|
|
|
deprecatedProperties := loader.GetDeprecatedProperties(dicts...)
|
|
|
|
if len(deprecatedProperties) > 0 {
|
|
|
|
fmt.Fprintf(dockerCli.Err(), "Ignoring deprecated options:\n\n%s\n\n",
|
|
|
|
propertyWarnings(deprecatedProperties))
|
|
|
|
}
|
2018-02-21 12:31:52 -05:00
|
|
|
return config, nil
|
2018-01-29 16:18:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func getDictsFrom(configFiles []composetypes.ConfigFile) []map[string]interface{} {
|
|
|
|
dicts := []map[string]interface{}{}
|
|
|
|
|
|
|
|
for _, configFile := range configFiles {
|
|
|
|
dicts = append(dicts, configFile.Config)
|
|
|
|
}
|
|
|
|
|
|
|
|
return dicts
|
|
|
|
}
|
|
|
|
|
|
|
|
func propertyWarnings(properties map[string]string) string {
|
|
|
|
var msgs []string
|
|
|
|
for name, description := range properties {
|
|
|
|
msgs = append(msgs, fmt.Sprintf("%s: %s", name, description))
|
|
|
|
}
|
|
|
|
sort.Strings(msgs)
|
|
|
|
return strings.Join(msgs, "\n\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
func getConfigDetails(composefiles []string, stdin io.Reader) (composetypes.ConfigDetails, error) {
|
|
|
|
var details composetypes.ConfigDetails
|
|
|
|
|
|
|
|
if len(composefiles) == 0 {
|
2019-12-06 08:05:33 -05:00
|
|
|
return details, errors.New("Please specify a Compose file (with --compose-file)")
|
2018-01-29 16:18:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if composefiles[0] == "-" && len(composefiles) == 1 {
|
|
|
|
workingDir, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
return details, err
|
|
|
|
}
|
|
|
|
details.WorkingDir = workingDir
|
|
|
|
} else {
|
|
|
|
absPath, err := filepath.Abs(composefiles[0])
|
|
|
|
if err != nil {
|
|
|
|
return details, err
|
|
|
|
}
|
|
|
|
details.WorkingDir = filepath.Dir(absPath)
|
|
|
|
}
|
|
|
|
|
|
|
|
var err error
|
|
|
|
details.ConfigFiles, err = loadConfigFiles(composefiles, stdin)
|
|
|
|
if err != nil {
|
|
|
|
return details, err
|
|
|
|
}
|
|
|
|
// Take the first file version (2 files can't have different version)
|
|
|
|
details.Version = schema.Version(details.ConfigFiles[0].Config)
|
|
|
|
details.Environment, err = buildEnvironment(os.Environ())
|
|
|
|
return details, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func buildEnvironment(env []string) (map[string]string, error) {
|
|
|
|
result := make(map[string]string, len(env))
|
|
|
|
for _, s := range env {
|
|
|
|
// if value is empty, s is like "K=", not "K".
|
|
|
|
if !strings.Contains(s, "=") {
|
|
|
|
return result, errors.Errorf("unexpected environment %q", s)
|
|
|
|
}
|
|
|
|
kv := strings.SplitN(s, "=", 2)
|
|
|
|
result[kv[0]] = kv[1]
|
|
|
|
}
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func loadConfigFiles(filenames []string, stdin io.Reader) ([]composetypes.ConfigFile, error) {
|
|
|
|
var configFiles []composetypes.ConfigFile
|
|
|
|
|
|
|
|
for _, filename := range filenames {
|
|
|
|
configFile, err := loadConfigFile(filename, stdin)
|
|
|
|
if err != nil {
|
|
|
|
return configFiles, err
|
|
|
|
}
|
|
|
|
configFiles = append(configFiles, *configFile)
|
|
|
|
}
|
|
|
|
|
|
|
|
return configFiles, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func loadConfigFile(filename string, stdin io.Reader) (*composetypes.ConfigFile, error) {
|
|
|
|
var bytes []byte
|
|
|
|
var err error
|
|
|
|
|
|
|
|
if filename == "-" {
|
2022-02-25 08:31:31 -05:00
|
|
|
bytes, err = io.ReadAll(stdin)
|
2018-01-29 16:18:43 -05:00
|
|
|
} else {
|
2022-02-25 08:31:31 -05:00
|
|
|
bytes, err = os.ReadFile(filename)
|
2018-01-29 16:18:43 -05:00
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
config, err := loader.ParseYAML(bytes)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &composetypes.ConfigFile{
|
|
|
|
Filename: filename,
|
|
|
|
Config: config,
|
|
|
|
}, nil
|
|
|
|
}
|