1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4use vector_lib::configurable::configurable_component;
5
6pub mod cloud_storage;
7pub mod pubsub;
8pub mod stackdriver;
9
10#[configurable_component]
20#[derive(Clone, Debug, Default)]
21pub struct GcpTypedResource {
22 #[configurable(metadata(docs::examples = "global", docs::examples = "gce_instance"))]
26 pub r#type: String,
27
28 #[serde(flatten)]
30 #[configurable(metadata(
31 docs::additional_props_description = "Values for all of the labels listed in the associated monitored resource descriptor.\n\nFor example, Compute Engine VM instances use the labels `projectId`, `instanceId`, and `zone`."
32 ))]
33 #[configurable(metadata(docs::examples = "label_examples()"))]
34 pub labels: HashMap<String, String>,
35}
36
37fn label_examples() -> HashMap<String, String> {
38 let mut example = HashMap::new();
39 example.insert("type".to_string(), "global".to_string());
40 example.insert("projectId".to_string(), "vector-123456".to_string());
41 example.insert("instanceId".to_string(), "Twilight".to_string());
42 example.insert("zone".to_string(), "us-central1-a".to_string());
43
44 example
45}
46
47#[derive(Deserialize, Serialize, Debug, Clone, Copy)]
48#[serde(rename_all = "UPPERCASE")]
49pub enum GcpMetricKind {
50 Cumulative,
51 Gauge,
52}
53
54#[derive(Serialize, Debug, Clone, Copy)]
55#[serde(rename_all = "UPPERCASE")]
56pub enum GcpValueType {
57 Int64,
58}
59
60#[derive(Serialize, Debug, Clone, Copy)]
61pub struct GcpPoint {
62 pub interval: GcpInterval,
63 pub value: GcpPointValue,
64}
65
66#[derive(Serialize, Debug, Clone, Copy)]
67#[serde(rename_all = "camelCase")]
68pub struct GcpInterval {
69 #[serde(
70 skip_serializing_if = "Option::is_none",
71 serialize_with = "serialize_optional_datetime"
72 )]
73 pub start_time: Option<chrono::DateTime<chrono::Utc>>,
74 #[serde(serialize_with = "serialize_datetime")]
75 pub end_time: chrono::DateTime<chrono::Utc>,
76}
77
78#[derive(Serialize, Debug, Clone, Copy)]
79#[serde(rename_all = "camelCase")]
80pub struct GcpPointValue {
81 #[serde(
82 skip_serializing_if = "Option::is_none",
83 serialize_with = "serialize_int64_value"
84 )]
85 pub int64_value: Option<i64>,
86}
87
88#[derive(Serialize, Debug, Clone)]
89#[serde(rename_all = "camelCase")]
90pub struct GcpMetric {
91 pub r#type: String,
92 pub labels: HashMap<String, String>,
93}
94
95#[derive(Serialize, Debug, Clone)]
96#[serde(rename_all = "camelCase")]
97pub struct GcpResource {
98 pub r#type: String,
99 pub labels: HashMap<String, String>,
100}
101
102#[derive(Serialize, Debug, Clone)]
103#[serde(rename_all = "camelCase")]
104pub struct GcpSerie {
105 pub metric: GcpMetric,
106 pub resource: GcpResource,
107 pub metric_kind: GcpMetricKind,
108 pub value_type: GcpValueType,
109 pub points: Vec<GcpPoint>,
110}
111
112#[derive(Serialize, Debug, Clone)]
113#[serde(rename_all = "camelCase")]
114pub struct GcpSeries<'a> {
115 time_series: &'a [GcpSerie],
116}
117
118fn serialize_int64_value<S>(value: &Option<i64>, serializer: S) -> Result<S::Ok, S::Error>
119where
120 S: serde::Serializer,
121{
122 serializer.serialize_str(value.as_ref().expect("always defined").to_string().as_str())
123}
124
125fn serialize_datetime<S>(
126 value: &chrono::DateTime<chrono::Utc>,
127 serializer: S,
128) -> Result<S::Ok, S::Error>
129where
130 S: serde::Serializer,
131{
132 serializer.serialize_str(
133 value
134 .to_rfc3339_opts(chrono::SecondsFormat::Nanos, true)
135 .as_str(),
136 )
137}
138
139fn serialize_optional_datetime<S>(
140 value: &Option<chrono::DateTime<chrono::Utc>>,
141 serializer: S,
142) -> Result<S::Ok, S::Error>
143where
144 S: serde::Serializer,
145{
146 serialize_datetime(value.as_ref().expect("always defined"), serializer)
147}
148
149#[cfg(test)]
150mod tests {
151 use chrono::TimeZone;
152
153 use super::*;
154
155 #[test]
157 fn serialize_gcp_series() {
158 let end_time = chrono::Utc
159 .with_ymd_and_hms(2023, 2, 14, 10, 0, 0)
160 .single()
161 .expect("invalid timestamp");
162 let gcp_series = GcpSeries {
163 time_series: &[GcpSerie {
164 metric: GcpMetric {
165 r#type: "custom.googleapis.com/my_namespace/metrics/my_metric".to_string(),
166 labels: [(
167 "my_metric_label".to_string(),
168 "my_metric_label_value".to_string(),
169 )]
170 .into(),
171 },
172 resource: GcpResource {
173 r#type: "my_resource".to_string(),
174 labels: [(
175 "my_resource_label".to_string(),
176 "my_resource_label_value".to_string(),
177 )]
178 .into(),
179 },
180 metric_kind: GcpMetricKind::Gauge,
181 value_type: GcpValueType::Int64,
182 points: vec![GcpPoint {
183 interval: GcpInterval {
184 start_time: None,
185 end_time,
186 },
187 value: GcpPointValue {
188 int64_value: Some(10),
189 },
190 }],
191 }],
192 };
193
194 let serialized = serde_json::to_string(&gcp_series).unwrap();
195
196 let value: serde_json::Value = serde_json::from_str(&serialized).unwrap();
198 let expected: serde_json::Value = serde_json::from_str(r#"{"timeSeries":[{"metric":{"type":"custom.googleapis.com/my_namespace/metrics/my_metric","labels":{"my_metric_label":"my_metric_label_value"}},"resource":{"type":"my_resource","labels":{"my_resource_label":"my_resource_label_value"}},"metricKind":"GAUGE","valueType": "INT64","points":[{"interval":{"endTime":"2023-02-14T10:00:00.000000000Z"},"value":{"int64Value":"10"}}]}]}"#).unwrap();
199
200 assert_eq!(value, expected);
201 }
202}