github.com/bdwilliams/libcompose@v0.3.1-0.20160826154243-d81a9bdacff0/config/merge_v1.go (about)

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