vector/config/
api.rs

1use std::net::{Ipv4Addr, SocketAddr};
2
3use url::Url;
4use vector_lib::configurable::configurable_component;
5
6/// API options.
7#[configurable_component]
8#[configurable(metadata(
9    docs::warnings = "The API currently does not support authentication. Only enable it in isolated environments or for debugging. It must not be exposed to untrusted clients."
10))]
11#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12#[serde(default, deny_unknown_fields)]
13pub struct Options {
14    /// Whether the API is enabled for this Vector instance.
15    #[serde(default = "default_enabled")]
16    #[configurable(metadata(docs::common = true, docs::required = false))]
17    pub enabled: bool,
18
19    /// The network address to which the API should bind. If you're running
20    /// Vector in a Docker container, bind to `0.0.0.0`. Otherwise
21    /// the API will not be exposed outside the container.
22    #[serde(default = "default_address")]
23    #[configurable(metadata(docs::examples = "0.0.0.0:8686"))]
24    #[configurable(metadata(docs::examples = "127.0.0.1:1234"))]
25    #[configurable(metadata(docs::common = true, docs::required = false))]
26    pub address: Option<SocketAddr>,
27}
28
29impl_generate_config_from_default!(Options);
30
31impl Default for Options {
32    fn default() -> Self {
33        Self {
34            enabled: default_enabled(),
35            address: default_address(),
36        }
37    }
38}
39
40const fn default_enabled() -> bool {
41    false
42}
43
44/// By default, the API binds to 127.0.0.1:8686. This function should remain public;
45/// `vector top`  will use it to determine which to connect to by default, if no URL
46/// override is provided.
47pub fn default_address() -> Option<SocketAddr> {
48    Some(SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 8686))
49}
50
51/// Default gRPC API address for `vector top` and other API clients
52pub fn default_grpc_url() -> Url {
53    let addr = default_address().unwrap();
54    Url::parse(&format!("http://{addr}"))
55        .expect("Couldn't parse default API URL. Please report this.")
56}
57
58impl Options {
59    pub fn merge(&mut self, other: Self) -> Result<(), String> {
60        // Merge options
61
62        // Try to merge address
63        let address = match (self.address, other.address) {
64            (None, b) => b,
65            (Some(a), None) => Some(a),
66            (Some(a), Some(b)) if a == b => Some(a),
67            // Prefer non default address
68            (Some(a), Some(b)) => {
69                match (Some(a) == default_address(), Some(b) == default_address()) {
70                    (false, false) => return Err(format!("Conflicting `api` address: {a}, {b} .")),
71                    (false, true) => Some(a),
72                    (true, _) => Some(b),
73                }
74            }
75        };
76
77        let options = Options {
78            address,
79            enabled: self.enabled | other.enabled,
80        };
81
82        *self = options;
83        Ok(())
84    }
85}
86
87#[test]
88fn bool_merge() {
89    let mut a = Options {
90        enabled: true,
91        address: None,
92    };
93
94    a.merge(Options::default()).unwrap();
95
96    assert_eq!(
97        a,
98        Options {
99            enabled: true,
100            address: default_address(),
101        }
102    );
103}
104
105#[test]
106fn bind_merge() {
107    let address = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 9000);
108    let mut a = Options {
109        enabled: true,
110        address: Some(address),
111    };
112
113    a.merge(Options::default()).unwrap();
114
115    assert_eq!(
116        a,
117        Options {
118            enabled: true,
119            address: Some(address),
120        }
121    );
122}
123
124#[test]
125fn bind_conflict() {
126    let mut a = Options {
127        address: Some(SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 9000)),
128        ..Options::default()
129    };
130
131    let b = Options {
132        address: Some(SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 9001)),
133        ..Options::default()
134    };
135
136    assert!(a.merge(b).is_err());
137}