go.temporal.io/server/common/config/loader.go

331 LOC · 130 covered · 201 uncovered · 52 ranges · 33 concepts · 19 introducers · 20 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the filefx.go ×1 · 4 introduced LOCfx.go ×1collector.go ×7 · 33 introduced LOCcollector.go ×7data_store_factory.go ×29 · 703 introduced LOCdata_store_factory.go ×2…TestNewServer · 0 introduced LOCTestNewServermetric_client.go ×3 · 18 introduced LOCmetric_client.go ×3request_response.pb.go ×12 · 137 introduced LOCrequest_response.pb.go ×…fx.go ×2 · 8 introduced LOCfx.go ×2fx.go ×3 · 21 introduced LOCfx.go ×3TestInitCurrentClusterMetadataRecord · 0 introduced LOCTestInitCurrentClusterMe…env->env+zone_combined, just_base.yaml_env_and_zone_defined, +3 · 0 introduced LOCenv->env+zone_combined, …base.yaml_and_development.yaml_and_development_zone.yaml · 0 introduced LOCbase.yaml_and_developmen…loader.go ×3 · 8 introduced LOCloader.go ×3just_base.yaml · 0 introduced LOCjust_base.yamlbase.yaml_and_development.yaml · 0 introduced LOCbase.yaml_and_developmen…loader.go ×1 · 3 introduced LOCloader.go ×1loader.go ×1 · 2 introduced LOCloader.go ×1static_config_without_template · 0 introduced LOCstatic_config_without_te…loader.go ×1 · 3 introduced LOCloader.go ×1loader.go ×1 · 2 introduced LOCloader.go ×1loader.go ×1 · 2 introduced LOCloader.go ×1loader.go ×8 · 15 introduced LOCloader.go ×8loader.go ×8 · 16 introduced LOCloader.go ×8loader.go ×2 · 4 introduced LOCloader.go ×2loader.go ×1 · 1 introduced LOCloader.go ×1loader.go ×1 · 2 introduced LOCloader.go ×1loader.go ×1 · 1 introduced LOCloader.go ×1loader.go ×9 · 29 introduced LOCloader.go ×9loader.go ×2 · 5 introduced LOCloader.go ×2loader.go ×4 · 13 introduced LOCloader.go ×4loader.go ×2 · 7 introduced LOCloader.go ×2loader.go ×1 · 3 introduced LOCloader.go ×1loader.go ×1 · 2 introduced LOCloader.go ×1loader.go ×4 · 12 introduced LOCloader.go ×4invalid_yaml_returns_error · introduced test · go.temporal.io/server/common/config/TestLoad/invalid_yaml_returns_errorinvalid_yaml_returns_err…non-existent_directory_returns_error · introduced test · go.temporal.io/server/common/config/TestLoad/non-existent_directory_returns_errornon-existent_directory_r…non-existent_file_path_returns_error · introduced test · go.temporal.io/server/common/config/TestLoad/non-existent_file_path_returns_errornon-existent_file_path_r…static_config_without_template · introduced test · go.temporal.io/server/common/config/TestLoad/static_config_without_templatestatic_config_without_te…template_config_with_file_path_uses_system_env_vars · introduced test · go.temporal.io/server/common/config/TestLoad/template_config_with_file_path_uses_system_env_varstemplate_config_with_fil…base.yaml_and_development.yaml · introduced test · go.temporal.io/server/common/config/TestPathResolution/base.yaml_and_development.yamlbase.yaml_and_developmen…base.yaml_and_development.yaml_and_development_zone.yaml · introduced test · go.temporal.io/server/common/config/TestPathResolution/base.yaml_and_development.yaml_and_development_zone.yamlbase.yaml_and_developmen…base.yaml_and_development.yaml_and_development_zone.yaml#01 · introduced test · go.temporal.io/server/common/config/TestPathResolution/base.yaml_and_development.yaml_and_development_zone.yaml#01base.yaml_and_developmen…base.yaml_and_prod_east.yaml_env_and_zone_defined · introduced test · go.temporal.io/server/common/config/TestPathResolution/base.yaml_and_prod_east.yaml_env_and_zone_definedbase.yaml_and_prod_east.…env->env+zone_combined · introduced test · go.temporal.io/server/common/config/TestPathResolution/env->env+zone_combinedenv->env+zone_combinedjust_base.yaml · introduced test · go.temporal.io/server/common/config/TestPathResolution/just_base.yamljust_base.yamljust_base.yaml_env_and_zone_defined · introduced test · go.temporal.io/server/common/config/TestPathResolution/just_base.yaml_env_and_zone_definedjust_base.yaml_env_and_z…prod_east.yaml_env_and_zone_defined · introduced test · go.temporal.io/server/common/config/TestPathResolution/prod_east.yaml_env_and_zone_definedprod_east.yaml_env_and_z…TestInitCurrentClusterMetadataRecord · introduced test · go.temporal.io/server/temporal/TestInitCurrentClusterMetadataRecordTestInitCurrentClusterMe…TestNewServer · introduced test · go.temporal.io/server/temporal/TestNewServerTestNewServerTestNewServerWithJSONEncoding · introduced test · go.temporal.io/server/temporal/TestNewServerWithJSONEncodingTestNewServerWithJSONEnc…with_OTEL_Collector_running · introduced test · go.temporal.io/server/temporal/TestNewServerWithOTEL/with_OTEL_Collector_runningwith_OTEL_Collector_runn…without_OTEL_Collector_running · introduced test · go.temporal.io/server/temporal/TestNewServerWithOTEL/without_OTEL_Collector_runningwithout_OTEL_Collector_r…TestOverwriteCurrentClusterMetadataWithDBRecord · introduced test · go.temporal.io/server/temporal/TestOverwriteCurrentClusterMetadataWithDBRecordTestOverwriteCurrentClus…TestUpdateCurrentClusterMetadataRecord · introduced test · go.temporal.io/server/temporal/TestUpdateCurrentClusterMetadataRecordTestUpdateCurrentCluster…Focused file · go.temporal.io/server/common/config/loader.go · 331 LOCconfig/loader.go

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 package config
2
3 import (
4 "bufio"
5 "bytes"
6 _ "embed"
7 "errors"
8 "fmt"
9 "io"
10 stdlog "log"
11 "os"
12 "path/filepath"
13 "strings"
14 "text/template"
15
16 "github.com/Masterminds/sprig/v3"
17 "gopkg.in/yaml.v3"
18 )
19
20 //go:embed config_template_embedded.yaml
21 var embeddedConfigTemplate []byte
22
23 var (
24 // ErrConfigFilesNotFound is returned when no config files are found in the specified directory
25 ErrConfigFilesNotFound = errors.New("no config files found")
26 )
27
28 const (
29 // EnvKeyRoot the environment variable key for runtime root dir
30 EnvKeyRoot = "TEMPORAL_ROOT"
31 // EnvKeyConfigDir the environment variable key for config dir
32 EnvKeyConfigDir = "TEMPORAL_CONFIG_DIR"
33 // EnvKeyEnvironment is the environment variable key for environment
34 EnvKeyEnvironment = "TEMPORAL_ENVIRONMENT"
35 // EnvKeyAvailabilityZone is the environment variable key for AZ
36 EnvKeyAvailabilityZone = "TEMPORAL_AVAILABILITY_ZONE"
37 // EnvKeyAvailabilityZoneTypo is the old environment variable key for AZ that
38 // included a typo. This is deprecated and only here to support backwards
39 // compatibility.
40 EnvKeyAvailabilityZoneTypo = "TEMPORAL_AVAILABILTY_ZONE"
41 // EnvKeyAllowNoAuth is the environment variable key for setting no authorizer
42 EnvKeyAllowNoAuth = "TEMPORAL_ALLOW_NO_AUTH"
43 // EnvKeyConfigFile is the environment variable key for specifying a config file path
44 EnvKeyConfigFile = "TEMPORAL_SERVER_CONFIG_FILE_PATH"
45 )
46
47 const (
48 baseFile = "base.yaml"
49 envDevelopment = "development"
50 defaultConfigDir = "config"
51 enableTemplate = "enable-template"
52 commentSearchLimit = 1024
53 )
54
55 // Load loads the configuration from a set of
56 // yaml config files found in the config directory
57 //
58 // The loader first fetches the set of files matching
59 // a pre-determined naming convention, then sorts
60 // them by hierarchy order and after that, simply
61 type loadOptions struct {
62 env string
63 configDir string
64 zone string
65 configFilePath string
66 useEmbeddedOnly bool
67 }
68
69 type loadOption func(*loadOptions)
70
71 // WithEnv sets the environment name for configuration loading (e.g., "development", "production").
72 // If empty, defaults to "development".
73 > func WithEnv(env string) loadOption { loader.go ×1
74 > return func(o *loadOptions) {
75 > if env != "" {
76 > o.env = env loader.go ×1
77 > }
78 }
79 }
80
81 // WithConfigDir sets the directory path where configuration files are located.
82 // If empty, defaults to "config".
83 > func WithConfigDir(configDir string) loadOption { loader.go ×9
84 > return func(o *loadOptions) {
85 > if configDir != "" {
86 > o.configDir = configDir
87 > }
88 }
89 }
90
91 // WithZone sets the availability zone for configuration loading.
92 // This is used to load zone-specific configuration overrides (e.g., "us-east-1a").
93 > func WithZone(zone string) loadOption { loader.go ×1
94 > return func(o *loadOptions) {
95 > if zone != "" {
96 > o.zone = zone loader.go ×3
97 > }
98 }
99 }
100
101 // WithConfigFile sets a specific configuration file path to load.
102 // When provided, only this file will be loaded, bypassing the legacy hierarchical loading.
103 > func WithConfigFile(configFilePath string) loadOption { loader.go ×2
104 > return func(o *loadOptions) {
105 > if configFilePath != "" {
106 > o.configFilePath = configFilePath
107 > }
108 }
109 }
110
111 // WithEmbedded forces the loader to use only the embedded configuration template.
112 // This loads configuration from environment variables only, using the embedded template.
113 func WithEmbedded() loadOption {
114 return func(o *loadOptions) {
115 o.useEmbeddedOnly = true
116 }
117 }
118
119 // Load loads and validates the Temporal server configuration.
120 // It supports multiple loading strategies based on the provided options:
121 // - Embedded template with environment variables (WithEmbedded)
122 // - Single config file (WithConfigFile)
123 // - Legacy hierarchical config directory (WithConfigDir, WithEnv, WithZone)
124 //
125 // Configuration files can be templated using Go template syntax with sprig-compatible
126 // functions. To enable templating, add "# enable-template" comment in the first 1KB of the file.
127 //
128 // Returns the loaded configuration or an error if loading or validation fails.
129 > func Load(opts ...loadOption) (*Config, error) { loader.go ×4
130 > cfg := &Config{}
131 > options := &loadOptions{}
132 >
133 > for _, opt := range opts {
134 > opt(options)
135 > }
136
137 > if err := options.load(cfg); err != nil { loader.go ×4
138 > return nil, err loader.go ×1
139 > }
140 > return cfg, nil loader.go ×1
141 }
142
143 > func (opts *loadOptions) load(config any) error { loader.go ×4
144 >
145 > if opts.useEmbeddedOnly {
146 stdlog.Println("Loading configuration from environment variables only")
147 return loadAndUnmarshalContent(embeddedConfigTemplate, "config_template_embedded.yaml", config)
148 }
149
150 > if opts.configFilePath != "" { loader.go ×4
151 > content, err := readConfigFile(opts.configFilePath) loader.go ×2
152 > if err != nil {
153 > return err loader.go ×2
154 > }
155 > return loadAndUnmarshalContent(content, filepath.Base(opts.configFilePath), config) loader.go ×8
156 }
157 > return opts.loadLegacy(config) loader.go ×9
158
159 }
160
161 // loadLegacy loads configuration data from a set of YAML files
162 // located in the config directory.
163 //
164 // Deprecated: This loader is maintained only for backward compatibility
165 // and should not be used in new code.
166 //
167 // The loader first identifies all files matching a predefined
168 // naming convention, then sorts them according to their hierarchy.
169 // It then loads the files sequentially, with key/value pairs in
170 // later files overriding those in earlier ones.
171 //
172 // The hierarchy, from lowest to highest precedence, is as follows:
173 //
174 // base.yaml
175 // env.yaml -- where "environment" is one of the input parameters (e.g., "development")
176 // env_az.yaml -- where "zone" is another input parameter
177
178 > func (opts *loadOptions) loadLegacy(config any) error { loader.go ×9
179 > stdlog.Printf("Loading config; env=%v,zone=%v,configDir=%v\n", opts.env, opts.zone, opts.configDir)
180 > if opts.env == "" {
181 > opts.env = envDevelopment loader.go ×1
182 > }
183 > if opts.configDir == "" { loader.go ×9
184 opts.configDir = defaultConfigDir
185 }
186
187 > stdlog.Printf("Loading config; env=%v,zone=%v,configDir=%v\n", opts.env, opts.zone, opts.configDir) loader.go ×9
188 >
189 > files, err := getConfigFiles(opts.env, opts.configDir, opts.zone)
190 > if err != nil {
191 > return fmt.Errorf("failed to get config files: %w", err) loader.go ×2
192 > }
193
194 > stdlog.Printf("Loading config files=%v\n", files) loader.go ×8
195 >
196 > for _, f := range files {
197 > data, err := readConfigFile(f)
198 > if err != nil {
199 return err
200 }
201
202 > processedData, err := processConfigFile(data, filepath.Base(f)) loader.go ×8
203 > if err != nil {
204 return err
205 }
206
207 > err = yaml.Unmarshal(processedData, config) loader.go ×8
208 > if err != nil {
209 > return err loader.go ×1
210 > }
211 }
212
213 > validate := newValidator() loader.go ×1
214 > return validate.Validate(config)
215 }
216
217 > func readConfigFile(path string) ([]byte, error) { loader.go ×1
218 > data, err := os.ReadFile(path)
219 > if err != nil {
220 > return nil, fmt.Errorf("could not read config file: %s. error: %w", path, err) loader.go ×2
221 >
222 > }
223 > return data, nil loader.go ×4
224 }
225
226 // processConfigFile processes a config file, rendering it as a template if enabled
227 > func processConfigFile(data []byte, filename string) ([]byte, error) { loader.go ×4
228 > // If the config file contains "enable-template" in a comment within the first 1KB, then
229 > // we will treat the file as a template and render it.
230 > templating, err := checkTemplatingEnabled(data)
231 > if err != nil {
232 return nil, err
233 }
234
235 > if !templating { loader.go ×4
236 > return data, nil loader.go ×8
237 > }
238
239 > stdlog.Printf("Processing config file as template; filename=%v\n", filename) loader.go ×8
240 > tpl, err := template.New(filename).Funcs(sprig.FuncMap()).Parse(string(data))
241 > if err != nil {
242 return nil, err
243 }
244
245 > var rendered bytes.Buffer loader.go ×8
246 > err = tpl.Execute(&rendered, nil)
247 > if err != nil {
248 return nil, err
249 }
250
251 > return rendered.Bytes(), nil loader.go ×8
252 }
253
254 > func loadAndUnmarshalContent(content []byte, filename string, config any) error { loader.go ×8
255 > processed, err := processConfigFile(content, filename)
256 > if err != nil {
257 return fmt.Errorf("failed to process config file %s: %w", filename, err)
258 }
259
260 > if err := yaml.Unmarshal(processed, config); err != nil { loader.go ×8
261 return fmt.Errorf("failed to unmarshal config file %s: %w", filename, err)
262 }
263
264 > validate := newValidator() loader.go ×8
265 > return validate.Validate(config)
266 }
267
268 > func checkTemplatingEnabled(content []byte) (bool, error) { loader.go ×4
269 > scanner := bufio.NewScanner(io.LimitReader(bytes.NewReader(content), commentSearchLimit))
270 > for scanner.Scan() {
271 > line := strings.TrimSpace(scanner.Text())
272 >
273 > if strings.HasPrefix(line, "#") && strings.Contains(line, enableTemplate) {
274 > return true, nil loader.go ×8
275 > }
276 }
277
278 > return false, scanner.Err() loader.go ×8
279 }
280
281 // getConfigFiles returns the list of config files to
282 // process in the hierarchy order
283 > func getConfigFiles(env string, configDir string, zone string) ([]string, error) { loader.go ×9
284 > candidates := make([]string, 2, 3)
285 > candidates[0] = filepath.Join(configDir, baseFile)
286 > candidates[1] = filepath.Join(configDir, file(env, "yaml"))
287 >
288 > if zone != "" {
289 > f := file(concat(env, zone), "yaml") loader.go ×3
290 > candidates = append(candidates, filepath.Join(configDir, f))
291 > }
292
293 > result := make([]string, 0, len(candidates)) loader.go ×9
294 >
295 > for _, c := range candidates {
296 > _, err := os.Stat(c)
297 > if errors.Is(err, os.ErrNotExist) {
298 > continue loader.go ×1
299 }
300 > if err != nil { loader.go ×8
301 return nil, fmt.Errorf("error accessing config file %s: %w", c, err)
302 }
303 > result = append(result, c) loader.go ×8
304 }
305 > if len(result) == 0 { loader.go ×9
306 > return nil, fmt.Errorf("%w in directory: %s", ErrConfigFilesNotFound, configDir) loader.go ×2
307 > }
308
309 > return result, nil loader.go ×8
310 }
311
312 > func concat(a, b string) string { loader.go ×3
313 > return a + "_" + b
314 > }
315
316 > func file(name string, suffix string) string { loader.go ×9
317 > return name + "." + suffix
318 > }
319
320 func loadEnvMap() map[string]string {
321 environ := os.Environ()
322 envMap := make(map[string]string, len(environ))
323
324 for _, env := range environ {
325 key, value, found := strings.Cut(env, "=")
326 if found && key != "" {
327 envMap[key] = value
328 }
329 }
330 return envMap
331 }