dubbo.apache.org/dubbo-go/v3@v3.1.1/config/config_loader_options.go (about) 1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package config 19 20 import ( 21 "os" 22 "path/filepath" 23 "runtime" 24 "strings" 25 26 "dubbo.apache.org/dubbo-go/v3/common/constant" 27 "dubbo.apache.org/dubbo-go/v3/common/constant/file" 28 "github.com/dubbogo/gost/log/logger" 29 "github.com/knadh/koanf" 30 "github.com/pkg/errors" 31 ) 32 33 type loaderConf struct { 34 suffix string // loaderConf file extension default yaml 35 path string // loaderConf file path default ./conf/dubbogo.yaml 36 delim string // loaderConf file delim default . 37 bytes []byte // config bytes 38 rc *RootConfig // user provide rootConfig built by config api 39 name string // config file name 40 } 41 42 func NewLoaderConf(opts ...LoaderConfOption) *loaderConf { 43 configFilePath := "../conf/dubbogo.yaml" 44 if configFilePathFromEnv := os.Getenv(constant.ConfigFileEnvKey); configFilePathFromEnv != "" { 45 configFilePath = configFilePathFromEnv 46 } 47 name, suffix := resolverFilePath(configFilePath) 48 conf := &loaderConf{ 49 suffix: suffix, 50 path: absolutePath(configFilePath), 51 delim: ".", 52 name: name, 53 } 54 for _, opt := range opts { 55 opt.apply(conf) 56 } 57 if conf.rc != nil { 58 return conf 59 } 60 if len(conf.bytes) <= 0 { 61 if bytes, err := os.ReadFile(conf.path); err != nil { 62 panic(err) 63 } else { 64 conf.bytes = bytes 65 } 66 } 67 return conf 68 } 69 70 type LoaderConfOption interface { 71 apply(vc *loaderConf) 72 } 73 74 type loaderConfigFunc func(*loaderConf) 75 76 func (fn loaderConfigFunc) apply(vc *loaderConf) { 77 fn(vc) 78 } 79 80 // WithGenre set load config file suffix 81 // Deprecated: replaced by WithSuffix 82 func WithGenre(suffix string) LoaderConfOption { 83 return loaderConfigFunc(func(conf *loaderConf) { 84 g := strings.ToLower(suffix) 85 if err := checkFileSuffix(g); err != nil { 86 panic(err) 87 } 88 conf.suffix = g 89 }) 90 } 91 92 // WithSuffix set load config file suffix 93 func WithSuffix(suffix file.Suffix) LoaderConfOption { 94 return loaderConfigFunc(func(conf *loaderConf) { 95 conf.suffix = string(suffix) 96 }) 97 } 98 99 // WithPath set load config path 100 func WithPath(path string) LoaderConfOption { 101 return loaderConfigFunc(func(conf *loaderConf) { 102 conf.path = absolutePath(path) 103 if bytes, err := os.ReadFile(conf.path); err != nil { 104 panic(err) 105 } else { 106 conf.bytes = bytes 107 } 108 name, suffix := resolverFilePath(path) 109 conf.suffix = suffix 110 conf.name = name 111 }) 112 } 113 114 func WithRootConfig(rc *RootConfig) LoaderConfOption { 115 return loaderConfigFunc(func(conf *loaderConf) { 116 conf.rc = rc 117 }) 118 } 119 120 func WithDelim(delim string) LoaderConfOption { 121 return loaderConfigFunc(func(conf *loaderConf) { 122 conf.delim = delim 123 }) 124 } 125 126 // WithBytes set load config bytes 127 func WithBytes(bytes []byte) LoaderConfOption { 128 return loaderConfigFunc(func(conf *loaderConf) { 129 conf.bytes = bytes 130 }) 131 } 132 133 // absolutePath get absolut path 134 func absolutePath(inPath string) string { 135 136 if inPath == "$HOME" || strings.HasPrefix(inPath, "$HOME"+string(os.PathSeparator)) { 137 inPath = userHomeDir() + inPath[5:] 138 } 139 140 if filepath.IsAbs(inPath) { 141 return filepath.Clean(inPath) 142 } 143 144 p, err := filepath.Abs(inPath) 145 if err == nil { 146 return filepath.Clean(p) 147 } 148 149 return "" 150 } 151 152 // userHomeDir get gopath 153 func userHomeDir() string { 154 if runtime.GOOS == "windows" { 155 home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") 156 if home == "" { 157 home = os.Getenv("USERPROFILE") 158 } 159 return home 160 } 161 return os.Getenv("HOME") 162 } 163 164 // checkFileSuffix check file suffix 165 func checkFileSuffix(suffix string) error { 166 for _, g := range []string{"json", "toml", "yaml", "yml", "properties"} { 167 if g == suffix { 168 return nil 169 } 170 } 171 return errors.Errorf("no support file suffix: %s", suffix) 172 } 173 174 // resolverFilePath resolver file path 175 // eg: give a ./conf/dubbogo.yaml return dubbogo and yaml 176 func resolverFilePath(path string) (name, suffix string) { 177 paths := strings.Split(path, "/") 178 fileName := strings.Split(paths[len(paths)-1], ".") 179 if len(fileName) < 2 { 180 return fileName[0], string(file.YAML) 181 } 182 return fileName[0], fileName[1] 183 } 184 185 // MergeConfig merge config file 186 func (conf *loaderConf) MergeConfig(koan *koanf.Koanf) *koanf.Koanf { 187 var ( 188 activeKoan *koanf.Koanf 189 activeConf *loaderConf 190 ) 191 active := koan.String("dubbo.profiles.active") 192 active = getLegalActive(active) 193 logger.Infof("The following profiles are active: %s", active) 194 if defaultActive != active { 195 path := conf.getActiveFilePath(active) 196 if !pathExists(path) { 197 logger.Debugf("Config file:%s not exist skip config merge", path) 198 return koan 199 } 200 activeConf = NewLoaderConf(WithPath(path)) 201 activeKoan = GetConfigResolver(activeConf) 202 if err := koan.Merge(activeKoan); err != nil { 203 logger.Debugf("Config merge err %s", err) 204 } 205 } 206 return koan 207 } 208 209 func (conf *loaderConf) getActiveFilePath(active string) string { 210 suffix := constant.DotSeparator + conf.suffix 211 return strings.ReplaceAll(conf.path, suffix, "") + "-" + active + suffix 212 } 213 214 func pathExists(path string) bool { 215 if _, err := os.Stat(path); err == nil { 216 return true 217 } else { 218 return !os.IsNotExist(err) 219 } 220 }