Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions Cargo.lock

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

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ members = [
"dpd-api",
"dpd-client",
"dpd-types",
"dropshot-apis",
"packet",
"pcap",
"swadm",
Expand Down Expand Up @@ -62,12 +63,14 @@ bytes = "1.6"
camino = { version = "1.1", features = ["serde1"] }
cfg-if = "1"
chrono = "0.4"
colored = "2"
clap = { version = "4.5.8", features = [ "derive" ] }
colored = "2"
csv = "1.3"
curl = "0.4"
display-error-chain = "0.2"
dropshot = "0.16.3"
dropshot-api-manager = "0.2.2"
dropshot-api-manager-types = "0.2.2"
expectorate = "1"
futures = "0.3"
http = "1.2.0"
Expand Down
2 changes: 1 addition & 1 deletion dpd-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ oxnet.workspace = true
schemars.workspace = true
serde.workspace = true
thiserror.workspace = true
transceiver-controller.workspace = true
transceiver-controller = { workspace = true, features = ["api-traits"] }
uuid.workspace = true
21 changes: 0 additions & 21 deletions dpd/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,6 @@ pub struct Cli {
pub(crate) enum Args {
/// Run the Dendrite API server.
Run(Box<Opt>),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that we only have the run command, we can just drop the subcommand structure entirely.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, we've retained the subcommand structure in other servers even if it's the only command.

If you still think we should get rid of the run command, can this be a followup? Would have to update all the places that run the binary as well, which adds risk for something that's pretty time-constrained.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If our standard practice is to keep the vestigial run subcommand, I'm fine with that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks -- see https://github.com/oxidecomputer/omicron/blob/5f7ab65c8c8b215c6cf71b0d6e018574aa2e7f92/sled-agent/src/bin/sled-agent.rs#L25-L31 for an example. I guess one advantage is that if we do need to add a second command in the future, the places that run the binary don't have to be updated again.

/// Generate an OpenAPI specification for the Dendrite server.
Openapi,
}

/// dataplane controller for oxide switch
Expand Down Expand Up @@ -791,29 +789,10 @@ fn main() -> anyhow::Result<()> {
let cli = Cli::parse();

match cli.args {
Args::Openapi => print_openapi(),
Args::Run(opt) => oxide_tokio_rt::run(run_dpd(*opt)),
}
}

fn print_openapi() -> anyhow::Result<()> {
// TODO: Once migrated to the OpenAPI manager, this should use the stub API
// description. But there are currently additional backend-specific methods
// added by the tofino-asic and softnpu features -- those would need to be
// migrated to the API trait (possibly via a uniform API across all
// backends).
crate::api_server::http_api()
.openapi(
"Oxide Switch Dataplane Controller",
semver::Version::new(0, 1, 0),
)
.description("API for managing the Oxide rack switch")
.contact_url("https://oxide.computer")
.contact_email("api@oxide.computer")
.write(&mut std::io::stdout())
.context("writing OpenAPI specification")
}

async fn run_dpd(opt: Opt) -> anyhow::Result<()> {
let config = config::build_config(&opt)?;

Expand Down
25 changes: 0 additions & 25 deletions dpd/tests/test_openapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,6 @@

#![allow(clippy::missing_safety_doc)]

// Test that the OpenAPI document matches the one in the repository.
//
// Note that we only test the ASIC version, which should be a strict superset
// of the others, including the counter-related endpoints.
#[cfg(all(feature = "tofino_asic", test))]
#[test]
fn test_dpd_openapi() {
let dpd_path = env!("CARGO_BIN_EXE_dpd");
let openapi_path =
concat!(env!("CARGO_MANIFEST_DIR"), "/../openapi/dpd.json");
let output = std::process::Command::new(dpd_path)
.arg("openapi")
.output()
.expect("failed to run `dpd`");
if !output.status.success() {
let mut msg = String::from("\n`dpd openapi` failed!\n\n");
for line in String::from_utf8_lossy(&output.stderr).lines() {
msg.push_str(&format!("stderr: {line}\n"));
}
panic!("{}", msg);
}
let output = std::str::from_utf8(&output.stdout).expect("Non-UTF8 output");
expectorate::assert_contents(openapi_path, output);
}

// NOTE: This is a horrible hack that appears to be necessary.
//
// We use a mapfile in the tofino_sde repo to declare the QSFP management
Expand Down
14 changes: 14 additions & 0 deletions dropshot-apis/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "dendrite-dropshot-apis"
version = "0.1.0"
edition = "2024"
license = "MPL-2.0"

[dependencies]
anyhow.workspace = true
camino.workspace = true
clap.workspace = true
dpd-api.workspace = true
dropshot-api-manager-types.workspace = true
dropshot-api-manager.workspace = true
semver.workspace = true
78 changes: 78 additions & 0 deletions dropshot-apis/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// 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 std::process::ExitCode;

use anyhow::Context;
use camino::Utf8PathBuf;
use clap::Parser;
use dpd_api::*;
use dropshot_api_manager::{Environment, ManagedApiConfig, ManagedApis};
use dropshot_api_manager_types::{ManagedApiMetadata, Versions};

pub fn environment() -> anyhow::Result<Environment> {
// The workspace root is one level up from this crate's directory.
let workspace_root = Utf8PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.to_path_buf();
let env = Environment::new(
// This is the command used to run the OpenAPI manager.
"cargo xtask openapi".to_owned(),
workspace_root,
// This is the location within the workspace root where the OpenAPI
// documents are stored.
"openapi",
)?;
Ok(env)
}

/// The list of APIs managed by the OpenAPI manager.
pub fn all_apis() -> anyhow::Result<ManagedApis> {
let apis = vec![ManagedApiConfig {
ident: "dpd",
versions: Versions::Lockstep {
version: semver::Version::new(0, 1, 0),
},
title: "Oxide Switch Dataplane Controller",
metadata: ManagedApiMetadata {
description: Some("API for managing the Oxide rack switch"),
contact_url: Some("https://oxide.computer"),
contact_email: Some("api@oxide.computer"),
..Default::default()
},
api_description: dpd_api_mod::stub_api_description,
extra_validation: None,
}];

let apis = ManagedApis::new(apis).context("error creating ManagedApis")?;
Ok(apis)
}

fn main() -> anyhow::Result<ExitCode> {
let app = dropshot_api_manager::App::parse();
let env = environment()?;
let apis = all_apis()?;

Ok(app.exec(&env, &apis))
}

#[cfg(test)]
mod test {
use dropshot_api_manager::test_util::check_apis_up_to_date;

use super::*;

// Also recommended: a test which ensures documents are up-to-date. The
// OpenAPI manager comes with a helper function for this, called
// `check_apis_up_to_date`.
#[test]
fn test_apis_up_to_date() -> anyhow::Result<ExitCode> {
let env = environment()?;
let apis = all_apis()?;

let result = check_apis_up_to_date(&env, &apis)?;
Ok(result.to_exit_code())
}
}
Loading