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