Skip to content

Commit e908347

Browse files
committed
feat(dataplane,args,mgmt): Thread k8s config from args to mgmt
Previously we assumed a grpc server would be used. This commit changes that so that a grpc server is started if a grpc related command line arguments are passed. In their absence, a k8s server is assumed. This is a reasonable default setup as we plan to deprecate the gRPC configuration. Signed-off-by: Manish Vachharajani <[email protected]>
1 parent d6556b2 commit e908347

File tree

5 files changed

+141
-55
lines changed

5 files changed

+141
-55
lines changed

args/src/lib.rs

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,7 @@ pub struct ConfigServerSection {
570570
#[rkyv(attr(derive(PartialEq, Eq, Debug)))]
571571
pub struct LaunchConfiguration {
572572
/// Dynamic configuration server settings
573-
pub config_server: ConfigServerSection,
573+
pub config_server: Option<ConfigServerSection>,
574574
/// Packet processing driver configuration
575575
pub driver: DriverConfigSection,
576576
/// CLI server configuration
@@ -1065,11 +1065,12 @@ impl TryFrom<CmdArgs> for LaunchConfiguration {
10651065

10661066
fn try_from(value: CmdArgs) -> Result<Self, InvalidCmdArguments> {
10671067
Ok(LaunchConfiguration {
1068-
config_server: ConfigServerSection {
1069-
address: value
1070-
.grpc_address()
1071-
.map_err(InvalidCmdArguments::InvalidGrpcAddress)?,
1072-
},
1068+
config_server: value
1069+
.grpc_address()
1070+
.map_err(InvalidCmdArguments::InvalidGrpcAddress)?
1071+
.map(|grpc_address| ConfigServerSection {
1072+
address: grpc_address,
1073+
}),
10731074
driver: match &value.driver {
10741075
Some(driver) if driver == "dpdk" => {
10751076
// TODO: adjust command line to specify lcore usage more flexibly in next PR
@@ -1172,14 +1173,14 @@ Note: multiple interfaces can be specified separated by commas and no spaces"
11721173
#[arg(
11731174
long,
11741175
value_name = "ADDRESS",
1175-
default_value = "[::1]:50051",
1176-
help = "IP Address and port or UNIX socket path to listen for management connections"
1176+
help = "IP Address and port or UNIX socket path to listen for GRPC management connections.
1177+
If this parameter and --grpc_unix_socket are both absent, the dataplane will directly fetch configuration from K8s"
11771178
)]
1178-
grpc_address: String,
1179+
grpc_address: Option<String>,
11791180

11801181
/// Treat grpc-address as a UNIX socket path
11811182
#[arg(long, help = "Use a unix socket to listen for management connections")]
1182-
grpc_unix_socket: bool,
1183+
grpc_unix_socket: Option<bool>,
11831184

11841185
#[arg(
11851186
long,
@@ -1364,27 +1365,33 @@ impl CmdArgs {
13641365
/// Returns an error if:
13651366
/// - Unix socket path is not absolute when `--grpc-unix-socket` is set
13661367
/// - TCP address cannot be parsed as a valid `IP:PORT` combination
1367-
pub fn grpc_address(&self) -> Result<GrpcAddress, String> {
1368+
pub fn grpc_address(&self) -> Result<Option<GrpcAddress>, String> {
1369+
if self.grpc_address.is_none() && self.grpc_unix_socket.is_none() {
1370+
return Ok(None);
1371+
}
1372+
1373+
let grpc_address = if let Some(addr) = self.grpc_address.as_ref() {
1374+
addr
1375+
} else {
1376+
"0.0.0.0:50051"
1377+
};
1378+
13681379
// If UNIX socket flag is set, treat the address as a UNIX socket path
1369-
if self.grpc_unix_socket {
1380+
if self.grpc_unix_socket.unwrap_or(false) {
13701381
// Validate that the address is a valid UNIX socket path
1371-
let grpc_path = PathBuf::from(&self.grpc_address);
1382+
let grpc_path = PathBuf::from(grpc_address);
13721383
if !grpc_path.is_absolute() {
13731384
return Err(format!(
1374-
"Invalid configuration: --grpc-unix-socket flag is set, but --grpc-address '{}' is not a valid absolute UNIX socket path",
1375-
self.grpc_address
1385+
"Invalid configuration: --grpc-unix-socket flag is set, but --grpc-address '{grpc_address}' is not a valid absolute UNIX socket path",
13761386
));
13771387
}
1378-
return Ok(GrpcAddress::UnixSocket(self.grpc_address.clone()));
1388+
return Ok(Some(GrpcAddress::UnixSocket(grpc_address.to_string())));
13791389
}
13801390

13811391
// Otherwise, parse as a TCP socket address
1382-
match self.grpc_address.parse::<SocketAddr>() {
1383-
Ok(addr) => Ok(GrpcAddress::Tcp(addr)),
1384-
Err(e) => Err(format!(
1385-
"Invalid gRPC TCP address '{}': {e}",
1386-
self.grpc_address
1387-
)),
1392+
match grpc_address.parse::<SocketAddr>() {
1393+
Ok(addr) => Ok(Some(GrpcAddress::Tcp(addr))),
1394+
Err(e) => Err(format!("Invalid gRPC TCP address '{grpc_address}': {e}",)),
13881395
}
13891396
}
13901397

dataplane/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ fn main() {
141141
vpc_stats_store: setup.vpc_stats_store,
142142
},
143143
})
144-
.expect("Failed to start gRPC server");
144+
.expect("Failed to start management");
145145

146146
/* start driver with the provided pipeline builder */
147147
let e = match args.driver_name() {

mgmt/src/processor/k8s_client.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// Copyright Open Network Fabric Authors
3+
4+
use tokio::sync::mpsc::Sender;
5+
6+
use crate::processor::proc::ConfigChannelRequest;
7+
8+
#[derive(Debug, thiserror::Error)]
9+
pub enum K8sClientError {
10+
// Define error variants here
11+
}
12+
13+
pub async fn k8s_start_client(_tx: Sender<ConfigChannelRequest>) -> Result<(), K8sClientError> {
14+
unimplemented!()
15+
}

mgmt/src/processor/launch.rs

Lines changed: 95 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,58 @@
11
// SPDX-License-Identifier: Apache-2.0
22
// Copyright Open Network Fabric Authors
33

4+
use crate::processor::k8s_client::K8sClientError;
5+
use crate::processor::k8s_client::k8s_start_client;
46
use crate::processor::proc::ConfigChannelRequest;
57
use crate::processor::proc::ConfigProcessor;
6-
use args::GrpcAddress;
8+
79
use std::fmt::Display;
8-
use std::io::Error;
910
use std::net::SocketAddr;
1011
use std::os::unix::fs::PermissionsExt;
1112
use std::path::{Path, PathBuf};
1213
use std::pin::Pin;
1314
use std::task::{Context, Poll};
15+
1416
use tokio::io;
1517
use tokio::net::UnixListener;
1618
use tokio::sync::mpsc::Sender;
1719
use tokio_stream::Stream;
20+
use tonic::transport::Server;
21+
22+
use args::GrpcAddress;
23+
use tracing::{debug, error, info, warn};
1824

1925
use crate::grpc::server::create_config_service;
2026
use crate::processor::proc::ConfigProcessorParams;
21-
use tonic::transport::Server;
22-
use tracing::{debug, error, info, warn};
27+
28+
#[derive(Debug, thiserror::Error)]
29+
pub enum LaunchError {
30+
#[error("GRPC server error: {0}")]
31+
GrpcServerError(tonic::transport::Error),
32+
#[error("IO error: {0}")]
33+
IoError(std::io::Error),
34+
#[error("Error in K8s client task: {0}")]
35+
K8sClientError(K8sClientError),
36+
#[error("Error starting/waiting for K8s client task: {0}")]
37+
K8sClientJoinError(tokio::task::JoinError),
38+
#[error("K8s client exited prematurely")]
39+
PrematureK8sClientExit,
40+
#[error("Grpc server exited prematurely")]
41+
PrematureGrpcExit,
42+
#[error("Config processor exited prematurely")]
43+
PrematureProcessorExit,
44+
45+
#[error("Error in Config Processor task: {0}")]
46+
ProcessorError(std::io::Error),
47+
#[error("Error starting/waiting for Config Processor task: {0}")]
48+
ProcessorJoinError(tokio::task::JoinError),
49+
}
2350

2451
/// Start the gRPC server on TCP
2552
async fn start_grpc_server_tcp(
2653
addr: SocketAddr,
2754
channel_tx: Sender<ConfigChannelRequest>,
28-
) -> Result<(), Error> {
55+
) -> Result<(), LaunchError> {
2956
info!("Starting gRPC server on TCP address: {addr}");
3057
let config_service = create_config_service(channel_tx);
3158

@@ -35,7 +62,7 @@ async fn start_grpc_server_tcp(
3562
.await
3663
.map_err(|e| {
3764
error!("Failed to start gRPC server");
38-
Error::other(e.to_string())
65+
LaunchError::GrpcServerError(e)
3966
})
4067
}
4168

@@ -69,7 +96,7 @@ impl Stream for UnixAcceptor {
6996
async fn start_grpc_server_unix(
7097
socket_path: &Path,
7198
channel_tx: Sender<ConfigChannelRequest>,
72-
) -> Result<(), Error> {
99+
) -> Result<(), LaunchError> {
73100
info!(
74101
"Starting gRPC server on UNIX socket: {}",
75102
socket_path.display()
@@ -89,7 +116,7 @@ async fn start_grpc_server_unix(
89116
if !parent.exists() {
90117
if let Err(e) = std::fs::create_dir_all(parent) {
91118
error!("Failed to create parent directory: {e}");
92-
return Err(e);
119+
return Err(LaunchError::IoError(e));
93120
}
94121
}
95122
}
@@ -102,7 +129,7 @@ async fn start_grpc_server_unix(
102129
}
103130
Err(e) => {
104131
error!("Failed to bind UNIX socket: {e}");
105-
return Err(e);
132+
return Err(LaunchError::IoError(e));
106133
}
107134
};
108135

@@ -125,7 +152,7 @@ async fn start_grpc_server_unix(
125152
.await
126153
.map_err(|e| {
127154
error!("Failed to start gRPC server");
128-
Error::other(e.to_string())
155+
LaunchError::GrpcServerError(e)
129156
})?;
130157

131158
// Clean up the socket file after server shutdown
@@ -155,19 +182,14 @@ impl Display for ServerAddress {
155182
}
156183

157184
pub struct MgmtParams {
158-
pub grpc_addr: GrpcAddress,
185+
pub grpc_addr: Option<GrpcAddress>,
159186
pub processor_params: ConfigProcessorParams,
160187
}
161188

162189
/// Start the mgmt service with either type of socket
163-
pub fn start_mgmt(params: MgmtParams) -> Result<std::thread::JoinHandle<()>, Error> {
164-
/* build server address from provided grpc address */
165-
let server_address = match params.grpc_addr {
166-
GrpcAddress::Tcp(addr) => ServerAddress::Tcp(addr),
167-
GrpcAddress::UnixSocket(path) => ServerAddress::Unix(path.into()),
168-
};
169-
debug!("Will start gRPC listening on {server_address}");
170-
190+
pub fn start_mgmt(
191+
params: MgmtParams,
192+
) -> Result<std::thread::JoinHandle<Result<(), LaunchError>>, std::io::Error> {
171193
std::thread::Builder::new()
172194
.name("mgmt".to_string())
173195
.spawn(move || {
@@ -180,19 +202,60 @@ pub fn start_mgmt(params: MgmtParams) -> Result<std::thread::JoinHandle<()>, Err
180202
.build()
181203
.expect("Tokio runtime creation failed");
182204

183-
/* block thread to run gRPC and configuration processor */
184-
rt.block_on(async {
185-
let (processor, tx) = ConfigProcessor::new(params.processor_params);
186-
tokio::spawn(async { processor.run().await });
187-
188-
// Start the appropriate server based on address type
189-
let result = match server_address {
190-
ServerAddress::Tcp(sock_addr) => start_grpc_server_tcp(sock_addr, tx).await,
191-
ServerAddress::Unix(path) => start_grpc_server_unix(&path, tx).await,
205+
if let Some(grpc_addr) = params.grpc_addr {
206+
/* build server address from provided grpc address */
207+
let server_address = match grpc_addr {
208+
GrpcAddress::Tcp(addr) => ServerAddress::Tcp(addr),
209+
GrpcAddress::UnixSocket(path) => ServerAddress::Unix(path.into()),
192210
};
193-
if let Err(e) = result {
194-
error!("Failed to start gRPC server: {e}");
195-
}
196-
});
211+
debug!("Will start gRPC listening on {server_address}");
212+
213+
/* block thread to run gRPC and configuration processor */
214+
rt.block_on(async {
215+
let (processor, tx) = ConfigProcessor::new(params.processor_params);
216+
tokio::spawn(async { processor.run().await });
217+
218+
// Start the appropriate server based on address type
219+
let result = match server_address {
220+
ServerAddress::Tcp(sock_addr) => start_grpc_server_tcp(sock_addr, tx).await,
221+
ServerAddress::Unix(path) => start_grpc_server_unix(&path, tx).await,
222+
};
223+
if let Err(e) = result {
224+
error!("Failed to start gRPC server: {e}");
225+
Err(e)
226+
} else {
227+
error!("GRPC server exited prematurely");
228+
Err(LaunchError::PrematureGrpcExit)
229+
}
230+
})
231+
} else {
232+
debug!("Will start watching k8s for configuration changes");
233+
rt.block_on(async {
234+
let (processor, tx) = ConfigProcessor::new(params.processor_params);
235+
let processor_handle = tokio::spawn(async { processor.run().await });
236+
let k8s_handle = tokio::spawn(async move { k8s_start_client(tx.clone()).await });
237+
tokio::select! {
238+
result = processor_handle => {
239+
match result {
240+
Ok(_) => {
241+
error!("Configuration processor task exited unexpectedly");
242+
Err(LaunchError::PrematureProcessorExit)?
243+
}
244+
Err(e) => { Err::<(), LaunchError>(LaunchError::ProcessorJoinError(e)) }
245+
}
246+
}
247+
result = k8s_handle => {
248+
match result {
249+
Ok(result) => { result.inspect_err(|e| error!("K8s client task failed: {e}")).map_err(LaunchError::K8sClientError)?;
250+
error!("Kubernetes client task exited unexpectedly");
251+
Err(LaunchError::PrematureK8sClientExit)?
252+
}
253+
Err(e) => { Err(LaunchError::K8sClientJoinError(e))? }
254+
}
255+
}
256+
}?;
257+
Ok::<(), LaunchError>(())
258+
})
259+
}
197260
})
198261
}

mgmt/src/processor/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@
77
pub(crate) mod confbuild;
88
mod display;
99
pub(crate) mod gwconfigdb;
10+
pub(crate) mod k8s_client;
1011
pub(crate) mod launch;
1112
pub(crate) mod proc;

0 commit comments

Comments
 (0)