go.temporal.io/server@v1.23.0/common/config/loader.go (about)

     1  // The MIT License
     2  //
     3  // Copyright (c) 2020 Temporal Technologies Inc.  All rights reserved.
     4  //
     5  // Copyright (c) 2020 Uber Technologies, Inc.
     6  //
     7  // Permission is hereby granted, free of charge, to any person obtaining a copy
     8  // of this software and associated documentation files (the "Software"), to deal
     9  // in the Software without restriction, including without limitation the rights
    10  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    11  // copies of the Software, and to permit persons to whom the Software is
    12  // furnished to do so, subject to the following conditions:
    13  //
    14  // The above copyright notice and this permission notice shall be included in
    15  // all copies or substantial portions of the Software.
    16  //
    17  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    18  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    19  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    20  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    21  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    22  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    23  // THE SOFTWARE.
    24  
    25  package config
    26  
    27  import (
    28  	"fmt"
    29  	stdlog "log"
    30  	"os"
    31  
    32  	"gopkg.in/validator.v2"
    33  	"gopkg.in/yaml.v3"
    34  )
    35  
    36  const (
    37  	// EnvKeyRoot the environment variable key for runtime root dir
    38  	EnvKeyRoot = "TEMPORAL_ROOT"
    39  	// EnvKeyConfigDir the environment variable key for config dir
    40  	EnvKeyConfigDir = "TEMPORAL_CONFIG_DIR"
    41  	// EnvKeyEnvironment is the environment variable key for environment
    42  	EnvKeyEnvironment = "TEMPORAL_ENVIRONMENT"
    43  	// EnvKeyAvailabilityZone is the environment variable key for AZ
    44  	EnvKeyAvailabilityZone = "TEMPORAL_AVAILABILITY_ZONE"
    45  	// EnvKeyAvailabilityZoneTypo is the old environment variable key for AZ that
    46  	// included a typo. This is deprecated and only here to support backwards
    47  	// compatibility.
    48  	EnvKeyAvailabilityZoneTypo = "TEMPORAL_AVAILABILTY_ZONE"
    49  	// EnvKeyAllowNoAuth is the environment variable key for setting no authorizer
    50  	EnvKeyAllowNoAuth = "TEMPORAL_ALLOW_NO_AUTH"
    51  )
    52  
    53  const (
    54  	baseFile         = "base.yaml"
    55  	envDevelopment   = "development"
    56  	defaultConfigDir = "config"
    57  )
    58  
    59  // Load loads the configuration from a set of
    60  // yaml config files found in the config directory
    61  //
    62  // The loader first fetches the set of files matching
    63  // a pre-determined naming convention, then sorts
    64  // them by hierarchy order and after that, simply
    65  // loads the files one after another with the
    66  // key/values in the later files overriding the key/values
    67  // in the earlier files
    68  //
    69  // The hierarchy is as follows from lowest to highest
    70  //
    71  //	base.yaml
    72  //	    env.yaml   -- environment is one of the input params ex-development
    73  //	      env_az.yaml -- zone is another input param
    74  func Load(env string, configDir string, zone string, config interface{}) error {
    75  	if len(env) == 0 {
    76  		env = envDevelopment
    77  	}
    78  	if len(configDir) == 0 {
    79  		configDir = defaultConfigDir
    80  	}
    81  
    82  	// TODO: remove log dependency.
    83  	stdlog.Printf("Loading config; env=%v,zone=%v,configDir=%v\n", env, zone, configDir)
    84  
    85  	files, err := getConfigFiles(env, configDir, zone)
    86  	if err != nil {
    87  		return err
    88  	}
    89  
    90  	// TODO: remove log dependency.
    91  	stdlog.Printf("Loading config files=%v\n", files)
    92  
    93  	for _, f := range files {
    94  		// This is tagged nosec because the file names being read are for config files that are not user supplied
    95  		// #nosec
    96  		data, err := os.ReadFile(f)
    97  		if err != nil {
    98  			return err
    99  		}
   100  		err = yaml.Unmarshal(data, config)
   101  		if err != nil {
   102  			return err
   103  		}
   104  	}
   105  
   106  	return validator.Validate(config)
   107  }
   108  
   109  // Helper function for loading configuration
   110  func LoadConfig(env string, configDir string, zone string) (*Config, error) {
   111  	config := Config{}
   112  	err := Load(env, configDir, zone, &config)
   113  	if err != nil {
   114  		return nil, fmt.Errorf("config file corrupted: %w", err)
   115  	}
   116  	return &config, nil
   117  }
   118  
   119  // getConfigFiles returns the list of config files to
   120  // process in the hierarchy order
   121  func getConfigFiles(env string, configDir string, zone string) ([]string, error) {
   122  
   123  	candidates := []string{
   124  		path(configDir, baseFile),
   125  		path(configDir, file(env, "yaml")),
   126  	}
   127  
   128  	if len(zone) > 0 {
   129  		f := file(concat(env, zone), "yaml")
   130  		candidates = append(candidates, path(configDir, f))
   131  	}
   132  
   133  	var result []string
   134  
   135  	for _, c := range candidates {
   136  		if _, err := os.Stat(c); err != nil {
   137  			continue
   138  		}
   139  		result = append(result, c)
   140  	}
   141  
   142  	if len(result) == 0 {
   143  		return nil, fmt.Errorf("no config files found within %v", configDir)
   144  	}
   145  
   146  	return result, nil
   147  }
   148  
   149  func concat(a, b string) string {
   150  	return a + "_" + b
   151  }
   152  
   153  func file(name string, suffix string) string {
   154  	return name + "." + suffix
   155  }
   156  
   157  func path(dir string, file string) string {
   158  	return dir + "/" + file
   159  }