diff --git a/.config/hakari.toml b/.config/hakari.toml index 62f15df276..0d883dc6f6 100644 --- a/.config/hakari.toml +++ b/.config/hakari.toml @@ -6,6 +6,10 @@ hakari-package = "omicron-workspace-hack" # Format for `workspace-hack = ...` lines in other Cargo.tomls. Requires cargo-hakari 0.9.8 or above. dep-format-version = "4" +# Output lines as `omicron-workspace-hack.workspace = true`. Requires +# cargo-hakari 0.9.28 or above. +workspace-hack-line-style = "workspace-dotted" + # Setting workspace.resolver = "2" in the root Cargo.toml is HIGHLY recommended. # Hakari works much better with the new feature resolver. # For more about the new feature resolver, see: @@ -27,4 +31,3 @@ exact-versions = true [traversal-excludes] workspace-members = ["xtask"] - diff --git a/Cargo.lock b/Cargo.lock index 6e8cc702ef..4a89ff456a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,6 +67,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom 0.2.10", + "once_cell", + "version_check", +] + [[package]] name = "ahash" version = "0.8.3" @@ -474,20 +485,20 @@ dependencies = [ [[package]] name = "bhyve_api" version = "0.0.0" -source = "git+https://github.com/oxidecomputer/propolis?rev=de6369aa45a255f896da0a3ddd2b7152c036a4e9#de6369aa45a255f896da0a3ddd2b7152c036a4e9" +source = "git+https://github.com/oxidecomputer/propolis?rev=42c878b71a58d430dfc306126af5d40ca816d70f#42c878b71a58d430dfc306126af5d40ca816d70f" dependencies = [ "bhyve_api_sys", "libc", - "num_enum 0.5.11", + "strum", ] [[package]] name = "bhyve_api_sys" version = "0.0.0" -source = "git+https://github.com/oxidecomputer/propolis?rev=de6369aa45a255f896da0a3ddd2b7152c036a4e9#de6369aa45a255f896da0a3ddd2b7152c036a4e9" +source = "git+https://github.com/oxidecomputer/propolis?rev=42c878b71a58d430dfc306126af5d40ca816d70f#42c878b71a58d430dfc306126af5d40ca816d70f" dependencies = [ "libc", - "num_enum 0.5.11", + "strum", ] [[package]] @@ -1211,6 +1222,18 @@ dependencies = [ "libc", ] +[[package]] +name = "cpuid_profile_config" +version = "0.0.0" +source = "git+https://github.com/oxidecomputer/propolis?rev=42c878b71a58d430dfc306126af5d40ca816d70f#42c878b71a58d430dfc306126af5d40ca816d70f" +dependencies = [ + "propolis", + "serde", + "serde_derive", + "thiserror", + "toml 0.7.8", +] + [[package]] name = "crc" version = "3.0.1" @@ -1413,7 +1436,7 @@ dependencies = [ [[package]] name = "crucible" version = "0.0.1" -source = "git+https://github.com/oxidecomputer/crucible?rev=aeb69dda26c7e1a8b6eada425670cd4b83f91c07#aeb69dda26c7e1a8b6eada425670cd4b83f91c07" +source = "git+https://github.com/oxidecomputer/crucible?rev=20273bcca1fd5834ebc3e67dfa7020f0e99ad681#20273bcca1fd5834ebc3e67dfa7020f0e99ad681" dependencies = [ "aes-gcm-siv", "anyhow", @@ -1447,17 +1470,18 @@ dependencies = [ "tokio", "tokio-rustls", "tokio-util", - "toml 0.7.8", + "toml 0.8.0", "tracing", "usdt", "uuid", "version_check", + "workspace-hack", ] [[package]] name = "crucible-agent-client" version = "0.0.1" -source = "git+https://github.com/oxidecomputer/crucible?rev=aeb69dda26c7e1a8b6eada425670cd4b83f91c07#aeb69dda26c7e1a8b6eada425670cd4b83f91c07" +source = "git+https://github.com/oxidecomputer/crucible?rev=20273bcca1fd5834ebc3e67dfa7020f0e99ad681#20273bcca1fd5834ebc3e67dfa7020f0e99ad681" dependencies = [ "anyhow", "chrono", @@ -1467,24 +1491,26 @@ dependencies = [ "schemars", "serde", "serde_json", + "workspace-hack", ] [[package]] name = "crucible-client-types" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/crucible?rev=aeb69dda26c7e1a8b6eada425670cd4b83f91c07#aeb69dda26c7e1a8b6eada425670cd4b83f91c07" +source = "git+https://github.com/oxidecomputer/crucible?rev=20273bcca1fd5834ebc3e67dfa7020f0e99ad681#20273bcca1fd5834ebc3e67dfa7020f0e99ad681" dependencies = [ "base64 0.21.4", "schemars", "serde", "serde_json", "uuid", + "workspace-hack", ] [[package]] name = "crucible-common" version = "0.0.1" -source = "git+https://github.com/oxidecomputer/crucible?rev=aeb69dda26c7e1a8b6eada425670cd4b83f91c07#aeb69dda26c7e1a8b6eada425670cd4b83f91c07" +source = "git+https://github.com/oxidecomputer/crucible?rev=20273bcca1fd5834ebc3e67dfa7020f0e99ad681#20273bcca1fd5834ebc3e67dfa7020f0e99ad681" dependencies = [ "anyhow", "atty", @@ -1502,16 +1528,17 @@ dependencies = [ "tempfile", "thiserror", "tokio-rustls", - "toml 0.7.8", + "toml 0.8.0", "twox-hash", "uuid", "vergen", + "workspace-hack", ] [[package]] name = "crucible-pantry-client" version = "0.0.1" -source = "git+https://github.com/oxidecomputer/crucible?rev=aeb69dda26c7e1a8b6eada425670cd4b83f91c07#aeb69dda26c7e1a8b6eada425670cd4b83f91c07" +source = "git+https://github.com/oxidecomputer/crucible?rev=20273bcca1fd5834ebc3e67dfa7020f0e99ad681#20273bcca1fd5834ebc3e67dfa7020f0e99ad681" dependencies = [ "anyhow", "chrono", @@ -1522,32 +1549,36 @@ dependencies = [ "serde", "serde_json", "uuid", + "workspace-hack", ] [[package]] name = "crucible-protocol" version = "0.0.0" -source = "git+https://github.com/oxidecomputer/crucible?rev=aeb69dda26c7e1a8b6eada425670cd4b83f91c07#aeb69dda26c7e1a8b6eada425670cd4b83f91c07" +source = "git+https://github.com/oxidecomputer/crucible?rev=20273bcca1fd5834ebc3e67dfa7020f0e99ad681#20273bcca1fd5834ebc3e67dfa7020f0e99ad681" dependencies = [ "anyhow", "bincode", "bytes", "crucible-common", "num_enum 0.7.0", + "schemars", "serde", "tokio-util", "uuid", + "workspace-hack", ] [[package]] name = "crucible-smf" version = "0.0.0" -source = "git+https://github.com/oxidecomputer/crucible?rev=aeb69dda26c7e1a8b6eada425670cd4b83f91c07#aeb69dda26c7e1a8b6eada425670cd4b83f91c07" +source = "git+https://github.com/oxidecomputer/crucible?rev=20273bcca1fd5834ebc3e67dfa7020f0e99ad681#20273bcca1fd5834ebc3e67dfa7020f0e99ad681" dependencies = [ "libc", "num-derive", "num-traits", "thiserror", + "workspace-hack", ] [[package]] @@ -1985,10 +2016,10 @@ checksum = "7e1a8646b2c125eeb9a84ef0faa6d2d102ea0d5da60b824ade2743263117b848" [[package]] name = "dladm" version = "0.0.0" -source = "git+https://github.com/oxidecomputer/propolis?rev=de6369aa45a255f896da0a3ddd2b7152c036a4e9#de6369aa45a255f896da0a3ddd2b7152c036a4e9" +source = "git+https://github.com/oxidecomputer/propolis?rev=42c878b71a58d430dfc306126af5d40ca816d70f#42c878b71a58d430dfc306126af5d40ca816d70f" dependencies = [ "libc", - "num_enum 0.5.11", + "strum", ] [[package]] @@ -2322,26 +2353,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "enum-iterator" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7add3873b5dd076766ee79c8e406ad1a472c385476b9e38849f8eec24f1be689" -dependencies = [ - "enum-iterator-derive", -] - -[[package]] -name = "enum-iterator-derive" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eecf8589574ce9b895052fa12d69af7a233f99e6107f5cb8dd1044f2a17bfdcb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.32", -] - [[package]] name = "env_logger" version = "0.9.3" @@ -2404,9 +2415,9 @@ dependencies = [ [[package]] name = "expectorate" -version = "1.0.7" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "710ab6a2d57038a835d66f78d5af3fa5d27c1ec4682f823b9203c48826cb0591" +checksum = "de6f19b25bdfa2747ae775f37cd109c31f1272d4e4c83095be0727840aa1d75f" dependencies = [ "console", "newline-converter", @@ -2965,6 +2976,9 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.6", +] [[package]] name = "hashbrown" @@ -2972,7 +2986,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash", + "ahash 0.8.3", ] [[package]] @@ -2981,7 +2995,7 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" dependencies = [ - "ahash", + "ahash 0.8.3", "allocator-api2", ] @@ -3219,7 +3233,7 @@ dependencies = [ [[package]] name = "hubtools" version = "0.4.1" -source = "git+https://github.com/oxidecomputer/hubtools.git?branch=main#0c642f6e1f83b74725c7119a546bc26ac7452a48" +source = "git+https://github.com/oxidecomputer/hubtools.git?branch=main#2481445b80f8476041f62a1c8b6301e4918c63ed" dependencies = [ "lpc55_areas", "lpc55_sign", @@ -4008,7 +4022,7 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "lpc55_areas" version = "0.2.4" -source = "git+https://github.com/oxidecomputer/lpc55_support#4051a3b9421573dc36ed6098b292a7609a3cf98b" +source = "git+https://github.com/oxidecomputer/lpc55_support#96f064eaae5e95930efaab6c29fd1b2e22225dac" dependencies = [ "bitfield", "clap 4.4.3", @@ -4018,8 +4032,8 @@ dependencies = [ [[package]] name = "lpc55_sign" -version = "0.3.2" -source = "git+https://github.com/oxidecomputer/lpc55_support#4051a3b9421573dc36ed6098b292a7609a3cf98b" +version = "0.3.3" +source = "git+https://github.com/oxidecomputer/lpc55_support#96f064eaae5e95930efaab6c29fd1b2e22225dac" dependencies = [ "byteorder", "const-oid", @@ -5125,6 +5139,7 @@ dependencies = [ "diesel", "dropshot", "expectorate", + "futures", "humantime", "internal-dns 0.1.0", "ipnetwork", @@ -5139,6 +5154,7 @@ dependencies = [ "omicron-rpaths", "omicron-test-utils", "omicron-workspace-hack", + "oximeter-client", "pq-sys", "regex", "serde", @@ -5370,6 +5386,7 @@ dependencies = [ "futures", "futures-channel", "futures-core", + "futures-executor", "futures-io", "futures-sink", "futures-task", @@ -5377,11 +5394,13 @@ dependencies = [ "gateway-messages", "generic-array", "getrandom 0.2.10", + "hashbrown 0.12.3", "hashbrown 0.13.2", "hashbrown 0.14.0", "hex", "hyper", "hyper-rustls", + "indexmap 1.9.3", "indexmap 2.0.0", "inout", "ipnetwork", @@ -5399,7 +5418,9 @@ dependencies = [ "num-traits", "once_cell", "openapiv3", + "parking_lot 0.12.1", "petgraph", + "phf_shared 0.11.2", "postgres-types", "ppv-lite86", "predicates 3.0.3", @@ -5433,6 +5454,7 @@ dependencies = [ "toml_datetime", "toml_edit 0.19.15", "tracing", + "tracing-core", "trust-dns-proto", "unicode-bidi", "unicode-normalization", @@ -5716,6 +5738,7 @@ name = "oximeter-client" version = "0.1.0" dependencies = [ "chrono", + "futures", "omicron-common 0.1.0", "omicron-workspace-hack", "progenitor", @@ -5729,24 +5752,31 @@ dependencies = [ name = "oximeter-collector" version = "0.1.0" dependencies = [ + "anyhow", "clap 4.4.3", "dropshot", "expectorate", "futures", "internal-dns 0.1.0", "nexus-client 0.1.0", + "nexus-types", "omicron-common 0.1.0", "omicron-test-utils", "omicron-workspace-hack", "openapi-lint", "openapiv3", "oximeter 0.1.0", + "oximeter-client", "oximeter-db", + "rand 0.8.5", "reqwest", + "schemars", "serde", "serde_json", "slog", + "slog-async", "slog-dtrace", + "slog-term", "subprocess", "thiserror", "tokio", @@ -5821,7 +5851,9 @@ dependencies = [ name = "oximeter-producer" version = "0.1.0" dependencies = [ + "anyhow", "chrono", + "clap 4.4.3", "dropshot", "nexus-client 0.1.0", "omicron-common 0.1.0", @@ -6556,7 +6588,7 @@ dependencies = [ [[package]] name = "propolis" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/propolis?rev=de6369aa45a255f896da0a3ddd2b7152c036a4e9#de6369aa45a255f896da0a3ddd2b7152c036a4e9" +source = "git+https://github.com/oxidecomputer/propolis?rev=42c878b71a58d430dfc306126af5d40ca816d70f#42c878b71a58d430dfc306126af5d40ca816d70f" dependencies = [ "anyhow", "bhyve_api", @@ -6571,7 +6603,6 @@ dependencies = [ "lazy_static", "libc", "nexus-client 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", - "num_enum 0.5.11", "oximeter 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "propolis_types", "rfb", @@ -6579,6 +6610,7 @@ dependencies = [ "serde_arrays", "serde_json", "slog", + "strum", "thiserror", "tokio", "usdt", @@ -6589,7 +6621,7 @@ dependencies = [ [[package]] name = "propolis-client" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/propolis?rev=de6369aa45a255f896da0a3ddd2b7152c036a4e9#de6369aa45a255f896da0a3ddd2b7152c036a4e9" +source = "git+https://github.com/oxidecomputer/propolis?rev=42c878b71a58d430dfc306126af5d40ca816d70f#42c878b71a58d430dfc306126af5d40ca816d70f" dependencies = [ "async-trait", "base64 0.21.4", @@ -6606,14 +6638,14 @@ dependencies = [ "slog", "thiserror", "tokio", - "tokio-tungstenite 0.17.2", + "tokio-tungstenite 0.20.1", "uuid", ] [[package]] name = "propolis-server" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/propolis?rev=de6369aa45a255f896da0a3ddd2b7152c036a4e9#de6369aa45a255f896da0a3ddd2b7152c036a4e9" +source = "git+https://github.com/oxidecomputer/propolis?rev=42c878b71a58d430dfc306126af5d40ca816d70f#42c878b71a58d430dfc306126af5d40ca816d70f" dependencies = [ "anyhow", "async-trait", @@ -6628,7 +6660,6 @@ dependencies = [ "const_format", "crucible-client-types", "dropshot", - "enum-iterator", "erased-serde", "futures", "http", @@ -6636,7 +6667,6 @@ dependencies = [ "internal-dns 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "lazy_static", "nexus-client 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", - "num_enum 0.5.11", "omicron-common 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "oximeter 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "oximeter-producer 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", @@ -6654,9 +6684,10 @@ dependencies = [ "slog-bunyan", "slog-dtrace", "slog-term", + "strum", "thiserror", "tokio", - "tokio-tungstenite 0.17.2", + "tokio-tungstenite 0.20.1", "tokio-util", "toml 0.7.8", "usdt", @@ -6666,8 +6697,9 @@ dependencies = [ [[package]] name = "propolis-server-config" version = "0.0.0" -source = "git+https://github.com/oxidecomputer/propolis?rev=de6369aa45a255f896da0a3ddd2b7152c036a4e9#de6369aa45a255f896da0a3ddd2b7152c036a4e9" +source = "git+https://github.com/oxidecomputer/propolis?rev=42c878b71a58d430dfc306126af5d40ca816d70f#42c878b71a58d430dfc306126af5d40ca816d70f" dependencies = [ + "cpuid_profile_config", "serde", "serde_derive", "thiserror", @@ -6677,7 +6709,7 @@ dependencies = [ [[package]] name = "propolis_types" version = "0.0.0" -source = "git+https://github.com/oxidecomputer/propolis?rev=de6369aa45a255f896da0a3ddd2b7152c036a4e9#de6369aa45a255f896da0a3ddd2b7152c036a4e9" +source = "git+https://github.com/oxidecomputer/propolis?rev=42c878b71a58d430dfc306126af5d40ca816d70f#42c878b71a58d430dfc306126af5d40ca816d70f" dependencies = [ "schemars", "serde", @@ -7144,9 +7176,9 @@ dependencies = [ [[package]] name = "ringbuffer" -version = "0.14.2" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eba9638e96ac5a324654f8d47fb71c5e21abef0f072740ed9c1d4b0801faa37" +checksum = "3df6368f71f205ff9c33c076d170dd56ebf68e8161c733c0caa07a7a5509ed53" [[package]] name = "ron" @@ -7908,17 +7940,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "sha-1" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" -dependencies = [ - "cfg-if 1.0.0", - "cpufeatures", - "digest", -] - [[package]] name = "sha1" version = "0.10.5" @@ -8755,18 +8776,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.48" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" +checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.48" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" +checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" dependencies = [ "proc-macro2", "quote", @@ -9019,26 +9040,26 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.17.2" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" +checksum = "54319c93411147bced34cb5609a80e0a8e44c5999c93903a81cd866630ec0bfd" dependencies = [ "futures-util", "log", "tokio", - "tungstenite 0.17.3", + "tungstenite 0.18.0", ] [[package]] name = "tokio-tungstenite" -version = "0.18.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54319c93411147bced34cb5609a80e0a8e44c5999c93903a81cd866630ec0bfd" +checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" dependencies = [ "futures-util", "log", "tokio", - "tungstenite 0.18.0", + "tungstenite 0.20.1", ] [[package]] @@ -9206,6 +9227,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", + "valuable", ] [[package]] @@ -9387,9 +9409,9 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.17.3" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" +checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788" dependencies = [ "base64 0.13.1", "byteorder", @@ -9398,7 +9420,7 @@ dependencies = [ "httparse", "log", "rand 0.8.5", - "sha-1", + "sha1", "thiserror", "url", "utf-8", @@ -9406,13 +9428,13 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.18.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788" +checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" dependencies = [ - "base64 0.13.1", "byteorder", "bytes", + "data-encoding", "http", "httparse", "log", @@ -9429,8 +9451,8 @@ version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ - "cfg-if 1.0.0", - "rand 0.8.5", + "cfg-if 0.1.10", + "rand 0.4.6", "static_assertions", ] @@ -9694,6 +9716,12 @@ dependencies = [ "serde", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "vcpkg" version = "0.2.15" @@ -9728,17 +9756,16 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "viona_api" version = "0.0.0" -source = "git+https://github.com/oxidecomputer/propolis?rev=de6369aa45a255f896da0a3ddd2b7152c036a4e9#de6369aa45a255f896da0a3ddd2b7152c036a4e9" +source = "git+https://github.com/oxidecomputer/propolis?rev=42c878b71a58d430dfc306126af5d40ca816d70f#42c878b71a58d430dfc306126af5d40ca816d70f" dependencies = [ "libc", - "num_enum 0.5.11", "viona_api_sys", ] [[package]] name = "viona_api_sys" version = "0.0.0" -source = "git+https://github.com/oxidecomputer/propolis?rev=de6369aa45a255f896da0a3ddd2b7152c036a4e9#de6369aa45a255f896da0a3ddd2b7152c036a4e9" +source = "git+https://github.com/oxidecomputer/propolis?rev=42c878b71a58d430dfc306126af5d40ca816d70f#42c878b71a58d430dfc306126af5d40ca816d70f" dependencies = [ "libc", ] @@ -10317,6 +10344,61 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "workspace-hack" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/crucible?rev=20273bcca1fd5834ebc3e67dfa7020f0e99ad681#20273bcca1fd5834ebc3e67dfa7020f0e99ad681" +dependencies = [ + "bitflags 2.4.0", + "bytes", + "cc", + "chrono", + "console", + "crossbeam-utils", + "crypto-common", + "digest", + "either", + "futures-channel", + "futures-core", + "futures-executor", + "futures-sink", + "futures-util", + "getrandom 0.2.10", + "hashbrown 0.12.3", + "hex", + "hyper", + "indexmap 1.9.3", + "libc", + "log", + "mio", + "num-traits", + "once_cell", + "openapiv3", + "parking_lot 0.12.1", + "phf_shared 0.11.2", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rand_core 0.6.4", + "reqwest", + "rustls", + "schemars", + "semver 1.0.18", + "serde", + "slog", + "syn 1.0.109", + "syn 2.0.32", + "time", + "time-macros", + "tokio", + "tokio-util", + "toml_datetime", + "toml_edit 0.19.15", + "tracing", + "tracing-core", + "usdt", + "uuid", +] + [[package]] name = "wyz" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 1d24ab3ab4..d16b179293 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -161,10 +161,10 @@ cookie = "0.16" criterion = { version = "0.5.1", features = [ "async_tokio" ] } crossbeam = "0.8" crossterm = { version = "0.27.0", features = ["event-stream"] } -crucible-agent-client = { git = "https://github.com/oxidecomputer/crucible", rev = "aeb69dda26c7e1a8b6eada425670cd4b83f91c07" } -crucible-client-types = { git = "https://github.com/oxidecomputer/crucible", rev = "aeb69dda26c7e1a8b6eada425670cd4b83f91c07" } -crucible-pantry-client = { git = "https://github.com/oxidecomputer/crucible", rev = "aeb69dda26c7e1a8b6eada425670cd4b83f91c07" } -crucible-smf = { git = "https://github.com/oxidecomputer/crucible", rev = "aeb69dda26c7e1a8b6eada425670cd4b83f91c07" } +crucible-agent-client = { git = "https://github.com/oxidecomputer/crucible", rev = "20273bcca1fd5834ebc3e67dfa7020f0e99ad681" } +crucible-client-types = { git = "https://github.com/oxidecomputer/crucible", rev = "20273bcca1fd5834ebc3e67dfa7020f0e99ad681" } +crucible-pantry-client = { git = "https://github.com/oxidecomputer/crucible", rev = "20273bcca1fd5834ebc3e67dfa7020f0e99ad681" } +crucible-smf = { git = "https://github.com/oxidecomputer/crucible", rev = "20273bcca1fd5834ebc3e67dfa7020f0e99ad681" } curve25519-dalek = "4" datatest-stable = "0.1.3" display-error-chain = "0.1.1" @@ -180,7 +180,7 @@ dns-service-client = { path = "dns-service-client" } dpd-client = { path = "dpd-client" } dropshot = { git = "https://github.com/oxidecomputer/dropshot", branch = "main", features = [ "usdt-probes" ] } either = "1.9.0" -expectorate = "1.0.7" +expectorate = "1.1.0" fatfs = "0.3.6" flate2 = "1.0.27" flume = "0.11.0" @@ -229,6 +229,7 @@ nexus-db-queries = { path = "nexus/db-queries" } nexus-defaults = { path = "nexus/defaults" } omicron-certificates = { path = "certificates" } omicron-passwords = { path = "passwords" } +omicron-workspace-hack = "0.1.0" nexus-test-interface = { path = "nexus/test-interface" } nexus-test-utils-macros = { path = "nexus/test-utils-macros" } nexus-test-utils = { path = "nexus/test-utils" } @@ -276,9 +277,9 @@ pretty-hex = "0.3.0" proc-macro2 = "1.0" progenitor = { git = "https://github.com/oxidecomputer/progenitor", branch = "main" } progenitor-client = { git = "https://github.com/oxidecomputer/progenitor", branch = "main" } -bhyve_api = { git = "https://github.com/oxidecomputer/propolis", rev = "de6369aa45a255f896da0a3ddd2b7152c036a4e9" } -propolis-client = { git = "https://github.com/oxidecomputer/propolis", rev = "de6369aa45a255f896da0a3ddd2b7152c036a4e9", features = [ "generated-migration" ] } -propolis-server = { git = "https://github.com/oxidecomputer/propolis", rev = "de6369aa45a255f896da0a3ddd2b7152c036a4e9", default-features = false, features = ["mock-only"] } +bhyve_api = { git = "https://github.com/oxidecomputer/propolis", rev = "42c878b71a58d430dfc306126af5d40ca816d70f" } +propolis-client = { git = "https://github.com/oxidecomputer/propolis", rev = "42c878b71a58d430dfc306126af5d40ca816d70f", features = [ "generated-migration" ] } +propolis-server = { git = "https://github.com/oxidecomputer/propolis", rev = "42c878b71a58d430dfc306126af5d40ca816d70f", default-features = false, features = ["mock-only"] } proptest = "1.2.0" quote = "1.0" rand = "0.8.5" @@ -554,3 +555,10 @@ opt-level = 3 [patch.crates-io.pq-sys] git = 'https://github.com/oxidecomputer/pq-sys' branch = "oxide/omicron" + +# Using the workspace-hack via this patch directive means that it only applies +# while building within this workspace. If another workspace imports a crate +# from here via a git dependency, it will not have the workspace-hack applied +# to it. +[patch.crates-io.omicron-workspace-hack] +path = "workspace-hack" diff --git a/api_identity/Cargo.toml b/api_identity/Cargo.toml index 9faf2a1878..547defa7c5 100644 --- a/api_identity/Cargo.toml +++ b/api_identity/Cargo.toml @@ -14,4 +14,4 @@ proc-macro = true proc-macro2.workspace = true quote.workspace = true syn.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true diff --git a/bootstore/Cargo.toml b/bootstore/Cargo.toml index eefe05c8d6..18e3e3876b 100644 --- a/bootstore/Cargo.toml +++ b/bootstore/Cargo.toml @@ -36,7 +36,7 @@ zeroize.workspace = true # utils`. Unfortunately, it doesn't appear possible to put the `pq-sys` dep # only in `[dev-dependencies]`. pq-sys = "*" -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true [dev-dependencies] assert_matches.workspace = true diff --git a/bootstrap-agent-client/Cargo.toml b/bootstrap-agent-client/Cargo.toml index 17989a5c5f..42ae59b7aa 100644 --- a/bootstrap-agent-client/Cargo.toml +++ b/bootstrap-agent-client/Cargo.toml @@ -17,4 +17,4 @@ serde.workspace = true sled-hardware.workspace = true slog.workspace = true uuid.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true diff --git a/caboose-util/Cargo.toml b/caboose-util/Cargo.toml index 253d54643d..91bf00741e 100644 --- a/caboose-util/Cargo.toml +++ b/caboose-util/Cargo.toml @@ -7,4 +7,4 @@ license = "MPL-2.0" [dependencies] anyhow.workspace = true hubtools.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true diff --git a/certificates/Cargo.toml b/certificates/Cargo.toml index d20d257e4c..87b12fd167 100644 --- a/certificates/Cargo.toml +++ b/certificates/Cargo.toml @@ -12,7 +12,7 @@ openssl-sys.workspace = true thiserror.workspace = true omicron-common.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true [dev-dependencies] omicron-test-utils.workspace = true diff --git a/common/Cargo.toml b/common/Cargo.toml index bda88d0d43..75c1efab55 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -40,7 +40,7 @@ toml.workspace = true uuid.workspace = true parse-display.workspace = true progenitor.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true [dev-dependencies] camino-tempfile.workspace = true diff --git a/common/src/api/internal/nexus.rs b/common/src/api/internal/nexus.rs index 018869ce14..983976bbb7 100644 --- a/common/src/api/internal/nexus.rs +++ b/common/src/api/internal/nexus.rs @@ -67,7 +67,7 @@ pub struct InstanceRuntimeState { /// Information announced by a metric server, used so that clients can contact it and collect /// available metric data from it. -#[derive(Debug, Clone, JsonSchema, Serialize, Deserialize)] +#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize, PartialEq)] pub struct ProducerEndpoint { pub id: Uuid, pub address: SocketAddr, diff --git a/crdb-seed/Cargo.toml b/crdb-seed/Cargo.toml index fa71fe7e8a..8d6d570d08 100644 --- a/crdb-seed/Cargo.toml +++ b/crdb-seed/Cargo.toml @@ -13,4 +13,4 @@ omicron-test-utils.workspace = true ring.workspace = true slog.workspace = true tokio.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true diff --git a/ddm-admin-client/Cargo.toml b/ddm-admin-client/Cargo.toml index 3814446b3e..4d00f329e7 100644 --- a/ddm-admin-client/Cargo.toml +++ b/ddm-admin-client/Cargo.toml @@ -15,7 +15,7 @@ tokio.workspace = true omicron-common.workspace = true sled-hardware.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true [build-dependencies] anyhow.workspace = true diff --git a/deploy/Cargo.toml b/deploy/Cargo.toml index 17bacd6354..1a6c05a546 100644 --- a/deploy/Cargo.toml +++ b/deploy/Cargo.toml @@ -14,7 +14,7 @@ serde.workspace = true serde_derive.workspace = true thiserror.workspace = true toml.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true [[bin]] name = "thing-flinger" diff --git a/dev-tools/omdb/Cargo.toml b/dev-tools/omdb/Cargo.toml index 5a05e93db9..cd4af6e947 100644 --- a/dev-tools/omdb/Cargo.toml +++ b/dev-tools/omdb/Cargo.toml @@ -16,11 +16,13 @@ diesel.workspace = true dropshot.workspace = true humantime.workspace = true internal-dns.workspace = true +futures.workspace = true nexus-client.workspace = true nexus-db-model.workspace = true nexus-db-queries.workspace = true nexus-types.workspace = true omicron-common.workspace = true +oximeter-client.workspace = true # See omicron-rpaths for more about the "pq-sys" dependency. pq-sys = "*" serde.workspace = true @@ -32,8 +34,8 @@ tabled.workspace = true textwrap.workspace = true tokio = { workspace = true, features = [ "full" ] } uuid.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../../workspace-hack" } ipnetwork.workspace = true +omicron-workspace-hack.workspace = true [dev-dependencies] expectorate.workspace = true diff --git a/dev-tools/omdb/src/bin/omdb/main.rs b/dev-tools/omdb/src/bin/omdb/main.rs index 166ed3043f..d1a56e1d80 100644 --- a/dev-tools/omdb/src/bin/omdb/main.rs +++ b/dev-tools/omdb/src/bin/omdb/main.rs @@ -42,6 +42,7 @@ use std::net::SocketAddrV6; mod db; mod nexus; +mod oximeter; mod sled_agent; #[tokio::main] @@ -57,6 +58,7 @@ async fn main() -> Result<(), anyhow::Error> { match &args.command { OmdbCommands::Db(db) => db.run_cmd(&args, &log).await, OmdbCommands::Nexus(nexus) => nexus.run_cmd(&args, &log).await, + OmdbCommands::Oximeter(oximeter) => oximeter.run_cmd(&log).await, OmdbCommands::SledAgent(sled) => sled.run_cmd(&args, &log).await, } } @@ -155,6 +157,8 @@ enum OmdbCommands { Db(db::DbArgs), /// Debug a specific Nexus instance Nexus(nexus::NexusArgs), + /// Query oximeter collector state + Oximeter(oximeter::OximeterArgs), /// Debug a specific Sled SledAgent(sled_agent::SledAgentArgs), } diff --git a/dev-tools/omdb/src/bin/omdb/oximeter.rs b/dev-tools/omdb/src/bin/omdb/oximeter.rs new file mode 100644 index 0000000000..e0f20556a2 --- /dev/null +++ b/dev-tools/omdb/src/bin/omdb/oximeter.rs @@ -0,0 +1,94 @@ +// 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/. + +//! omdb commands that query oximeter + +use anyhow::Context; +use clap::Args; +use clap::Subcommand; +use futures::TryStreamExt; +use oximeter_client::types::ProducerEndpoint; +use oximeter_client::Client; +use slog::Logger; +use std::net::SocketAddr; +use std::time::Duration; +use tabled::Table; +use tabled::Tabled; +use uuid::Uuid; + +#[derive(Debug, Args)] +pub struct OximeterArgs { + /// URL of the oximeter collector to query + #[arg(long, env("OMDB_OXIMETER_URL"))] + oximeter_url: String, + + #[command(subcommand)] + command: OximeterCommands, +} + +/// Subcommands that query oximeter collector state +#[derive(Debug, Subcommand)] +enum OximeterCommands { + /// List the producers the collector is assigned to poll + ListProducers, +} + +impl OximeterArgs { + fn client(&self, log: &Logger) -> Client { + Client::new( + &self.oximeter_url, + log.new(slog::o!("component" => "oximeter-client")), + ) + } + + pub async fn run_cmd(&self, log: &Logger) -> anyhow::Result<()> { + let client = self.client(log); + match self.command { + OximeterCommands::ListProducers => { + self.list_producers(client).await + } + } + } + + async fn list_producers(&self, client: Client) -> anyhow::Result<()> { + let info = client + .collector_info() + .await + .context("failed to fetch collector info")?; + let producers: Vec = client + .producers_list_stream(None) + .map_ok(Producer::from) + .try_collect() + .await + .context("failed to list producers")?; + let table = Table::new(producers) + .with(tabled::settings::Style::empty()) + .with(tabled::settings::Padding::new(0, 1, 0, 0)) + .to_string(); + println!("Collector ID: {}\n", info.id); + println!("{table}"); + Ok(()) + } +} + +#[derive(Tabled)] +#[tabled(rename_all = "SCREAMING_SNAKE_CASE")] +struct Producer { + id: Uuid, + address: SocketAddr, + base_route: String, + interval: String, +} + +impl From for Producer { + fn from(p: ProducerEndpoint) -> Self { + let interval = Duration::new(p.interval.secs, p.interval.nanos); + Self { + id: p.id, + address: p.address.parse().unwrap(), + base_route: p.base_route, + interval: humantime::format_duration(interval).to_string(), + } + } +} diff --git a/dev-tools/omdb/tests/usage_errors.out b/dev-tools/omdb/tests/usage_errors.out index b5421b76af..dc2a16bc47 100644 --- a/dev-tools/omdb/tests/usage_errors.out +++ b/dev-tools/omdb/tests/usage_errors.out @@ -11,6 +11,7 @@ Usage: omdb [OPTIONS] Commands: db Query the control plane database (CockroachDB) nexus Debug a specific Nexus instance + oximeter Query oximeter collector state sled-agent Debug a specific Sled help Print this message or the help of the given subcommand(s) @@ -33,6 +34,7 @@ Usage: omdb [OPTIONS] Commands: db Query the control plane database (CockroachDB) nexus Debug a specific Nexus instance + oximeter Query oximeter collector state sled-agent Debug a specific Sled help Print this message or the help of the given subcommand(s) diff --git a/dev-tools/omicron-dev/Cargo.toml b/dev-tools/omicron-dev/Cargo.toml index 95da4d42ef..5439b69c76 100644 --- a/dev-tools/omicron-dev/Cargo.toml +++ b/dev-tools/omicron-dev/Cargo.toml @@ -28,7 +28,7 @@ signal-hook-tokio.workspace = true tokio = { workspace = true, features = [ "full" ] } tokio-postgres.workspace = true toml.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../../workspace-hack" } +omicron-workspace-hack.workspace = true [dev-dependencies] camino-tempfile.workspace = true diff --git a/dev-tools/xtask/src/main.rs b/dev-tools/xtask/src/main.rs index 3e52d742f5..93d91799bc 100644 --- a/dev-tools/xtask/src/main.rs +++ b/dev-tools/xtask/src/main.rs @@ -133,12 +133,6 @@ fn cmd_check_workspace_deps() -> Result<()> { } } - if name == WORKSPACE_HACK_PACKAGE_NAME { - // Skip over workspace-hack because hakari doesn't yet support - // workspace deps: https://github.com/guppy-rs/guppy/issues/7 - continue; - } - non_workspace_dependencies .entry(name.to_owned()) .or_insert_with(Vec::new) diff --git a/dns-server/Cargo.toml b/dns-server/Cargo.toml index d7606dcff5..f91cbfafdb 100644 --- a/dns-server/Cargo.toml +++ b/dns-server/Cargo.toml @@ -30,7 +30,7 @@ trust-dns-proto.workspace = true trust-dns-resolver.workspace = true trust-dns-server.workspace = true uuid.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true [dev-dependencies] expectorate.workspace = true diff --git a/dns-service-client/Cargo.toml b/dns-service-client/Cargo.toml index e351d90da2..681c06672f 100644 --- a/dns-service-client/Cargo.toml +++ b/dns-service-client/Cargo.toml @@ -14,4 +14,4 @@ serde.workspace = true serde_json.workspace = true slog.workspace = true uuid.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true diff --git a/docs/how-to-run.adoc b/docs/how-to-run.adoc index 7539c5183f..aa1ee3c73d 100644 --- a/docs/how-to-run.adoc +++ b/docs/how-to-run.adoc @@ -697,3 +697,37 @@ To build a recovery host image: ---- $ ./tools/build-host-image.sh -R $HELIOS_PATH /work/trampoline-global-zone-packages.tar.gz ---- + + +== Running `oximeter` in standalone mode + +`oximeter` is the program used to collect metrics from producers in the control +plane. Normally, the producers register themselves with `nexus`, which creates a +durable assignment between the producer and an `oximeter` collector in the +database. That allows components to survive restarts, while still producing +metrics. + +To ease development, `oximeter` can be run in "standalone" mode. In this case, a +mock `nexus` server is started, with only the minimal subset of the internal API +needed to register producers and collectors. Neither CockroachDB nor ClickHouse +is required, although ClickHouse _can_ be used, if one wants to see how data is +inserted into the database. + +To run `oximeter` in standalone, use: + +[source,console] +---- +$ cargo run --bin oximeter -- standalone +---- + +The producer should still register with `nexus` as normal, which is usually done +with an explicit IP address and port. This defaults to `[::1]:12221`. + +When run this way, `oximeter` will print the samples it collects from the +producers to its logs, like so: + +[source,console] +---- +Sep 26 17:48:56.006 INFO sample: Sample { measurement: Measurement { timestamp: 2023-09-26T17:48:56.004565890Z, datum: CumulativeF64(Cumulative { start_time: 2023-09-26T17:48:45.997404777Z, value: 10.007154703 }) }, timeseries_name: "virtual_machine:cpu_busy", target: FieldSet { name: "virtual_machine", fields: {"instance_id": Field { name: "instance_id", value: Uuid(564ef6df-d5f6-4204-88f7-5c615859cfa7) }, "project_id": Field { name: "project_id", value: Uuid(2dc7e1c9-f8ac-49d7-8292-46e9e2b1a61d) }} }, metric: FieldSet { name: "cpu_busy", fields: {"cpu_id": Field { name: "cpu_id", value: I64(0) }} } }, component: results-sink, collector_id: 78c7c9a5-1569-460a-8899-aada9ad5db6c, component: oximeter-standalone, component: nexus-standalone, file: oximeter/collector/src/lib.rs:280 +Sep 26 17:48:56.006 INFO sample: Sample { measurement: Measurement { timestamp: 2023-09-26T17:48:56.004700841Z, datum: CumulativeF64(Cumulative { start_time: 2023-09-26T17:48:45.997405187Z, value: 10.007154703 }) }, timeseries_name: "virtual_machine:cpu_busy", target: FieldSet { name: "virtual_machine", fields: {"instance_id": Field { name: "instance_id", value: Uuid(564ef6df-d5f6-4204-88f7-5c615859cfa7) }, "project_id": Field { name: "project_id", value: Uuid(2dc7e1c9-f8ac-49d7-8292-46e9e2b1a61d) }} }, metric: FieldSet { name: "cpu_busy", fields: {"cpu_id": Field { name: "cpu_id", value: I64(1) }} } }, component: results-sink, collector_id: 78c7c9a5-1569-460a-8899-aada9ad5db6c, component: oximeter-standalone, component: nexus-standalone, file: oximeter/collector/src/lib.rs:280 +---- diff --git a/dpd-client/Cargo.toml b/dpd-client/Cargo.toml index 26807f7d79..0239c6d9b0 100644 --- a/dpd-client/Cargo.toml +++ b/dpd-client/Cargo.toml @@ -17,7 +17,7 @@ ipnetwork.workspace = true http.workspace = true schemars.workspace = true rand.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true [build-dependencies] anyhow.workspace = true diff --git a/end-to-end-tests/Cargo.toml b/end-to-end-tests/Cargo.toml index 5ff0f9b377..732a4a2091 100644 --- a/end-to-end-tests/Cargo.toml +++ b/end-to-end-tests/Cargo.toml @@ -24,4 +24,4 @@ tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } toml.workspace = true trust-dns-resolver.workspace = true uuid.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true diff --git a/gateway-cli/Cargo.toml b/gateway-cli/Cargo.toml index 0d179750ea..ba66fa4c4f 100644 --- a/gateway-cli/Cargo.toml +++ b/gateway-cli/Cargo.toml @@ -24,4 +24,4 @@ uuid.workspace = true gateway-client.workspace = true gateway-messages.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true diff --git a/gateway-client/Cargo.toml b/gateway-client/Cargo.toml index 96a1eb221f..fc33174107 100644 --- a/gateway-client/Cargo.toml +++ b/gateway-client/Cargo.toml @@ -15,4 +15,4 @@ serde_json.workspace = true schemars.workspace = true slog.workspace = true uuid.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true diff --git a/gateway-test-utils/Cargo.toml b/gateway-test-utils/Cargo.toml index 9d80e63f05..81b7686eb2 100644 --- a/gateway-test-utils/Cargo.toml +++ b/gateway-test-utils/Cargo.toml @@ -14,4 +14,4 @@ slog.workspace = true sp-sim.workspace = true tokio.workspace = true uuid.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true diff --git a/gateway/Cargo.toml b/gateway/Cargo.toml index f5abce88e9..07934a6ad3 100644 --- a/gateway/Cargo.toml +++ b/gateway/Cargo.toml @@ -34,7 +34,7 @@ tokio-tungstenite.workspace = true tokio-util.workspace = true toml.workspace = true uuid.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true [dev-dependencies] expectorate.workspace = true diff --git a/illumos-utils/Cargo.toml b/illumos-utils/Cargo.toml index e292097bc5..e521b54d02 100644 --- a/illumos-utils/Cargo.toml +++ b/illumos-utils/Cargo.toml @@ -29,7 +29,7 @@ zone.workspace = true # only enabled via the `testing` feature mockall = { workspace = true, optional = true } -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true [target.'cfg(target_os = "illumos")'.dependencies] opte-ioctl.workspace = true diff --git a/installinator-artifact-client/Cargo.toml b/installinator-artifact-client/Cargo.toml index 18447b8e83..c3ddc529d9 100644 --- a/installinator-artifact-client/Cargo.toml +++ b/installinator-artifact-client/Cargo.toml @@ -15,4 +15,4 @@ serde_json.workspace = true slog.workspace = true update-engine.workspace = true uuid.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true diff --git a/installinator-artifactd/Cargo.toml b/installinator-artifactd/Cargo.toml index 9318b725db..b14ca4002f 100644 --- a/installinator-artifactd/Cargo.toml +++ b/installinator-artifactd/Cargo.toml @@ -20,7 +20,7 @@ uuid.workspace = true installinator-common.workspace = true omicron-common.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true [dev-dependencies] expectorate.workspace = true diff --git a/installinator-common/Cargo.toml b/installinator-common/Cargo.toml index 0f1bf86901..8fea234e20 100644 --- a/installinator-common/Cargo.toml +++ b/installinator-common/Cargo.toml @@ -15,4 +15,4 @@ serde_json.workspace = true serde_with.workspace = true thiserror.workspace = true update-engine.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true diff --git a/installinator/Cargo.toml b/installinator/Cargo.toml index 428ea0d08e..a4f170ddba 100644 --- a/installinator/Cargo.toml +++ b/installinator/Cargo.toml @@ -42,7 +42,7 @@ toml.workspace = true tufaceous-lib.workspace = true update-engine.workspace = true uuid.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true [dev-dependencies] omicron-test-utils.workspace = true @@ -57,4 +57,4 @@ tokio-stream.workspace = true [features] image-standard = [] image-trampoline = [] -rack-topology-single-sled = [] \ No newline at end of file +rack-topology-single-sled = [] diff --git a/internal-dns-cli/Cargo.toml b/internal-dns-cli/Cargo.toml index fb5780d22a..dab92c6d7c 100644 --- a/internal-dns-cli/Cargo.toml +++ b/internal-dns-cli/Cargo.toml @@ -13,4 +13,4 @@ omicron-common.workspace = true slog.workspace = true tokio.workspace = true trust-dns-resolver.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true diff --git a/internal-dns/Cargo.toml b/internal-dns/Cargo.toml index d680ab3ce1..ecb2d48bda 100644 --- a/internal-dns/Cargo.toml +++ b/internal-dns/Cargo.toml @@ -17,7 +17,7 @@ thiserror.workspace = true trust-dns-proto.workspace = true trust-dns-resolver.workspace = true uuid.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true [dev-dependencies] assert_matches.workspace = true diff --git a/ipcc-key-value/Cargo.toml b/ipcc-key-value/Cargo.toml index 128fde9a01..04aea9f939 100644 --- a/ipcc-key-value/Cargo.toml +++ b/ipcc-key-value/Cargo.toml @@ -11,7 +11,7 @@ omicron-common.workspace = true serde.workspace = true thiserror.workspace = true uuid.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true [dev-dependencies] omicron-common = { workspace = true, features = ["testing"] } diff --git a/key-manager/Cargo.toml b/key-manager/Cargo.toml index 69ae3b25bd..c44ec61ea4 100644 --- a/key-manager/Cargo.toml +++ b/key-manager/Cargo.toml @@ -14,5 +14,5 @@ slog.workspace = true thiserror.workspace = true tokio.workspace = true zeroize.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true diff --git a/nexus-client/Cargo.toml b/nexus-client/Cargo.toml index d59c013992..2734142f9f 100644 --- a/nexus-client/Cargo.toml +++ b/nexus-client/Cargo.toml @@ -18,4 +18,4 @@ serde.workspace = true serde_json.workspace = true slog.workspace = true uuid.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true diff --git a/nexus/Cargo.toml b/nexus/Cargo.toml index 91872e2c32..3de6dac7c0 100644 --- a/nexus/Cargo.toml +++ b/nexus/Cargo.toml @@ -90,7 +90,7 @@ oximeter.workspace = true oximeter-instruments = { workspace = true, features = ["http-instruments"] } oximeter-producer.workspace = true rustls = { workspace = true } -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true [dev-dependencies] async-bb8-diesel.workspace = true diff --git a/nexus/authz-macros/Cargo.toml b/nexus/authz-macros/Cargo.toml index 3d55afa477..15f18cb9c8 100644 --- a/nexus/authz-macros/Cargo.toml +++ b/nexus/authz-macros/Cargo.toml @@ -14,4 +14,4 @@ quote.workspace = true serde.workspace = true serde_tokenstream.workspace = true syn.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../../workspace-hack" } +omicron-workspace-hack.workspace = true diff --git a/nexus/db-macros/Cargo.toml b/nexus/db-macros/Cargo.toml index ce206bb56e..053c381ac9 100644 --- a/nexus/db-macros/Cargo.toml +++ b/nexus/db-macros/Cargo.toml @@ -15,7 +15,7 @@ quote.workspace = true serde.workspace = true serde_tokenstream.workspace = true syn = { workspace = true, features = ["extra-traits"] } -omicron-workspace-hack = { version = "0.1", path = "../../workspace-hack" } +omicron-workspace-hack.workspace = true [dev-dependencies] rustfmt-wrapper.workspace = true diff --git a/nexus/db-model/Cargo.toml b/nexus/db-model/Cargo.toml index aedbb9168b..a5cb9a06be 100644 --- a/nexus/db-model/Cargo.toml +++ b/nexus/db-model/Cargo.toml @@ -36,7 +36,7 @@ nexus-defaults.workspace = true nexus-types.workspace = true omicron-passwords.workspace = true sled-agent-client.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../../workspace-hack" } +omicron-workspace-hack.workspace = true [dev-dependencies] expectorate.workspace = true diff --git a/nexus/db-queries/Cargo.toml b/nexus/db-queries/Cargo.toml index af01c1732b..eaf3dc1295 100644 --- a/nexus/db-queries/Cargo.toml +++ b/nexus/db-queries/Cargo.toml @@ -63,7 +63,7 @@ nexus-types.workspace = true omicron-common.workspace = true omicron-passwords.workspace = true oximeter.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../../workspace-hack" } +omicron-workspace-hack.workspace = true [dev-dependencies] assert_matches.workspace = true diff --git a/nexus/defaults/Cargo.toml b/nexus/defaults/Cargo.toml index 09a95fa839..0724b5bf4d 100644 --- a/nexus/defaults/Cargo.toml +++ b/nexus/defaults/Cargo.toml @@ -11,4 +11,4 @@ rand.workspace = true serde_json.workspace = true omicron-common.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../../workspace-hack" } +omicron-workspace-hack.workspace = true diff --git a/nexus/test-interface/Cargo.toml b/nexus/test-interface/Cargo.toml index e0743e84bc..0071ffaa28 100644 --- a/nexus/test-interface/Cargo.toml +++ b/nexus/test-interface/Cargo.toml @@ -12,4 +12,4 @@ nexus-types.workspace = true omicron-common.workspace = true slog.workspace = true uuid.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../../workspace-hack" } +omicron-workspace-hack.workspace = true diff --git a/nexus/test-utils-macros/Cargo.toml b/nexus/test-utils-macros/Cargo.toml index 1bfa25017a..d3d28a7640 100644 --- a/nexus/test-utils-macros/Cargo.toml +++ b/nexus/test-utils-macros/Cargo.toml @@ -11,4 +11,4 @@ proc-macro = true proc-macro2.workspace = true quote.workspace = true syn = { workspace = true, features = [ "fold", "parsing" ] } -omicron-workspace-hack = { version = "0.1", path = "../../workspace-hack" } +omicron-workspace-hack.workspace = true diff --git a/nexus/test-utils/Cargo.toml b/nexus/test-utils/Cargo.toml index a2e7600e93..8eb8df4a5b 100644 --- a/nexus/test-utils/Cargo.toml +++ b/nexus/test-utils/Cargo.toml @@ -38,4 +38,4 @@ tempfile.workspace = true trust-dns-proto.workspace = true trust-dns-resolver.workspace = true uuid.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../../workspace-hack" } +omicron-workspace-hack.workspace = true diff --git a/nexus/types/Cargo.toml b/nexus/types/Cargo.toml index f7ffafec52..c499714c31 100644 --- a/nexus/types/Cargo.toml +++ b/nexus/types/Cargo.toml @@ -25,4 +25,4 @@ api_identity.workspace = true dns-service-client.workspace = true omicron-common.workspace = true omicron-passwords.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../../workspace-hack" } +omicron-workspace-hack.workspace = true diff --git a/openapi/oximeter.json b/openapi/oximeter.json index 6781b77892..ebc7957c2e 100644 --- a/openapi/oximeter.json +++ b/openapi/oximeter.json @@ -10,7 +10,76 @@ "version": "0.0.1" }, "paths": { + "/info": { + "get": { + "operationId": "collector_info", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CollectorInfo" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, "/producers": { + "get": { + "operationId": "producers_list", + "parameters": [ + { + "in": "query", + "name": "limit", + "description": "Maximum number of items returned by a single call", + "schema": { + "nullable": true, + "type": "integer", + "format": "uint32", + "minimum": 1 + } + }, + { + "in": "query", + "name": "page_token", + "description": "Token returned by previous call to retrieve the subsequent page", + "schema": { + "nullable": true, + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProducerEndpointResultsPage" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + }, + "x-dropshot-pagination": { + "required": [] + } + }, "post": { "operationId": "producers_post", "requestBody": { @@ -35,6 +104,33 @@ } } } + }, + "/producers/{producer_id}": { + "delete": { + "operationId": "producer_delete", + "parameters": [ + { + "in": "path", + "name": "producer_id", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "204": { + "description": "successful deletion" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } } }, "components": { @@ -51,6 +147,19 @@ } }, "schemas": { + "CollectorInfo": { + "type": "object", + "properties": { + "id": { + "description": "The collector's UUID.", + "type": "string", + "format": "uuid" + } + }, + "required": [ + "id" + ] + }, "Duration": { "type": "object", "properties": { @@ -113,6 +222,27 @@ "id", "interval" ] + }, + "ProducerEndpointResultsPage": { + "description": "A single page of results", + "type": "object", + "properties": { + "items": { + "description": "list of items on this page of results", + "type": "array", + "items": { + "$ref": "#/components/schemas/ProducerEndpoint" + } + }, + "next_page": { + "nullable": true, + "description": "token used to fetch the next page of results (if any)", + "type": "string" + } + }, + "required": [ + "items" + ] } } } diff --git a/oxide-client/Cargo.toml b/oxide-client/Cargo.toml index df34ab9721..3cb411729d 100644 --- a/oxide-client/Cargo.toml +++ b/oxide-client/Cargo.toml @@ -21,4 +21,4 @@ thiserror.workspace = true tokio = { workspace = true, features = [ "net" ] } trust-dns-resolver.workspace = true uuid.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true diff --git a/oximeter-client/Cargo.toml b/oximeter-client/Cargo.toml index 297dfb6c92..e54b152415 100644 --- a/oximeter-client/Cargo.toml +++ b/oximeter-client/Cargo.toml @@ -6,10 +6,11 @@ license = "MPL-2.0" [dependencies] chrono.workspace = true +futures.workspace = true omicron-common.workspace = true progenitor.workspace = true reqwest = { workspace = true, features = ["json", "rustls-tls", "stream"] } serde.workspace = true slog.workspace = true uuid.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true diff --git a/oximeter/collector/Cargo.toml b/oximeter/collector/Cargo.toml index c8c4030dba..470d9db312 100644 --- a/oximeter/collector/Cargo.toml +++ b/oximeter/collector/Cargo.toml @@ -6,23 +6,30 @@ description = "The oximeter metric collection server" license = "MPL-2.0" [dependencies] +anyhow.workspace = true clap.workspace = true dropshot.workspace = true futures.workspace = true internal-dns.workspace = true nexus-client.workspace = true +nexus-types.workspace = true omicron-common.workspace = true oximeter.workspace = true +oximeter-client.workspace = true oximeter-db.workspace = true +rand.workspace = true reqwest = { workspace = true, features = [ "json" ] } +schemars.workspace = true serde.workspace = true slog.workspace = true +slog-async.workspace = true slog-dtrace.workspace = true +slog-term.workspace = true thiserror.workspace = true tokio.workspace = true toml.workspace = true uuid.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../../workspace-hack" } +omicron-workspace-hack.workspace = true [dev-dependencies] expectorate.workspace = true diff --git a/oximeter/collector/src/bin/oximeter.rs b/oximeter/collector/src/bin/oximeter.rs index bf54cf33fa..8c4bf0e27c 100644 --- a/oximeter/collector/src/bin/oximeter.rs +++ b/oximeter/collector/src/bin/oximeter.rs @@ -3,12 +3,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. //! Main entry point to run an `oximeter` server in the control plane. -// Copyright 2021 Oxide Computer Company + +// Copyright 2023 Oxide Computer Company use clap::Parser; use omicron_common::cmd::fatal; use omicron_common::cmd::CmdError; -use oximeter_collector::{oximeter_api, Config, Oximeter, OximeterArguments}; +use oximeter_collector::oximeter_api; +use oximeter_collector::standalone_nexus_api; +use oximeter_collector::Config; +use oximeter_collector::Oximeter; +use oximeter_collector::OximeterArguments; +use oximeter_collector::StandaloneNexus; +use slog::Level; +use std::net::Ipv6Addr; +use std::net::SocketAddr; use std::net::SocketAddrV6; use std::path::PathBuf; use uuid::Uuid; @@ -23,6 +32,16 @@ pub fn run_openapi() -> Result<(), String> { .map_err(|e| e.to_string()) } +pub fn run_standalone_openapi() -> Result<(), String> { + standalone_nexus_api() + .openapi("Oxide Nexus API", "0.0.1") + .description("API for interacting with Nexus") + .contact_url("https://oxide.computer") + .contact_email("api@oxide.computer") + .write(&mut std::io::stdout()) + .map_err(|e| e.to_string()) +} + /// Run an oximeter metric collection server in the Oxide Control Plane. #[derive(Parser)] #[clap(name = "oximeter", about = "See README.adoc for more information")] @@ -36,12 +55,71 @@ enum Args { #[clap(name = "CONFIG_FILE", action)] config_file: PathBuf, + /// The UUID for this instance of the `oximeter` collector. #[clap(short, long, action)] id: Uuid, + /// The socket address at which `oximeter`'s HTTP server runs. #[clap(short, long, action)] address: SocketAddrV6, }, + + /// Run `oximeter` in standalone mode for development. + /// + /// In this mode, `oximeter` can be used to test the collection of metrics + /// from producers, without requiring all the normal machinery of the + /// control plane. The collector is run as usual, but additionally starts a + /// API server to stand-in for Nexus. The registrations of the producers and + /// collectors occurs through the normal code path, but uses this mock Nexus + /// instead of the real thing. + Standalone { + /// The ID for the collector. + /// + /// Default is to generate a new, random UUID. + #[arg(long, default_value_t = Uuid::new_v4())] + id: Uuid, + + /// Address at which `oximeter` itself listens. + /// + /// This address can be used to register new producers, after the + /// program has already started. + #[arg( + long, + default_value_t = SocketAddrV6::new(Ipv6Addr::LOCALHOST, 12223, 0, 0) + )] + address: SocketAddrV6, + + /// The address for the mock Nexus server used to register. + /// + /// This program starts a mock version of Nexus, which is used only to + /// register the producers and collectors. This allows them to operate + /// as they usually would, registering each other with Nexus so that an + /// assignment between them can be made. + #[arg( + long, + default_value_t = SocketAddrV6::new(Ipv6Addr::LOCALHOST, 12221, 0, 0) + )] + nexus: SocketAddrV6, + + /// The address for ClickHouse. + /// + /// If not provided, `oximeter` will not attempt to insert records into + /// the database at all. In this mode, the program will print the + /// collected samples, instead of inserting them into the database. + #[arg(long)] + clickhouse: Option, + + /// The log-level. + #[arg(long, default_value_t = Level::Info, value_parser = parse_log_level)] + log_level: Level, + }, + + /// Print the fake Nexus's standalone API. + StandaloneOpenapi, +} + +fn parse_log_level(s: &str) -> Result { + s.parse().map_err(|_| "Invalid log level".to_string()) } #[tokio::main] @@ -65,5 +143,26 @@ async fn do_run() -> Result<(), CmdError> { .await .map_err(|e| CmdError::Failure(e.to_string())) } + Args::Standalone { id, address, nexus, clickhouse, log_level } => { + // Start the standalone Nexus server, for registration of both the + // collector and producers. + let nexus_server = StandaloneNexus::new(nexus.into(), log_level) + .map_err(|e| CmdError::Failure(e.to_string()))?; + let args = OximeterArguments { id, address }; + Oximeter::new_standalone( + nexus_server.log(), + &args, + nexus_server.local_addr(), + clickhouse, + ) + .await + .unwrap() + .serve_forever() + .await + .map_err(|e| CmdError::Failure(e.to_string())) + } + Args::StandaloneOpenapi => { + run_standalone_openapi().map_err(CmdError::Failure) + } } } diff --git a/oximeter/collector/src/lib.rs b/oximeter/collector/src/lib.rs index bf75b567ea..6674d65ecd 100644 --- a/oximeter/collector/src/lib.rs +++ b/oximeter/collector/src/lib.rs @@ -4,35 +4,71 @@ //! Implementation of the `oximeter` metric collection server. -// Copyright 2021 Oxide Computer Company - -use dropshot::{ - endpoint, ApiDescription, ConfigDropshot, ConfigLogging, HttpError, - HttpResponseUpdatedNoContent, HttpServer, HttpServerStarter, - RequestContext, TypedBody, -}; -use internal_dns::resolver::{ResolveError, Resolver}; +// Copyright 2023 Oxide Computer Company + +use anyhow::anyhow; +use anyhow::Context; +use dropshot::endpoint; +use dropshot::ApiDescription; +use dropshot::ConfigDropshot; +use dropshot::ConfigLogging; +use dropshot::EmptyScanParams; +use dropshot::HttpError; +use dropshot::HttpResponseDeleted; +use dropshot::HttpResponseOk; +use dropshot::HttpResponseUpdatedNoContent; +use dropshot::HttpServer; +use dropshot::HttpServerStarter; +use dropshot::PaginationParams; +use dropshot::Query; +use dropshot::RequestContext; +use dropshot::ResultsPage; +use dropshot::TypedBody; +use dropshot::WhichPage; +use internal_dns::resolver::ResolveError; +use internal_dns::resolver::Resolver; use internal_dns::ServiceName; -use omicron_common::address::{CLICKHOUSE_PORT, NEXUS_INTERNAL_PORT}; +use omicron_common::address::CLICKHOUSE_PORT; +use omicron_common::address::NEXUS_INTERNAL_PORT; use omicron_common::api::internal::nexus::ProducerEndpoint; -use omicron_common::{backoff, FileKv}; -use oximeter::types::{ProducerResults, ProducerResultsItem}; -use oximeter_db::{Client, DbWrite}; -use serde::{Deserialize, Serialize}; -use slog::{debug, error, info, o, trace, warn, Drain, Logger}; -use std::collections::{btree_map::Entry, BTreeMap}; -use std::net::{SocketAddr, SocketAddrV6}; +use omicron_common::backoff; +use omicron_common::FileKv; +use oximeter::types::ProducerResults; +use oximeter::types::ProducerResultsItem; +use oximeter_db::Client; +use oximeter_db::DbWrite; +use serde::Deserialize; +use serde::Serialize; +use slog::debug; +use slog::error; +use slog::info; +use slog::o; +use slog::trace; +use slog::warn; +use slog::Drain; +use slog::Logger; +use std::collections::btree_map::Entry; +use std::collections::BTreeMap; +use std::net::SocketAddr; +use std::net::SocketAddrV6; +use std::ops::Bound; use std::path::Path; use std::sync::Arc; use std::time::Duration; use thiserror::Error; -use tokio::{ - sync::mpsc, sync::oneshot, sync::Mutex, task::JoinHandle, time::interval, -}; +use tokio::sync::mpsc; +use tokio::sync::oneshot; +use tokio::sync::Mutex; +use tokio::task::JoinHandle; +use tokio::time::interval; use uuid::Uuid; +mod standalone; +pub use standalone::standalone_nexus_api; +pub use standalone::Server as StandaloneNexus; + /// Errors collecting metric data -#[derive(Debug, Clone, Error)] +#[derive(Debug, Error)] pub enum Error { #[error("Error running Oximeter collector server: {0}")] Server(String), @@ -45,6 +81,48 @@ pub enum Error { #[error(transparent)] ResolveError(#[from] ResolveError), + + #[error("No producer is registered with ID")] + NoSuchProducer(Uuid), + + #[error("Error running standalone")] + Standalone(#[from] anyhow::Error), +} + +impl From for HttpError { + fn from(e: Error) -> Self { + match e { + Error::NoSuchProducer(id) => HttpError::for_not_found( + None, + format!("No such producer: {id}"), + ), + _ => HttpError::for_internal_error(e.to_string()), + } + } +} + +/// A simple representation of a producer, used mostly for standalone mode. +/// +/// These are usually specified as a structured string, formatted like: +/// `"@
"`. +#[derive(Copy, Clone, Debug)] +pub struct ProducerInfo { + /// The ID of the producer. + pub id: Uuid, + /// The address on which the producer listens. + pub address: SocketAddr, +} + +impl std::str::FromStr for ProducerInfo { + type Err = anyhow::Error; + fn from_str(s: &str) -> Result { + let (id, addr) = s + .split_once('@') + .context("Producer info should written as @
")?; + let id = id.parse().context("Invalid UUID")?; + let address = addr.parse().context("Invalid address")?; + Ok(Self { id, address }) + } } type CollectionToken = oneshot::Sender<()>; @@ -61,7 +139,6 @@ enum CollectionMessage { // from its producer. Update(ProducerEndpoint), // Request that the task exit - #[allow(dead_code)] Shutdown, } @@ -72,7 +149,7 @@ async fn perform_collection( outbox: &mpsc::Sender<(Option, ProducerResults)>, token: Option, ) { - info!(log, "collecting from producer"); + debug!(log, "collecting from producer"); let res = client .get(format!( "http://{}{}", @@ -187,6 +264,44 @@ struct CollectionTask { pub task: JoinHandle<()>, } +// A task run by `oximeter` in standalone mode, which simply prints results as +// they're received. +async fn results_printer( + log: Logger, + mut rx: mpsc::Receiver<(Option, ProducerResults)>, +) { + loop { + match rx.recv().await { + Some((_, results)) => { + for res in results.into_iter() { + match res { + ProducerResultsItem::Ok(samples) => { + for sample in samples.into_iter() { + info!( + log, + ""; + "sample" => ?sample, + ); + } + } + ProducerResultsItem::Err(e) => { + error!( + log, + "received error from a producer"; + "err" => ?e, + ); + } + } + } + } + None => { + debug!(log, "result queue closed, exiting"); + return; + } + } + } +} + // Aggregation point for all results, from all collection tasks. async fn results_sink( log: Logger, @@ -286,6 +401,20 @@ pub struct DbConfig { pub batch_interval: u64, } +impl DbConfig { + pub const DEFAULT_BATCH_SIZE: usize = 1000; + pub const DEFAULT_BATCH_INTERVAL: u64 = 5; + + // Construct config with an address, using the defaults for other fields + fn with_address(address: SocketAddr) -> Self { + Self { + address: Some(address), + batch_size: Self::DEFAULT_BATCH_SIZE, + batch_interval: Self::DEFAULT_BATCH_INTERVAL, + } + } +} + /// The internal agent the oximeter server uses to collect metrics from producers. #[derive(Debug)] pub struct OximeterAgent { @@ -295,7 +424,8 @@ pub struct OximeterAgent { // Handle to the TX-side of a channel for collecting results from the collection tasks result_sender: mpsc::Sender<(Option, ProducerResults)>, // The actual tokio tasks running the collection on a timer. - collection_tasks: Arc>>, + collection_tasks: + Arc>>, } impl OximeterAgent { @@ -307,7 +437,10 @@ impl OximeterAgent { log: &Logger, ) -> Result { let (result_sender, result_receiver) = mpsc::channel(8); - let log = log.new(o!("component" => "oximeter-agent", "collector_id" => id.to_string())); + let log = log.new(o!( + "component" => "oximeter-agent", + "collector_id" => id.to_string(), + )); let insertion_log = log.new(o!("component" => "results-sink")); // Construct the ClickHouse client first, propagate an error if we can't reach the @@ -347,6 +480,61 @@ impl OximeterAgent { }) } + /// Construct a new standalone `oximeter` collector. + pub async fn new_standalone( + id: Uuid, + db_config: Option, + log: &Logger, + ) -> Result { + let (result_sender, result_receiver) = mpsc::channel(8); + let log = log.new(o!( + "component" => "oximeter-standalone", + "collector_id" => id.to_string(), + )); + + // If we have configuration for ClickHouse, we'll spawn the results + // sink task as usual. If not, we'll spawn a dummy task that simply + // prints the results as they're received. + let insertion_log = log.new(o!("component" => "results-sink")); + if let Some(db_config) = db_config { + let Some(address) = db_config.address else { + return Err(Error::Standalone(anyhow!( + "Must provide explicit IP address in standalone mode" + ))); + }; + let client = Client::new(address, &log); + let replicated = client.is_oximeter_cluster().await?; + if !replicated { + client.init_single_node_db().await?; + } else { + client.init_replicated_db().await?; + } + + // Spawn the task for aggregating and inserting all metrics + tokio::spawn(async move { + results_sink( + insertion_log, + client, + db_config.batch_size, + Duration::from_secs(db_config.batch_interval), + result_receiver, + ) + .await + }); + } else { + tokio::spawn(results_printer(insertion_log, result_receiver)); + } + + // Construct the ClickHouse client first, propagate an error if we can't reach the + // database. + Ok(Self { + id, + log, + result_sender, + collection_tasks: Arc::new(Mutex::new(BTreeMap::new())), + }) + } + /// Register a new producer with this oximeter instance. pub async fn register_producer( &self, @@ -355,30 +543,36 @@ impl OximeterAgent { let id = info.id; match self.collection_tasks.lock().await.entry(id) { Entry::Vacant(value) => { - info!(self.log, "registered new metric producer"; - "producer_id" => id.to_string(), - "address" => info.address, + debug!( + self.log, + "registered new metric producer"; + "producer_id" => id.to_string(), + "address" => info.address, ); // Build channel to control the task and receive results. let (tx, rx) = mpsc::channel(4); let q = self.result_sender.clone(); let log = self.log.new(o!("component" => "collection-task", "producer_id" => id.to_string())); + let info_clone = info.clone(); let task = tokio::spawn(async move { - collection_task(log, info, rx, q).await; + collection_task(log, info_clone, rx, q).await; }); - value.insert(CollectionTask { inbox: tx, task }); + value.insert((info, CollectionTask { inbox: tx, task })); } - Entry::Occupied(value) => { - info!( + Entry::Occupied(mut value) => { + debug!( self.log, - "received request to register existing metric producer, updating collection information"; + "received request to register existing metric \ + producer, updating collection information"; "producer_id" => id.to_string(), "interval" => ?info.interval, "address" => info.address, ); + value.get_mut().0 = info.clone(); value .get() + .1 .inbox .send(CollectionMessage::Update(info)) .await @@ -395,10 +589,10 @@ impl OximeterAgent { pub async fn force_collection(&self) { let mut collection_oneshots = vec![]; let collection_tasks = self.collection_tasks.lock().await; - for task in collection_tasks.iter() { + for (_id, (_endpoint, task)) in collection_tasks.iter() { let (tx, rx) = oneshot::channel(); // Scrape from each producer, into oximeter... - task.1.inbox.send(CollectionMessage::Collect(tx)).await.unwrap(); + task.inbox.send(CollectionMessage::Collect(tx)).await.unwrap(); // ... and keep track of the token that indicates once the metric // has made it into Clickhouse. collection_oneshots.push(rx); @@ -412,6 +606,55 @@ impl OximeterAgent { // successfully, or an error occurred in the collection pathway. futures::future::join_all(collection_oneshots).await; } + + /// List existing producers. + pub async fn list_producers( + &self, + start_id: Option, + limit: usize, + ) -> Vec { + let start = if let Some(id) = start_id { + Bound::Excluded(id) + } else { + Bound::Unbounded + }; + self.collection_tasks + .lock() + .await + .range((start, Bound::Unbounded)) + .take(limit) + .map(|(_id, (info, _t))| info.clone()) + .collect() + } + + /// Delete a producer by ID, stopping its collection task. + pub async fn delete_producer(&self, id: Uuid) -> Result<(), Error> { + let (_info, task) = self + .collection_tasks + .lock() + .await + .remove(&id) + .ok_or_else(|| Error::NoSuchProducer(id))?; + debug!( + self.log, + "removed collection task from set"; + "producer_id" => %id, + ); + match task.inbox.send(CollectionMessage::Shutdown).await { + Ok(_) => debug!( + self.log, + "shut down collection task"; + "producer_id" => %id, + ), + Err(e) => error!( + self.log, + "failed to shut down collection task"; + "producer_id" => %id, + "error" => ?e, + ), + } + Ok(()) + } } /// Configuration used to initialize an oximeter server @@ -440,6 +683,7 @@ impl Config { } } +/// Arguments for running the `oximeter` collector. pub struct OximeterArguments { pub id: Uuid, pub address: SocketAddrV6, @@ -447,7 +691,7 @@ pub struct OximeterArguments { /// A server used to collect metrics from components in the control plane. pub struct Oximeter { - _agent: Arc, + agent: Arc, server: HttpServer>, } @@ -572,7 +816,67 @@ impl Oximeter { .expect("Expected an infinite retry loop contacting Nexus"); info!(log, "oximeter registered with nexus"; "id" => ?agent.id); - Ok(Self { _agent: agent, server }) + Ok(Self { agent, server }) + } + + /// Create a new `oximeter` collector running in standalone mode. + pub async fn new_standalone( + log: &Logger, + args: &OximeterArguments, + nexus: SocketAddr, + clickhouse: Option, + ) -> Result { + let db_config = clickhouse.map(DbConfig::with_address); + let agent = Arc::new( + OximeterAgent::new_standalone(args.id, db_config, &log).await?, + ); + + let dropshot_log = log.new(o!("component" => "dropshot")); + let server = HttpServerStarter::new( + &ConfigDropshot { + bind_address: SocketAddr::V6(args.address), + ..Default::default() + }, + oximeter_api(), + Arc::clone(&agent), + &dropshot_log, + ) + .map_err(|e| Error::Server(e.to_string()))? + .start(); + info!(log, "started oximeter standalone server"); + + // Notify the standalone nexus. + let client = reqwest::Client::new(); + let notify_nexus = || async { + debug!(log, "contacting nexus"); + client + .post(format!("http://{}/metrics/collectors", nexus)) + .json(&nexus_client::types::OximeterInfo { + address: server.local_addr().to_string(), + collector_id: agent.id, + }) + .send() + .await + .map_err(|e| backoff::BackoffError::transient(e.to_string()))? + .error_for_status() + .map_err(|e| backoff::BackoffError::transient(e.to_string())) + }; + let log_notification_failure = |error, delay| { + warn!( + log, + "failed to contact nexus, will retry in {:?}", delay; + "error" => ?error + ); + }; + backoff::retry_notify( + backoff::retry_policy_internal_service(), + notify_nexus, + log_notification_failure, + ) + .await + .expect("Expected an infinite retry loop contacting Nexus"); + + Ok(Self { agent, server }) } /// Serve requests forever, consuming the server. @@ -592,6 +896,20 @@ impl Oximeter { pub async fn force_collect(&self) { self.server.app_private().force_collection().await } + + /// List producers. + pub async fn list_producers( + &self, + start: Option, + limit: usize, + ) -> Vec { + self.agent.list_producers(start, limit).await + } + + /// Delete a producer by ID, stopping its collection task. + pub async fn delete_producer(&self, id: Uuid) -> Result<(), Error> { + self.agent.delete_producer(id).await + } } // Build the HTTP API internal to the control plane @@ -599,6 +917,12 @@ pub fn oximeter_api() -> ApiDescription> { let mut api = ApiDescription::new(); api.register(producers_post) .expect("Could not register producers_post API handler"); + api.register(producers_list) + .expect("Could not register producers_list API handler"); + api.register(producer_delete) + .expect("Could not register producers_delete API handler"); + api.register(collector_info) + .expect("Could not register collector_info API handler"); api } @@ -616,6 +940,79 @@ async fn producers_post( agent .register_producer(producer_info) .await - .map_err(|e| HttpError::for_internal_error(e.to_string()))?; - Ok(HttpResponseUpdatedNoContent()) + .map_err(HttpError::from) + .map(|_| HttpResponseUpdatedNoContent()) +} + +// Parameters for paginating the list of producers. +#[derive(Clone, Copy, Debug, Deserialize, schemars::JsonSchema, Serialize)] +struct ProducerPage { + id: Uuid, +} + +// List all producers +#[endpoint { + method = GET, + path = "/producers", +}] +async fn producers_list( + request_context: RequestContext>, + query: Query>, +) -> Result>, HttpError> { + let agent = request_context.context(); + let pagination = query.into_inner(); + let limit = request_context.page_limit(&pagination)?.get() as usize; + let start = match &pagination.page { + WhichPage::First(..) => None, + WhichPage::Next(ProducerPage { id }) => Some(*id), + }; + let producers = agent.list_producers(start, limit).await; + ResultsPage::new( + producers, + &EmptyScanParams {}, + |info: &ProducerEndpoint, _| ProducerPage { id: info.id }, + ) + .map(HttpResponseOk) +} + +#[derive(Clone, Copy, Debug, Deserialize, schemars::JsonSchema, Serialize)] +struct ProducerIdPathParams { + producer_id: Uuid, +} + +// Delete a producer by ID. +#[endpoint { + method = DELETE, + path = "/producers/{producer_id}", +}] +async fn producer_delete( + request_context: RequestContext>, + path: dropshot::Path, +) -> Result { + let agent = request_context.context(); + let producer_id = path.into_inner().producer_id; + agent + .delete_producer(producer_id) + .await + .map_err(HttpError::from) + .map(|_| HttpResponseDeleted()) +} + +#[derive(Clone, Copy, Debug, Deserialize, schemars::JsonSchema, Serialize)] +pub struct CollectorInfo { + /// The collector's UUID. + pub id: Uuid, +} + +// Return identifying information about this collector +#[endpoint { + method = GET, + path = "/info", +}] +async fn collector_info( + request_context: RequestContext>, +) -> Result, HttpError> { + let agent = request_context.context(); + let info = CollectorInfo { id: agent.id }; + Ok(HttpResponseOk(info)) } diff --git a/oximeter/collector/src/standalone.rs b/oximeter/collector/src/standalone.rs new file mode 100644 index 0000000000..826a5f4663 --- /dev/null +++ b/oximeter/collector/src/standalone.rs @@ -0,0 +1,263 @@ +// 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/. + +//! Implementation of a standalone fake Nexus, simply for registering producers +//! and collectors with one another. + +// Copyright 2023 Oxide Computer Company + +use crate::Error; +use dropshot::endpoint; +use dropshot::ApiDescription; +use dropshot::ConfigDropshot; +use dropshot::HttpError; +use dropshot::HttpResponseUpdatedNoContent; +use dropshot::HttpServer; +use dropshot::HttpServerStarter; +use dropshot::RequestContext; +use dropshot::TypedBody; +use nexus_types::internal_api::params::OximeterInfo; +use omicron_common::api::internal::nexus::ProducerEndpoint; +use omicron_common::FileKv; +use oximeter_client::Client; +use rand::seq::IteratorRandom; +use slog::debug; +use slog::error; +use slog::info; +use slog::o; +use slog::Drain; +use slog::Level; +use slog::Logger; +use std::collections::HashMap; +use std::net::SocketAddr; +use std::sync::Arc; +use tokio::sync::Mutex; +use uuid::Uuid; + +// An assignment of a producer to an oximeter collector. +#[derive(Debug)] +struct ProducerAssignment { + producer: ProducerEndpoint, + collector_id: Uuid, +} + +#[derive(Debug)] +struct Inner { + // Map of producers by ID to their information and assigned oximeter + // collector. + producers: HashMap, + // Map of available oximeter collectors. + collectors: HashMap, +} + +impl Inner { + fn random_collector(&self) -> Option<(Uuid, OximeterInfo)> { + self.collectors + .iter() + .choose(&mut rand::thread_rng()) + .map(|(id, info)| (*id, *info)) + } +} + +// A stripped-down Nexus server, with only the APIs for registering metric +// producers and collectors. +#[derive(Debug)] +pub struct StandaloneNexus { + pub log: Logger, + inner: Mutex, +} + +impl StandaloneNexus { + fn new(log: Logger) -> Self { + Self { + log, + inner: Mutex::new(Inner { + producers: HashMap::new(), + collectors: HashMap::new(), + }), + } + } + + async fn register_producer( + &self, + info: &ProducerEndpoint, + ) -> Result<(), HttpError> { + let mut inner = self.inner.lock().await; + let assignment = match inner.producers.get_mut(&info.id) { + None => { + // There is no record for this producer. + // + // Select a random collector, and assign it to the producer. + // We'll return the assignment from this match block. + let Some((collector_id, collector_info)) = + inner.random_collector() + else { + return Err(HttpError::for_unavail( + None, + String::from("No collectors available"), + )); + }; + let client = Client::new( + format!("http://{}", collector_info.address).as_str(), + self.log.clone(), + ); + client.producers_post(&info.into()).await.map_err(|e| { + HttpError::for_internal_error(e.to_string()) + })?; + let assignment = + ProducerAssignment { producer: info.clone(), collector_id }; + assignment + } + Some(existing_assignment) => { + // We have a record, first check if it matches the assignment we + // have. + if &existing_assignment.producer == info { + return Ok(()); + } + + // This appears to be a re-registration, e.g., the producer + // changed its IP address. Re-register it with the collector to + // which it's already assigned. + let collector_id = existing_assignment.collector_id; + let collector_info = + inner.collectors.get(&collector_id).unwrap(); + let client = Client::new( + format!("http://{}", collector_info.address).as_str(), + self.log.clone(), + ); + client.producers_post(&info.into()).await.map_err(|e| { + HttpError::for_internal_error(e.to_string()) + })?; + ProducerAssignment { producer: info.clone(), collector_id } + } + }; + inner.producers.insert(info.id, assignment); + Ok(()) + } + + async fn register_collector( + &self, + info: OximeterInfo, + ) -> Result<(), HttpError> { + // If this is being registered again, send all its assignments again. + let mut inner = self.inner.lock().await; + if inner.collectors.insert(info.collector_id, info).is_some() { + let client = Client::new( + format!("http://{}", info.address).as_str(), + self.log.clone(), + ); + for producer_info in + inner.producers.values().filter_map(|assignment| { + if assignment.collector_id == info.collector_id { + Some(&assignment.producer) + } else { + None + } + }) + { + client.producers_post(&producer_info.into()).await.map_err( + |e| HttpError::for_internal_error(e.to_string()), + )?; + } + } + Ok(()) + } +} + +// Build the HTTP API of the fake Nexus for registration. +pub fn standalone_nexus_api() -> ApiDescription> { + let mut api = ApiDescription::new(); + api.register(cpapi_producers_post) + .expect("Could not register cpapi_producers_post API handler"); + api.register(cpapi_collectors_post) + .expect("Could not register cpapi_collectors_post API handler"); + api +} + +/// Accept a registration from a new metric producer +#[endpoint { + method = POST, + path = "/metrics/producers", + }] +async fn cpapi_producers_post( + request_context: RequestContext>, + producer_info: TypedBody, +) -> Result { + let context = request_context.context(); + let producer_info = producer_info.into_inner(); + context + .register_producer(&producer_info) + .await + .map(|_| HttpResponseUpdatedNoContent()) + .map_err(|e| HttpError::for_internal_error(e.to_string())) +} + +/// Accept a notification of a new oximeter collection server. +#[endpoint { + method = POST, + path = "/metrics/collectors", + }] +async fn cpapi_collectors_post( + request_context: RequestContext>, + oximeter_info: TypedBody, +) -> Result { + let context = request_context.context(); + let oximeter_info = oximeter_info.into_inner(); + context + .register_collector(oximeter_info) + .await + .map(|_| HttpResponseUpdatedNoContent()) + .map_err(|e| HttpError::for_internal_error(e.to_string())) +} + +/// A standalone Nexus server, with APIs only for registering metric collectors +/// and producers. +pub struct Server { + server: HttpServer>, +} + +impl Server { + /// Create a new server listening on the provided address. + pub fn new(address: SocketAddr, log_level: Level) -> Result { + let decorator = slog_term::TermDecorator::new().build(); + let drain = slog_term::FullFormat::new(decorator).build().fuse(); + let drain = slog_async::Async::new(drain).build().fuse(); + let drain = slog::LevelFilter::new(drain, log_level).fuse(); + let (drain, registration) = slog_dtrace::with_drain(drain); + let log = slog::Logger::root(drain.fuse(), o!(FileKv)); + if let slog_dtrace::ProbeRegistration::Failed(e) = registration { + let msg = format!("failed to register DTrace probes: {}", e); + error!(log, "{}", msg); + return Err(Error::Server(msg)); + } else { + debug!(log, "registered DTrace probes"); + } + + let nexus = Arc::new(StandaloneNexus::new( + log.new(slog::o!("component" => "nexus-standalone")), + )); + let server = HttpServerStarter::new( + &ConfigDropshot { bind_address: address, ..Default::default() }, + standalone_nexus_api(), + Arc::clone(&nexus), + &log, + ) + .map_err(|e| Error::Server(e.to_string()))? + .start(); + info!( + log, + "created standalone nexus server for metric collections"; + "address" => %address, + ); + Ok(Self { server }) + } + + pub fn log(&self) -> &Logger { + &self.server.app_private().log + } + + pub fn local_addr(&self) -> SocketAddr { + self.server.local_addr() + } +} diff --git a/oximeter/collector/tests/output/cmd-oximeter-noargs-stderr b/oximeter/collector/tests/output/cmd-oximeter-noargs-stderr index 7b736fe8a1..3f0fd4726d 100644 --- a/oximeter/collector/tests/output/cmd-oximeter-noargs-stderr +++ b/oximeter/collector/tests/output/cmd-oximeter-noargs-stderr @@ -3,9 +3,11 @@ See README.adoc for more information Usage: oximeter Commands: - openapi Print the external OpenAPI Spec document and exit - run Start an Oximeter server - help Print this message or the help of the given subcommand(s) + openapi Print the external OpenAPI Spec document and exit + run Start an Oximeter server + standalone Run `oximeter` in standalone mode for development + standalone-openapi Print the fake Nexus's standalone API + help Print this message or the help of the given subcommand(s) Options: -h, --help Print help diff --git a/oximeter/db/Cargo.toml b/oximeter/db/Cargo.toml index 77bce09db9..ad6d584b1b 100644 --- a/oximeter/db/Cargo.toml +++ b/oximeter/db/Cargo.toml @@ -25,7 +25,7 @@ thiserror.workspace = true tokio = { workspace = true, features = [ "rt-multi-thread", "macros" ] } usdt.workspace = true uuid.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../../workspace-hack" } +omicron-workspace-hack.workspace = true [dev-dependencies] itertools.workspace = true diff --git a/oximeter/instruments/Cargo.toml b/oximeter/instruments/Cargo.toml index 4adff0463a..3653ab8011 100644 --- a/oximeter/instruments/Cargo.toml +++ b/oximeter/instruments/Cargo.toml @@ -12,7 +12,7 @@ oximeter.workspace = true tokio.workspace = true http = { workspace = true, optional = true } uuid.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../../workspace-hack" } +omicron-workspace-hack.workspace = true [features] default = ["http-instruments"] diff --git a/oximeter/oximeter-macro-impl/Cargo.toml b/oximeter/oximeter-macro-impl/Cargo.toml index ff116e1c9d..df9ed547ed 100644 --- a/oximeter/oximeter-macro-impl/Cargo.toml +++ b/oximeter/oximeter-macro-impl/Cargo.toml @@ -12,4 +12,4 @@ proc-macro = true proc-macro2.workspace = true quote.workspace = true syn = { workspace = true, features = [ "full", "extra-traits" ] } -omicron-workspace-hack = { version = "0.1", path = "../../workspace-hack" } +omicron-workspace-hack.workspace = true diff --git a/oximeter/oximeter/Cargo.toml b/oximeter/oximeter/Cargo.toml index b2aa15f85e..7d01b8f8be 100644 --- a/oximeter/oximeter/Cargo.toml +++ b/oximeter/oximeter/Cargo.toml @@ -15,7 +15,7 @@ schemars = { workspace = true, features = [ "uuid1", "bytes", "chrono" ] } serde.workspace = true thiserror.workspace = true uuid.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../../workspace-hack" } +omicron-workspace-hack.workspace = true [dev-dependencies] approx.workspace = true diff --git a/oximeter/producer/Cargo.toml b/oximeter/producer/Cargo.toml index f171f57e8a..ef2f16c8ad 100644 --- a/oximeter/producer/Cargo.toml +++ b/oximeter/producer/Cargo.toml @@ -19,4 +19,8 @@ slog-dtrace.workspace = true tokio.workspace = true thiserror.workspace = true uuid.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../../workspace-hack" } +omicron-workspace-hack.workspace = true + +[dev-dependencies] +anyhow.workspace = true +clap.workspace = true diff --git a/oximeter/producer/examples/producer.rs b/oximeter/producer/examples/producer.rs index 9ff30032ca..dd9722c80a 100644 --- a/oximeter/producer/examples/producer.rs +++ b/oximeter/producer/examples/producer.rs @@ -6,14 +6,17 @@ // Copyright 2023 Oxide Computer Company +use anyhow::Context; use chrono::DateTime; use chrono::Utc; +use clap::Parser; use dropshot::ConfigDropshot; use dropshot::ConfigLogging; use dropshot::ConfigLoggingLevel; use dropshot::HandlerTaskMode; use omicron_common::api::internal::nexus::ProducerEndpoint; use oximeter::types::Cumulative; +use oximeter::types::ProducerRegistry; use oximeter::types::Sample; use oximeter::Metric; use oximeter::MetricsError; @@ -22,9 +25,22 @@ use oximeter::Target; use oximeter_producer::Config; use oximeter_producer::LogConfig; use oximeter_producer::Server; +use std::net::SocketAddr; use std::time::Duration; use uuid::Uuid; +/// Run an example oximeter metric producer. +#[derive(Parser)] +struct Args { + /// The address to use for the producer server. + #[arg(long, default_value = "[::1]:0")] + address: SocketAddr, + + /// The address of nexus at which to register. + #[arg(long, default_value = "[::1]:12221")] + nexus: SocketAddr, +} + /// Example target describing a virtual machine. #[derive(Debug, Clone, Target)] pub struct VirtualMachine { @@ -93,30 +109,29 @@ impl Producer for CpuBusyProducer { } #[tokio::main] -async fn main() { - let address = "[::1]:0".parse().unwrap(); +async fn main() -> anyhow::Result<()> { + let args = Args::parse(); let dropshot = ConfigDropshot { - bind_address: address, + bind_address: args.address, request_body_max_bytes: 2048, default_handler_task_mode: HandlerTaskMode::Detached, }; let log = LogConfig::Config(ConfigLogging::StderrTerminal { level: ConfigLoggingLevel::Debug, }); + let registry = ProducerRegistry::new(); + let producer = CpuBusyProducer::new(4); + registry.register_producer(producer).unwrap(); let server_info = ProducerEndpoint { - id: Uuid::new_v4(), - address, + id: registry.producer_id(), + address: args.address, base_route: "/collect".to_string(), interval: Duration::from_secs(10), }; - let config = Config { - server_info, - registration_address: "[::1]:12221".parse().unwrap(), - dropshot, - log, - }; - let server = Server::start(&config).await.unwrap(); - let producer = CpuBusyProducer::new(4); - server.registry().register_producer(producer).unwrap(); - server.serve_forever().await.unwrap(); + let config = + Config { server_info, registration_address: args.nexus, dropshot, log }; + let server = Server::with_registry(registry, &config) + .await + .context("failed to create producer")?; + server.serve_forever().await.context("server failed") } diff --git a/oximeter/producer/src/lib.rs b/oximeter/producer/src/lib.rs index 01910af8e8..2354f9c217 100644 --- a/oximeter/producer/src/lib.rs +++ b/oximeter/producer/src/lib.rs @@ -40,6 +40,9 @@ pub enum Error { #[error("Error registering as metric producer: {0}")] RegistrationError(String), + + #[error("Producer registry and config UUIDs do not match")] + UuidMismatch, } /// Either configuration for building a logger, or an actual logger already @@ -82,14 +85,59 @@ impl Server { /// Start a new metric server, registering it with the chosen endpoint, and listening for /// requests on the associated address and route. pub async fn start(config: &Config) -> Result { - // Clone mutably, as we may update the address after the server starts, see below. - let mut config = config.clone(); + Self::with_registry( + ProducerRegistry::with_id(config.server_info.id), + &config, + ) + .await + } + + /// Create a new metric producer server, with an existing registry. + pub async fn with_registry( + registry: ProducerRegistry, + config: &Config, + ) -> Result { + Self::new_impl( + registry, + config.server_info.clone(), + &config.registration_address, + &config.dropshot, + &config.log, + ) + .await + } + + /// Serve requests for metrics. + pub async fn serve_forever(self) -> Result<(), Error> { + self.server.await.map_err(Error::Server) + } + + /// Close the server + pub async fn close(self) -> Result<(), Error> { + self.server.close().await.map_err(Error::Server) + } + + /// Return the [`ProducerRegistry`] managed by this server. + /// + /// The registry is thread-safe and clonable, so the returned reference can be used throughout + /// an application to register types implementing the [`Producer`](oximeter::traits::Producer) + /// trait. The samples generated by the registered producers will be included in response to a + /// request on the collection endpoint. + pub fn registry(&self) -> &ProducerRegistry { + &self.registry + } + + /// Return the server's local listening address + pub fn address(&self) -> std::net::SocketAddr { + self.server.local_addr() + } + fn build_logger(log: &LogConfig) -> Result { // Build a logger, either using the configuration or actual logger // provided. First build the base logger from the configuration or a // clone of the provided logger, and then add the DTrace and Dropshot // loggers on top of it. - let base_logger = match config.log { + let base_logger = match log { LogConfig::Config(conf) => conf .to_logger("metric-server") .map_err(|msg| Error::Server(msg.to_string()))?, @@ -104,74 +152,64 @@ impl Server { } else { debug!(log, "registered DTrace probes"); } - let dropshot_log = log.new(o!("component" => "dropshot")); + Ok(log) + } - // Build the producer registry and server that uses it as its context. - let registry = ProducerRegistry::with_id(config.server_info.id); - let server = HttpServerStarter::new( - &config.dropshot, + fn build_dropshot_server( + log: &Logger, + registry: &ProducerRegistry, + dropshot: &ConfigDropshot, + ) -> Result, Error> { + let dropshot_log = log.new(o!("component" => "dropshot")); + HttpServerStarter::new( + dropshot, metric_server_api(), registry.clone(), &dropshot_log, ) - .map_err(|e| Error::Server(e.to_string()))? - .start(); - - // Client code may decide to assign a specific address and/or port, or to listen on any - // available address and port, assigned by the OS. For example, `[::1]:0` would assign any - // port on localhost. If needed, update the address in the `ProducerEndpoint` with the - // actual address the server has bound. - // - // TODO-robustness: Is there a better way to do this? We'd like to support users picking an - // exact address or using whatever's available. The latter is useful during tests or other - // situations in which we don't know which ports are available. - if config.server_info.address != server.local_addr() { - assert_eq!(config.server_info.address.port(), 0); + .map_err(|e| Error::Server(e.to_string())) + .map(HttpServerStarter::start) + } + + // Create a new server registering with Nexus. + async fn new_impl( + registry: ProducerRegistry, + mut server_info: ProducerEndpoint, + registration_address: &SocketAddr, + dropshot: &ConfigDropshot, + log: &LogConfig, + ) -> Result { + if registry.producer_id() != server_info.id { + return Err(Error::UuidMismatch); + } + let log = Self::build_logger(log)?; + let server = Self::build_dropshot_server(&log, ®istry, dropshot)?; + + // Update the producer endpoint address with the actual server's + // address, to handle cases where client listens on any available + // address. + if server_info.address != server.local_addr() { + assert_eq!(server_info.address.port(), 0); debug!( log, "Requested any available port, Dropshot server has been bound to {}", server.local_addr(), ); - config.server_info.address = server.local_addr(); + server_info.address = server.local_addr(); } debug!(log, "registering metric server as a producer"); - register(config.registration_address, &log, &config.server_info) - .await?; + register(*registration_address, &log, &server_info).await?; info!( log, - "starting oximeter metric server"; - "route" => config.server_info.collection_route(), + "starting oximeter metric producer server"; + "route" => server_info.collection_route(), "producer_id" => ?registry.producer_id(), - "address" => config.server_info.address, + "address" => server.local_addr(), + "interval" => ?server_info.interval, ); Ok(Self { registry, server }) } - - /// Serve requests for metrics. - pub async fn serve_forever(self) -> Result<(), Error> { - self.server.await.map_err(Error::Server) - } - - /// Close the server - pub async fn close(self) -> Result<(), Error> { - self.server.close().await.map_err(Error::Server) - } - - /// Return the [`ProducerRegistry`] managed by this server. - /// - /// The registry is thread-safe and clonable, so the returned reference can be used throughout - /// an application to register types implementing the [`Producer`](oximeter::traits::Producer) - /// trait. The samples generated by the registered producers will be included in response to a - /// request on the collection endpoint. - pub fn registry(&self) -> &ProducerRegistry { - &self.registry - } - - /// Return the server's local listening address - pub fn address(&self) -> std::net::SocketAddr { - self.server.local_addr() - } } // Register API endpoints of the `Server`. diff --git a/package-manifest.toml b/package-manifest.toml index c776f6d96d..ff229e5def 100644 --- a/package-manifest.toml +++ b/package-manifest.toml @@ -381,10 +381,10 @@ only_for_targets.image = "standard" # 3. Use source.type = "manual" instead of "prebuilt" source.type = "prebuilt" source.repo = "crucible" -source.commit = "aeb69dda26c7e1a8b6eada425670cd4b83f91c07" +source.commit = "20273bcca1fd5834ebc3e67dfa7020f0e99ad681" # The SHA256 digest is automatically posted to: # https://buildomat.eng.oxide.computer/public/file/oxidecomputer/crucible/image//crucible.sha256.txt -source.sha256 = "3845327bde9df585ee8771c85eefc3e63a48981f14298d5fca62f4f6fe25c917" +source.sha256 = "0671570dfed8bff8e64c42a41269d961426bdd07e72b9ca8c2e3f28e7ead3c1c" output.type = "zone" [package.crucible-pantry] @@ -392,10 +392,10 @@ service_name = "crucible_pantry" only_for_targets.image = "standard" source.type = "prebuilt" source.repo = "crucible" -source.commit = "aeb69dda26c7e1a8b6eada425670cd4b83f91c07" +source.commit = "20273bcca1fd5834ebc3e67dfa7020f0e99ad681" # The SHA256 digest is automatically posted to: # https://buildomat.eng.oxide.computer/public/file/oxidecomputer/crucible/image//crucible-pantry.sha256.txt -source.sha256 = "a3f2fc92d9ae184a66c402dfe33b1d1c128f356d6be70671de421be600d4064a" +source.sha256 = "c35cc24945d047f8d77e438ee606e6a83be64f0f97356fdc3308be716dcf3718" output.type = "zone" # Refer to @@ -406,10 +406,10 @@ service_name = "propolis-server" only_for_targets.image = "standard" source.type = "prebuilt" source.repo = "propolis" -source.commit = "de6369aa45a255f896da0a3ddd2b7152c036a4e9" +source.commit = "42c878b71a58d430dfc306126af5d40ca816d70f" # The SHA256 digest is automatically posted to: # https://buildomat.eng.oxide.computer/public/file/oxidecomputer/propolis/image//propolis-server.sha256.txt -source.sha256 = "182597a153793096826992f499a94be54c746e346a3566802e1fe7e78b2ccf2f" +source.sha256 = "dce4d82bb936e990262abcaa279eee7e33a19930880b23f49fa3851cded18567" output.type = "zone" [package.maghemite] diff --git a/package/Cargo.toml b/package/Cargo.toml index 9fc4610020..b840938db0 100644 --- a/package/Cargo.toml +++ b/package/Cargo.toml @@ -34,7 +34,7 @@ tokio = { workspace = true, features = [ "full" ] } toml.workspace = true topological-sort.workspace = true walkdir.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true [dev-dependencies] expectorate.workspace = true diff --git a/passwords/Cargo.toml b/passwords/Cargo.toml index cbd569ef4c..8adcf75a2e 100644 --- a/passwords/Cargo.toml +++ b/passwords/Cargo.toml @@ -11,7 +11,7 @@ thiserror.workspace = true schemars.workspace = true serde.workspace = true serde_with.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true [dev-dependencies] argon2alt = { package = "rust-argon2", version = "1.0" } diff --git a/rpaths/Cargo.toml b/rpaths/Cargo.toml index 7671be4968..45e6c9b925 100644 --- a/rpaths/Cargo.toml +++ b/rpaths/Cargo.toml @@ -5,4 +5,4 @@ edition = "2021" license = "MPL-2.0" [dependencies] -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true diff --git a/sled-agent-client/Cargo.toml b/sled-agent-client/Cargo.toml index 01c1032a51..b2ed07caba 100644 --- a/sled-agent-client/Cargo.toml +++ b/sled-agent-client/Cargo.toml @@ -15,4 +15,4 @@ reqwest = { workspace = true, features = [ "json", "rustls-tls", "stream" ] } serde.workspace = true slog.workspace = true uuid.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true diff --git a/sled-agent/Cargo.toml b/sled-agent/Cargo.toml index d4ccfc97c8..82d7411d1a 100644 --- a/sled-agent/Cargo.toml +++ b/sled-agent/Cargo.toml @@ -76,7 +76,7 @@ uuid.workspace = true zeroize.workspace = true zone.workspace = true static_assertions.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true [target.'cfg(target_os = "illumos")'.dependencies] opte-ioctl.workspace = true diff --git a/sled-agent/src/sim/sled_agent.rs b/sled-agent/src/sim/sled_agent.rs index e53295f823..42fff355a5 100644 --- a/sled-agent/src/sim/sled_agent.rs +++ b/sled-agent/src/sim/sled_agent.rs @@ -617,14 +617,8 @@ impl SledAgent { ..Default::default() }; let propolis_log = log.new(o!("component" => "propolis-server-mock")); - let config = propolis_server::config::Config { - bootrom: Default::default(), - pci_bridges: Default::default(), - chipset: Default::default(), - devices: Default::default(), - block_devs: Default::default(), - }; - let private = Arc::new(PropolisContext::new(config, propolis_log)); + let private = + Arc::new(PropolisContext::new(Default::default(), propolis_log)); info!(log, "Starting mock propolis-server..."); let dropshot_log = log.new(o!("component" => "dropshot")); let mock_api = propolis_server::mock_server::api(); diff --git a/sled-hardware/Cargo.toml b/sled-hardware/Cargo.toml index 880f93441c..14ae15996b 100644 --- a/sled-hardware/Cargo.toml +++ b/sled-hardware/Cargo.toml @@ -24,7 +24,7 @@ thiserror.workspace = true tofino.workspace = true tokio.workspace = true uuid.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true [target.'cfg(target_os = "illumos")'.dependencies] illumos-devinfo = { git = "https://github.com/oxidecomputer/illumos-devinfo", branch = "main" } diff --git a/sp-sim/Cargo.toml b/sp-sim/Cargo.toml index 2a1ae19468..07d956e41e 100644 --- a/sp-sim/Cargo.toml +++ b/sp-sim/Cargo.toml @@ -21,7 +21,7 @@ sprockets-rot.workspace = true thiserror.workspace = true tokio = { workspace = true, features = [ "full" ] } toml.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true [[bin]] name = "sp-sim" diff --git a/test-utils/Cargo.toml b/test-utils/Cargo.toml index a0227a4de2..9e21f3ca12 100644 --- a/test-utils/Cargo.toml +++ b/test-utils/Cargo.toml @@ -25,7 +25,7 @@ usdt.workspace = true rcgen.workspace = true regex.workspace = true reqwest.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true [dev-dependencies] expectorate.workspace = true diff --git a/tools/console_version b/tools/console_version index dba32c3e94..0c30c707e1 100644 --- a/tools/console_version +++ b/tools/console_version @@ -1,2 +1,2 @@ -COMMIT="af6536d587a17a65398407ca03d364345aa24342" -SHA2="00701652eb1e495fd22409dcdf74ebae2ba081529f65fb41c5ac3a2fef50a149" +COMMIT="0cc1e03a24b3f5da275d15b969978a385d6b3b27" +SHA2="46a186fc3bf919a3aa2871aeab8441e4a13ed134f912b5d76c7ff891fed66cee" diff --git a/tufaceous-lib/Cargo.toml b/tufaceous-lib/Cargo.toml index 8b5c4fa7ca..bcfcee6b9c 100644 --- a/tufaceous-lib/Cargo.toml +++ b/tufaceous-lib/Cargo.toml @@ -32,7 +32,7 @@ toml.workspace = true tough.workspace = true url = "2.4.1" zip.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true [dev-dependencies] omicron-test-utils.workspace = true diff --git a/tufaceous/Cargo.toml b/tufaceous/Cargo.toml index f3e3b815d2..e48513e24c 100644 --- a/tufaceous/Cargo.toml +++ b/tufaceous/Cargo.toml @@ -18,7 +18,7 @@ slog-async.workspace = true slog-envlogger.workspace = true slog-term.workspace = true tufaceous-lib.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true [dev-dependencies] assert_cmd.workspace = true diff --git a/update-engine/Cargo.toml b/update-engine/Cargo.toml index 25ade83f34..af988bf091 100644 --- a/update-engine/Cargo.toml +++ b/update-engine/Cargo.toml @@ -21,7 +21,7 @@ schemars = { workspace = true, features = ["uuid1"] } slog.workspace = true tokio = { workspace = true, features = ["macros", "sync", "time", "rt-multi-thread"] } uuid.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true [dev-dependencies] buf-list.workspace = true diff --git a/wicket-common/Cargo.toml b/wicket-common/Cargo.toml index 229561cd38..b87e742133 100644 --- a/wicket-common/Cargo.toml +++ b/wicket-common/Cargo.toml @@ -13,4 +13,4 @@ serde.workspace = true serde_json.workspace = true thiserror.workspace = true update-engine.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true diff --git a/wicket-dbg/Cargo.toml b/wicket-dbg/Cargo.toml index bc22424c69..e7e8a58468 100644 --- a/wicket-dbg/Cargo.toml +++ b/wicket-dbg/Cargo.toml @@ -22,7 +22,7 @@ wicket.workspace = true # used only by wicket-dbg binary reedline = "0.23.0" -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true [[bin]] name = "wicket-dbg" diff --git a/wicket/Cargo.toml b/wicket/Cargo.toml index 58605c8037..5392e72e9f 100644 --- a/wicket/Cargo.toml +++ b/wicket/Cargo.toml @@ -46,7 +46,7 @@ omicron-passwords.workspace = true update-engine.workspace = true wicket-common.workspace = true wicketd-client.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true [dev-dependencies] assert_cmd.workspace = true diff --git a/wicketd-client/Cargo.toml b/wicketd-client/Cargo.toml index 2d959f1f8d..814309b975 100644 --- a/wicketd-client/Cargo.toml +++ b/wicketd-client/Cargo.toml @@ -18,4 +18,4 @@ slog.workspace = true update-engine.workspace = true uuid.workspace = true wicket-common.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true diff --git a/wicketd/Cargo.toml b/wicketd/Cargo.toml index 6df5e0e4e5..1044e1ff51 100644 --- a/wicketd/Cargo.toml +++ b/wicketd/Cargo.toml @@ -54,7 +54,7 @@ sled-hardware.workspace = true tufaceous-lib.workspace = true update-engine.workspace = true wicket-common.workspace = true -omicron-workspace-hack = { version = "0.1", path = "../workspace-hack" } +omicron-workspace-hack.workspace = true [[bin]] name = "wicketd" diff --git a/workspace-hack/Cargo.toml b/workspace-hack/Cargo.toml index 820b2d2336..8854ef27bc 100644 --- a/workspace-hack/Cargo.toml +++ b/workspace-hack/Cargo.toml @@ -39,6 +39,7 @@ flate2 = { version = "1.0.27" } futures = { version = "0.3.28" } futures-channel = { version = "0.3.28", features = ["sink"] } futures-core = { version = "0.3.28" } +futures-executor = { version = "0.3.28" } futures-io = { version = "0.3.28", default-features = false, features = ["std"] } futures-sink = { version = "0.3.28" } futures-task = { version = "0.3.28", default-features = false, features = ["std"] } @@ -48,9 +49,11 @@ generic-array = { version = "0.14.7", default-features = false, features = ["mor getrandom = { version = "0.2.10", default-features = false, features = ["js", "rdrand", "std"] } hashbrown-582f2526e08bb6a0 = { package = "hashbrown", version = "0.14.0", features = ["raw"] } hashbrown-594e8ee84c453af0 = { package = "hashbrown", version = "0.13.2" } +hashbrown-5ef9efb8ec2df382 = { package = "hashbrown", version = "0.12.3", features = ["raw"] } hex = { version = "0.4.3", features = ["serde"] } hyper = { version = "0.14.27", features = ["full"] } -indexmap = { version = "2.0.0", features = ["serde"] } +indexmap-dff4ba8e3ae991db = { package = "indexmap", version = "1.9.3", default-features = false, features = ["serde-1", "std"] } +indexmap-f595c2ba2a3f28df = { package = "indexmap", version = "2.0.0", features = ["serde"] } inout = { version = "0.1.3", default-features = false, features = ["std"] } ipnetwork = { version = "0.20.0", features = ["schemars"] } itertools = { version = "0.10.5" } @@ -65,11 +68,13 @@ num-integer = { version = "0.1.45", features = ["i128"] } num-iter = { version = "0.1.43", default-features = false, features = ["i128"] } num-traits = { version = "0.2.16", features = ["i128", "libm"] } openapiv3 = { version = "1.0.3", default-features = false, features = ["skip_serializing_defaults"] } +parking_lot = { version = "0.12.1", features = ["send_guard"] } petgraph = { version = "0.6.4", features = ["serde-1"] } +phf_shared = { version = "0.11.2" } postgres-types = { version = "0.2.6", default-features = false, features = ["with-chrono-0_4", "with-serde_json-1", "with-uuid-1"] } ppv-lite86 = { version = "0.2.17", default-features = false, features = ["simd", "std"] } predicates = { version = "3.0.3" } -rand = { version = "0.8.5", features = ["min_const_gen"] } +rand = { version = "0.8.5", features = ["min_const_gen", "small_rng"] } rand_chacha = { version = "0.3.1" } regex = { version = "1.9.5" } regex-automata = { version = "0.3.8", default-features = false, features = ["dfa-onepass", "dfa-search", "hybrid", "meta", "nfa-backtrack", "perf-inline", "perf-literal", "unicode"] } @@ -86,7 +91,7 @@ slog = { version = "2.7.0", features = ["dynamic-keys", "max_level_trace", "rele spin = { version = "0.9.8" } string_cache = { version = "0.8.7" } subtle = { version = "2.5.0" } -syn-dff4ba8e3ae991db = { package = "syn", version = "1.0.109", features = ["extra-traits", "fold", "full", "visit"] } +syn-dff4ba8e3ae991db = { package = "syn", version = "1.0.109", features = ["extra-traits", "fold", "full", "visit", "visit-mut"] } syn-f595c2ba2a3f28df = { package = "syn", version = "2.0.32", features = ["extra-traits", "fold", "full", "visit", "visit-mut"] } textwrap = { version = "0.16.0" } time = { version = "0.3.27", features = ["formatting", "local-offset", "macros", "parsing"] } @@ -94,9 +99,8 @@ tokio = { version = "1.32.0", features = ["full", "test-util"] } tokio-postgres = { version = "0.7.10", features = ["with-chrono-0_4", "with-serde_json-1", "with-uuid-1"] } tokio-stream = { version = "0.1.14", features = ["net"] } toml = { version = "0.7.8" } -toml_datetime = { version = "0.6.3", default-features = false, features = ["serde"] } -toml_edit = { version = "0.19.15", features = ["serde"] } tracing = { version = "0.1.37", features = ["log"] } +tracing-core = { version = "0.1.31" } trust-dns-proto = { version = "0.22.0" } unicode-bidi = { version = "0.3.13" } unicode-normalization = { version = "0.1.22" } @@ -133,6 +137,7 @@ flate2 = { version = "1.0.27" } futures = { version = "0.3.28" } futures-channel = { version = "0.3.28", features = ["sink"] } futures-core = { version = "0.3.28" } +futures-executor = { version = "0.3.28" } futures-io = { version = "0.3.28", default-features = false, features = ["std"] } futures-sink = { version = "0.3.28" } futures-task = { version = "0.3.28", default-features = false, features = ["std"] } @@ -142,9 +147,11 @@ generic-array = { version = "0.14.7", default-features = false, features = ["mor getrandom = { version = "0.2.10", default-features = false, features = ["js", "rdrand", "std"] } hashbrown-582f2526e08bb6a0 = { package = "hashbrown", version = "0.14.0", features = ["raw"] } hashbrown-594e8ee84c453af0 = { package = "hashbrown", version = "0.13.2" } +hashbrown-5ef9efb8ec2df382 = { package = "hashbrown", version = "0.12.3", features = ["raw"] } hex = { version = "0.4.3", features = ["serde"] } hyper = { version = "0.14.27", features = ["full"] } -indexmap = { version = "2.0.0", features = ["serde"] } +indexmap-dff4ba8e3ae991db = { package = "indexmap", version = "1.9.3", default-features = false, features = ["serde-1", "std"] } +indexmap-f595c2ba2a3f28df = { package = "indexmap", version = "2.0.0", features = ["serde"] } inout = { version = "0.1.3", default-features = false, features = ["std"] } ipnetwork = { version = "0.20.0", features = ["schemars"] } itertools = { version = "0.10.5" } @@ -159,11 +166,13 @@ num-integer = { version = "0.1.45", features = ["i128"] } num-iter = { version = "0.1.43", default-features = false, features = ["i128"] } num-traits = { version = "0.2.16", features = ["i128", "libm"] } openapiv3 = { version = "1.0.3", default-features = false, features = ["skip_serializing_defaults"] } +parking_lot = { version = "0.12.1", features = ["send_guard"] } petgraph = { version = "0.6.4", features = ["serde-1"] } +phf_shared = { version = "0.11.2" } postgres-types = { version = "0.2.6", default-features = false, features = ["with-chrono-0_4", "with-serde_json-1", "with-uuid-1"] } ppv-lite86 = { version = "0.2.17", default-features = false, features = ["simd", "std"] } predicates = { version = "3.0.3" } -rand = { version = "0.8.5", features = ["min_const_gen"] } +rand = { version = "0.8.5", features = ["min_const_gen", "small_rng"] } rand_chacha = { version = "0.3.1" } regex = { version = "1.9.5" } regex-automata = { version = "0.3.8", default-features = false, features = ["dfa-onepass", "dfa-search", "hybrid", "meta", "nfa-backtrack", "perf-inline", "perf-literal", "unicode"] } @@ -180,7 +189,7 @@ slog = { version = "2.7.0", features = ["dynamic-keys", "max_level_trace", "rele spin = { version = "0.9.8" } string_cache = { version = "0.8.7" } subtle = { version = "2.5.0" } -syn-dff4ba8e3ae991db = { package = "syn", version = "1.0.109", features = ["extra-traits", "fold", "full", "visit"] } +syn-dff4ba8e3ae991db = { package = "syn", version = "1.0.109", features = ["extra-traits", "fold", "full", "visit", "visit-mut"] } syn-f595c2ba2a3f28df = { package = "syn", version = "2.0.32", features = ["extra-traits", "fold", "full", "visit", "visit-mut"] } textwrap = { version = "0.16.0" } time = { version = "0.3.27", features = ["formatting", "local-offset", "macros", "parsing"] } @@ -189,9 +198,8 @@ tokio = { version = "1.32.0", features = ["full", "test-util"] } tokio-postgres = { version = "0.7.10", features = ["with-chrono-0_4", "with-serde_json-1", "with-uuid-1"] } tokio-stream = { version = "0.1.14", features = ["net"] } toml = { version = "0.7.8" } -toml_datetime = { version = "0.6.3", default-features = false, features = ["serde"] } -toml_edit = { version = "0.19.15", features = ["serde"] } tracing = { version = "0.1.37", features = ["log"] } +tracing-core = { version = "0.1.31" } trust-dns-proto = { version = "0.22.0" } unicode-bidi = { version = "0.3.13" } unicode-normalization = { version = "0.1.22" } @@ -250,6 +258,8 @@ hyper-rustls = { version = "0.24.1" } mio = { version = "0.8.8", features = ["net", "os-ext"] } once_cell = { version = "1.18.0", features = ["unstable"] } rustix = { version = "0.38.9", features = ["fs", "termios"] } +toml_datetime = { version = "0.6.3", default-features = false, features = ["serde"] } +toml_edit = { version = "0.19.15", features = ["serde"] } [target.x86_64-unknown-illumos.build-dependencies] bitflags-f595c2ba2a3f28df = { package = "bitflags", version = "2.4.0", default-features = false, features = ["std"] } @@ -257,5 +267,7 @@ hyper-rustls = { version = "0.24.1" } mio = { version = "0.8.8", features = ["net", "os-ext"] } once_cell = { version = "1.18.0", features = ["unstable"] } rustix = { version = "0.38.9", features = ["fs", "termios"] } +toml_datetime = { version = "0.6.3", default-features = false, features = ["serde"] } +toml_edit = { version = "0.19.15", features = ["serde"] } ### END HAKARI SECTION