Make use of existing modules and functions in order to output the merged configs. Added skip interpolation flag of variables, so that you can pipe the output back to stack deploy without much hassle. Signed-off-by: Stoica-Marcu Floris-Andrei <floris.sm@gmail.com> Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
154 lines
4.1 KiB
Go
154 lines
4.1 KiB
Go
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.
|
|
func LoadComposefile(dockerCli command.Cli, opts options.Deploy) (*composetypes.Config, error) {
|
|
configDetails, err := GetConfigDetails(opts.Composefiles, dockerCli.In())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
dicts := getDictsFrom(configDetails.ConfigFiles)
|
|
config, err := loader.Load(configDetails)
|
|
if err != nil {
|
|
if fpe, ok := err.(*loader.ForbiddenPropertiesError); ok {
|
|
//nolint:revive // ignore capitalization error; this error is intentionally formatted multi-line
|
|
return nil, errors.Errorf("Compose file contains unsupported options:\n\n%s\n",
|
|
propertyWarnings(fpe.Properties))
|
|
}
|
|
|
|
return nil, err
|
|
}
|
|
|
|
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))
|
|
}
|
|
return config, nil
|
|
}
|
|
|
|
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")
|
|
}
|
|
|
|
// GetConfigDetails parse the composefiles specified in the cli and returns their ConfigDetails
|
|
func GetConfigDetails(composefiles []string, stdin io.Reader) (composetypes.ConfigDetails, error) {
|
|
var details composetypes.ConfigDetails
|
|
|
|
if len(composefiles) == 0 {
|
|
return details, errors.New("Please specify a Compose file (with --compose-file)")
|
|
}
|
|
|
|
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 == "-" {
|
|
bytes, err = io.ReadAll(stdin)
|
|
} else {
|
|
bytes, err = os.ReadFile(filename)
|
|
}
|
|
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
|
|
}
|