Skip to content

Commit 834d9fc

Browse files
committed
vmm: Add auto generated openapi docs
1 parent 3ef2693 commit 834d9fc

17 files changed

Lines changed: 1214 additions & 1 deletion

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ node_modules/
88
.venv
99
/tmp
1010
.claude/settings.local.json
11+
__pycache__

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.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ hex = { version = "0.4.3", default-features = false }
112112
hex_fmt = "0.3.0"
113113
hex-literal = "1.0.0"
114114
prost = "0.13.5"
115+
prost-types = "0.13.5"
115116
scale = { version = "3.7.4", package = "parity-scale-codec", features = [
116117
"derive",
117118
] }

gateway/rpc/src/generated.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
#![allow(async_fn_in_trait)]
22

3+
pub const FILE_DESCRIPTOR_SET: &[u8] =
4+
include_bytes!(concat!(env!("OUT_DIR"), "/file_descriptor_set.bin"));
5+
36
include!(concat!(env!("OUT_DIR"), "/gateway.rs"));

guest-agent/rpc/src/generated.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
#![allow(async_fn_in_trait)]
22

3+
pub const FILE_DESCRIPTOR_SET: &[u8] =
4+
include_bytes!(concat!(env!("OUT_DIR"), "/file_descriptor_set.bin"));
5+
36
include!(concat!(env!("OUT_DIR"), "/dstack_guest.rs"));

guest-api/src/generated/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
#![allow(async_fn_in_trait)]
22

3+
pub const FILE_DESCRIPTOR_SET: &[u8] =
4+
include_bytes!(concat!(env!("OUT_DIR"), "/file_descriptor_set.bin"));
5+
36
include!(concat!(env!("OUT_DIR"), "/guest_api.rs"));

host-api/src/generated/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
#![allow(async_fn_in_trait)]
22

3+
pub const FILE_DESCRIPTOR_SET: &[u8] =
4+
include_bytes!(concat!(env!("OUT_DIR"), "/file_descriptor_set.bin"));
5+
36
include!(concat!(env!("OUT_DIR"), "/host_api.rs"));

kms/rpc/src/generated.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
#![allow(async_fn_in_trait)]
22

3+
pub const FILE_DESCRIPTOR_SET: &[u8] =
4+
include_bytes!(concat!(env!("OUT_DIR"), "/file_descriptor_set.bin"));
5+
36
include!(concat!(env!("OUT_DIR"), "/kms.rs"));

ra-rpc/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ bon.workspace = true
2222
rocket-vsock-listener = { workspace = true, optional = true }
2323
serde.workspace = true
2424
x509-parser.workspace = true
25+
prost-types = { workspace = true, optional = true }
2526

2627
[features]
2728
default = ["rocket", "client"]
2829
rocket = ["dep:rocket", "dep:rocket-vsock-listener"]
2930
client = ["reqwest"]
31+
openapi = ["dep:prost-types"]

ra-rpc/prpc-openapi.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Generating OpenAPI docs for pRPC services
2+
3+
This repository now ships a lightweight OpenAPI generator inside `ra-rpc`. It can
4+
derive a specification (plus a Swagger UI test page) directly from the protobuf
5+
descriptors that are already produced during the `prpc_build` step.
6+
7+
## 1. Enable the feature
8+
9+
Add the `openapi` feature when depending on `ra-rpc`:
10+
11+
```toml
12+
[dependencies]
13+
ra-rpc = { path = "../ra-rpc", features = ["openapi", "rocket"] }
14+
```
15+
16+
The `rocket` feature is optional if you only need the JSON document and plan to
17+
serve it through another framework.
18+
19+
## 2. Export the descriptor from your `*-rpc` crate
20+
21+
Every RPC crate already has access to `file_descriptor_set.bin`. Expose it so
22+
that application binaries can include it:
23+
24+
```rust
25+
pub const FILE_DESCRIPTOR_SET: &[u8] =
26+
include_bytes!(concat!(env!("OUT_DIR"), "/file_descriptor_set.bin"));
27+
```
28+
29+
This repository now does that for all existing RPC crates.
30+
31+
## 3. Build and mount the document
32+
33+
```rust
34+
use ra_rpc::openapi::{
35+
build_openapi_doc, DescriptorSource, DocumentInfo, ServiceConfig, SwaggerUiConfig,
36+
};
37+
38+
fn openapi_doc() -> anyhow::Result<ra_rpc::openapi::OpenApiDoc> {
39+
let descriptor = dstack_guest_agent_rpc::FILE_DESCRIPTOR_SET;
40+
let sources = vec![DescriptorSource::new(
41+
descriptor,
42+
vec![
43+
// Mounts /prpc/Worker.Version (prefix is optional when you don't trim).
44+
ServiceConfig::new("Worker", "/prpc").with_method_prefix("Worker."),
45+
],
46+
)];
47+
48+
let info = DocumentInfo::new("Guest Worker API", env!("CARGO_PKG_VERSION"))
49+
.with_description("Auto generated from protobuf descriptors")
50+
.add_server("https://example.com/prpc");
51+
52+
let ui = SwaggerUiConfig {
53+
title: "Guest Worker RPC".into(),
54+
..Default::default()
55+
};
56+
57+
build_openapi_doc(&sources, &info, ui)
58+
}
59+
```
60+
61+
Serving it through Rocket is one line:
62+
63+
```rust
64+
let openapi = openapi_doc()?;
65+
let rocket = ra_rpc::rocket_helper::mount_openapi_docs(rocket, openapi, "/rpc-docs");
66+
```
67+
68+
* `GET /rpc-docs/openapi.json` returns the specification.
69+
* `GET /rpc-docs/docs` serves a Swagger UI page backed by the same spec.
70+
71+
You can mount as many descriptor sources as you need (for example when the same
72+
binary exposes both admin and user RPC stacks). Just add more `DescriptorSource`
73+
entries that point to the relevant `FILE_DESCRIPTOR_SET` constants.

0 commit comments

Comments
 (0)