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