Skip to content

Commit

Permalink
[oximeter] turn API into a trait (#6130)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
sunshowers authored Jul 22, 2024
1 parent 28c9a8b commit e346fd1
Show file tree
Hide file tree
Showing 13 changed files with 189 additions and 166 deletions.
15 changes: 15 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ members = [
"nexus/test-utils-macros",
"nexus/test-utils",
"nexus/types",
"oximeter/api",
"oximeter/collector",
"oximeter/db",
"oximeter/impl",
Expand Down Expand Up @@ -164,6 +165,7 @@ default-members = [
"nexus/test-utils-macros",
"nexus/test-utils",
"nexus/types",
"oximeter/api",
"oximeter/collector",
"oximeter/db",
"oximeter/impl",
Expand Down Expand Up @@ -408,6 +410,7 @@ opte-ioctl = { git = "https://github.com/oxidecomputer/opte", rev = "3dc9a3dd8d3
oso = "0.27"
owo-colors = "4.0.0"
oximeter = { path = "oximeter/oximeter" }
oximeter-api = { path = "oximeter/api" }
oximeter-client = { path = "clients/oximeter-client" }
oximeter-db = { path = "oximeter/db/", default-features = false }
oximeter-collector = { path = "oximeter/collector" }
Expand Down
1 change: 1 addition & 0 deletions dev-tools/openapi-manager/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ omicron-workspace-hack.workspace = true
openapiv3.workspace = true
openapi-lint.workspace = true
owo-colors.workspace = true
oximeter-api.workspace = true
serde_json.workspace = true
similar.workspace = true
supports-color.workspace = true
Expand Down
10 changes: 10 additions & 0 deletions dev-tools/openapi-manager/src/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,16 @@ pub fn all_apis() -> Vec<ApiSpec> {
filename: "nexus-internal.json",
extra_validation: None,
},
ApiSpec {
title: "Oxide Oximeter API",
version: "0.0.1",
description: "API for interacting with oximeter",
boundary: ApiBoundary::Internal,
api_description:
oximeter_api::oximeter_api_mod::stub_api_description,
filename: "oximeter.json",
extra_validation: None,
},
ApiSpec {
title: "Oxide Technician Port Control Service",
version: "0.0.1",
Expand Down
4 changes: 4 additions & 0 deletions openapi/oximeter.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"paths": {
"/info": {
"get": {
"summary": "Return identifying information about this collector.",
"operationId": "collector_info",
"responses": {
"200": {
Expand All @@ -35,6 +36,7 @@
},
"/producers": {
"get": {
"summary": "List all producers.",
"operationId": "producers_list",
"parameters": [
{
Expand Down Expand Up @@ -81,6 +83,7 @@
}
},
"post": {
"summary": "Handle a request from Nexus to register a new producer with this collector.",
"operationId": "producers_post",
"requestBody": {
"content": {
Expand All @@ -107,6 +110,7 @@
},
"/producers/{producer_id}": {
"delete": {
"summary": "Delete a producer by ID.",
"operationId": "producer_delete",
"parameters": [
{
Expand Down
16 changes: 16 additions & 0 deletions oximeter/api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "oximeter-api"
version = "0.1.0"
edition = "2021"

[lints]
workspace = true

[dependencies]
chrono.workspace = true
dropshot.workspace = true
omicron-common.workspace = true
omicron-workspace-hack.workspace = true
schemars.workspace = true
serde.workspace = true
uuid.workspace = true
79 changes: 79 additions & 0 deletions oximeter/api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use chrono::{DateTime, Utc};
use dropshot::{
EmptyScanParams, HttpError, HttpResponseDeleted, HttpResponseOk,
HttpResponseUpdatedNoContent, PaginationParams, Query, RequestContext,
ResultsPage, TypedBody,
};
use omicron_common::api::internal::nexus::ProducerEndpoint;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

#[dropshot::api_description {
module = "oximeter_api_mod",
}]
pub trait OximeterApi {
type Context;

/// Handle a request from Nexus to register a new producer with this collector.
#[endpoint {
method = POST,
path = "/producers",
}]
async fn producers_post(
request_context: RequestContext<Self::Context>,
body: TypedBody<ProducerEndpoint>,
) -> Result<HttpResponseUpdatedNoContent, HttpError>;

/// List all producers.
#[endpoint {
method = GET,
path = "/producers",
}]
async fn producers_list(
request_context: RequestContext<Self::Context>,
query: Query<PaginationParams<EmptyScanParams, ProducerPage>>,
) -> Result<HttpResponseOk<ResultsPage<ProducerEndpoint>>, HttpError>;

/// Delete a producer by ID.
#[endpoint {
method = DELETE,
path = "/producers/{producer_id}",
}]
async fn producer_delete(
request_context: RequestContext<Self::Context>,
path: dropshot::Path<ProducerIdPathParams>,
) -> Result<HttpResponseDeleted, HttpError>;

/// Return identifying information about this collector.
#[endpoint {
method = GET,
path = "/info",
}]
async fn collector_info(
request_context: RequestContext<Self::Context>,
) -> Result<HttpResponseOk<CollectorInfo>, HttpError>;
}

/// Parameters for paginating the list of producers.
#[derive(Clone, Copy, Debug, Deserialize, JsonSchema, Serialize)]
pub struct ProducerPage {
pub id: Uuid,
}

#[derive(Clone, Copy, Debug, Deserialize, JsonSchema, Serialize)]
pub struct ProducerIdPathParams {
pub producer_id: Uuid,
}

#[derive(Clone, Copy, Debug, Deserialize, JsonSchema, Serialize)]
pub struct CollectorInfo {
/// The collector's UUID.
pub id: Uuid,
/// Last time we refreshed our producer list with Nexus.
pub last_refresh: Option<DateTime<Utc>>,
}
1 change: 1 addition & 0 deletions oximeter/collector/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ internal-dns.workspace = true
nexus-types.workspace = true
omicron-common.workspace = true
oximeter.workspace = true
oximeter-api.workspace = true
oximeter-client.workspace = true
oximeter-db.workspace = true
rand.workspace = true
Expand Down
17 changes: 0 additions & 17 deletions oximeter/collector/src/bin/oximeter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use anyhow::{anyhow, Context};
use clap::Parser;
use omicron_common::cmd::fatal;
use omicron_common::cmd::CmdError;
use oximeter_collector::oximeter_api;
use oximeter_collector::standalone_nexus_api;
use oximeter_collector::Config;
use oximeter_collector::Oximeter;
Expand All @@ -23,16 +22,6 @@ use std::net::SocketAddrV6;
use std::path::PathBuf;
use uuid::Uuid;

pub fn run_openapi() -> Result<(), String> {
oximeter_api()
.openapi("Oxide Oximeter API", "0.0.1")
.description("API for interacting with oximeter")
.contact_url("https://oxide.computer")
.contact_email("[email protected]")
.write(&mut std::io::stdout())
.map_err(|e| e.to_string())
}

pub fn run_standalone_openapi() -> Result<(), String> {
standalone_nexus_api()
.openapi("Oxide Nexus API", "0.0.1")
Expand All @@ -47,9 +36,6 @@ pub fn run_standalone_openapi() -> Result<(), String> {
#[derive(Parser)]
#[clap(name = "oximeter", about = "See README.adoc for more information")]
enum Args {
/// Print the external OpenAPI Spec document and exit
Openapi,

/// Start an Oximeter server
Run {
/// Path to TOML file with configuration for the server
Expand Down Expand Up @@ -133,9 +119,6 @@ async fn main() {
async fn do_run() -> Result<(), CmdError> {
let args = Args::parse();
match args {
Args::Openapi => {
run_openapi().map_err(|err| CmdError::Failure(anyhow!(err)))
}
Args::Run { config_file, id, address } => {
let config = Config::from_file(config_file).unwrap();
let args = OximeterArguments { id, address };
Expand Down
Loading

0 comments on commit e346fd1

Please sign in to comment.