Skip to content

Commit a7ff828

Browse files
feat: support service generation
Signed-off-by: Brooks Townsend <brooksmtownsend@gmail.com>
1 parent 6df22c1 commit a7ff828

File tree

9 files changed

+493
-95
lines changed

9 files changed

+493
-95
lines changed

Cargo.lock

Lines changed: 20 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
src/generated/*

crates/control-interface-proto/Cargo.toml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,19 @@ repository.workspace = true
1010
[dependencies]
1111
# Required for generating
1212
proto = { workspace = true }
13-
# Required for using
13+
# Required for using the types
1414
prost = { workspace = true, features = ["derive", "std"] }
1515

16+
# TEMPORARY: not required for this crate, required for consumers
17+
# maybe consider keeping this and the generated code? idk
18+
anyhow = { workspace = true }
19+
async-nats = { workspace = true, features = ["ring"] }
20+
bytes = { workspace = true }
21+
futures = { workspace = true }
22+
1623
[build-dependencies]
1724
prost-build = { workspace = true, features = ["format"] }
25+
nats-service-generator = { path = "../nats-service-generator" }
1826

1927
[dev-dependencies]
2028
async-nats = { workspace = true, features = ["ring"] }

crates/control-interface-proto/build.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use nats_service_generator::NatsServiceGenerator;
2+
13
fn main() -> std::io::Result<()> {
24
// Find all the .proto files
35
let protos = std::fs::read_dir("src")?
@@ -12,6 +14,10 @@ fn main() -> std::io::Result<()> {
1214
})
1315
})
1416
.collect::<Vec<_>>();
15-
prost_build::compile_protos(&protos, &["src/"])?;
17+
18+
prost_build::Config::new()
19+
.out_dir("src/generated")
20+
.service_generator(Box::new(NatsServiceGenerator))
21+
.compile_protos(&protos, &["src/"])?;
1622
Ok(())
1723
}

crates/control-interface-proto/src/ctl.proto

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ syntax = "proto3";
22

33
package wasmcloud.types;
44

5+
service ControlInterfaceService {
6+
// Request/Response
7+
rpc StartComponent(StartComponentRequest) returns (StartComponentResponse);
8+
}
9+
510
// CTL message to start component
611
message StartComponentRequest {
712
// OCI image reference

crates/control-interface-proto/src/lib.rs

Lines changed: 38 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -2,134 +2,80 @@
22
// It is important to maintain the same structure as in the proto.
33
pub mod wasmcloud {
44
pub mod ctl {
5-
include!(concat!(env!("OUT_DIR"), "/wasmcloud.types.rs"));
5+
include!("generated/wasmcloud.types.rs");
66
}
77
}
88

99
#[cfg(test)]
1010
mod test {
1111
use super::wasmcloud;
12+
use crate::wasmcloud::ctl::{ControlInterfaceServiceClient, ControlInterfaceServiceServer};
1213

13-
use bytes::BytesMut;
14-
use prost::Message;
15-
use tokio::task::JoinSet;
16-
17-
// (Host-side) implementation of the ControlInterface
18-
pub trait ControlServer {
19-
fn start_component(
20-
&self,
21-
request: wasmcloud::ctl::StartComponentRequest,
22-
) -> impl futures::Future<Output = Result<wasmcloud::ctl::StartComponentResponse, String>> + Send;
23-
}
24-
pub struct WasmcloudControlServer {}
25-
impl ControlServer for WasmcloudControlServer {
14+
struct Host;
15+
impl ControlInterfaceServiceServer for Host {
2616
async fn start_component(
2717
&self,
2818
request: wasmcloud::ctl::StartComponentRequest,
29-
) -> Result<wasmcloud::ctl::StartComponentResponse, String> {
19+
) -> anyhow::Result<wasmcloud::ctl::StartComponentResponse> {
3020
let component_id = format!("{}-{}", request.reference, request.max_instances);
3121
Ok(wasmcloud::ctl::StartComponentResponse {
3222
component_id,
3323
message: "everything went smoothly and we're going to be okay".to_string(),
3424
})
3525
}
3626
}
37-
async fn start_server<S>(server: S, client: async_nats::Client) -> Result<JoinSet<()>, String>
38-
where
39-
S: ControlServer + Send + 'static,
40-
{
41-
let mut sub = client
42-
.subscribe("wasmbus.ctl.proto.>")
43-
.await
44-
.expect("to subscribe to start component");
45-
use futures::StreamExt;
46-
let mut tasks = JoinSet::new();
47-
tasks.spawn(async move {
48-
while let Some(message) = sub.next().await {
49-
if message.subject.contains("start.component") {
50-
let request = wasmcloud::ctl::StartComponentRequest::decode(message.payload)
51-
.expect("to decode request");
52-
let reply = server
53-
.start_component(request)
54-
.await
55-
.expect("to start component");
56-
if let Some(reply_to) = message.reply {
57-
let mut buf = BytesMut::with_capacity(reply.encoded_len());
58-
reply
59-
.encode(&mut buf)
60-
.expect("to encode without reaching capacity");
61-
client
62-
.publish(reply_to, buf.into())
63-
.await
64-
.expect("to send reply");
65-
}
66-
}
67-
}
68-
});
6927

70-
Ok(tasks)
71-
}
28+
// Test and validate host functionality without NATS
29+
#[tokio::test]
30+
async fn unit_test_proto() {
31+
let host = Host {};
32+
let request = wasmcloud::ctl::StartComponentRequest {
33+
reference: "test".to_string(),
34+
max_instances: 1,
35+
};
36+
let response = host
37+
.start_component(request)
38+
.await
39+
.expect("host to handle request");
7240

73-
// (wash/wadm/client-side) implementation of the ControlInterface
74-
pub trait ControlClient {
75-
async fn start_component(
76-
&self,
77-
request: wasmcloud::ctl::StartComponentRequest,
78-
) -> Result<wasmcloud::ctl::StartComponentResponse, String>;
79-
}
80-
pub struct WasmcloudControlClient {
81-
client: async_nats::Client,
82-
}
83-
impl WasmcloudControlClient {
84-
pub fn new(client: async_nats::Client) -> Self {
85-
Self { client }
86-
}
87-
}
88-
impl ControlClient for WasmcloudControlClient {
89-
async fn start_component(
90-
&self,
91-
request: wasmcloud::ctl::StartComponentRequest,
92-
) -> Result<wasmcloud::ctl::StartComponentResponse, String> {
93-
let mut buf = BytesMut::with_capacity(request.encoded_len());
94-
request
95-
.encode(&mut buf)
96-
.expect("to encode without reaching capacity");
97-
let reply = self
98-
.client
99-
.request("wasmbus.ctl.proto.start.component", buf.into())
100-
.await
101-
.expect("to send request");
102-
wasmcloud::ctl::StartComponentResponse::decode(reply.payload).map_err(|e| e.to_string())
103-
}
41+
assert_eq!(response.component_id, "test-1");
42+
assert_eq!(
43+
response.message,
44+
"everything went smoothly and we're going to be okay"
45+
);
10446
}
10547

48+
// Test and validate host functionality with NATS
10649
#[tokio::test]
10750
async fn end_to_end_proto() {
108-
let server = WasmcloudControlServer {};
10951
let nats_client = async_nats::connect("nats://127.0.0.1:4222")
11052
.await
11153
.expect("should connect to NATS");
112-
let _task = start_server(server, nats_client)
113-
.await
114-
.expect("to start server");
115-
116-
let client = WasmcloudControlClient::new(
117-
async_nats::connect("nats://127.0.0.1:4222")
54+
let host = Host {};
55+
let server = tokio::spawn({
56+
let nats_client = nats_client.clone();
57+
wasmcloud::ctl::start_server(host, nats_client)
11858
.await
119-
.expect("should connect to NATS"),
120-
);
121-
let reply = client
122-
.start_component(wasmcloud::ctl::StartComponentRequest {
59+
.expect("to subscribe and start server")
60+
});
61+
62+
// Let subscriptions get set up
63+
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
64+
65+
let reply = nats_client
66+
.start_component(crate::wasmcloud::ctl::StartComponentRequest {
12367
reference: "test".to_string(),
12468
max_instances: 1,
12569
})
12670
.await
127-
.expect("reply to be okay");
71+
.expect("should start component");
12872

12973
assert_eq!(reply.component_id, "test-1");
13074
assert_eq!(
13175
reply.message,
13276
"everything went smoothly and we're going to be okay"
13377
);
78+
79+
server.abort();
13480
}
13581
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "nats-service-generator"
3+
version = "0.1.0"
4+
authors.workspace = true
5+
categories.workspace = true
6+
edition.workspace = true
7+
license.workspace = true
8+
repository.workspace = true
9+
10+
[dependencies]
11+
convert_case = "0.7.1"
12+
prost-build = { workspace = true, features = ["format"] }

0 commit comments

Comments
 (0)