Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

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

12 changes: 12 additions & 0 deletions common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,18 @@ pub fn now_db_precision() -> chrono::DateTime<chrono::Utc> {
ts - std::time::Duration::from_nanos(u64::from(only_nanos))
}

/// The base release version of the Oxide software.
///
/// Under current policy, each new release is a major version bump, and
/// generally referred to only by the major version (e.g. 8.0.0 is referred
/// to as "v8", "version 8", or "release 8" to customers). The use of semantic
/// versioning is mostly to hedge for perhaps wanting something more granular in
/// the future.
///
/// This is used in the releng build process (which appends build metadata to
/// produce the full version string) and by the system version API endpoint.
pub const RELEASE_VERSION: semver::Version = semver::Version::new(19, 0, 0);

pub const OMICRON_DPD_TAG: &str = "omicron";

/// A wrapper struct that does nothing other than elide the inner value from
Expand Down
1 change: 1 addition & 0 deletions dev-tools/releng/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ clap.workspace = true
fs-err = { workspace = true, features = ["tokio"] }
futures.workspace = true
hex.workspace = true
omicron-common.workspace = true
omicron-pins.workspace = true
omicron-workspace-hack.workspace = true
omicron-zone-package.workspace = true
Expand Down
13 changes: 1 addition & 12 deletions dev-tools/releng/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ use clap::Parser;
use fs_err::tokio as fs;
use omicron_zone_package::config::Config;
use omicron_zone_package::config::PackageName;
use semver::Version;
use slog::Drain;
use slog::Logger;
use slog::debug;
Expand All @@ -38,16 +37,6 @@ use tokio::sync::Semaphore;
use crate::cmd::Command;
use crate::job::Jobs;

/// The base version we're currently building. Build information is appended to
/// this later on.
///
/// Under current policy, each new release is a major version bump, and
/// generally referred to only by the major version (e.g. 8.0.0 is referred
/// to as "v8", "version 8", or "release 8" to customers). The use of semantic
/// versioning is mostly to hedge for perhaps wanting something more granular in
/// the future.
const BASE_VERSION: Version = Version::new(19, 0, 0);

const RETRY_ATTEMPTS: usize = 3;

#[derive(Debug, Clone, Copy)]
Expand Down Expand Up @@ -249,7 +238,7 @@ async fn main() -> Result<()> {
.trim()
.to_owned();

let mut version = BASE_VERSION.clone();
let mut version = omicron_common::RELEASE_VERSION.clone();
// Differentiate between CI and local builds. We use `0.word` as the
// prerelease field because it comes before `alpha`.
version.pre =
Expand Down
1 change: 1 addition & 0 deletions nexus/external-api/output/nexus_tags.txt
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ user_builtin_view GET /v1/system/users-builtin/{user
API operations found with tag "system/status"
OPERATION ID METHOD URL PATH
ping GET /v1/ping
system_version GET /v1/system/version

API operations found with tag "system/subnet-pools"
OPERATION ID METHOD URL PATH
Expand Down
21 changes: 21 additions & 0 deletions nexus/external-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ api_versions!([
// | date-based version should be at the top of the list.
// v
// (next_yyyy_mm_dd_nn, IDENT),
(2026_04_08_00, SYSTEM_VERSION),
(2026_03_25_00, SUBNET_POOL_UTILIZATION_REMAINING),
(2026_03_24_00, ADD_ICMPV6_FIREWALL_SUPPORT),
(2026_03_23_00, RENAME_PREFIX_LEN),
Expand Down Expand Up @@ -397,6 +398,26 @@ pub trait NexusExternalApi {
}))
}

// No auth required: this only returns hard-coded values.

/// Fetch system version
///
/// Returns the current version of the Oxide software running on this
/// system.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Probably want to word this more precisely.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think the term "Oxide software" could mean many things. For the purpose of this endpoint do you want the API version specifically?

During an update several components will be on different versions during a period of time. This endpoint may confuse our customers during this period thinking all components are running on said version, when in reality they might not be. Because of this, I would not call it "system version".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I mostly mean API version. The most literal thing is "API and control plane version" — my understanding is that these are the same thing because when we do handoff from old Nexus to new Nexus, we are switching to the new API and to the new control plane at the same time.

I don't want users to have to see or think about what we normally call the API version — 2026032500.0.0. We use it internally because we have many API versions between releases, but for the end user, API versions correspond 1-1 with release numbers like v19. I want to key the versioned docs off the release number and turn 2026032500.0.0 into more of an implementation detail. This might mean we should add the release version to the OpenAPI schema metadata.

So I agree that "system version" is not a good name for this endpoint at all. "API version" is better. "Control plane version" is pretty direct, but I am worried about the user being able to infer everything they should from that. It's probably a lot easier to infer from "API version" that it doesn't just mean the API shape but also the machinery powering the API. So "API version" is the best option to me so far.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

+1 on "API version". I don't love "control plane version", because during an update, "the control plane" is on a mix of versions. But because of the way Nexus handoff is structured, there's always exactly one "API version".

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I agree, "API version" is the most correct term

#[endpoint {
method = GET,
path = "/v1/system/version",
tags = ["system/status"],
versions = VERSION_SYSTEM_VERSION..,
}]
async fn system_version(
_rqctx: RequestContext<Self::Context>,
) -> Result<HttpResponseOk<latest::system::SystemVersion>, HttpError> {
Ok(HttpResponseOk(latest::system::SystemVersion {
version: omicron_common::RELEASE_VERSION,
}))
}

