Line data Source code
1 : //! Structs representing the JSON formats used in the compute_ctl's HTTP API.
2 :
3 : use std::fmt::Display;
4 :
5 : use chrono::{DateTime, Utc};
6 : use jsonwebtoken::jwk::JwkSet;
7 : use serde::{Deserialize, Serialize, Serializer};
8 :
9 : use crate::privilege::Privilege;
10 : use crate::spec::{ComputeSpec, Database, ExtVersion, PgIdent, Role};
11 :
12 0 : #[derive(Serialize, Debug, Deserialize)]
13 : pub struct GenericAPIError {
14 : pub error: String,
15 : }
16 :
17 : /// All configuration parameters necessary for a compute. When
18 : /// [`ComputeConfig::spec`] is provided, it means that the compute is attached
19 : /// to a tenant. [`ComputeConfig::compute_ctl_config`] will always be provided
20 : /// and contains parameters necessary for operating `compute_ctl` independently
21 : /// of whether a tenant is attached to the compute or not.
22 : ///
23 : /// This also happens to be the body of `compute_ctl`'s /configure request.
24 0 : #[derive(Debug, Deserialize, Serialize)]
25 : pub struct ComputeConfig {
26 : /// The compute spec
27 : pub spec: Option<ComputeSpec>,
28 :
29 : /// The compute_ctl configuration
30 : #[allow(dead_code)]
31 : pub compute_ctl_config: ComputeCtlConfig,
32 : }
33 :
34 : impl From<ControlPlaneConfigResponse> for ComputeConfig {
35 0 : fn from(value: ControlPlaneConfigResponse) -> Self {
36 0 : Self {
37 0 : spec: value.spec,
38 0 : compute_ctl_config: value.compute_ctl_config,
39 0 : }
40 0 : }
41 : }
42 :
43 : #[derive(Debug, Clone, Serialize)]
44 : pub struct ExtensionInstallResponse {
45 : pub extension: PgIdent,
46 : pub version: ExtVersion,
47 : }
48 :
49 : #[derive(Serialize, Default, Debug, Clone)]
50 : #[serde(tag = "status", rename_all = "snake_case")]
51 : pub enum LfcPrewarmState {
52 : #[default]
53 : NotPrewarmed,
54 : Prewarming,
55 : Completed,
56 : Failed {
57 : error: String,
58 : },
59 : }
60 :
61 : #[derive(Serialize, Default, Debug, Clone, PartialEq)]
62 : #[serde(tag = "status", rename_all = "snake_case")]
63 : pub enum LfcOffloadState {
64 : #[default]
65 : NotOffloaded,
66 : Offloading,
67 : Completed,
68 : Failed {
69 : error: String,
70 : },
71 : }
72 :
73 : /// Response of the /status API
74 0 : #[derive(Serialize, Debug, Deserialize)]
75 : #[serde(rename_all = "snake_case")]
76 : pub struct ComputeStatusResponse {
77 : pub start_time: DateTime<Utc>,
78 : pub tenant: Option<String>,
79 : pub timeline: Option<String>,
80 : pub status: ComputeStatus,
81 : #[serde(serialize_with = "rfc3339_serialize")]
82 : pub last_active: Option<DateTime<Utc>>,
83 : pub error: Option<String>,
84 : }
85 :
86 0 : #[derive(Serialize, Clone, Copy, Debug, Deserialize, PartialEq, Eq, Default)]
87 : #[serde(rename_all = "snake_case")]
88 : pub enum TerminateMode {
89 : #[default]
90 : /// wait 30s till returning from /terminate to allow control plane to get the error
91 : Fast,
92 : /// return from /terminate immediately as soon as all components are terminated
93 : Immediate,
94 : }
95 :
96 0 : #[derive(Serialize, Clone, Copy, Debug, Deserialize, PartialEq, Eq)]
97 : #[serde(rename_all = "snake_case")]
98 : pub enum ComputeStatus {
99 : // Spec wasn't provided at start, waiting for it to be
100 : // provided by control-plane.
101 : Empty,
102 : // Compute configuration was requested.
103 : ConfigurationPending,
104 : // Compute node has spec and initial startup and
105 : // configuration is in progress.
106 : Init,
107 : // Compute is configured and running.
108 : Running,
109 : // New spec is being applied.
110 : Configuration,
111 : // Either startup or configuration failed,
112 : // compute will exit soon or is waiting for
113 : // control-plane to terminate it.
114 : Failed,
115 : // Termination requested
116 : TerminationPending { mode: TerminateMode },
117 : // Terminated Postgres
118 : Terminated,
119 : }
120 :
121 0 : #[derive(Deserialize, Serialize)]
122 : pub struct TerminateResponse {
123 : pub lsn: Option<utils::lsn::Lsn>,
124 : }
125 :
126 : impl Display for ComputeStatus {
127 0 : fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128 0 : match self {
129 0 : ComputeStatus::Empty => f.write_str("empty"),
130 0 : ComputeStatus::ConfigurationPending => f.write_str("configuration-pending"),
131 0 : ComputeStatus::Init => f.write_str("init"),
132 0 : ComputeStatus::Running => f.write_str("running"),
133 0 : ComputeStatus::Configuration => f.write_str("configuration"),
134 0 : ComputeStatus::Failed => f.write_str("failed"),
135 0 : ComputeStatus::TerminationPending { .. } => f.write_str("termination-pending"),
136 0 : ComputeStatus::Terminated => f.write_str("terminated"),
137 : }
138 0 : }
139 : }
140 :
141 0 : pub fn rfc3339_serialize<S>(x: &Option<DateTime<Utc>>, s: S) -> Result<S::Ok, S::Error>
142 0 : where
143 0 : S: Serializer,
144 : {
145 0 : if let Some(x) = x {
146 0 : x.to_rfc3339().serialize(s)
147 : } else {
148 0 : s.serialize_none()
149 : }
150 0 : }
151 :
152 : /// Response of the /metrics.json API
153 : #[derive(Clone, Debug, Default, Serialize)]
154 : pub struct ComputeMetrics {
155 : /// Time spent waiting in pool
156 : pub wait_for_spec_ms: u64,
157 :
158 : /// Time spent checking if safekeepers are synced
159 : pub sync_sk_check_ms: u64,
160 :
161 : /// Time spent syncing safekeepers (walproposer.c).
162 : /// In most cases this should be zero.
163 : pub sync_safekeepers_ms: u64,
164 :
165 : /// Time it took to establish a pg connection to the pageserver.
166 : /// This is two roundtrips, so it's a good proxy for compute-pageserver
167 : /// latency. The latency is usually 0.2ms, but it's not safe to assume
168 : /// that.
169 : pub pageserver_connect_micros: u64,
170 :
171 : /// Time to get basebackup from pageserver and write it to disk.
172 : pub basebackup_ms: u64,
173 :
174 : /// Compressed size of basebackup received.
175 : pub basebackup_bytes: u64,
176 :
177 : /// Time spent starting potgres. This includes initialization of shared
178 : /// buffers, preloading extensions, and other pg operations.
179 : pub start_postgres_ms: u64,
180 :
181 : /// Time spent applying pg catalog updates that were made in the console
182 : /// UI. This should be 0 when startup time matters, since cplane tries
183 : /// to do these updates eagerly, and passes the skip_pg_catalog_updates
184 : /// when it's safe to skip this step.
185 : pub config_ms: u64,
186 :
187 : /// Total time, from when we receive the spec to when we're ready to take
188 : /// pg connections.
189 : pub total_startup_ms: u64,
190 : pub load_ext_ms: u64,
191 : pub num_ext_downloaded: u64,
192 : pub largest_ext_size: u64, // these are measured in bytes
193 : pub total_ext_download_size: u64,
194 : }
195 :
196 : #[derive(Clone, Debug, Default, Serialize)]
197 : pub struct CatalogObjects {
198 : pub roles: Vec<Role>,
199 : pub databases: Vec<Database>,
200 : }
201 :
202 0 : #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
203 : pub struct ComputeCtlConfig {
204 : /// Set of JSON web keys that the compute can use to authenticate
205 : /// communication from the control plane.
206 : pub jwks: JwkSet,
207 : pub tls: Option<TlsConfig>,
208 : }
209 :
210 : impl Default for ComputeCtlConfig {
211 0 : fn default() -> Self {
212 0 : Self {
213 0 : jwks: JwkSet {
214 0 : keys: Vec::default(),
215 0 : },
216 0 : tls: None,
217 0 : }
218 0 : }
219 : }
220 :
221 0 : #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
222 : pub struct TlsConfig {
223 : pub key_path: String,
224 : pub cert_path: String,
225 : }
226 :
227 : /// Response of the `/computes/{compute_id}/spec` control-plane API.
228 0 : #[derive(Deserialize, Debug)]
229 : pub struct ControlPlaneConfigResponse {
230 : pub spec: Option<ComputeSpec>,
231 : pub status: ControlPlaneComputeStatus,
232 : pub compute_ctl_config: ComputeCtlConfig,
233 : }
234 :
235 0 : #[derive(Deserialize, Clone, Copy, Debug, PartialEq, Eq)]
236 : #[serde(rename_all = "snake_case")]
237 : pub enum ControlPlaneComputeStatus {
238 : // Compute is known to control-plane, but it's not
239 : // yet attached to any timeline / endpoint.
240 : Empty,
241 : // Compute is attached to some timeline / endpoint and
242 : // should be able to start with provided spec.
243 : Attached,
244 : }
245 :
246 : #[derive(Clone, Debug, Default, Serialize)]
247 : pub struct InstalledExtension {
248 : pub extname: String,
249 : pub version: String,
250 : pub n_databases: u32, // Number of databases using this extension
251 : pub owned_by_superuser: String,
252 : }
253 :
254 : #[derive(Clone, Debug, Default, Serialize)]
255 : pub struct InstalledExtensions {
256 : pub extensions: Vec<InstalledExtension>,
257 : }
258 :
259 : #[derive(Clone, Debug, Default, Serialize)]
260 : pub struct ExtensionInstallResult {
261 : pub extension: PgIdent,
262 : pub version: ExtVersion,
263 : }
264 : #[derive(Clone, Debug, Default, Serialize)]
265 : pub struct SetRoleGrantsResponse {
266 : pub database: PgIdent,
267 : pub schema: PgIdent,
268 : pub privileges: Vec<Privilege>,
269 : pub role: PgIdent,
270 : }
|