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