1mod config_builder;
2mod loader;
3mod secret;
4mod source;
5
6use std::{
7 collections::HashMap,
8 fmt::Debug,
9 fs::{File, ReadDir},
10 path::{Path, PathBuf},
11 sync::{Mutex, OnceLock},
12};
13
14pub use config_builder::ConfigBuilderLoader;
15use glob::glob;
16use loader::process::Process;
17pub use loader::*;
18pub use secret::*;
19pub use source::*;
20use vector_lib::configurable::NamedComponent;
21
22use super::{
23 Config, ConfigPath, Format, FormatHint, ProviderConfig, builder::ConfigBuilder, format,
24 validation, vars,
25};
26use crate::signal;
27
28pub static CONFIG_PATHS: Mutex<Vec<ConfigPath>> = Mutex::new(Vec::new());
29
30static ALLOW_ENV_VAR_INTERPOLATION: OnceLock<bool> = OnceLock::new();
31
32pub fn set_env_var_interpolation(allow: bool) {
35 ALLOW_ENV_VAR_INTERPOLATION
36 .set(allow)
37 .expect("set_env_var_interpolation must only be called once");
38}
39
40pub fn env_var_interpolation_enabled() -> bool {
43 *ALLOW_ENV_VAR_INTERPOLATION.get().unwrap_or(&false)
44}
45
46pub(super) fn read_dir<P: AsRef<Path> + Debug>(path: P) -> Result<ReadDir, Vec<String>> {
47 path.as_ref()
48 .read_dir()
49 .map_err(|err| vec![format!("Could not read config dir: {:?}, {}.", path, err)])
50}
51
52pub(super) fn component_name<P: AsRef<Path> + Debug>(path: P) -> Result<String, Vec<String>> {
53 path.as_ref()
54 .file_stem()
55 .and_then(|name| name.to_str())
56 .map(|name| name.to_string())
57 .ok_or_else(|| vec![format!("Couldn't get component name for file: {:?}", path)])
58}
59
60pub(super) fn open_file<P: AsRef<Path> + Debug>(path: P) -> Option<File> {
61 match File::open(&path) {
62 Ok(f) => Some(f),
63 Err(error) => {
64 if let std::io::ErrorKind::NotFound = error.kind() {
65 error!(
66 message = "Config file not found in path.",
67 ?path,
68 internal_log_rate_limit = false
69 );
70 None
71 } else {
72 error!(message = "Error opening config file.", %error, ?path, internal_log_rate_limit = false);
73 None
74 }
75 }
76 }
77}
78
79pub fn merge_path_lists(
82 path_lists: Vec<(&[PathBuf], FormatHint)>,
83) -> impl Iterator<Item = (PathBuf, FormatHint)> + '_ {
84 path_lists
85 .into_iter()
86 .flat_map(|(paths, format)| paths.iter().cloned().map(move |path| (path, format)))
87}
88
89pub fn process_paths(config_paths: &[ConfigPath]) -> Option<Vec<ConfigPath>> {
92 let starting_paths = if !config_paths.is_empty() {
93 config_paths.to_owned()
94 } else {
95 default_config_paths()
96 };
97
98 let mut paths = Vec::new();
99
100 for config_path in &starting_paths {
101 let config_pattern: &PathBuf = config_path.into();
102
103 let matches: Vec<PathBuf> = match glob(config_pattern.to_str().expect("No ability to glob"))
104 {
105 Ok(glob_paths) => glob_paths.filter_map(Result::ok).collect(),
106 Err(err) => {
107 error!(message = "Failed to read glob pattern.", path = ?config_pattern, error = ?err);
108 return None;
109 }
110 };
111
112 if matches.is_empty() {
113 error!(message = "Config file not found in path.", path = ?config_pattern, internal_log_rate_limit = false);
114 std::process::exit(exitcode::CONFIG);
115 }
116
117 match config_path {
118 ConfigPath::File(_, format) => {
119 for path in matches {
120 paths.push(ConfigPath::File(path, *format));
121 }
122 }
123 ConfigPath::Dir(_) => {
124 for path in matches {
125 paths.push(ConfigPath::Dir(path))
126 }
127 }
128 }
129 }
130
131 paths.sort();
132 paths.dedup();
133 drop(
135 CONFIG_PATHS
136 .lock()
137 .map(|mut guard| guard.clone_from(&paths)),
138 );
139
140 Some(paths)
141}
142
143pub fn load_from_paths(config_paths: &[ConfigPath]) -> Result<Config, Vec<String>> {
144 let builder = ConfigBuilderLoader::default().load_from_paths(config_paths)?;
145 let (config, build_warnings) = builder.build_with_warnings()?;
146
147 for warning in build_warnings {
148 warn!("{}", warning);
149 }
150
151 Ok(config)
152}
153
154pub async fn load_from_paths_with_provider_and_secrets(
158 config_paths: &[ConfigPath],
159 signal_handler: &mut signal::SignalHandler,
160 allow_empty: bool,
161) -> Result<Config, Vec<String>> {
162 let secrets_backends_loader = loader_from_paths(SecretBackendLoader::default(), config_paths)?;
163 let secrets = secrets_backends_loader
164 .retrieve_secrets(signal_handler)
165 .await
166 .map_err(|e| vec![e])?;
167
168 let mut builder = ConfigBuilderLoader::default()
169 .allow_empty(allow_empty)
170 .secrets(secrets)
171 .load_from_paths(config_paths)?;
172
173 validation::check_provider(&builder)?;
174 signal_handler.clear();
175
176 if let Some(mut provider) = builder.provider {
178 builder = provider.build(signal_handler).await?;
179 debug!(message = "Provider configured.", provider = ?provider.get_component_name());
180 }
181
182 finalize_config(builder).await
183}
184
185pub async fn load_from_str_with_secrets(
186 input: &str,
187 format: Format,
188 signal_handler: &mut signal::SignalHandler,
189 allow_empty: bool,
190) -> Result<Config, Vec<String>> {
191 let secrets_backends_loader =
192 loader_from_input(SecretBackendLoader::default(), input.as_bytes(), format)?;
193 let secrets = secrets_backends_loader
194 .retrieve_secrets(signal_handler)
195 .await
196 .map_err(|e| vec![e])?;
197
198 let builder = ConfigBuilderLoader::default()
199 .allow_empty(allow_empty)
200 .secrets(secrets)
201 .load_from_input(input.as_bytes(), format)?;
202 signal_handler.clear();
203
204 finalize_config(builder).await
205}
206
207async fn finalize_config(builder: ConfigBuilder) -> Result<Config, Vec<String>> {
208 let (new_config, build_warnings) = builder.build_with_warnings()?;
209
210 validation::check_buffer_preconditions(&new_config).await?;
211
212 for warning in build_warnings {
213 warn!("{}", warning);
214 }
215
216 Ok(new_config)
217}
218
219pub(super) fn loader_from_input<T, L, R>(
220 mut loader: L,
221 input: R,
222 format: Format,
223) -> Result<T, Vec<String>>
224where
225 T: serde::de::DeserializeOwned,
226 L: Loader<T> + Process,
227 R: std::io::Read,
228{
229 loader.load_from_str(input, format).map(|_| loader.take())
230}
231
232pub(super) fn loader_from_paths<T, L>(
234 mut loader: L,
235 config_paths: &[ConfigPath],
236) -> Result<T, Vec<String>>
237where
238 T: serde::de::DeserializeOwned,
239 L: Loader<T> + Process,
240{
241 let mut errors = Vec::new();
242
243 for config_path in config_paths {
244 match config_path {
245 ConfigPath::File(path, format_hint) => {
246 match loader.load_from_file(
247 path,
248 format_hint
249 .or_else(move || Format::from_path(&path).ok())
250 .unwrap_or_default(),
251 ) {
252 Ok(()) => {}
253 Err(errs) => errors.extend(errs),
254 };
255 }
256 ConfigPath::Dir(path) => {
257 match loader.load_from_dir(path) {
258 Ok(()) => {}
259 Err(errs) => errors.extend(errs),
260 };
261 }
262 }
263 }
264
265 if errors.is_empty() {
266 Ok(loader.take())
267 } else {
268 Err(errors)
269 }
270}
271
272pub fn load_source_from_paths(
274 config_paths: &[ConfigPath],
275) -> Result<toml::value::Table, Vec<String>> {
276 loader_from_paths(SourceLoader::new(), config_paths)
277}
278
279pub fn load_from_str(input: &str, format: Format) -> Result<Config, Vec<String>> {
280 let builder = load_from_inputs(std::iter::once((input.as_bytes(), format)))?;
281 let (config, build_warnings) = builder.build_with_warnings()?;
282
283 for warning in build_warnings {
284 warn!("{}", warning);
285 }
286
287 Ok(config)
288}
289
290fn load_from_inputs(
291 inputs: impl IntoIterator<Item = (impl std::io::Read, Format)>,
292) -> Result<ConfigBuilder, Vec<String>> {
293 let mut config = Config::builder();
294 let mut errors = Vec::new();
295
296 for (input, format) in inputs {
297 if let Err(errs) = load(input, format).and_then(|n| config.append(n)) {
298 errors.extend(errs.iter().map(|e| e.to_string()));
300 }
301 }
302
303 if errors.is_empty() {
304 Ok(config)
305 } else {
306 Err(errors)
307 }
308}
309
310pub fn prepare_input<R: std::io::Read>(
311 mut input: R,
312 interpolate_env: bool,
313) -> Result<String, Vec<String>> {
314 let mut source_string = String::new();
315 input
316 .read_to_string(&mut source_string)
317 .map_err(|e| vec![e.to_string()])?;
318
319 if interpolate_env {
320 let mut vars: HashMap<String, String> = std::env::vars_os()
321 .filter_map(|(k, v)| match (k.into_string(), v.into_string()) {
322 (Ok(k), Ok(v)) => Some((k, v)),
323 _ => None,
324 })
325 .collect();
326
327 if !vars.contains_key("HOSTNAME")
328 && let Ok(hostname) = crate::get_hostname()
329 {
330 vars.insert("HOSTNAME".into(), hostname);
331 }
332 vars::interpolate(&source_string, &vars)
333 } else {
334 Ok(source_string)
335 }
336}
337
338pub fn load<R: std::io::Read, T>(input: R, format: Format) -> Result<T, Vec<String>>
339where
340 T: serde::de::DeserializeOwned,
341{
342 let with_vars = prepare_input(input, false)?;
344
345 format::deserialize(&with_vars, format)
346}
347
348#[cfg(not(windows))]
349fn default_path() -> PathBuf {
350 "/etc/vector/vector.yaml".into()
351}
352
353#[cfg(windows)]
354fn default_path() -> PathBuf {
355 let program_files =
356 std::env::var("ProgramFiles").expect("%ProgramFiles% environment variable must be defined");
357 format!("{}\\Vector\\config\\vector.yaml", program_files).into()
358}
359
360fn default_config_paths() -> Vec<ConfigPath> {
361 #[cfg(not(windows))]
362 let default_path = default_path();
363 #[cfg(windows)]
364 let default_path = default_path();
365
366 vec![ConfigPath::File(default_path, Some(Format::Yaml))]
367}