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

Filter by extension

Filter by extension


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

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

11 changes: 11 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ members = [
"clients/ddm-admin-client",
"clients/dns-service-client",
"clients/dpd-client",
"clients/ereporter-client",
"clients/gateway-client",
"clients/installinator-client",
"clients/nexus-client",
Expand Down Expand Up @@ -41,6 +42,9 @@ members = [
"dns-server",
"dns-server-api",
"end-to-end-tests",
"ereporter",
"ereporter/api",
"ereporter/test-utils",
"gateway",
"gateway-api",
"gateway-cli",
Expand Down Expand Up @@ -132,6 +136,7 @@ default-members = [
"clients/ddm-admin-client",
"clients/dns-service-client",
"clients/dpd-client",
"clients/ereporter-client",
"clients/gateway-client",
"clients/installinator-client",
"clients/nexus-client",
Expand Down Expand Up @@ -164,6 +169,9 @@ default-members = [
"dns-server",
"dns-server-api",
"end-to-end-tests",
"ereporter",
"ereporter/api",
"ereporter/test-utils",
"gateway",
"gateway-api",
"gateway-cli",
Expand Down Expand Up @@ -346,6 +354,9 @@ dpd-client = { path = "clients/dpd-client" }
dropshot = { version = "0.12.0", features = [ "usdt-probes" ] }
dyn-clone = "1.0.17"
either = "1.13.0"
ereporter-api = { path = "ereporter/api" }
ereporter-client = { path = "clients/ereporter-client" }
ereporter = { path = "ereporter" }
expectorate = "1.1.0"
fatfs = "0.3.6"
filetime = "0.2.25"
Expand Down
19 changes: 19 additions & 0 deletions clients/ereporter-client/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "ereporter-client"
version = "0.1.0"
edition = "2021"
license = "MPL-2.0"

[lints]
workspace = true

[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.workspace = true
32 changes: 32 additions & 0 deletions clients/ereporter-client/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// 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/.

// Copyright 2021 Oxide Computer Company

//! Interface for API requests to an ereporter.

progenitor::generate_api!(
spec = "../../openapi/ereporter.json",
inner_type = slog::Logger,
pre_hook = (|log: &slog::Logger, request: &reqwest::Request| {
slog::debug!(log, "client request";
"method" => %request.method(),
"uri" => %request.url(),
"body" => ?&request.body(),
);
}),
post_hook = (|log: &slog::Logger, result: &Result<_, _>| {
slog::debug!(log, "client response"; "result" => ?result);
}),

replace = {
Generation = omicron_common::api::external::Generation,
},
);

impl omicron_common::api::external::ClientError for types::Error {
fn message(&self) -> String {
self.message.clone()
}
}
1 change: 1 addition & 0 deletions dev-tools/openapi-manager/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ cockroach-admin-api.workspace = true
clap.workspace = true
dns-server-api.workspace = true
dropshot.workspace = true
ereporter-api.workspace = true
fs-err.workspace = true
gateway-api.workspace = true
indent_write.workspace = true
Expand Down
9 changes: 9 additions & 0 deletions dev-tools/openapi-manager/src/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,15 @@ pub fn all_apis() -> Vec<ApiSpec> {
filename: "wicketd.json",
extra_validation: None,
},
ApiSpec {
title: "Oxide Error Reporter API",
version: "0.0.1",
description: "API for collecting error reports from a reporter.",
boundary: ApiBoundary::Internal,
api_description: ereporter_api::ereporter_api_mod::stub_api_description,
filename: "ereporter.json",
extra_validation: None,
},
// Add your APIs here! Please keep this list sorted by filename.
]
}
Expand Down
31 changes: 31 additions & 0 deletions ereporter/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[package]
name = "ereporter"
version = "0.1.0"
edition = "2021"

[dependencies]
anyhow.workspace = true
chrono.workspace = true
dropshot.workspace = true
ereporter-api.workspace = true
internal-dns.workspace = true
nexus-client.workspace = true
omicron-common.workspace = true
omicron-workspace-hack.workspace = true
uuid.workspace = true
serde.workspace = true
slog.workspace = true
slog-error-chain.workspace = true
slog-dtrace.workspace = true
thiserror.workspace = true
tokio = { workspace = true, features = ["sync"] }

[dev-dependencies]
clap.workspace = true
slog.workspace = true
slog-async.workspace = true
slog-term.workspace = true
tokio = { workspace = true, features = ["sync", "macros", "full"] }

[lints]
workspace = true
16 changes: 16 additions & 0 deletions ereporter/api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "ereporter-api"
version = "0.1.0"
edition = "2021"

[lints]
workspace = true

[dependencies]
chrono.workspace = true
dropshot.workspace = true
omicron-common.workspace = true
omicron-workspace-hack.workspace = true
schemars.workspace = true
serde.workspace = true
uuid.workspace = true
105 changes: 105 additions & 0 deletions ereporter/api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use chrono::{DateTime, Utc};
use dropshot::{
EmptyScanParams, HttpError, HttpResponseDeleted, HttpResponseOk,
PaginationParams, Query, RequestContext, ResultsPage,
};
use omicron_common::api::external::Generation;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use uuid::Uuid;

#[dropshot::api_description]
pub trait EreporterApi {
type Context;

/// Get a list of ereports from `reporter_id`, paginated by sequence number.
#[endpoint {
method = GET,
path = "/ereports/{reporter_id}"
}]
async fn ereports_list(
request_context: RequestContext<Self::Context>,
path: dropshot::Path<ListPathParams>,
query: Query<PaginationParams<EmptyScanParams, Generation>>,
) -> Result<HttpResponseOk<ResultsPage<Entry>>, HttpError>;

/// Informs the reporter with the given `reporter_id` that its ereports up
/// to the sequence number `seq` have been successfully ingested into
/// persistant storage.
///
/// The reporter may now freely discard ereports with sequence numbers less
/// than or equal to `seq`.
#[endpoint {
method = DELETE,
path = "/ereports/{reporter_id}/{seq}"
}]
async fn ereports_acknowledge(
request_context: RequestContext<Self::Context>,
path: dropshot::Path<AcknowledgePathParams>,
) -> Result<HttpResponseDeleted, HttpError>;
}

