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 }