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 }