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 }