Skip to content

Commit e346fd1

Browse files
authored
[oximeter] turn API into a trait (#6130)
Use the OpenAPI manager to manage this trait as well. An interesting question is the standalone Nexus impl also defined by Oximeter -- should that switch to using the internal API crate? As things stand we'd have to write tons of methods that just error out, but I have some thoughts on how to make that easier on the Dropshot side. For now we've decided to leave it as-is, but it's a good data point to consider for future Dropshot improvements.
1 parent 28c9a8b commit e346fd1

File tree

13 files changed

+189
-166
lines changed

13 files changed

+189
-166
lines changed

Cargo.lock

+15
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ members = [
6363
"nexus/test-utils-macros",
6464
"nexus/test-utils",
6565
"nexus/types",
66+
"oximeter/api",
6667
"oximeter/collector",
6768
"oximeter/db",
6869
"oximeter/impl",
@@ -164,6 +165,7 @@ default-members = [
164165
"nexus/test-utils-macros",
165166
"nexus/test-utils",
166167
"nexus/types",
168+
"oximeter/api",
167169
"oximeter/collector",
168170
"oximeter/db",
169171
"oximeter/impl",
@@ -408,6 +410,7 @@ opte-ioctl = { git = "https://github.com/oxidecomputer/opte", rev = "3dc9a3dd8d3
408410
oso = "0.27"
409411
owo-colors = "4.0.0"
410412
oximeter = { path = "oximeter/oximeter" }
413+
oximeter-api = { path = "oximeter/api" }
411414
oximeter-client = { path = "clients/oximeter-client" }
412415
oximeter-db = { path = "oximeter/db/", default-features = false }
413416
oximeter-collector = { path = "oximeter/collector" }

dev-tools/openapi-manager/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ omicron-workspace-hack.workspace = true
2424
openapiv3.workspace = true
2525
openapi-lint.workspace = true
2626
owo-colors.workspace = true
27+
oximeter-api.workspace = true
2728
serde_json.workspace = true
2829
similar.workspace = true
2930
supports-color.workspace = true

dev-tools/openapi-manager/src/spec.rs

+10
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,16 @@ pub fn all_apis() -> Vec<ApiSpec> {
6666
filename: "nexus-internal.json",
6767
extra_validation: None,
6868
},
69+
ApiSpec {
70+
title: "Oxide Oximeter API",
71+
version: "0.0.1",
72+
description: "API for interacting with oximeter",
73+
boundary: ApiBoundary::Internal,
74+
api_description:
75+
oximeter_api::oximeter_api_mod::stub_api_description,
76+
filename: "oximeter.json",
77+
extra_validation: None,
78+
},
6979
ApiSpec {
7080
title: "Oxide Technician Port Control Service",
7181
version: "0.0.1",

openapi/oximeter.json

+4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"paths": {
1313
"/info": {
1414
"get": {
15+
"summary": "Return identifying information about this collector.",
1516
"operationId": "collector_info",
1617
"responses": {
1718
"200": {
@@ -35,6 +36,7 @@
3536
},
3637
"/producers": {
3738
"get": {
39+
"summary": "List all producers.",
3840
"operationId": "producers_list",
3941
"parameters": [
4042
{
@@ -81,6 +83,7 @@
8183
}
8284
},
8385
"post": {
86+
"summary": "Handle a request from Nexus to register a new producer with this collector.",
8487
"operationId": "producers_post",
8588
"requestBody": {
8689
"content": {
@@ -107,6 +110,7 @@
107110
},
108111
"/producers/{producer_id}": {
109112
"delete": {
113+
"summary": "Delete a producer by ID.",
110114
"operationId": "producer_delete",
111115
"parameters": [
112116
{

oximeter/api/Cargo.toml

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[package]
2+
name = "oximeter-api"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[lints]
7+
workspace = true
8+
9+
[dependencies]
10+
chrono.workspace = true
11+
dropshot.workspace = true
12+
omicron-common.workspace = true
13+
omicron-workspace-hack.workspace = true
14+
schemars.workspace = true
15+
serde.workspace = true
16+
uuid.workspace = true

oximeter/api/src/lib.rs

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
5+
use chrono::{DateTime, Utc};
6+
use dropshot::{
7+
EmptyScanParams, HttpError, HttpResponseDeleted, HttpResponseOk,
8+
HttpResponseUpdatedNoContent, PaginationParams, Query, RequestContext,
9+
ResultsPage, TypedBody,
10+
};
11+
use omicron_common::api::internal::nexus::ProducerEndpoint;
12+
use schemars::JsonSchema;
13+
use serde::{Deserialize, Serialize};
14+
use uuid::Uuid;
15+
16+
#[dropshot::api_description {
17+
module = "oximeter_api_mod",
18+
}]
19+
pub trait OximeterApi {
20+
type Context;
21+
22+
/// Handle a request from Nexus to register a new producer with this collector.
23+
#[endpoint {
24+
method = POST,
25+
path = "/producers",
26+
}]
27+
async fn producers_post(
28+
request_context: RequestContext<Self::Context>,
29+
body: TypedBody<ProducerEndpoint>,
30+
) -> Result<HttpResponseUpdatedNoContent, HttpError>;
31+
32+
/// List all producers.
33+
#[endpoint {
34+
method = GET,
35+
path = "/producers",
36+
}]
37+
async fn producers_list(
38+
request_context: RequestContext<Self::Context>,
39+
query: Query<PaginationParams<EmptyScanParams, ProducerPage>>,
40+
) -> Result<HttpResponseOk<ResultsPage<ProducerEndpoint>>, HttpError>;
41+
42+
/// Delete a producer by ID.
43+
#[endpoint {
44+
method = DELETE,
45+
path = "/producers/{producer_id}",
46+
}]
47+
async fn producer_delete(
48+
request_context: RequestContext<Self::Context>,
49+
path: dropshot::Path<ProducerIdPathParams>,
50+
) -> Result<HttpResponseDeleted, HttpError>;
51+
52+
/// Return identifying information about this collector.
53+
#[endpoint {
54+
method = GET,
55+
path = "/info",
56+
}]
57+
async fn collector_info(
58+
request_context: RequestContext<Self::Context>,
59+
) -> Result<HttpResponseOk<CollectorInfo>, HttpError>;
60+
}
61+
62+
/// Parameters for paginating the list of producers.
63+
#[derive(Clone, Copy, Debug, Deserialize, JsonSchema, Serialize)]
64+
pub struct ProducerPage {
65+
pub id: Uuid,
66+
}
67+
68+
#[derive(Clone, Copy, Debug, Deserialize, JsonSchema, Serialize)]
69+
pub struct ProducerIdPathParams {
70+
pub producer_id: Uuid,
71+
}
72+
73+
#[derive(Clone, Copy, Debug, Deserialize, JsonSchema, Serialize)]
74+
pub struct CollectorInfo {
75+
/// The collector's UUID.
76+
pub id: Uuid,
77+
/// Last time we refreshed our producer list with Nexus.
78+
pub last_refresh: Option<DateTime<Utc>>,
79+
}

oximeter/collector/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ internal-dns.workspace = true
1919
nexus-types.workspace = true
2020
omicron-common.workspace = true
2121
oximeter.workspace = true
22+
oximeter-api.workspace = true
2223
oximeter-client.workspace = true
2324
oximeter-db.workspace = true
2425
rand.workspace = true

oximeter/collector/src/bin/oximeter.rs

-17
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ use anyhow::{anyhow, Context};
1010
use clap::Parser;
1111
use omicron_common::cmd::fatal;
1212
use omicron_common::cmd::CmdError;
13-
use oximeter_collector::oximeter_api;
1413
use oximeter_collector::standalone_nexus_api;
1514
use oximeter_collector::Config;
1615
use oximeter_collector::Oximeter;
@@ -23,16 +22,6 @@ use std::net::SocketAddrV6;
2322
use std::path::PathBuf;
2423
use uuid::Uuid;
2524

26-
pub fn run_openapi() -> Result<(), String> {
27-
oximeter_api()
28-
.openapi("Oxide Oximeter API", "0.0.1")
29-
.description("API for interacting with oximeter")
30-
.contact_url("https://oxide.computer")
31-
.contact_email("[email protected]")
32-
.write(&mut std::io::stdout())
33-
.map_err(|e| e.to_string())
34-
}
35-
3625
pub fn run_standalone_openapi() -> Result<(), String> {
3726
standalone_nexus_api()
3827
.openapi("Oxide Nexus API", "0.0.1")
@@ -47,9 +36,6 @@ pub fn run_standalone_openapi() -> Result<(), String> {
4736
#[derive(Parser)]
4837
#[clap(name = "oximeter", about = "See README.adoc for more information")]
4938
enum Args {
50-
/// Print the external OpenAPI Spec document and exit
51-
Openapi,
52-
5339
/// Start an Oximeter server
5440
Run {
5541
/// Path to TOML file with configuration for the server
@@ -133,9 +119,6 @@ async fn main() {
133119
async fn do_run() -> Result<(), CmdError> {
134120
let args = Args::parse();
135121
match args {
136-
Args::Openapi => {
137-
run_openapi().map_err(|err| CmdError::Failure(anyhow!(err)))
138-
}
139122
Args::Run { config_file, id, address } => {
140123
let config = Config::from_file(config_file).unwrap();
141124
let args = OximeterArguments { id, address };

0 commit comments

Comments
 (0)