package stack // https://github.com/docker/cli/blob/master/cli/command/stack/loader/loader.go import ( "context" "fmt" "io/ioutil" "path/filepath" "sort" "strings" "coopcloud.tech/abra/pkg/i18n" composeGoCli "github.com/compose-spec/compose-go/v2/cli" composeGoTypes "github.com/compose-spec/compose-go/v2/types" "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" "github.com/sirupsen/logrus" ) type LoadConf struct { ComposeFiles []string AppEnv map[string]string } func LoadCompose(conf LoadConf) (*composeGoTypes.Project, error) { var project *composeGoTypes.Project // NOTE(d1): silence compose-go internal logger logrus.SetOutput(ioutil.Discard) var projectOptions *composeGoCli.ProjectOptions if len(conf.ComposeFiles) == 0 { return project, errors.New(i18n.G("LoadCompose: provide compose files")) } if len(conf.AppEnv) == 0 { var err error projectOptions, err = composeGoCli.NewProjectOptions( conf.ComposeFiles, composeGoCli.WithInterpolation(false), ) if err != nil { return project, err } } else { var env []string for k, v := range conf.AppEnv { env = append(env, fmt.Sprintf("%s=%s", k, v)) } var err error projectOptions, err = composeGoCli.NewProjectOptions( conf.ComposeFiles, composeGoCli.WithEnv(env), ) if err != nil { return project, err } } project, err := projectOptions.LoadProject(context.Background()) if err != nil { return project, err } return project, 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") } func getConfigDetails(composefiles []string, appEnv map[string]string) (composetypes.ConfigDetails, error) { var details composetypes.ConfigDetails absPath, err := filepath.Abs(composefiles[0]) if err != nil { return details, err } details.WorkingDir = filepath.Dir(absPath) details.ConfigFiles, err = loadConfigFiles(composefiles) 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 = appEnv 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.New(i18n.G("unexpected environment %q", s)) } kv := strings.SplitN(s, "=", 2) result[kv[0]] = kv[1] } return result, nil } func loadConfigFiles(filenames []string) ([]composetypes.ConfigFile, error) { var configFiles []composetypes.ConfigFile for _, filename := range filenames { configFile, err := loadConfigFile(filename) if err != nil { return configFiles, err } configFiles = append(configFiles, *configFile) } return configFiles, nil } func loadConfigFile(filename string) (*composetypes.ConfigFile, error) { var bytes []byte var err error bytes, err = ioutil.ReadFile(filename) if err != nil { return nil, err } config, err := loader.ParseYAML(bytes) if err != nil { return nil, fmt.Errorf("%s: %s", filename, err) } return &composetypes.ConfigFile{ Filename: filename, Config: config, }, nil }