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 }