merge.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. package config
  2. import (
  3. "bufio"
  4. "bytes"
  5. "fmt"
  6. "strings"
  7. yaml "github.com/cloudfoundry-incubator/candiedyaml"
  8. "github.com/docker/docker/pkg/urlutil"
  9. )
  10. var (
  11. noMerge = []string{
  12. "links",
  13. "volumes_from",
  14. }
  15. defaultParseOptions = ParseOptions{
  16. Interpolate: true,
  17. Validate: true,
  18. }
  19. )
  20. // Merge merges a compose file into an existing set of service configs
  21. func Merge(existingServices *ServiceConfigs, environmentLookup EnvironmentLookup, resourceLookup ResourceLookup, file string, bytes []byte, options *ParseOptions) (map[string]*ServiceConfig, map[string]*VolumeConfig, map[string]*NetworkConfig, error) {
  22. if options == nil {
  23. options = &defaultParseOptions
  24. }
  25. var config Config
  26. if err := yaml.Unmarshal(bytes, &config); err != nil {
  27. return nil, nil, nil, err
  28. }
  29. var serviceConfigs map[string]*ServiceConfig
  30. var volumeConfigs map[string]*VolumeConfig
  31. var networkConfigs map[string]*NetworkConfig
  32. if config.Version == "2" {
  33. var err error
  34. serviceConfigs, err = MergeServicesV2(existingServices, environmentLookup, resourceLookup, file, bytes, options)
  35. if err != nil {
  36. return nil, nil, nil, err
  37. }
  38. volumeConfigs, err = ParseVolumes(bytes)
  39. if err != nil {
  40. return nil, nil, nil, err
  41. }
  42. networkConfigs, err = ParseNetworks(bytes)
  43. if err != nil {
  44. return nil, nil, nil, err
  45. }
  46. } else {
  47. serviceConfigsV1, err := MergeServicesV1(existingServices, environmentLookup, resourceLookup, file, bytes, options)
  48. if err != nil {
  49. return nil, nil, nil, err
  50. }
  51. serviceConfigs, err = ConvertServices(serviceConfigsV1)
  52. if err != nil {
  53. return nil, nil, nil, err
  54. }
  55. }
  56. adjustValues(serviceConfigs)
  57. if options.Postprocess != nil {
  58. var err error
  59. serviceConfigs, err = options.Postprocess(serviceConfigs)
  60. if err != nil {
  61. return nil, nil, nil, err
  62. }
  63. }
  64. return serviceConfigs, volumeConfigs, networkConfigs, nil
  65. }
  66. func adjustValues(configs map[string]*ServiceConfig) {
  67. // yaml parser turns "no" into "false" but that is not valid for a restart policy
  68. for _, v := range configs {
  69. if v.Restart == "false" {
  70. v.Restart = "no"
  71. }
  72. }
  73. }
  74. func readEnvFile(resourceLookup ResourceLookup, inFile string, serviceData RawService) (RawService, error) {
  75. if _, ok := serviceData["env_file"]; !ok {
  76. return serviceData, nil
  77. }
  78. envFiles := serviceData["env_file"].([]interface{})
  79. if len(envFiles) == 0 {
  80. return serviceData, nil
  81. }
  82. if resourceLookup == nil {
  83. return nil, fmt.Errorf("Can not use env_file in file %s no mechanism provided to load files", inFile)
  84. }
  85. var vars []interface{}
  86. if _, ok := serviceData["environment"]; ok {
  87. vars = serviceData["environment"].([]interface{})
  88. }
  89. for i := len(envFiles) - 1; i >= 0; i-- {
  90. envFile := envFiles[i].(string)
  91. content, _, err := resourceLookup.Lookup(envFile, inFile)
  92. if err != nil {
  93. return nil, err
  94. }
  95. if err != nil {
  96. return nil, err
  97. }
  98. scanner := bufio.NewScanner(bytes.NewBuffer(content))
  99. for scanner.Scan() {
  100. line := strings.TrimSpace(scanner.Text())
  101. key := strings.SplitAfter(line, "=")[0]
  102. found := false
  103. for _, v := range vars {
  104. if strings.HasPrefix(v.(string), key) {
  105. found = true
  106. break
  107. }
  108. }
  109. if !found {
  110. vars = append(vars, line)
  111. }
  112. }
  113. if scanner.Err() != nil {
  114. return nil, scanner.Err()
  115. }
  116. }
  117. serviceData["environment"] = vars
  118. delete(serviceData, "env_file")
  119. return serviceData, nil
  120. }
  121. func mergeConfig(baseService, serviceData RawService) RawService {
  122. for k, v := range serviceData {
  123. // Image and build are mutually exclusive in merge
  124. if k == "image" {
  125. delete(baseService, "build")
  126. } else if k == "build" {
  127. delete(baseService, "image")
  128. }
  129. existing, ok := baseService[k]
  130. if ok {
  131. baseService[k] = merge(existing, v)
  132. } else {
  133. baseService[k] = v
  134. }
  135. }
  136. return baseService
  137. }
  138. // IsValidRemote checks if the specified string is a valid remote (for builds)
  139. func IsValidRemote(remote string) bool {
  140. return urlutil.IsGitURL(remote) || urlutil.IsURL(remote)
  141. }