1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
use crate::workload_api::error::WorkloadApiError;
use std::fmt;
use thiserror::Error;
/// Errors returned by `X509Source`.
#[derive(Debug, Error)]
pub enum X509SourceError {
/// Failed to retrieve or refresh X.509 material from the source.
#[error("x509 source error: {0}")]
Source(#[from] WorkloadApiError),
/// No SVID could be selected from the received context.
///
/// This can occur when:
/// - The picker rejects all available SVIDs
/// - No default SVID is available and no picker is configured
#[error("no suitable svid found")]
NoSuitableSvid,
/// The source was closed.
#[error("source is closed")]
Closed,
/// The workload API stream ended.
///
/// This error occurs when the gRPC stream from the Workload API terminates
/// normally (returns `None`). This is distinct from `Source(WorkloadApiError)`,
/// which represents actual errors during stream operations.
///
/// **When this occurs:**
/// - The stream reaches end-of-stream (normal termination)
/// - The connection is closed by the server
///
/// **Not to be confused with:**
/// - `Source(WorkloadApiError::EmptyResponse)` - occurs when the API returns
/// empty data, not when the stream ends
/// - `Source(WorkloadApiError::Transport(...))` - occurs for transport-level errors
#[error("workload api stream ended")]
StreamEnded,
/// Resource limit exceeded.
///
/// This error indicates that a received X.509 context exceeds one of the configured
/// resource limits. The error includes the kind of limit, the configured limit, and
/// the actual value that exceeded it.
#[error("resource limit exceeded: {kind} (limit={limit}, actual={actual})")]
ResourceLimitExceeded {
/// The kind of limit that was exceeded.
kind: LimitKind,
/// The configured limit value.
limit: usize,
/// The actual value that exceeded the limit.
actual: usize,
},
/// Shutdown timeout exceeded.
///
/// This error occurs when `shutdown_with_timeout()` is called and the background
/// tasks do not complete within the specified timeout.
#[error("shutdown timeout exceeded")]
ShutdownTimeout,
}
/// The kind of resource limit that was exceeded.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum LimitKind {
/// Maximum number of SVIDs exceeded.
MaxSvids,
/// Maximum number of bundles exceeded.
MaxBundles,
/// Maximum bundle DER bytes exceeded.
MaxBundleDerBytes,
}
impl LimitKind {
/// Returns a stable string representation of the limit kind.
///
/// This is useful for error messages, metrics labels, and logging.
pub const fn as_str(self) -> &'static str {
match self {
Self::MaxSvids => "max_svids",
Self::MaxBundles => "max_bundles",
Self::MaxBundleDerBytes => "max_bundle_der_bytes",
}
}
}
impl fmt::Display for LimitKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
/// Error kinds for structured metrics reporting.
///
/// Use these stable, low-cardinality labels when recording metrics.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum MetricsErrorKind {
/// Failed to create a Workload API client.
ClientCreation,
/// Failed to connect to the Workload API stream.
StreamConnect,
/// Error occurred while reading from the stream.
StreamError,
/// The Workload API stream ended unexpectedly.
StreamEnded,
/// Initial synchronization with the Workload API failed.
InitialSyncFailed,
/// No suitable SVID could be selected from the context.
NoSuitableSvid,
/// Resource limit exceeded: maximum SVID count.
LimitMaxSvids,
/// Resource limit exceeded: maximum bundle count.
LimitMaxBundles,
/// Resource limit exceeded: maximum bundle DER bytes.
LimitMaxBundleDerBytes,
/// An X.509 context update was rejected (validation failed).
UpdateRejected,
/// Failed to join supervisor task during shutdown.
SupervisorJoinFailed,
}
impl MetricsErrorKind {
/// Returns a string representation of the error kind.
///
/// This is useful for metrics systems that require string labels.
pub const fn as_str(self) -> &'static str {
match self {
Self::ClientCreation => "client_creation",
Self::StreamConnect => "stream_connect",
Self::StreamError => "stream_error",
Self::StreamEnded => "stream_ended",
Self::InitialSyncFailed => "initial_sync_failed",
Self::NoSuitableSvid => "no_suitable_svid",
Self::LimitMaxSvids => "limit_max_svids",
Self::LimitMaxBundles => "limit_max_bundles",
Self::LimitMaxBundleDerBytes => "limit_max_bundle_der_bytes",
Self::UpdateRejected => "update_rejected",
Self::SupervisorJoinFailed => "supervisor_join_failed",
}
}
}
impl fmt::Display for MetricsErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}