Adding new endpoints cuts across many concepts in Nexus, from the HTTP interface, authorization, the database, and several tests. This guide attempts to act as a "check-list" when adding new resources.
HTTP
Add endpoint definitions for either the internal or external API
The External API is customer-facing, and provides interfaces for both developers and operators
The Internal API is internal, and provides interfaces for services on the Oxide rack (such as the Sled Agent) to call
Add the corresponding implementations to the respective
http_entrypoints.rsfiles:These endpoints typically call into the Application layer, and do not access the database directly
Inputs and Outputs
Lookup & Authorization
Declare a new resource-to-be-looked-up via
lookup_resource!in lookup.rs (Example)This defines a new struct named after your resource, with some auto-generated methods, including
lookup_for(look up the authz object),fetch_for(look up and return the object), and more
Add helper functions to
LookupPathto make it possible to fetch the resource by either UUID or name (Example)These are often named
pub fn <my_resource>_name, orpub fn <my_resource>_id
Use the
authz_resource!macro to define a newauthz::…structure, which is returned from the Lookup functions (Example)If you define
polar_snippet = InProjectLimitedorpolar_snippet = InProjectFull(for developer resources) orpolar_snippet = FleetChild(for operator resources), most of the polar policy is automatically defined for youIf you define
polar_snippet = Custom, you should edit the omicron.polar file to describe the authorization policy for your object (Example)
Either way, you should add reference the new resource when constructing the Oso structure
Application
Add any "business logic" for the resource to the app directory
This layer bridges the gap between the database and external services.
If your application logic involes any multi-step operations which would be interrupted by Nexus stopping mid-execution (due to reboot, crash, failure, etc), it is recommended to use a saga to define the operations durably.
Database
CREATE TABLEfor the resource in dbinit.sql (Example)Add an equivalent schema for the resource in schema.rs, which allows Diesel to translate raw SQL to rust queries (Example)
Add a Rust representation of the database object to the DB model (Example)
Methods to send queries to the database are defined as part of the datastore structure (Example)
Testing
Authorization
There exists a policy test which compares all Oso objects against an expected policy. New resources are usually added to resources.rs to get coverage.
OpenAPI
Once you’ve added or changed endpoint definitions in
nexus-external-apiornexus-internal-api, you’ll need to update the corresponding OpenAPI documents (the JSON files inopenapi/).To update all OpenAPI documents, run
cargo xtask openapi generate.This does not require you to provide an implementation, or to get either omicron-nexus or omicron-sled-agent to compile: just the definition in the API crate is sufficient.
Integration Tests
Nexus' integration tests are used to cross the HTTP interface for testing. Typically, one file is used "per-resource".
These tests use a simulated Sled Agent, and keep the "Nexus" object in-process, so it can still be accessed and modified for invasive testing.
Saga Tests
Sagas are implictly tested through most integration tests, but they are also subject to more scrutiny, since they must fulfill certain properties (such as idempotency and an ability to unwind). These properties are typically tested in the same file as the saga definition (Example)