merge_v1.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. package config
  2. import (
  3. "fmt"
  4. "path"
  5. "github.com/Sirupsen/logrus"
  6. yaml "github.com/cloudfoundry-incubator/candiedyaml"
  7. "github.com/docker/libcompose/utils"
  8. )
  9. // MergeServicesV1 merges a v1 compose file into an existing set of service configs
  10. func MergeServicesV1(existingServices *ServiceConfigs, environmentLookup EnvironmentLookup, resourceLookup ResourceLookup, file string, bytes []byte, options *ParseOptions) (map[string]*ServiceConfigV1, error) {
  11. datas := make(RawServiceMap)
  12. if err := yaml.Unmarshal(bytes, &datas); err != nil {
  13. return nil, err
  14. }
  15. if options.Interpolate {
  16. if err := Interpolate(environmentLookup, &datas); err != nil {
  17. return nil, err
  18. }
  19. }
  20. if options.Preprocess != nil {
  21. var err error
  22. datas, err = options.Preprocess(datas)
  23. if err != nil {
  24. return nil, err
  25. }
  26. }
  27. if options.Validate {
  28. if err := validate(datas); err != nil {
  29. return nil, err
  30. }
  31. }
  32. for name, data := range datas {
  33. data, err := parseV1(resourceLookup, environmentLookup, file, data, datas, options)
  34. if err != nil {
  35. logrus.Errorf("Failed to parse service %s: %v", name, err)
  36. return nil, err
  37. }
  38. if serviceConfig, ok := existingServices.Get(name); ok {
  39. var rawExistingService RawService
  40. if err := utils.Convert(serviceConfig, &rawExistingService); err != nil {
  41. return nil, err
  42. }
  43. data = mergeConfig(rawExistingService, data)
  44. }
  45. datas[name] = data
  46. }
  47. if options.Validate {
  48. for name, data := range datas {
  49. err := validateServiceConstraints(data, name)
  50. if err != nil {
  51. return nil, err
  52. }
  53. }
  54. }
  55. serviceConfigs := make(map[string]*ServiceConfigV1)
  56. if err := utils.Convert(datas, &serviceConfigs); err != nil {
  57. return nil, err
  58. }
  59. return serviceConfigs, nil
  60. }
  61. func parseV1(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. serviceData = resolveContextV1(inFile, serviceData)
  67. value, ok := serviceData["extends"]
  68. if !ok {
  69. return serviceData, nil
  70. }
  71. mapValue, ok := value.(map[interface{}]interface{})
  72. if !ok {
  73. return serviceData, nil
  74. }
  75. if resourceLookup == nil {
  76. return nil, fmt.Errorf("Can not use extends in file %s no mechanism provided to files", inFile)
  77. }
  78. file := asString(mapValue["file"])
  79. service := asString(mapValue["service"])
  80. if service == "" {
  81. return serviceData, nil
  82. }
  83. var baseService RawService
  84. if file == "" {
  85. if serviceData, ok := datas[service]; ok {
  86. baseService, err = parseV1(resourceLookup, environmentLookup, inFile, serviceData, datas, options)
  87. } else {
  88. return nil, fmt.Errorf("Failed to find service %s to extend", service)
  89. }
  90. } else {
  91. bytes, resolved, err := resourceLookup.Lookup(file, inFile)
  92. if err != nil {
  93. logrus.Errorf("Failed to lookup file %s: %v", file, err)
  94. return nil, err
  95. }
  96. var baseRawServices RawServiceMap
  97. if err := yaml.Unmarshal(bytes, &baseRawServices); err != nil {
  98. return nil, err
  99. }
  100. if options.Interpolate {
  101. err = Interpolate(environmentLookup, &baseRawServices)
  102. if err != nil {
  103. return nil, err
  104. }
  105. }
  106. if options.Preprocess != nil {
  107. var err error
  108. baseRawServices, err = options.Preprocess(baseRawServices)
  109. if err != nil {
  110. return nil, err
  111. }
  112. }
  113. if options.Validate {
  114. if err := validate(baseRawServices); err != nil {
  115. return nil, err
  116. }
  117. }
  118. baseService, ok = baseRawServices[service]
  119. if !ok {
  120. return nil, fmt.Errorf("Failed to find service %s in file %s", service, file)
  121. }
  122. baseService, err = parseV1(resourceLookup, environmentLookup, resolved, baseService, baseRawServices, options)
  123. }
  124. if err != nil {
  125. return nil, err
  126. }
  127. baseService = clone(baseService)
  128. logrus.Debugf("Merging %#v, %#v", baseService, serviceData)
  129. for _, k := range noMerge {
  130. if _, ok := baseService[k]; ok {
  131. source := file
  132. if source == "" {
  133. source = inFile
  134. }
  135. return nil, fmt.Errorf("Cannot extend service '%s' in %s: services with '%s' cannot be extended", service, source, k)
  136. }
  137. }
  138. baseService = mergeConfig(baseService, serviceData)
  139. logrus.Debugf("Merged result %#v", baseService)
  140. return baseService, nil
  141. }
  142. func resolveContextV1(inFile string, serviceData RawService) RawService {
  143. context := asString(serviceData["build"])
  144. if context == "" {
  145. return serviceData
  146. }
  147. if IsValidRemote(context) {
  148. return serviceData
  149. }
  150. current := path.Dir(inFile)
  151. if context == "." {
  152. context = current
  153. } else {
  154. current = path.Join(current, context)
  155. }
  156. serviceData["build"] = current
  157. return serviceData
  158. }