github.com/connyay/libcompose@v0.4.0/config/merge_v2.go (about)

     1  package config
     2  
     3  import (
     4  	"fmt"
     5  	"path"
     6  	"strings"
     7  
     8  	"github.com/Sirupsen/logrus"
     9  	"github.com/docker/libcompose/utils"
    10  )
    11  
    12  // MergeServicesV2 merges a v2 compose file into an existing set of service configs
    13  func MergeServicesV2(existingServices *ServiceConfigs, environmentLookup EnvironmentLookup, resourceLookup ResourceLookup, file string, datas RawServiceMap, options *ParseOptions) (map[string]*ServiceConfig, error) {
    14  	if options.Validate {
    15  		if err := validateV2(datas); err != nil {
    16  			return nil, err
    17  		}
    18  	}
    19  
    20  	for name, data := range datas {
    21  		data, err := parseV2(resourceLookup, environmentLookup, file, data, datas, options)
    22  		if err != nil {
    23  			logrus.Errorf("Failed to parse service %s: %v", name, err)
    24  			return nil, err
    25  		}
    26  
    27  		if serviceConfig, ok := existingServices.Get(name); ok {
    28  			var rawExistingService RawService
    29  			if err := utils.Convert(serviceConfig, &rawExistingService); err != nil {
    30  				return nil, err
    31  			}
    32  
    33  			data = mergeConfig(rawExistingService, data)
    34  		}
    35  
    36  		datas[name] = data
    37  	}
    38  
    39  	if options.Validate {
    40  		var errs []string
    41  		for name, data := range datas {
    42  			err := validateServiceConstraintsv2(data, name)
    43  			if err != nil {
    44  				errs = append(errs, err.Error())
    45  			}
    46  		}
    47  		if len(errs) != 0 {
    48  			return nil, fmt.Errorf(strings.Join(errs, "\n"))
    49  		}
    50  	}
    51  
    52  	serviceConfigs := make(map[string]*ServiceConfig)
    53  	if err := utils.Convert(datas, &serviceConfigs); err != nil {
    54  		return nil, err
    55  	}
    56  
    57  	return serviceConfigs, nil
    58  }
    59  
    60  func parseV2(resourceLookup ResourceLookup, environmentLookup EnvironmentLookup, inFile string, serviceData RawService, datas RawServiceMap, options *ParseOptions) (RawService, error) {
    61  	serviceData, err := readEnvFile(resourceLookup, inFile, serviceData)
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  
    66  	serviceData = resolveContextV2(inFile, serviceData)
    67  
    68  	value, ok := serviceData["extends"]
    69  	if !ok {
    70  		return serviceData, nil
    71  	}
    72  
    73  	mapValue, ok := value.(map[interface{}]interface{})
    74  	if !ok {
    75  		return serviceData, nil
    76  	}
    77  
    78  	if resourceLookup == nil {
    79  		return nil, fmt.Errorf("Can not use extends in file %s no mechanism provided to files", inFile)
    80  	}
    81  
    82  	file := asString(mapValue["file"])
    83  	service := asString(mapValue["service"])
    84  
    85  	if service == "" {
    86  		return serviceData, nil
    87  	}
    88  
    89  	var baseService RawService
    90  
    91  	if file == "" {
    92  		if serviceData, ok := datas[service]; ok {
    93  			baseService, err = parseV2(resourceLookup, environmentLookup, inFile, serviceData, datas, options)
    94  		} else {
    95  			return nil, fmt.Errorf("Failed to find service %s to extend", service)
    96  		}
    97  	} else {
    98  		bytes, resolved, err := resourceLookup.Lookup(file, inFile)
    99  		if err != nil {
   100  			logrus.Errorf("Failed to lookup file %s: %v", file, err)
   101  			return nil, err
   102  		}
   103  
   104  		config, err := CreateConfig(bytes)
   105  		if err != nil {
   106  			return nil, err
   107  		}
   108  		baseRawServices := config.Services
   109  
   110  		if options.Interpolate {
   111  			if err = InterpolateRawServiceMap(&baseRawServices, environmentLookup); err != nil {
   112  				return nil, err
   113  			}
   114  		}
   115  
   116  		if options.Validate {
   117  			if err := validate(baseRawServices); err != nil {
   118  				return nil, err
   119  			}
   120  		}
   121  
   122  		baseService, ok = baseRawServices[service]
   123  		if !ok {
   124  			return nil, fmt.Errorf("Failed to find service %s in file %s", service, file)
   125  		}
   126  
   127  		baseService, err = parseV2(resourceLookup, environmentLookup, resolved, baseService, baseRawServices, options)
   128  	}
   129  
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  
   134  	baseService = clone(baseService)
   135  
   136  	logrus.Debugf("Merging %#v, %#v", baseService, serviceData)
   137  
   138  	for _, k := range noMerge {
   139  		if _, ok := baseService[k]; ok {
   140  			source := file
   141  			if source == "" {
   142  				source = inFile
   143  			}
   144  			return nil, fmt.Errorf("Cannot extend service '%s' in %s: services with '%s' cannot be extended", service, source, k)
   145  		}
   146  	}
   147  
   148  	baseService = mergeConfig(baseService, serviceData)
   149  
   150  	logrus.Debugf("Merged result %#v", baseService)
   151  
   152  	return baseService, nil
   153  }
   154  
   155  func resolveContextV2(inFile string, serviceData RawService) RawService {
   156  	if _, ok := serviceData["build"]; !ok {
   157  		return serviceData
   158  	}
   159  	var build map[interface{}]interface{}
   160  	if buildAsString, ok := serviceData["build"].(string); ok {
   161  		build = map[interface{}]interface{}{
   162  			"context": buildAsString,
   163  		}
   164  	} else {
   165  		build = serviceData["build"].(map[interface{}]interface{})
   166  	}
   167  	context := asString(build["context"])
   168  	if context == "" {
   169  		return serviceData
   170  	}
   171  
   172  	if IsValidRemote(context) {
   173  		return serviceData
   174  	}
   175  
   176  	current := path.Dir(inFile)
   177  
   178  	if context == "." {
   179  		context = current
   180  	} else {
   181  		current = path.Join(current, context)
   182  	}
   183  
   184  	build["context"] = current
   185  
   186  	return serviceData
   187  }