From e871d8ada247816ca09fdbebc676b2f823c91ff3 Mon Sep 17 00:00:00 2001 From: Rain Date: Sat, 31 Oct 2026 17:21:01 -0700 Subject: [PATCH] [meta] lint OpenAPI documents with openapi-lint Lint the latest and non-blessed versions. Also update dropshot-api-manager to 0.9.1, which explains how to fix validation failures on blessed versions. --- Cargo.lock | 291 ++++++++++++++++++++++++++++------ Cargo.toml | 6 +- bin/dropshot-apis/Cargo.toml | 2 + bin/dropshot-apis/src/main.rs | 25 ++- 4 files changed, 273 insertions(+), 51 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fb49b0633..6559bd543 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2220,9 +2220,9 @@ dependencies = [ [[package]] name = "dropshot-api-manager" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "638600c0fcc6b8aa0e30576ce7505639f2753474f4b90e222cf19cfd3a897643" +checksum = "e7d7b4232000e193b021e8ad64b6355a6a62b43e05bfa710e73ad1607f781f8b" dependencies = [ "anyhow", "atomicwrites", @@ -2253,9 +2253,9 @@ dependencies = [ [[package]] name = "dropshot-api-manager-types" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd52348da2ad7f65156bfec30234a723170944046d0411d7f334d7ccd6a63a3" +checksum = "91edb8710d002f0a89495e9e6365a1d0f27b9c8d9f5fabab802961b82b86acef" dependencies = [ "anyhow", "camino", @@ -3072,9 +3072,9 @@ dependencies = [ [[package]] name = "git-stub-vcs" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030ad7aaa1d066e8fb39ac2748f1a12b072eed4a4af8d2e073fc80ba74ab173c" +checksum = "5f1531d32cc3bded64fca8c105ae157168d071f5bb23ad15870bb3e2c3159eeb" dependencies = [ "atomicwrites", "camino", @@ -3589,11 +3589,59 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" dependencies = [ "displaydoc", - "yoke", + "yoke 0.7.5", "zerofrom", - "zerovec", + "zerovec 0.10.4", +] + +[[package]] +name = "icu_collections" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe090a42a0d3140fed8c63263ed1fecc02422cb2f4bfe55cd8de2de1b8e105dd" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke 0.8.4", + "zerofrom", + "zerovec 0.11.9", +] + +[[package]] +name = "icu_locale_core" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25d29b57c17c6528d0dc4c2141cfa2d08200465acb537a3909c8b9b8a4a984c0" +dependencies = [ + "displaydoc", + "litemap 0.8.3", + "serde", + "tinystr 0.8.5", + "writeable 0.6.4", + "zerovec 0.11.9", +] + +[[package]] +name = "icu_locale_fallback" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "406e9ae5b95d8aa37db6293405931b766a6172d1848daee22d70eb44aa78a43c" +dependencies = [ + "icu_locale_core", + "icu_locale_fallback_data", + "icu_provider 2.4.0", + "potential_utf", + "tinystr 0.8.5", + "zerovec 0.11.9", ] +[[package]] +name = "icu_locale_fallback_data" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3632ebbd0c5c3637ba1a40bdf8288f2cb9d2268e73441a69d5fc73d7cfbaa87c" + [[package]] name = "icu_locid" version = "1.5.0" @@ -3601,10 +3649,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" dependencies = [ "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", + "litemap 0.7.4", + "tinystr 0.7.6", + "writeable 0.5.5", + "zerovec 0.10.4", ] [[package]] @@ -3616,9 +3664,9 @@ dependencies = [ "displaydoc", "icu_locid", "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", + "icu_provider 1.5.0", + "tinystr 0.7.6", + "zerovec 0.10.4", ] [[package]] @@ -3634,15 +3682,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" dependencies = [ "displaydoc", - "icu_collections", + "icu_collections 1.5.0", "icu_normalizer_data", "icu_properties", - "icu_provider", + "icu_provider 1.5.0", "smallvec", "utf16_iter", "utf8_iter", "write16", - "zerovec", + "zerovec 0.10.4", ] [[package]] @@ -3658,12 +3706,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" dependencies = [ "displaydoc", - "icu_collections", + "icu_collections 1.5.0", "icu_locid_transform", "icu_properties_data", - "icu_provider", - "tinystr", - "zerovec", + "icu_provider 1.5.0", + "tinystr 0.7.6", + "zerovec 0.10.4", ] [[package]] @@ -3682,11 +3730,28 @@ dependencies = [ "icu_locid", "icu_provider_macros", "stable_deref_trait", - "tinystr", - "writeable", - "yoke", + "tinystr 0.7.6", + "writeable 0.5.5", + "yoke 0.7.5", + "zerofrom", + "zerovec 0.10.4", +] + +[[package]] +name = "icu_provider" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413099315636c588ee8f011190b329b971a09a8fae9d157e01dcae8379468514" +dependencies = [ + "displaydoc", + "icu_locale_core", + "serde", + "stable_deref_trait", + "writeable 0.6.4", + "yoke 0.8.4", "zerofrom", - "zerovec", + "zerotrie", + "zerovec 0.11.9", ] [[package]] @@ -3700,6 +3765,29 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "icu_segmenter" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61b195dc605f81aa181bec9b3bd14e26cc71b903e4644f6af578ca9772c7a710" +dependencies = [ + "icu_collections 2.4.0", + "icu_locale_fallback", + "icu_provider 2.4.0", + "icu_segmenter_data", + "potential_utf", + "smallvec", + "utf8_iter", + "zerotrie", + "zerovec 0.11.9", +] + +[[package]] +name = "icu_segmenter_data" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "913a7d294ad056474749152107309fdee3ec17d2482d1ab70fdc61a81c64b9a3" + [[package]] name = "id-arena" version = "2.3.0" @@ -4440,6 +4528,12 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" +[[package]] +name = "litemap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" + [[package]] name = "lock_api" version = "0.4.12" @@ -5379,6 +5473,18 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "openapi-lint" +version = "0.5.0" +source = "git+https://github.com/oxidecomputer/openapi-lint?branch=main#619147ca8203e3f287695a3ff89050c8b7346be4" +dependencies = [ + "heck 0.5.0", + "indexmap 2.14.0", + "lazy_static", + "openapiv3", + "regex", +] + [[package]] name = "openapiv3" version = "2.2.0" @@ -6395,6 +6501,17 @@ dependencies = [ "serde", ] +[[package]] +name = "potential_utf" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" +dependencies = [ + "serde_core", + "writeable 0.6.4", + "zerovec 0.11.9", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -7029,6 +7146,8 @@ dependencies = [ "clap", "dropshot-api-manager", "dropshot-api-manager-types", + "openapi-lint", + "openapiv3", "propolis-server-api", "semver 1.0.28", ] @@ -9243,6 +9362,17 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "synstructure" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + [[package]] name = "system-configuration" version = "0.7.0" @@ -9458,13 +9588,13 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.16.2" +version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" +checksum = "6ecfad6c3abc80a577f2b91c1e412ee57e7a060d430b553c1b0c940974ebcd49" dependencies = [ + "icu_segmenter", "smawk", "terminal_size 0.4.3", - "unicode-linebreak", "unicode-width 0.2.1", ] @@ -9597,7 +9727,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ "displaydoc", - "zerovec", + "zerovec 0.10.4", +] + +[[package]] +name = "tinystr" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abc840c238154d58f9e86b2792261edfde565a17696e1cda1a449de27979801a" +dependencies = [ + "displaydoc", + "serde_core", + "zerovec 0.11.9", ] [[package]] @@ -10386,12 +10527,6 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" -[[package]] -name = "unicode-linebreak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" - [[package]] name = "unicode-normalization" version = "0.1.24" @@ -11712,6 +11847,12 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +[[package]] +name = "writeable" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" + [[package]] name = "wyz" version = "0.5.1" @@ -11769,7 +11910,18 @@ checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ "serde", "stable_deref_trait", - "yoke-derive", + "yoke-derive 0.7.5", + "zerofrom", +] + +[[package]] +name = "yoke" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "417ef3fcf6c300b7dbd25c85531a746931c02ad536f3cf4786ed1e88223340b3" +dependencies = [ + "stable_deref_trait", + "yoke-derive 0.8.4", "zerofrom", ] @@ -11782,7 +11934,19 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.117", - "synstructure", + "synstructure 0.13.1", +] + +[[package]] +name = "yoke-derive" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8ebde2db3681e8c9980cc27822030e68752690ddfa9473e739aeb4dbde6d71" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", + "synstructure 0.14.0", ] [[package]] @@ -11828,23 +11992,23 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.5" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.5" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", - "synstructure", + "syn 3.0.9", + "synstructure 0.14.0", ] [[package]] @@ -11867,15 +12031,39 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "zerotrie" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" +dependencies = [ + "displaydoc", + "yoke 0.8.4", + "zerofrom", + "zerovec 0.11.9", +] + [[package]] name = "zerovec" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" dependencies = [ - "yoke", + "yoke 0.7.5", + "zerofrom", + "zerovec-derive 0.10.3", +] + +[[package]] +name = "zerovec" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f58a2799cfe9a1670c7ea8edbd52ef8edb93cb37a7f83bf31ba9e35d9dae6a40" +dependencies = [ + "serde", + "yoke 0.8.4", "zerofrom", - "zerovec-derive", + "zerovec-derive 0.11.7", ] [[package]] @@ -11889,6 +12077,17 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "zerovec-derive" +version = "0.11.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3743ddbf47738d929cd1ae2e199d40d0162979dfe31aef8ae646d2bf00bf97a" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + [[package]] name = "zmij" version = "1.0.19" diff --git a/Cargo.toml b/Cargo.toml index be052d5a5..c6c308172 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,6 +86,7 @@ internal-dns-types = { git = "https://github.com/oxidecomputer/omicron", rev = " nexus-client = { git = "https://github.com/oxidecomputer/omicron", rev = "5fd53a9c9ff2b0e47bfef3fe842b7516877b0162" } omicron-common = { git = "https://github.com/oxidecomputer/omicron", rev = "5fd53a9c9ff2b0e47bfef3fe842b7516877b0162" } omicron-zone-package = "0.12.2" +openapi-lint = { git = "https://github.com/oxidecomputer/openapi-lint", branch = "main" } oximeter-instruments = { git = "https://github.com/oxidecomputer/omicron", rev = "5fd53a9c9ff2b0e47bfef3fe842b7516877b0162", default-features = false, features = ["kstat"] } oximeter-producer = { git = "https://github.com/oxidecomputer/omicron", rev = "5fd53a9c9ff2b0e47bfef3fe842b7516877b0162" } oximeter = { git = "https://github.com/oxidecomputer/omicron", rev = "5fd53a9c9ff2b0e47bfef3fe842b7516877b0162" } @@ -122,8 +123,8 @@ const_format = "0.2" crossbeam-channel = "0.5" ctrlc = "3.2" dropshot = "0.18.0" -dropshot-api-manager = "0.9.0" -dropshot-api-manager-types = "0.9.0" +dropshot-api-manager = "0.9.1" +dropshot-api-manager-types = "0.9.1" erased-serde = "0.4" errno = "0.2.8" escargot = "0.5.15" @@ -147,6 +148,7 @@ newtype_derive = "0.1.6" newtype-uuid = { version = "1.0.1", features = [ "v4" ] } # "feature" for `utsname`, "poll" for `PollFlags` nix = { version = "0.31", features = [ "feature", "poll" ] } +openapiv3 = "2.2.0" owo-colors = "4" oxide-tokio-rt = "0.1.2" paste = "1.0.15" diff --git a/bin/dropshot-apis/Cargo.toml b/bin/dropshot-apis/Cargo.toml index b5b827b91..2430f53d1 100644 --- a/bin/dropshot-apis/Cargo.toml +++ b/bin/dropshot-apis/Cargo.toml @@ -10,5 +10,7 @@ camino.workspace = true clap.workspace = true dropshot-api-manager-types.workspace = true dropshot-api-manager.workspace = true +openapi-lint.workspace = true +openapiv3.workspace = true propolis-server-api.workspace = true semver.workspace = true diff --git a/bin/dropshot-apis/src/main.rs b/bin/dropshot-apis/src/main.rs index 5bb92acbb..e39eee68c 100644 --- a/bin/dropshot-apis/src/main.rs +++ b/bin/dropshot-apis/src/main.rs @@ -4,11 +4,14 @@ use std::process::ExitCode; -use anyhow::Context; +use anyhow::{Context, anyhow}; use camino::Utf8PathBuf; use clap::Parser; use dropshot_api_manager::{Environment, ManagedApiConfig, ManagedApis}; -use dropshot_api_manager_types::{ManagedApiMetadata, Versions}; +use dropshot_api_manager_types::{ + ManagedApiMetadata, ValidationContext, Versions, +}; +use openapiv3::OpenAPI; use propolis_server_api::*; pub fn environment() -> anyhow::Result { @@ -52,10 +55,26 @@ pub fn all_apis() -> anyhow::Result { let apis = ManagedApis::new(apis) .context("error creating ManagedApis")? - .with_git_stub_storage(); + .with_git_stub_storage() + .with_validation(validate); Ok(apis) } +fn validate(doc: &OpenAPI, mut cx: ValidationContext<'_>) { + // Lint the latest and non-blessed versions -- older blessed versions fail to lint but are + // immutable. + let is_latest_or_unblessed = + cx.is_latest() || cx.is_blessed() != Some(true); + if !is_latest_or_unblessed { + return; + } + + let errors = openapi_lint::validate(doc); + for error in errors { + cx.report_error(anyhow!(error)); + } +} + fn main() -> anyhow::Result { let app = dropshot_api_manager::App::parse(); let env = environment()?;