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(api("api"))]
8#[derive(Clone, Copy, Debug, Eq, PartialEq)]
9#[serde(default, deny_unknown_fields)]
10pub struct Options {
11    /// Whether the GraphQL API is enabled for this Vector instance.
12    #[serde(default = "default_enabled")]
13    #[configurable(metadata(docs::common = true, docs::required = false))]
14    pub enabled: bool,
15
16    /// The network address to which the API should bind. If you're running
17    /// Vector in a Docker container, bind to `0.0.0.0`. Otherwise
18    /// the API will not be exposed outside the container.
19    #[serde(default = "default_address")]
20    #[configurable(metadata(docs::examples = "0.0.0.0:8686"))]
21    #[configurable(metadata(docs::examples = "127.0.0.1:1234"))]
22    #[configurable(metadata(docs::common = true, docs::required = false))]
23    pub address: Option<SocketAddr>,
24
25    /// Whether the [GraphQL Playground](https://github.com/graphql/graphql-playground) is enabled
26    /// for the API. The Playground is accessible via the `/playground` endpoint
27    /// of the address set using the `bind` parameter. Note that the `playground`
28    /// endpoint will only be enabled if the `graphql` endpoint is also enabled.
29    #[serde(default = "default_playground")]
30    #[configurable(metadata(docs::common = false, docs::required = false))]
31    pub playground: bool,
32
33    /// Whether the endpoint for receiving and processing GraphQL queries is
34    /// enabled for the API. The endpoint is accessible via the `/graphql`
35    /// endpoint of the address set using the `bind` parameter.
36    #[serde(default = "default_graphql", skip_serializing_if = "is_true")]
37    #[configurable(metadata(docs::common = true, docs::required = false))]
38    pub graphql: bool,
39}
40
41impl_generate_config_from_default!(Options);
42
43impl Default for Options {
44    fn default() -> Self {
45        Self {
46            enabled: default_enabled(),
47            playground: default_playground(),
48            address: default_address(),
49            graphql: default_graphql(),
50        }
51    }
52}
53
54// serde passes struct fields as reference
55#[allow(clippy::trivially_copy_pass_by_ref)]
56const fn is_true(value: &bool) -> bool {
57    *value
58}
59
60const fn default_enabled() -> bool {
61    false
62}
63
64/// By default, the API binds to 127.0.0.1:8686. This function should remain public;
65/// `vector top`  will use it to determine which to connect to by default, if no URL
66/// override is provided.
67pub fn default_address() -> Option<SocketAddr> {
68    Some(SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 8686))
69}
70
71/// Default GraphQL API address
72pub fn default_graphql_url() -> Url {
73    let addr = default_address().unwrap();
74    Url::parse(&format!("http://{addr}/graphql"))
75        .expect("Couldn't parse default API URL. Please report this.")
76}
77
78const fn default_playground() -> bool {
79    true
80}
81
82const fn default_graphql() -> bool {
83    true
84}
85
86impl Options {
87    pub fn merge(&mut self, other: Self) -> Result<(), String> {
88        // Merge options
89
90        // Try to merge address
91        let address = match (self.address, other.address) {
92            (None, b) => b,
93            (Some(a), None) => Some(a),
94            (Some(a), Some(b)) if a == b => Some(a),
95            // Prefer non default address
96            (Some(a), Some(b)) => {
97                match (Some(a) == default_address(), Some(b) == default_address()) {
98                    (false, false) => return Err(format!("Conflicting `api` address: {a}, {b} .")),
99                    (false, true) => Some(a),
100                    (true, _) => Some(b),
101                }
102            }
103        };
104
105        let options = Options {
106            address,
107            enabled: self.enabled | other.enabled,
108            playground: self.playground & other.playground,
109            graphql: self.graphql & other.graphql,
110        };
111
112        *self = options;
113        Ok(())
114    }
115}
116
117#[test]
118fn bool_merge() {
119    let mut a = Options {
120        enabled: true,
121        address: None,
122        playground: false,
123        graphql: false,
124    };
125
126    a.merge(Options::default()).unwrap();
127
128    assert_eq!(
129        a,
130        Options {
131            enabled: true,
132            address: default_address(),
133            playground: false,
134            graphql: false
135        }
136    );
137}
138
139#[test]
140fn bind_merge() {
141    let address = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 9000);
142    let mut a = Options {
143        enabled: true,
144        address: Some(address),
145        playground: true,
146        graphql: true,
147    };
148
149    a.merge(Options::default()).unwrap();
150
151    assert_eq!(
152        a,
153        Options {
154            enabled: true,
155            address: Some(address),
156            playground: true,
157            graphql: true,
158        }
159    );
160}
161
162#[test]
163fn bind_conflict() {
164    let mut a = Options {
165        address: Some(SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 9000)),
166        ..Options::default()
167    };
168
169    let b = Options {
170        address: Some(SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 9001)),
171        ..Options::default()
172    };
173
174    assert!(a.merge(b).is_err());
175}