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  }