github.com/camronlevanger/libcompose@v0.4.1-0.20180423130544-6bb86d53fa21/config/merge_v1.go (about)

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