forked from toolshed/abra
		
	
		
			
				
	
	
		
			150 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			150 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package stack // https://github.com/docker/cli/blob/master/cli/command/stack/loader/loader.go
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"io/ioutil"
 | |
| 	"path/filepath"
 | |
| 	"sort"
 | |
| 	"strings"
 | |
| 
 | |
| 	"coopcloud.tech/abra/pkg/log"
 | |
| 	"github.com/docker/cli/cli/compose/loader"
 | |
| 	"github.com/docker/cli/cli/compose/schema"
 | |
| 	composetypes "github.com/docker/cli/cli/compose/types"
 | |
| )
 | |
| 
 | |
| // DontSkipValidation ensures validation is done for compose file loading
 | |
| func DontSkipValidation(opts *loader.Options) {
 | |
| 	opts.SkipValidation = false
 | |
| }
 | |
| 
 | |
| // SkipInterpolation skip interpolating environment variables.
 | |
| func SkipInterpolation(opts *loader.Options) {
 | |
| 	opts.SkipInterpolation = true
 | |
| }
 | |
| 
 | |
| // LoadComposefile parse the composefile specified in the cli and returns its Config and version.
 | |
| func LoadComposefile(opts Deploy, appEnv map[string]string, options ...func(*loader.Options)) (*composetypes.Config, error) {
 | |
| 	configDetails, err := getConfigDetails(opts.Composefiles, appEnv)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	if options == nil {
 | |
| 		options = []func(*loader.Options){DontSkipValidation}
 | |
| 	}
 | |
| 
 | |
| 	dicts := getDictsFrom(configDetails.ConfigFiles)
 | |
| 	config, err := loader.Load(configDetails, options...)
 | |
| 	if err != nil {
 | |
| 		if fpe, ok := err.(*loader.ForbiddenPropertiesError); ok {
 | |
| 			return nil, fmt.Errorf("compose file contains unsupported options: %s",
 | |
| 				propertyWarnings(fpe.Properties))
 | |
| 		}
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	recipeName, exists := appEnv["RECIPE"]
 | |
| 	if !exists {
 | |
| 		recipeName, _ = appEnv["TYPE"]
 | |
| 	}
 | |
| 
 | |
| 	unsupportedProperties := loader.GetUnsupportedProperties(dicts...)
 | |
| 	if len(unsupportedProperties) > 0 {
 | |
| 		log.Warnf("%s: ignoring unsupported options: %s",
 | |
| 			recipeName, strings.Join(unsupportedProperties, ", "))
 | |
| 	}
 | |
| 
 | |
| 	deprecatedProperties := loader.GetDeprecatedProperties(dicts...)
 | |
| 	if len(deprecatedProperties) > 0 {
 | |
| 		log.Warnf("%s: ignoring deprecated options: %s",
 | |
| 			recipeName, 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")
 | |
| }
 | |
| 
 | |
| 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, fmt.Errorf("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, err
 | |
| 	}
 | |
| 
 | |
| 	return &composetypes.ConfigFile{
 | |
| 		Filename: filename,
 | |
| 		Config:   config,
 | |
| 	}, nil
 | |
| }
 |