github.com/bdwilliams/libcompose@v0.3.1-0.20160826154243-d81a9bdacff0/config/merge_v2.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  // MergeServicesV2 merges a v2 compose file into an existing set of service configs
    13  func MergeServicesV2(existingServices *ServiceConfigs, environmentLookup EnvironmentLookup, resourceLookup ResourceLookup, file string, bytes []byte, options *ParseOptions) (map[string]*ServiceConfig, error) {
    14  	var config Config
    15  	if err := yaml.Unmarshal(bytes, &config); err != nil {
    16  		return nil, err
    17  	}
    18  
    19  	datas := config.Services
    20  
    21  	if options.Interpolate {
    22  		if err := Interpolate(environmentLookup, &datas); err != nil {
    23  			return nil, err
    24  		}
    25  	}
    26  
    27  	if options.Preprocess != nil {
    28  		var err error
    29  		datas, err = options.Preprocess(datas)
    30  		if err != nil {
    31  			return nil, err
    32  		}
    33  	}
    34  
    35  	for name, data := range datas {
    36  		data, err := parseV2(resourceLookup, environmentLookup, file, data, datas, options)
    37  		if err != nil {
    38  			logrus.Errorf("Failed to parse service %s: %v", name, err)
    39  			return nil, err
    40  		}
    41  
    42  		if serviceConfig, ok := existingServices.Get(name); ok {
    43  			var rawExistingService RawService
    44  			if err := utils.Convert(serviceConfig, &rawExistingService); err != nil {
    45  				return nil, err
    46  			}
    47  
    48  			data = mergeConfig(rawExistingService, data)
    49  		}
    50  
    51  		datas[name] = data
    52  	}
    53  
    54  	serviceConfigs := make(map[string]*ServiceConfig)
    55  	if err := utils.Convert(datas, &serviceConfigs); err != nil {
    56  		return nil, err
    57  	}
    58  
    59  	return serviceConfigs, nil
    60  }
    61  
    62  // ParseVolumes parses volumes in a compose file
    63  func ParseVolumes(bytes []byte) (map[string]*VolumeConfig, error) {
    64  	volumeConfigs := make(map[string]*VolumeConfig)
    65  
    66  	var config Config
    67  	if err := yaml.Unmarshal(bytes, &config); err != nil {
    68  		return nil, err
    69  	}
    70  
    71  	if err := utils.Convert(config.Volumes, &volumeConfigs); err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	return volumeConfigs, nil
    76  }
    77  
    78  // ParseNetworks parses networks in a compose file
    79  func ParseNetworks(bytes []byte) (map[string]*NetworkConfig, error) {
    80  	networkConfigs := make(map[string]*NetworkConfig)
    81  
    82  	var config Config
    83  	if err := yaml.Unmarshal(bytes, &config); err != nil {
    84  		return nil, err
    85  	}
    86  
    87  	if err := utils.Convert(config.Networks, &networkConfigs); err != nil {
    88  		return nil, err
    89  	}
    90  
    91  	for key, value := range networkConfigs {
    92  		if value == nil {
    93  			networkConfigs[key] = &NetworkConfig{}
    94  		}
    95  	}
    96  
    97  	return networkConfigs, nil
    98  }
    99  
   100  func parseV2(resourceLookup ResourceLookup, environmentLookup EnvironmentLookup, inFile string, serviceData RawService, datas RawServiceMap, options *ParseOptions) (RawService, error) {
   101  	serviceData, err := readEnvFile(resourceLookup, inFile, serviceData)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	serviceData = resolveContextV2(inFile, serviceData)
   107  
   108  	value, ok := serviceData["extends"]
   109  	if !ok {
   110  		return serviceData, nil
   111  	}
   112  
   113  	mapValue, ok := value.(map[interface{}]interface{})
   114  	if !ok {
   115  		return serviceData, nil
   116  	}
   117  
   118  	if resourceLookup == nil {
   119  		return nil, fmt.Errorf("Can not use extends in file %s no mechanism provided to files", inFile)
   120  	}
   121  
   122  	file := asString(mapValue["file"])
   123  	service := asString(mapValue["service"])
   124  
   125  	if service == "" {
   126  		return serviceData, nil
   127  	}
   128  
   129  	var baseService RawService
   130  
   131  	if file == "" {
   132  		if serviceData, ok := datas[service]; ok {
   133  			baseService, err = parseV2(resourceLookup, environmentLookup, inFile, serviceData, datas, options)
   134  		} else {
   135  			return nil, fmt.Errorf("Failed to find service %s to extend", service)
   136  		}
   137  	} else {
   138  		bytes, resolved, err := resourceLookup.Lookup(file, inFile)
   139  		if err != nil {
   140  			logrus.Errorf("Failed to lookup file %s: %v", file, err)
   141  			return nil, err
   142  		}
   143  
   144  		var config Config
   145  		if err := yaml.Unmarshal(bytes, &config); err != nil {
   146  			return nil, err
   147  		}
   148  
   149  		baseRawServices := config.Services
   150  
   151  		if options.Interpolate {
   152  			err = Interpolate(environmentLookup, &baseRawServices)
   153  			if err != nil {
   154  				return nil, err
   155  			}
   156  		}
   157  
   158  		baseService, ok = baseRawServices[service]
   159  		if !ok {
   160  			return nil, fmt.Errorf("Failed to find service %s in file %s", service, file)
   161  		}
   162  
   163  		baseService, err = parseV2(resourceLookup, environmentLookup, resolved, baseService, baseRawServices, options)
   164  	}
   165  
   166  	if err != nil {
   167  		return nil, err
   168  	}
   169  
   170  	baseService = clone(baseService)
   171  
   172  	logrus.Debugf("Merging %#v, %#v", baseService, serviceData)
   173  
   174  	for _, k := range noMerge {
   175  		if _, ok := baseService[k]; ok {
   176  			source := file
   177  			if source == "" {
   178  				source = inFile
   179  			}
   180  			return nil, fmt.Errorf("Cannot extend service '%s' in %s: services with '%s' cannot be extended", service, source, k)
   181  		}
   182  	}
   183  
   184  	baseService = mergeConfig(baseService, serviceData)
   185  
   186  	logrus.Debugf("Merged result %#v", baseService)
   187  
   188  	return baseService, nil
   189  }
   190  
   191  func resolveContextV2(inFile string, serviceData RawService) RawService {
   192  	if _, ok := serviceData["build"]; !ok {
   193  		return serviceData
   194  	}
   195  	var build map[interface{}]interface{}
   196  	if buildAsString, ok := serviceData["build"].(string); ok {
   197  		build = map[interface{}]interface{}{
   198  			"context": buildAsString,
   199  		}
   200  	} else {
   201  		build = serviceData["build"].(map[interface{}]interface{})
   202  	}
   203  	context := asString(build["context"])
   204  	if context == "" {
   205  		return serviceData
   206  	}
   207  
   208  	if IsValidRemote(context) {
   209  		return serviceData
   210  	}
   211  
   212  	current := path.Dir(inFile)
   213  
   214  	if context == "." {
   215  		context = current
   216  	} else {
   217  		current = path.Join(current, context)
   218  	}
   219  
   220  	build["context"] = current
   221  
   222  	return serviceData
   223  }