/// Fetch top-level IAM policy
#[endpoint {
method = GET,
Expand Down
10 changes: 10 additions & 0 deletions nexus/tests/integration_tests/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -557,3 +557,13 @@ async fn test_ping(cptestctx: &ControlPlaneTestContext) {
.await;
assert_eq!(health.status, system::PingStatus::Ok);
}

#[nexus_test]
async fn test_system_version(cptestctx: &ControlPlaneTestContext) {
let client = &cptestctx.external_client;

let version = NexusRequest::object_get(client, "/v1/system/version")
.execute_and_parse_unwrap::<system::SystemVersion>()
.await;
assert_eq!(version.version, omicron_common::RELEASE_VERSION);
}
1 change: 1 addition & 0 deletions nexus/tests/output/uncovered-authz-endpoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ console_settings_page (get "/settings/{path}")
console_system_page (get "/system/{path}")
console_silo_utilization (get "/utilization")
ping (get "/v1/ping")
system_version (get "/v1/system/version")
device_auth_request (post "/device/auth")
device_auth_confirm (post "/device/confirm")
device_access_token (post "/device/token")
Expand Down
1 change: 1 addition & 0 deletions nexus/types/versions/src/latest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@ pub mod system {
pub use crate::v2025_11_20_00::system::AllowListUpdate;
pub use crate::v2025_11_20_00::system::Ping;
pub use crate::v2025_11_20_00::system::PingStatus;
pub use crate::v2026_04_05_00::system::SystemVersion;
}

pub mod timeseries {
Expand Down
2 changes: 2 additions & 0 deletions nexus/types/versions/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,5 @@ pub mod v2026_03_14_00;
pub mod v2026_03_23_00;
#[path = "subnet_pool_utilization_remaining/mod.rs"]
pub mod v2026_03_25_00;
#[path = "system_version/mod.rs"]
pub mod v2026_04_05_00;
9 changes: 9 additions & 0 deletions nexus/types/versions/src/system_version/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// 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/.

//! Version `SYSTEM_VERSION` of the Nexus external API.
//!
//! Adds the `SystemVersion` type for the new `/v1/system/version` endpoint.

pub mod system;
15 changes: 15 additions & 0 deletions nexus/types/versions/src/system_version/system.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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/.

//! System types for version `SYSTEM_VERSION`.

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

/// The version of the Oxide software running on this system.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct SystemVersion {
/// The current system software version
pub version: semver::Version,
}
1 change: 1 addition & 0 deletions openapi/nexus/nexus-2026032500.0.0-cf1221.json.gitstub
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
254a0c51bc0beecb79c8a9dfccce8e7bc35b5ca4:openapi/nexus/nexus-2026032500.0.0-cf1221.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://oxide.computer",
"email": "api@oxide.computer"
},
"version": "2026032500.0.0"
"version": "2026040800.0.0"
},
"paths": {
"/device/auth": {
Expand Down Expand Up @@ -13418,6 +13418,34 @@
}
}
},
"/v1/system/version": {
"get": {
"tags": [
"system/status"
],
"summary": "Fetch system version",
"description": "Returns the current version of the Oxide software running on this system.",
"operationId": "system_version",
"responses": {
"200": {
"description": "successful operation",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SystemVersion"
}
}
}
},
"4XX": {
"$ref": "#/components/responses/Error"
},
"5XX": {
"$ref": "#/components/responses/Error"
}
}
}
},
"/v1/timeseries/query": {
"post": {
"tags": [
Expand Down Expand Up @@ -29361,6 +29389,20 @@
"vlan_id"
]
},
"SystemVersion": {
"description": "The version of the Oxide software running on this system.",
"type": "object",
"properties": {
"version": {
"description": "The current system software version",
"type": "string",
"pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$"
}
},
"required": [
"version"
]
},
"TargetRelease": {
"description": "View of a system software target release",
"type": "object",
Expand Down
2 changes: 1 addition & 1 deletion openapi/nexus/nexus-latest.json
Loading