Skip to content

Commit 4c7f298

Browse files
committed
feat(config): Add conversion of status types to K8s
Signed-off-by: Manish Vachharajani <[email protected]>
1 parent fdbdfd7 commit 4c7f298

File tree

9 files changed

+489
-0
lines changed

9 files changed

+489
-0
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ tracectl = { workspace = true }
1515
k8s-intf = { workspace = true }
1616

1717
# external
18+
chrono = { workspace = true }
1819
derive_builder = { workspace = true, default-features = false, features = ["default"] }
1920
ipnet = { workspace = true }
2021
linkme = { workspace = true }

config/src/converters/k8s/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
//! Converter for gateway-schema k8s objects to internal config and status
55
66
pub mod config;
7+
pub mod status;
78

89
use thiserror::Error;
910

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// Copyright Open Network Fabric Authors
3+
4+
use std::collections::BTreeMap;
5+
6+
use chrono::{DateTime, Utc};
7+
8+
use k8s_intf::gateway_agent_crd::{
9+
GatewayAgentStatus, GatewayAgentStatusState, GatewayAgentStatusStateFrr,
10+
GatewayAgentStatusStatePeerings, GatewayAgentStatusStateVpcs,
11+
};
12+
13+
use crate::converters::k8s::ToK8sConversionError;
14+
use crate::internal::status::DataplaneStatus;
15+
16+
pub struct DataplaneStatusForK8sConversion<'a> {
17+
pub last_applied_gen: Option<i64>,
18+
pub last_applied_time: Option<&'a DateTime<Utc>>,
19+
pub last_collected_time: Option<&'a DateTime<Utc>>,
20+
pub status: Option<&'a DataplaneStatus>,
21+
}
22+
23+
impl TryFrom<&DataplaneStatusForK8sConversion<'_>> for GatewayAgentStatus {
24+
type Error = ToK8sConversionError;
25+
26+
fn try_from(status: &DataplaneStatusForK8sConversion<'_>) -> Result<Self, Self::Error> {
27+
if status.last_applied_time.is_none() && status.last_applied_gen.is_some() {
28+
return Err(ToK8sConversionError::MissingData(
29+
"last_applied_gen is set, but last_applied_time is not".to_string(),
30+
));
31+
}
32+
33+
if status.last_collected_time.is_none() && status.status.is_some() {
34+
return Err(ToK8sConversionError::MissingData(
35+
"status is set, but last_collected_time is not".to_string(),
36+
));
37+
}
38+
39+
let frr_status = status
40+
.status
41+
.and_then(|status| {
42+
status
43+
.frr_status
44+
.as_ref()
45+
.map(GatewayAgentStatusStateFrr::try_from)
46+
})
47+
.transpose()?;
48+
49+
let vpcs = status
50+
.status
51+
.map(|status| {
52+
status
53+
.vpc_counters
54+
.iter()
55+
.map(|(k, v)| Ok((k.clone(), GatewayAgentStatusStateVpcs::try_from(v)?)))
56+
.collect::<Result<BTreeMap<_, _>, _>>()
57+
})
58+
.transpose()?;
59+
60+
let peerings = status
61+
.status
62+
.map(|status| {
63+
status
64+
.vpc_peering_counters
65+
.iter()
66+
.map(|(k, v)| Ok((k.clone(), GatewayAgentStatusStatePeerings::try_from(v)?)))
67+
.collect::<Result<BTreeMap<_, _>, _>>()
68+
})
69+
.transpose()?;
70+
71+
Ok(GatewayAgentStatus {
72+
agent_version: None,
73+
last_applied_gen: status.last_applied_gen,
74+
last_applied_time: status
75+
.last_applied_time
76+
.map(|lat| lat.to_rfc3339_opts(chrono::SecondsFormat::Nanos, true)),
77+
state: status.status.map(|_| GatewayAgentStatusState {
78+
frr: frr_status,
79+
last_collected_time: status
80+
.last_collected_time
81+
.map(|t| t.to_rfc3339_opts(chrono::SecondsFormat::Nanos, true)),
82+
peerings: peerings.filter(|c| !c.is_empty()),
83+
vpcs: vpcs.filter(|c| !c.is_empty()),
84+
}),
85+
})
86+
}
87+
}
88+
89+
#[cfg(test)]
90+
mod tests {
91+
use super::*;
92+
93+
use bolero::generator::*;
94+
use bolero::{Driver, TypeGenerator};
95+
use chrono::TimeZone;
96+
97+
use crate::internal::status::DataplaneStatus;
98+
use crate::internal::status::contract::LegalValue;
99+
100+
fn datetime_gen() -> impl ValueGenerator<Output = DateTime<Utc>> {
101+
(0..=i64::from(i32::MAX)).map_gen(|ts| Utc.timestamp_opt(ts, 0).unwrap())
102+
}
103+
104+
#[derive(Debug)]
105+
struct DataplaneStatusForK8sConversionOwned {
106+
status: Option<DataplaneStatus>,
107+
last_collected_time: Option<DateTime<Utc>>,
108+
last_applied_gen: Option<i64>,
109+
last_applied_time: Option<DateTime<Utc>>,
110+
}
111+
112+
impl TypeGenerator for LegalValue<DataplaneStatusForK8sConversionOwned> {
113+
fn generate<D: Driver>(d: &mut D) -> Option<Self> {
114+
let time_gen = datetime_gen();
115+
let last_applied_gen = d.produce()?;
116+
let last_applied_time = time_gen.generate(d)?;
117+
let status = d
118+
.produce::<Option<LegalValue<DataplaneStatus>>>()?
119+
.map(|v| v.take());
120+
let last_collected_time_raw = time_gen.generate(d)?;
121+
let last_collected_time = status.as_ref().map(|_| last_collected_time_raw);
122+
Some(LegalValue::new(DataplaneStatusForK8sConversionOwned {
123+
status,
124+
last_collected_time,
125+
last_applied_gen,
126+
last_applied_time: last_applied_gen.map(|_| last_applied_time),
127+
}))
128+
}
129+
}
130+
131+
#[test]
132+
fn test_dataplane_status_conversion() {
133+
bolero::check!()
134+
.with_type::<LegalValue<DataplaneStatusForK8sConversionOwned>>()
135+
.for_each(|status_owned| {
136+
let status_owned = status_owned.as_ref();
137+
let conv_status = DataplaneStatusForK8sConversion {
138+
status: status_owned.status.as_ref(),
139+
last_collected_time: status_owned.last_collected_time.as_ref(),
140+
last_applied_gen: status_owned.last_applied_gen,
141+
last_applied_time: status_owned.last_applied_time.as_ref(),
142+
};
143+
144+
let gateway_agent_status = GatewayAgentStatus::try_from(&conv_status).unwrap();
145+
146+
assert_eq!(
147+
gateway_agent_status.last_applied_gen,
148+
conv_status.last_applied_gen
149+
);
150+
assert_eq!(
151+
gateway_agent_status.last_applied_time,
152+
conv_status
153+
.last_applied_time
154+
.map(|lat| lat.to_rfc3339_opts(chrono::SecondsFormat::Nanos, true))
155+
);
156+
assert_eq!(
157+
gateway_agent_status
158+
.state
159+
.as_ref()
160+
.and_then(|s| s.last_collected_time.clone()),
161+
conv_status
162+
.last_collected_time
163+
.map(|t| t.to_rfc3339_opts(chrono::SecondsFormat::Nanos, true))
164+
);
165+
166+
assert_eq!(
167+
conv_status.status.is_some(),
168+
gateway_agent_status.state.is_some()
169+
);
170+
171+
if let Some(state) = gateway_agent_status.state.as_ref() {
172+
assert!(state.frr.is_some()); // Specifics tested elsewhere
173+
174+
match state.vpcs.as_ref() {
175+
Some(vpcs) => {
176+
// Specifics tested elsewhere
177+
assert_eq!(
178+
vpcs.len(),
179+
conv_status
180+
.status
181+
.expect("status should not be None")
182+
.vpc_counters
183+
.len()
184+
);
185+
}
186+
None => {
187+
assert!(
188+
conv_status.status.is_none()
189+
|| conv_status.status.unwrap().vpc_counters.is_empty()
190+
);
191+
}
192+
}
193+
194+
match state.peerings.as_ref() {
195+
// Specifics tested elsewhere
196+
Some(peerings) => assert_eq!(
197+
peerings.len(),
198+
conv_status
199+
.status
200+
.expect("status should not be None")
201+
.vpc_peering_counters
202+
.len()
203+
),
204+
None => assert!(
205+
conv_status.status.is_none()
206+
|| conv_status.status.unwrap().vpc_peering_counters.is_empty()
207+
),
208+
}
209+
}
210+
});
211+
}
212+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// Copyright Open Network Fabric Authors
3+
4+
use k8s_intf::gateway_agent_crd::GatewayAgentStatusStateFrr;
5+
6+
use crate::converters::k8s::ToK8sConversionError;
7+
use crate::internal::status::FrrStatus;
8+
9+
impl TryFrom<&FrrStatus> for GatewayAgentStatusStateFrr {
10+
type Error = ToK8sConversionError;
11+
12+
fn try_from(status: &FrrStatus) -> Result<Self, Self::Error> {
13+
Ok(GatewayAgentStatusStateFrr {
14+
last_applied_gen: Some(status.applied_config_gen),
15+
})
16+
}
17+
}
18+
19+
#[cfg(test)]
20+
mod test {
21+
use super::*;
22+
23+
use crate::internal::status::contract::LegalValue;
24+
25+
#[test]
26+
fn test_frr_status_conversion() {
27+
bolero::check!()
28+
.with_type::<LegalValue<FrrStatus>>()
29+
.for_each(|status| {
30+
let status = status.as_ref();
31+
let k8s_frr_status = GatewayAgentStatusStateFrr::try_from(status)
32+
.expect("Failed to convert frr status");
33+
34+
assert_eq!(
35+
status.applied_config_gen,
36+
k8s_frr_status
37+
.last_applied_gen
38+
.expect("K8s frr last applied gen not set"),
39+
);
40+
});
41+
}
42+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// Copyright Open Network Fabric Authors
3+
4+
//! Converter for gateway-schema k8s objects from internal status
5+
6+
pub mod dataplane_status;
7+
pub mod frr;
8+
pub mod peerings;
9+
pub mod vpcs;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// Copyright Open Network Fabric Authors
3+
4+
use k8s_intf::gateway_agent_crd::GatewayAgentStatusStatePeerings;
5+
6+
use crate::converters::k8s::ToK8sConversionError;
7+
use crate::internal::status::VpcPeeringCounters;
8+
9+
impl TryFrom<&VpcPeeringCounters> for GatewayAgentStatusStatePeerings {
10+
type Error = ToK8sConversionError;
11+
12+
fn try_from(counters: &VpcPeeringCounters) -> Result<Self, Self::Error> {
13+
Ok(GatewayAgentStatusStatePeerings {
14+
b: Some(counters.bytes),
15+
d: Some(counters.drops),
16+
p: Some(counters.packets),
17+
bps: Some(counters.bps),
18+
pps: Some(counters.pps),
19+
})
20+
}
21+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// Copyright Open Network Fabric Authors
3+
4+
use k8s_intf::gateway_agent_crd::GatewayAgentStatusStateVpcs;
5+
6+
use crate::converters::k8s::ToK8sConversionError;
7+
use crate::internal::status::VpcCounters;
8+
9+
impl TryFrom<&VpcCounters> for GatewayAgentStatusStateVpcs {
10+
type Error = ToK8sConversionError;
11+
12+
fn try_from(vpc_counters: &VpcCounters) -> Result<Self, Self::Error> {
13+
Ok(GatewayAgentStatusStateVpcs {
14+
b: Some(vpc_counters.bytes),
15+
d: Some(vpc_counters.drops),
16+
p: Some(vpc_counters.packets),
17+
})
18+
}
19+
}
20+
21+
#[cfg(test)]
22+
mod test {
23+
use super::*;
24+
25+
use crate::internal::status::contract::LegalValue;
26+
27+
#[test]
28+
fn test_frr_status_conversion() {
29+
bolero::check!()
30+
.with_type::<LegalValue<VpcCounters>>()
31+
.for_each(|status| {
32+
let status = status.as_ref();
33+
let k8s_vpc_status = GatewayAgentStatusStateVpcs::try_from(status)
34+
.expect("Failed to convert frr status");
35+
36+
assert_eq!(status.bytes, k8s_vpc_status.b.expect("K8s vpcs b not set"),);
37+
assert_eq!(status.drops, k8s_vpc_status.d.expect("K8s vpcs b not set"),);
38+
assert_eq!(
39+
status.packets,
40+
k8s_vpc_status.p.expect("K8s vpcs b not set"),
41+
);
42+
});
43+
}
44+
}

0 commit comments

Comments
 (0)