github.com/xeptore/docker-cli@v20.10.14+incompatible/cli/command/stack/loader/loader.go (about)

     1  package loader
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"sort"
    10  	"strings"
    11  
    12  	"github.com/docker/cli/cli/command"
    13  	"github.com/docker/cli/cli/command/stack/options"
    14  	"github.com/docker/cli/cli/compose/loader"
    15  	"github.com/docker/cli/cli/compose/schema"
    16  	composetypes "github.com/docker/cli/cli/compose/types"
    17  	"github.com/pkg/errors"
    18  )
    19  
    20  // LoadComposefile parse the composefile specified in the cli and returns its Config and version.
    21  func LoadComposefile(dockerCli command.Cli, opts options.Deploy) (*composetypes.Config, error) {
    22  	configDetails, err := getConfigDetails(opts.Composefiles, dockerCli.In())
    23  	if err != nil {
    24  		return nil, err
    25  	}
    26  
    27  	dicts := getDictsFrom(configDetails.ConfigFiles)
    28  	config, err := loader.Load(configDetails)
    29  	if err != nil {
    30  		if fpe, ok := err.(*loader.ForbiddenPropertiesError); ok {
    31  			return nil, errors.Errorf("Compose file contains unsupported options:\n\n%s\n",
    32  				propertyWarnings(fpe.Properties))
    33  		}
    34  
    35  		return nil, err
    36  	}
    37  
    38  	unsupportedProperties := loader.GetUnsupportedProperties(dicts...)
    39  	if len(unsupportedProperties) > 0 {
    40  		fmt.Fprintf(dockerCli.Err(), "Ignoring unsupported options: %s\n\n",
    41  			strings.Join(unsupportedProperties, ", "))
    42  	}
    43  
    44  	deprecatedProperties := loader.GetDeprecatedProperties(dicts...)
    45  	if len(deprecatedProperties) > 0 {
    46  		fmt.Fprintf(dockerCli.Err(), "Ignoring deprecated options:\n\n%s\n\n",
    47  			propertyWarnings(deprecatedProperties))
    48  	}
    49  	return config, nil
    50  }
    51  
    52  func getDictsFrom(configFiles []composetypes.ConfigFile) []map[string]interface{} {
    53  	dicts := []map[string]interface{}{}
    54  
    55  	for _, configFile := range configFiles {
    56  		dicts = append(dicts, configFile.Config)
    57  	}
    58  
    59  	return dicts
    60  }
    61  
    62  func propertyWarnings(properties map[string]string) string {
    63  	var msgs []string
    64  	for name, description := range properties {
    65  		msgs = append(msgs, fmt.Sprintf("%s: %s", name, description))
    66  	}
    67  	sort.Strings(msgs)
    68  	return strings.Join(msgs, "\n\n")
    69  }
    70  
    71  func getConfigDetails(composefiles []string, stdin io.Reader) (composetypes.ConfigDetails, error) {
    72  	var details composetypes.ConfigDetails
    73  
    74  	if len(composefiles) == 0 {
    75  		return details, errors.New("Please specify a Compose file (with --compose-file)")
    76  	}
    77  
    78  	if composefiles[0] == "-" && len(composefiles) == 1 {
    79  		workingDir, err := os.Getwd()
    80  		if err != nil {
    81  			return details, err
    82  		}
    83  		details.WorkingDir = workingDir
    84  	} else {
    85  		absPath, err := filepath.Abs(composefiles[0])
    86  		if err != nil {
    87  			return details, err
    88  		}
    89  		details.WorkingDir = filepath.Dir(absPath)
    90  	}
    91  
    92  	var err error
    93  	details.ConfigFiles, err = loadConfigFiles(composefiles, stdin)
    94  	if err != nil {
    95  		return details, err
    96  	}
    97  	// Take the first file version (2 files can't have different version)
    98  	details.Version = schema.Version(details.ConfigFiles[0].Config)
    99  	details.Environment, err = buildEnvironment(os.Environ())
   100  	return details, err
   101  }
   102  
   103  func buildEnvironment(env []string) (map[string]string, error) {
   104  	result := make(map[string]string, len(env))
   105  	for _, s := range env {
   106  		// if value is empty, s is like "K=", not "K".
   107  		if !strings.Contains(s, "=") {
   108  			return result, errors.Errorf("unexpected environment %q", s)
   109  		}
   110  		kv := strings.SplitN(s, "=", 2)
   111  		result[kv[0]] = kv[1]
   112  	}
   113  	return result, nil
   114  }
   115  
   116  func loadConfigFiles(filenames []string, stdin io.Reader) ([]composetypes.ConfigFile, error) {
   117  	var configFiles []composetypes.ConfigFile
   118  
   119  	for _, filename := range filenames {
   120  		configFile, err := loadConfigFile(filename, stdin)
   121  		if err != nil {
   122  			return configFiles, err
   123  		}
   124  		configFiles = append(configFiles, *configFile)
   125  	}
   126  
   127  	return configFiles, nil
   128  }
   129  
   130  func loadConfigFile(filename string, stdin io.Reader) (*composetypes.ConfigFile, error) {
   131  	var bytes []byte
   132  	var err error
   133  
   134  	if filename == "-" {
   135  		bytes, err = ioutil.ReadAll(stdin)
   136  	} else {
   137  		bytes, err = ioutil.ReadFile(filename)
   138  	}
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  
   143  	config, err := loader.ParseYAML(bytes)
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  
   148  	return &composetypes.ConfigFile{
   149  		Filename: filename,
   150  		Config:   config,
   151  	}, nil
   152  }