/// Path parameters to the [`EreporterApi::ereports_list`] endpoint.
#[derive(Clone, Copy, Debug, Deserialize, JsonSchema, Serialize)]
pub struct ListPathParams {
pub reporter_id: Uuid,
}

/// Path parameters to the [`EreporterApi::ereports_acknowledge`] endpoint.
#[derive(Clone, Copy, Debug, Deserialize, JsonSchema, Serialize)]
pub struct AcknowledgePathParams {
pub reporter_id: Uuid,
pub seq: Generation,
}

/// An entry in the ereport batch returned by a reporter.
#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)]
pub struct Entry {
/// UUID of the entity that generated this ereport.
pub reporter_id: Uuid,
/// The ereport's sequence number, unique with regards to ereports generated
/// by the entity with the `reporter_id`.
pub seq: Generation,

pub value: EntryKind,
}

/// Kinds of entry in a batch.
#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum EntryKind {
/// An ereport.
Ereport(Ereport),
/// Ereports may have been lost.
DataLoss {
/// The number of ereports that were discarded, if it is known.
///
/// If ereports are dropped because a buffer has reached its capacity,
/// the reporter is strongly encouraged to attempt to count the number
/// of ereports lost. In other cases, such as a reporter crashing and
/// restarting, the reporter may not be capable of determining the
/// number of ereports that were lost, or even *if* data loss actually
/// occurred. Therefore, a `None` here indicates *possible* data loss,
/// while a `Some(u32)` indicates *known* data loss.
dropped: Option<u32>,
},
}

/// An error report.
#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)]
pub struct Ereport {
/// A string indicating the kind of ereport.
///
/// This may be used by diagnosis engines as an indication of what `facts`
/// to expect.
pub class: String,
/// The UTC timestamp when this ereport was observed, as determined by the reporter.
pub time_created: DateTime<Utc>,
/// The set of facts (key-value pairs) associated with this ereport.
pub facts: HashMap<String, String>,
}
Loading
Loading