Skip to content

Commit 12fe562

Browse files
committed
feat(rust): added kafka-inlet command and relative config side
1 parent 8aa187e commit 12fe562

17 files changed

Lines changed: 500 additions & 27 deletions

File tree

implementations/rust/ockam/ockam_api/src/kafka/mod.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ mod secure_channel_map;
1313

1414
pub(crate) use inlet_controller::KafkaInletController;
1515
use ockam::identity::Identifier;
16-
use ockam_abac::expr::{eq, ident, str};
16+
use ockam_abac::expr::{eq, ident, or, str};
1717
use ockam_abac::Expr;
1818
use ockam_abac::{ABAC_HAS_CREDENTIAL_KEY, ABAC_IDENTIFIER_KEY, SUBJECT_KEY};
1919
use ockam_core::Address;
@@ -35,8 +35,11 @@ pub fn kafka_default_policy_expression() -> Expr {
3535
}
3636

3737
pub fn kafka_policy_expression(project_identifier: &Identifier) -> Expr {
38-
eq([
39-
ident(format!("{}.{}", SUBJECT_KEY, ABAC_IDENTIFIER_KEY)),
40-
str(project_identifier.to_string()),
38+
or([
39+
eq([
40+
ident(format!("{}.{}", SUBJECT_KEY, ABAC_IDENTIFIER_KEY)),
41+
str(project_identifier.to_string()),
42+
]),
43+
kafka_default_policy_expression(),
4144
])
4245
}

implementations/rust/ockam/ockam_api/src/kafka/secure_channel_map.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,10 @@ impl NodeManagerRelayCreator {
9090
relay_service: MultiAddr,
9191
alias: String,
9292
) -> Result<()> {
93-
let is_rust = !relay_service.starts_with(Project::CODE);
93+
let is_rust = {
94+
// we might create a relay in the producer passing through a project relay
95+
!(relay_service.starts_with(Project::CODE) && relay_service.len() == 1)
96+
};
9497

9598
let buffer: Vec<u8> = context
9699
.send_and_receive(

implementations/rust/ockam/ockam_api/src/nodes/service/default_address.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ impl DefaultAddress {
1414
pub const OKTA_IDENTITY_PROVIDER: &'static str = "okta";
1515
pub const KAFKA_OUTLET: &'static str = "kafka_outlet";
1616
pub const KAFKA_CONSUMER: &'static str = "kafka_consumer";
17+
pub const KAFKA_INLET: &'static str = "kafka_inlet";
1718
pub const KAFKA_PRODUCER: &'static str = "kafka_producer";
1819
pub const KAFKA_DIRECT: &'static str = "kafka_direct";
1920

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
use async_trait::async_trait;
2+
use std::net::SocketAddr;
3+
use std::sync::Arc;
4+
5+
use clap::{command, Args};
6+
use colorful::Colorful;
7+
use miette::miette;
8+
use ockam_api::colors::OckamColor;
9+
use ockam_api::nodes::models::services::{
10+
StartKafkaDirectRequest, StartKafkaRequest, StartServiceRequest,
11+
};
12+
use ockam_api::nodes::BackgroundNodeClient;
13+
use ockam_api::{fmt_log, fmt_ok};
14+
use tokio::sync::Mutex;
15+
use tokio::try_join;
16+
17+
use ockam_api::port_range::PortRange;
18+
use ockam_core::api::Request;
19+
use ockam_multiaddr::MultiAddr;
20+
use ockam_node::Context;
21+
22+
use crate::kafka::util::make_brokers_port_range;
23+
use crate::node::util::initialize_default_node;
24+
use crate::service::start::start_service_impl;
25+
use crate::util::process_nodes_multiaddr;
26+
use crate::{
27+
kafka::{kafka_default_consumer_server, kafka_inlet_default_addr},
28+
node::NodeOpts,
29+
util::parsers::socket_addr_parser,
30+
Command, CommandGlobalOpts,
31+
};
32+
33+
/// Create a new Kafka Inlet. Kafka clients v3.7.0 and earlier are supported. You can find the version you have with 'kafka-topics.sh --version'.
34+
#[derive(Clone, Debug, Args)]
35+
pub struct CreateCommand {
36+
#[command(flatten)]
37+
pub node_opts: NodeOpts,
38+
/// The local address of the service
39+
#[arg(long, default_value_t = kafka_inlet_default_addr())]
40+
pub addr: String,
41+
/// The address where to bind and where the client will connect to alongside its port, <address>:<port>.
42+
/// In case just a port is specified, the default loopback address (127.0.0.1:4000) will be used
43+
#[arg(long, default_value_t = kafka_default_consumer_server(), value_parser = socket_addr_parser)]
44+
pub from: SocketAddr,
45+
/// The address of the kafka bootstrap broker, conflicts with --to
46+
#[arg(long, required_unless_present = "to")]
47+
pub bootstrap_server: Option<String>,
48+
/// Local port range dynamically allocated to kafka brokers, must not overlap with the
49+
/// bootstrap port
50+
#[arg(long)]
51+
pub brokers_port_range: Option<PortRange>,
52+
/// The route to the Kafka outlet node, either the project in ockam orchestrator or a rust node, expected something like /project/<name>.
53+
/// Conflicts with --bootstrap-server
54+
#[arg(long, required_unless_present = "bootstrap_server", conflicts_with_all = ["bootstrap_server","consumer"])]
55+
pub to: Option<MultiAddr>,
56+
/// The route to the Kafka consumer node, valid only when --bootstrap-server is specified
57+
#[arg(long, requires = "bootstrap_server")]
58+
pub consumer: Option<MultiAddr>,
59+
}
60+
61+
#[async_trait]
62+
impl Command for CreateCommand {
63+
const NAME: &'static str = "kafka-inlet create";
64+
65+
async fn async_run(self, ctx: &Context, opts: CommandGlobalOpts) -> crate::Result<()> {
66+
let is_finished = Arc::new(Mutex::new(false));
67+
initialize_default_node(ctx, &opts).await?;
68+
69+
let brokers_port_range = self
70+
.brokers_port_range
71+
.unwrap_or_else(|| make_brokers_port_range(&self.from));
72+
let at_node = self.node_opts.at_node.clone();
73+
let addr = self.addr.clone();
74+
75+
let direct_future;
76+
let consumer_future;
77+
if let Some(bootstrap_server) = self.bootstrap_server {
78+
// TODO: allow arbitrary endpoints
79+
let bootstrap_server = bootstrap_server.parse().unwrap();
80+
let is_finished = is_finished.clone();
81+
82+
if self.to.is_some() {
83+
return Err(miette!("Cannot specify both --bootstrap-server and --to").into());
84+
}
85+
86+
// Creates a direct kafka service
87+
consumer_future = None;
88+
direct_future = Some(async move {
89+
let node = BackgroundNodeClient::create(ctx, &opts.state, &at_node).await?;
90+
91+
let payload = StartKafkaDirectRequest::new(
92+
self.from,
93+
bootstrap_server,
94+
brokers_port_range,
95+
self.consumer.clone(),
96+
);
97+
let payload = StartServiceRequest::new(payload, &addr);
98+
let req = Request::post("/node/services/kafka_direct").body(payload);
99+
start_service_impl(ctx, &node, "KafkaDirect", req).await?;
100+
101+
*is_finished.lock().await = true;
102+
103+
Ok(())
104+
});
105+
} else if let Some(to) = self.to {
106+
let is_finished = is_finished.clone();
107+
let to = process_nodes_multiaddr(&to, &opts.state).await?;
108+
let is_finished = is_finished.clone();
109+
110+
// Creates a Kafka consumer service (can also be used as a producer)
111+
direct_future = None;
112+
consumer_future = Some(async move {
113+
let node = BackgroundNodeClient::create(ctx, &opts.state, &at_node).await?;
114+
115+
let payload = StartKafkaRequest::new(self.from, brokers_port_range, to);
116+
let payload = StartServiceRequest::new(payload, &addr);
117+
let req = Request::post("/node/services/kafka_consumer").body(payload);
118+
start_service_impl(ctx, &node, "KafkaConsumer", req).await?;
119+
120+
*is_finished.lock().await = true;
121+
122+
Ok(())
123+
});
124+
} else {
125+
return Err(miette!("Must specify either --bootstrap-server or --to").into());
126+
};
127+
128+
opts.terminal
129+
.write_line(&fmt_log!("Creating KafkaInlet service...\n"))?;
130+
131+
let msgs = vec![
132+
format!(
133+
"Building KafkaInlet service {}",
134+
&self
135+
.addr
136+
.to_string()
137+
.color(OckamColor::PrimaryResource.color())
138+
),
139+
format!(
140+
"Creating KafkaInlet service at {}",
141+
&self
142+
.from
143+
.to_string()
144+
.color(OckamColor::PrimaryResource.color())
145+
),
146+
format!(
147+
"Setting brokers port range to {}",
148+
&brokers_port_range
149+
.to_string()
150+
.color(OckamColor::PrimaryResource.color())
151+
),
152+
];
153+
let progress_output = opts.terminal.progress_output(&msgs, &is_finished);
154+
if let Some(direct_future) = direct_future {
155+
let (_, _) = try_join!(direct_future, progress_output)?;
156+
} else {
157+
let (_, _) = try_join!(consumer_future.unwrap(), progress_output)?;
158+
}
159+
160+
opts.terminal
161+
.stdout()
162+
.plain(
163+
fmt_ok!(
164+
"KafkaInlet service started at {}\n",
165+
&self
166+
.from
167+
.to_string()
168+
.color(OckamColor::PrimaryResource.color())
169+
) + &fmt_log!(
170+
"Brokers port range set to {}\n\n",
171+
&brokers_port_range
172+
.to_string()
173+
.color(OckamColor::PrimaryResource.color())
174+
) + &fmt_log!(
175+
"{}\n",
176+
"Kafka clients v3.7.0 and earlier are supported."
177+
.color(OckamColor::FmtWARNBackground.color())
178+
) + &fmt_log!(
179+
"{}: '{}'.\n",
180+
"You can find the version you have with"
181+
.color(OckamColor::FmtWARNBackground.color()),
182+
"kafka-topics.sh --version".color(OckamColor::Success.color())
183+
),
184+
)
185+
.write_line()?;
186+
187+
Ok(())
188+
}
189+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
use clap::{command, Args, Subcommand};
2+
3+
use crate::kafka::inlet::create::CreateCommand;
4+
use crate::{Command, CommandGlobalOpts};
5+
6+
pub(crate) mod create;
7+
8+
/// Manage Kafka Inlets
9+
#[derive(Clone, Debug, Args)]
10+
#[command(arg_required_else_help = true, subcommand_required = true)]
11+
pub struct KafkaInletCommand {
12+
#[command(subcommand)]
13+
pub(crate) subcommand: KafkaInletSubcommand,
14+
}
15+
16+
#[derive(Clone, Debug, Subcommand)]
17+
pub enum KafkaInletSubcommand {
18+
Create(CreateCommand),
19+
}
20+
21+
impl KafkaInletCommand {
22+
pub fn run(self, opts: CommandGlobalOpts) -> miette::Result<()> {
23+
match self.subcommand {
24+
KafkaInletSubcommand::Create(c) => c.run(opts),
25+
}
26+
}
27+
28+
pub fn name(&self) -> String {
29+
match &self.subcommand {
30+
KafkaInletSubcommand::Create(c) => c.name(),
31+
}
32+
}
33+
}

implementations/rust/ockam/ockam_command/src/kafka/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use ockam_multiaddr::MultiAddr;
55

66
pub(crate) mod consumer;
77
pub(crate) mod direct;
8+
pub(crate) mod inlet;
89
pub(crate) mod outlet;
910
pub(crate) mod producer;
1011
pub(crate) mod util;
@@ -22,6 +23,10 @@ fn kafka_consumer_default_addr() -> String {
2223
DefaultAddress::KAFKA_CONSUMER.to_string()
2324
}
2425

26+
fn kafka_inlet_default_addr() -> String {
27+
DefaultAddress::KAFKA_INLET.to_string()
28+
}
29+
2530
fn kafka_direct_default_addr() -> String {
2631
DefaultAddress::KAFKA_DIRECT.to_string()
2732
}

implementations/rust/ockam/ockam_command/src/kafka/outlet/create.rs

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use async_trait::async_trait;
12
use std::net::SocketAddr;
23

34
use clap::{command, Args};
@@ -13,39 +14,31 @@ use ockam_api::{fmt_log, fmt_ok};
1314
use ockam_core::api::Request;
1415

1516
use crate::node::util::initialize_default_node;
16-
use crate::util::async_cmd;
1717
use crate::{
1818
kafka::{kafka_default_outlet_addr, kafka_default_outlet_server},
1919
node::NodeOpts,
2020
service::start::start_service_impl,
21-
CommandGlobalOpts,
21+
Command, CommandGlobalOpts,
2222
};
2323

2424
/// Create a new Kafka Outlet
2525
#[derive(Clone, Debug, Args)]
2626
pub struct CreateCommand {
2727
#[command(flatten)]
28-
node_opts: NodeOpts,
28+
pub node_opts: NodeOpts,
2929
/// The local address of the service
3030
#[arg(long, default_value_t = kafka_default_outlet_addr())]
31-
addr: String,
31+
pub addr: String,
3232
/// The address of the kafka bootstrap broker
3333
#[arg(long, default_value_t = kafka_default_outlet_server())]
34-
bootstrap_server: SocketAddr,
34+
pub bootstrap_server: SocketAddr,
3535
}
3636

37-
impl CreateCommand {
38-
pub fn run(self, opts: CommandGlobalOpts) -> miette::Result<()> {
39-
async_cmd(&self.name(), opts.clone(), |ctx| async move {
40-
self.async_run(&ctx, opts).await
41-
})
42-
}
43-
44-
pub fn name(&self) -> String {
45-
"create kafka outlet".into()
46-
}
37+
#[async_trait]
38+
impl Command for CreateCommand {
39+
const NAME: &'static str = "kafka-outlet create";
4740

48-
async fn async_run(&self, ctx: &Context, opts: CommandGlobalOpts) -> miette::Result<()> {
41+
async fn async_run(self, ctx: &Context, opts: CommandGlobalOpts) -> crate::Result<()> {
4942
initialize_default_node(ctx, &opts).await?;
5043
opts.terminal
5144
.write_line(&fmt_log!("Creating KafkaOutlet service"))?;

implementations/rust/ockam/ockam_command/src/kafka/outlet/mod.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
1-
use clap::{command, Args, Subcommand};
2-
3-
use crate::CommandGlobalOpts;
1+
pub(crate) mod create;
42

53
use self::create::CreateCommand;
6-
7-
mod create;
4+
use crate::{Command, CommandGlobalOpts};
5+
use clap::{command, Args, Subcommand};
86

97
/// Manage Kafka Outlets
108
#[derive(Clone, Debug, Args)]
119
#[command(arg_required_else_help = true, subcommand_required = true)]
1210
pub struct KafkaOutletCommand {
1311
#[command(subcommand)]
14-
subcommand: KafkaOutletSubcommand,
12+
pub(crate) subcommand: KafkaOutletSubcommand,
1513
}
1614

1715
#[derive(Clone, Debug, Subcommand)]

implementations/rust/ockam/ockam_command/src/node/create/config.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ pub struct NodeConfig {
5353
#[serde(flatten)]
5454
pub tcp_inlets: TcpInlets,
5555
#[serde(flatten)]
56+
pub kafka_inlet: KafkaInlet,
57+
#[serde(flatten)]
58+
pub kafka_outlet: KafkaOutlet,
59+
#[serde(flatten)]
5660
pub relays: Relays,
5761
}
5862

@@ -123,6 +127,8 @@ impl NodeConfig {
123127
self.policies.parse_commands(overrides)?.into(),
124128
self.tcp_outlets.parse_commands(overrides)?.into(),
125129
self.tcp_inlets.parse_commands(overrides)?.into(),
130+
self.kafka_inlet.parse_commands(overrides)?.into(),
131+
self.kafka_outlet.parse_commands(overrides)?.into(),
126132
];
127133

128134
// Run commands
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
name: kafka-portal
2+
3+
kafka-inlet:
4+
from: 127.0.0.1:19092
5+
to: /secure/api
6+
7+
kafka-outlet:
8+
bootstrap-server: 127.0.0.1:9092

0 commit comments

Comments
 (0)