From 56d53485d55be0f426b71060eece6931595e5f99 Mon Sep 17 00:00:00 2001 From: MasterPtato Date: Sat, 2 Nov 2024 00:03:53 +0000 Subject: [PATCH] feat: add fdb pool --- .../api/traefik-provider/src/route/core.rs | 2 +- .../config/server/rivet/cluster_provision.rs | 22 + .../src/config/server/rivet/dc_provision.rs | 1 + packages/infra/client/Cargo.lock | 92 +- .../infra/client/container-runner/Dockerfile | 2 +- packages/infra/client/echo/Dockerfile | 6 +- .../infra/client/isolate-v8-runner/Cargo.toml | 1 + .../infra/client/isolate-v8-runner/Dockerfile | 6 +- .../isolate-v8-runner/Dockerfile.dockerignore | 6 +- .../client/isolate-v8-runner/src/isolate.rs | 18 +- .../client/isolate-v8-runner/src/main.rs | 6 + .../client/isolate-v8-runner/src/utils.rs | 31 + packages/infra/client/manager/tests/common.rs | 74 +- .../infra/client/manager/tests/fdb.cluster | 1 + .../src/ops/datacenter/topology_get.rs | 2 +- packages/services/cluster/src/types.rs | 3 + .../cluster/src/workflows/datacenter/scale.rs | 121 +- .../src/workflows/datacenter/tls_issue.rs | 2 - .../services/cluster/src/workflows/prebake.rs | 1 + .../cluster/src/workflows/server/drain.rs | 2 +- .../install/install_scripts/components/fdb.rs | 19 + .../install/install_scripts/components/mod.rs | 7 + .../install_scripts/files/fdb_configure.sh | 8 + .../install_scripts/files/fdb_install.sh | 50 + .../files/fdp_prometheus_proxy.py | 1019 +++++++++++++++++ .../server/install/install_scripts/mod.rs | 14 + .../cluster/src/workflows/server/mod.rs | 30 +- .../cluster/src/workflows/server/undrain.rs | 2 +- .../standalone/metrics-publish/src/lib.rs | 31 +- packages/services/linode/src/types.rs | 3 + packages/services/pegboard/src/protocol.rs | 1 - sdks/full/openapi/openapi.yml | 199 +++- sdks/full/openapi_compat/openapi.yml | 241 ++++ sdks/full/typescript/archive.tgz | 4 +- .../resources/common/types/PoolType.d.ts | 3 +- .../resources/common/types/PoolType.d.ts | 2 +- sdks/runtime/typescript/archive.tgz | 4 +- 37 files changed, 1895 insertions(+), 141 deletions(-) create mode 100644 packages/infra/client/manager/tests/fdb.cluster create mode 100644 packages/services/cluster/src/workflows/server/install/install_scripts/components/fdb.rs create mode 100644 packages/services/cluster/src/workflows/server/install/install_scripts/files/fdb_configure.sh create mode 100644 packages/services/cluster/src/workflows/server/install/install_scripts/files/fdb_install.sh create mode 100644 packages/services/cluster/src/workflows/server/install/install_scripts/files/fdp_prometheus_proxy.py diff --git a/packages/api/traefik-provider/src/route/core.rs b/packages/api/traefik-provider/src/route/core.rs index 528921e988..7ac4c03ab1 100644 --- a/packages/api/traefik-provider/src/route/core.rs +++ b/packages/api/traefik-provider/src/route/core.rs @@ -35,7 +35,7 @@ pub async fn config( let config = types::TraefikConfigResponse::default(); // Fetch configs and catch any errors - // build_cdn(&ctx, &mut config).await?; + build_cdn(&ctx, &mut config).await?; // tracing::info!( // http_services = ?config.http.services.len(), diff --git a/packages/common/config/src/config/server/rivet/cluster_provision.rs b/packages/common/config/src/config/server/rivet/cluster_provision.rs index 43b66a3e74..e4bbbbd3cf 100644 --- a/packages/common/config/src/config/server/rivet/cluster_provision.rs +++ b/packages/common/config/src/config/server/rivet/cluster_provision.rs @@ -35,6 +35,7 @@ pub struct ClusterPools { pub pegboard: ClusterPoolPegboard, pub gg: ClusterPoolGg, pub ats: ClusterPoolAts, + pub fdb: ClusterPoolFdb, } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -218,6 +219,27 @@ impl ClusterPoolAts { } } +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "snake_case", deny_unknown_fields)] +pub struct ClusterPoolFdb { + pub vlan_ip_net: Option, + pub firewall_rules: Option>, +} + +impl ClusterPoolFdb { + pub fn vlan_ip_net(&self) -> Ipv4Net { + Ipv4Net::new(Ipv4Addr::new(10, 0, 2, 0), 26).unwrap() + } + + pub fn vlan_addr_range(&self) -> Ipv4AddrRange { + self.vlan_ip_net().hosts() + } + + pub fn firewall_rules(&self) -> Vec { + FirewallRule::base_rules() + } +} + #[derive(Debug, Serialize, Deserialize, Clone)] pub struct FirewallRule { pub label: String, diff --git a/packages/common/config/src/config/server/rivet/dc_provision.rs b/packages/common/config/src/config/server/rivet/dc_provision.rs index 99b0110f1f..af4a8cdc3f 100644 --- a/packages/common/config/src/config/server/rivet/dc_provision.rs +++ b/packages/common/config/src/config/server/rivet/dc_provision.rs @@ -34,6 +34,7 @@ pub enum PoolType { Ats, Pegboard, PegboardIsolate, + Fdb, } #[derive(Debug, Serialize, Deserialize, Clone)] diff --git a/packages/infra/client/Cargo.lock b/packages/infra/client/Cargo.lock index 299f2bde56..4a69d9618e 100644 --- a/packages/infra/client/Cargo.lock +++ b/packages/infra/client/Cargo.lock @@ -261,6 +261,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + [[package]] name = "async-stream" version = "0.3.6" @@ -438,6 +449,26 @@ dependencies = [ "which 4.4.2", ] +[[package]] +name = "bindgen" +version = "0.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "log", + "prettyplease 0.2.24", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.82", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -2348,6 +2379,58 @@ dependencies = [ "serde", ] +[[package]] +name = "foundationdb" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c30b4254cb4c01b50a74dad6aa48e17666726300b3adddb3c959a7e852f286bd" +dependencies = [ + "async-recursion", + "async-trait", + "foundationdb-gen", + "foundationdb-macros", + "foundationdb-sys", + "futures", + "memchr", + "rand", + "serde", + "serde_bytes", + "serde_json", + "static_assertions", + "uuid", +] + +[[package]] +name = "foundationdb-gen" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93b9deedf92107a1076e518d60931b6ed1a632ae0c51e7b491bfd42edb4148ce" +dependencies = [ + "xml-rs", +] + +[[package]] +name = "foundationdb-macros" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc84a5ff0dba78222551017f5625f3365aa09551c78cfaa44136fc6818c2611" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", + "try_map", +] + +[[package]] +name = "foundationdb-sys" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bae14dba30b8dcc4905a9189ebb18bc9db9744ef0ad8f2b94ef00d21e176964" +dependencies = [ + "bindgen 0.70.1", + "libc", +] + [[package]] name = "fqdn" version = "0.3.12" @@ -4083,6 +4166,7 @@ dependencies = [ "deno_ast", "deno_core", "deno_runtime", + "foundationdb", "futures-util", "netif", "nix 0.27.1", @@ -6810,6 +6894,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "try_map" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb1626d07cb5c1bb2cf17d94c0be4852e8a7c02b041acec9a8c5bdda99f9d580" + [[package]] name = "tungstenite" version = "0.23.0" @@ -7051,7 +7141,7 @@ version = "0.106.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a381badc47c6f15acb5fe0b5b40234162349ed9d4e4fd7c83a7f5547c0fc69c5" dependencies = [ - "bindgen", + "bindgen 0.69.5", "bitflags 2.6.0", "fslock", "gzip-header", diff --git a/packages/infra/client/container-runner/Dockerfile b/packages/infra/client/container-runner/Dockerfile index 8886a88a66..3d80fcadb6 100644 --- a/packages/infra/client/container-runner/Dockerfile +++ b/packages/infra/client/container-runner/Dockerfile @@ -3,4 +3,4 @@ FROM clux/muslrust:1.81.0-stable WORKDIR /app COPY Cargo.toml . COPY src/ src/ -RUN cargo build --release --bin container-runner +RUN cargo build --release --bin rivet-container-runner diff --git a/packages/infra/client/echo/Dockerfile b/packages/infra/client/echo/Dockerfile index 0642108b15..f85b479bb6 100644 --- a/packages/infra/client/echo/Dockerfile +++ b/packages/infra/client/echo/Dockerfile @@ -2,7 +2,7 @@ FROM clux/muslrust:1.81.0-stable AS rust WORKDIR /app COPY . . -RUN cargo build --release --bin echo-server +RUN cargo build --release --bin pegboard-echo-server # Create non-root user and group RUN useradd -m -d /home/nonroot -s /bin/sh nonroot @@ -18,9 +18,9 @@ FROM scratch COPY --from=rust /passwd /etc/passwd COPY --from=rust /group /etc/group -COPY --from=rust /app/target/x86_64-unknown-linux-musl/release/echo-server /echo-server +COPY --from=rust /app/target/x86_64-unknown-linux-musl/release/pegboard-echo-server /pegboard-echo-server # Switch to the non-root user USER nonroot -CMD ["/echo-server"] +CMD ["/pegboard-echo-server"] diff --git a/packages/infra/client/isolate-v8-runner/Cargo.toml b/packages/infra/client/isolate-v8-runner/Cargo.toml index 7a85a01d6f..d217c340f6 100644 --- a/packages/infra/client/isolate-v8-runner/Cargo.toml +++ b/packages/infra/client/isolate-v8-runner/Cargo.toml @@ -13,6 +13,7 @@ path = "src/main.rs" anyhow = "1.0.79" deno_ast = "0.42.1" deno_core = "0.312.0" +foundationdb = {version = "0.9.1", features = [ "fdb-7_1", "embedded-fdb-include" ] } futures-util = { version = "0.3" } netif = "0.1.6" nix = { version = "0.27", default-features = false, features = ["user", "signal"] } diff --git a/packages/infra/client/isolate-v8-runner/Dockerfile b/packages/infra/client/isolate-v8-runner/Dockerfile index 8d1383c7fa..0602a33903 100644 --- a/packages/infra/client/isolate-v8-runner/Dockerfile +++ b/packages/infra/client/isolate-v8-runner/Dockerfile @@ -5,6 +5,10 @@ WORKDIR /app COPY packages/infra/client/runner-protocol/ runner-protocol/ COPY packages/infra/client/isolate-v8-runner/Cargo.toml isolate-v8-runner/Cargo.toml COPY packages/infra/client/isolate-v8-runner/src/ isolate-v8-runner/src/ + +# Installs shared libs +RUN apt-get update && apt-get install -y libclang-dev + RUN \ --mount=type=cache,target=/root/.cargo/git \ --mount=type=cache,target=/root/.cargo/registry \ @@ -12,7 +16,7 @@ RUN \ cd isolate-v8-runner && \ cargo build --release --target x86_64-unknown-linux-gnu && \ mkdir -p /app/dist && \ - mv /app/isolate-v8-runner/target/x86_64-unknown-linux-gnu/release/isolate-v8-runner /app/dist/isolate-v8-runner + mv /app/isolate-v8-runner/target/x86_64-unknown-linux-gnu/release/rivet-isolate-v8-runner /app/dist/rivet-isolate-v8-runner # Create an empty image and copy binaries into it to minimize the size of the image FROM scratch diff --git a/packages/infra/client/isolate-v8-runner/Dockerfile.dockerignore b/packages/infra/client/isolate-v8-runner/Dockerfile.dockerignore index 58b2619df0..1d1a406700 100644 --- a/packages/infra/client/isolate-v8-runner/Dockerfile.dockerignore +++ b/packages/infra/client/isolate-v8-runner/Dockerfile.dockerignore @@ -1,6 +1,6 @@ * -!packages/infra/pegboard/runner-protocol -!packages/infra/pegboard/v8-isolate-runner/Cargo.toml -!packages/infra/pegboard/v8-isolate-runner/src +!packages/infra/client/runner-protocol +!packages/infra/client/isolate-v8-runner/Cargo.toml +!packages/infra/client/isolate-v8-runner/src diff --git a/packages/infra/client/isolate-v8-runner/src/isolate.rs b/packages/infra/client/isolate-v8-runner/src/isolate.rs index d6400d51f5..b9d3b46772 100644 --- a/packages/infra/client/isolate-v8-runner/src/isolate.rs +++ b/packages/infra/client/isolate-v8-runner/src/isolate.rs @@ -4,6 +4,7 @@ use std::{ os::fd::FromRawFd, path::{Path, PathBuf}, rc::Rc, + result::Result::{Err, Ok}, sync::{mpsc, Arc}, }; @@ -20,7 +21,7 @@ use nix::{libc, unistd::pipe}; use tokio::{fs, sync::watch}; use uuid::Uuid; -use crate::{config::Config, log_shipper}; +use crate::{config::Config, utils, log_shipper}; pub fn run(actors_path: PathBuf, actor_id: Uuid, stop_rx: watch::Receiver<()>) -> Result<()> { let actor_path = actors_path.join(actor_id.to_string()); @@ -121,6 +122,21 @@ async fn run_inner( ) -> Result { println!("{actor_id}: Starting isolate"); + // let db = utils::fdb_handle()?; + + // db.run(|trx, _maybe_committed| async move { + // trx.set(b"hello", b"world"); + + // Ok(()) + // }) + // .await?; + + // let res = db + // .run(|trx, _maybe_committed| async move { Ok(trx.get(b"hello", false).await?) }) + // .await?; + + // println!("{actor_id}: hello {:?}", std::str::from_utf8(&res.unwrap())?); + // Load script into a static module loader. No dynamic scripts can be loaded this way. let script_content = fs::read_to_string(actor_path.join("index.js")) .await diff --git a/packages/infra/client/isolate-v8-runner/src/main.rs b/packages/infra/client/isolate-v8-runner/src/main.rs index 4e98ae8538..50b3a86ebd 100644 --- a/packages/infra/client/isolate-v8-runner/src/main.rs +++ b/packages/infra/client/isolate-v8-runner/src/main.rs @@ -7,6 +7,8 @@ use std::{ use anyhow::*; use deno_runtime::deno_core::{v8_set_flags, JsRuntime}; +use deno_runtime::deno_core::JsRuntime; +use foundationdb as fdb; use futures_util::{stream::SplitStream, StreamExt}; use tokio::{ fs, @@ -36,6 +38,10 @@ async fn main() -> Result<()> { redirect_logs(working_path.join("log")).await?; + // Start FDB network thread + let _network = unsafe { fdb::boot() }; + tokio::spawn(utils::fdb_health_check()); + // Write PID to file fs::write( working_path.join("pid"), diff --git a/packages/infra/client/isolate-v8-runner/src/utils.rs b/packages/infra/client/isolate-v8-runner/src/utils.rs index 0da042ebdf..4c022f484b 100644 --- a/packages/infra/client/isolate-v8-runner/src/utils.rs +++ b/packages/infra/client/isolate-v8-runner/src/utils.rs @@ -1,5 +1,36 @@ +use std::{path::Path, result::Result::Ok, time::Duration}; + use anyhow::*; +use foundationdb::{self as fdb, options::DatabaseOption}; pub fn var(name: &str) -> Result { std::env::var(name).context(name.to_string()) } + +pub fn fdb_handle() -> fdb::FdbResult { + let cluster_file_path = Path::new("/etc/foundationdb/fdb.cluster"); + let db = fdb::Database::from_path(&cluster_file_path.display().to_string())?; + db.set_option(DatabaseOption::TransactionRetryLimit(10))?; + + Ok(db) +} + +pub async fn fdb_health_check() -> Result<()> { + let db = fdb_handle()?; + + loop { + match tokio::time::timeout( + Duration::from_secs(3), + db.run(|trx, _maybe_committed| async move { Ok(trx.get(b"", false).await?) }), + ) + .await + { + Ok(res) => { + res?; + } + Err(_) => eprintln!("db missed ping"), + } + + tokio::time::sleep(Duration::from_secs(3)).await; + } +} diff --git a/packages/infra/client/manager/tests/common.rs b/packages/infra/client/manager/tests/common.rs index cb6aa16c91..8c2f877115 100644 --- a/packages/infra/client/manager/tests/common.rs +++ b/packages/infra/client/manager/tests/common.rs @@ -2,6 +2,7 @@ use std::{ convert::Infallible, net::SocketAddr, path::{Path, PathBuf}, + time::Duration, sync::{ atomic::{AtomicBool, Ordering}, Arc, Once, @@ -197,15 +198,16 @@ pub async fn init_client(gen_path: &Path, working_path: &Path) -> Config { client_id: Uuid::new_v4(), datacenter_id: Uuid::new_v4(), network_ip: "127.0.0.1".parse().unwrap(), - vector_socket_addr: "127.0.0.1:5021".parse().unwrap(), + vector_socket_addr: Some("127.0.0.1:5021".parse().unwrap()), // Not necessary for the test flavor: protocol::ClientFlavor::Container, redirect_logs: false, + pegboard_ws_endpoint: Url::parse("http://127.0.0.1:5030").unwrap(), // Not necessary for the test - api_endpoint: "".to_string(), + api_public_endpoint: Url::parse("http://127.0.0.1").unwrap(), - working_path: working_path.to_path_buf(), + data_dir: working_path.to_path_buf(), container_runner_binary_path, isolate_runner_binary_path, }; @@ -326,7 +328,7 @@ pub async fn build_binaries(gen_path: &Path) { .unwrap(); build_runner(gen_path, "container").await; - build_runner(gen_path, "v8-isolate").await; + build_runner(gen_path, "isolate-v8").await; } async fn build_runner(gen_path: &Path, variant: &str) { @@ -363,9 +365,9 @@ async fn build_runner(gen_path: &Path, variant: &str) { let container_name = format!("temp-pegboard-{variant}-runner-container"); let binary_path_in_container = if variant == "container" { - format!("/app/target/x86_64-unknown-linux-musl/release/{variant}-runner") + format!("/app/target/x86_64-unknown-linux-musl/release/rivet-{variant}-runner") } else { - format!("/{variant}-runner") + format!("/rivet-{variant}-runner") }; // Create a temporary container @@ -443,7 +445,13 @@ pub async fn start_vector() { .join("tests") .join("vector.json"); - tracing::info!("{}", config_path.display()); + Command::new("docker") + .arg("rm") + .arg("test-vector") + .arg("--force") + .status() + .await + .unwrap(); let status = Command::new("docker") .arg("run") @@ -469,6 +477,53 @@ pub async fn start_vector() { assert!(status.success()); } +pub async fn start_fdb() { + Command::new("docker") + .arg("rm") + .arg("test-fdb") + .arg("--force") + .status() + .await + .unwrap(); + + let status = Command::new("docker") + .arg("run") + .arg("--rm") + .arg("-p") + .arg("4500:4500") + .arg("--name") + .arg("test-fdb") + .arg("foundationdb/foundationdb:7.1.19") + .status() + .await + .unwrap(); + + assert!(status.success()); +} + +pub async fn create_fdb_db() { + loop { + // Create db + let status = Command::new("docker") + .arg("exec") + .arg("test-fdb") + .arg("fdbcli") + .arg("--exec") + .arg(r#"configure new single ssd"#) + .status() + .await + .unwrap(); + + if status.success() { + break; + } else { + tracing::error!("failed to create fdb database"); + } + + tokio::time::sleep(Duration::from_secs(1)).await; + } +} + static SETUP_DEPENDENCIES: AtomicBool = AtomicBool::new(false); static mut TEMP_DIR_PATH: Option = None; @@ -489,6 +544,9 @@ pub async fn setup_dependencies() -> (Option, PathBuf) { tokio::spawn(start_vector()); + tokio::spawn(start_fdb()); + create_fdb_db().await; + (Some(tmp_dir), tmp_dir_path) } else { // SAFETY: Once SETUP_DEPENDENCIES is true, TEMP_DIR_PATH is guaranteed to be initialized. @@ -501,7 +559,7 @@ pub fn container_runner_path(gen_path: &Path) -> PathBuf { } pub fn v8_isolate_runner_path(gen_path: &Path) -> PathBuf { - gen_path.join("pegboard-v8-isolate-runner").to_path_buf() + gen_path.join("pegboard-isolate-v8-runner").to_path_buf() } pub fn image_path(gen_path: &Path) -> PathBuf { diff --git a/packages/infra/client/manager/tests/fdb.cluster b/packages/infra/client/manager/tests/fdb.cluster new file mode 100644 index 0000000000..3debff234c --- /dev/null +++ b/packages/infra/client/manager/tests/fdb.cluster @@ -0,0 +1 @@ +docker:docker@127.0.0.1:4500 diff --git a/packages/services/cluster/src/ops/datacenter/topology_get.rs b/packages/services/cluster/src/ops/datacenter/topology_get.rs index 2eaf5b30ed..7b5eda24c4 100644 --- a/packages/services/cluster/src/ops/datacenter/topology_get.rs +++ b/packages/services/cluster/src/ops/datacenter/topology_get.rs @@ -180,7 +180,7 @@ pub async fn cluster_datacenter_topology_get( .filter(|server| { matches!( server.pool_type, - PoolType::Gg | PoolType::Ats | PoolType::PegboardIsolate + PoolType::Gg | PoolType::Ats | PoolType::PegboardIsolate | PoolType::Fdb ) }) .collect::>(); diff --git a/packages/services/cluster/src/types.rs b/packages/services/cluster/src/types.rs index 48990449b9..fcaec9cc00 100644 --- a/packages/services/cluster/src/types.rs +++ b/packages/services/cluster/src/types.rs @@ -89,6 +89,7 @@ pub enum PoolType { Ats = 2, Pegboard = 3, PegboardIsolate = 4, + Fdb = 5, } impl std::fmt::Display for PoolType { @@ -99,6 +100,7 @@ impl std::fmt::Display for PoolType { PoolType::Ats => write!(f, "ats"), PoolType::Pegboard => write!(f, "pegboard"), PoolType::PegboardIsolate => write!(f, "pegboard-isolate"), + PoolType::Fdb => write!(f, "fdb"), } } } @@ -113,6 +115,7 @@ impl From for PoolType { rivet_config::config::rivet::dc_provision::PoolType::PegboardIsolate => { PoolType::PegboardIsolate } + rivet_config::config::rivet::dc_provision::PoolType::Fdb => PoolType::Fdb, } } } diff --git a/packages/services/cluster/src/workflows/datacenter/scale.rs b/packages/services/cluster/src/workflows/datacenter/scale.rs index 6c30760976..5c144da137 100644 --- a/packages/services/cluster/src/workflows/datacenter/scale.rs +++ b/packages/services/cluster/src/workflows/datacenter/scale.rs @@ -311,22 +311,20 @@ async fn scale_servers( .await?; } } - PoolType::Gg => { + PoolType::Gg | PoolType::Ats | PoolType::Fdb => { let installed_servers = active_servers.filter(|server| server.is_installed); let installed_count = installed_servers.clone().count(); if pctx.desired_count < installed_count { - scale_down_gg_servers(ctx, tx, actions, pctx, installed_servers, installed_count) - .await?; - } - } - PoolType::Ats => { - let installed_servers = active_servers.filter(|server| server.is_installed); - let installed_count = installed_servers.clone().count(); - - if pctx.desired_count < installed_count { - scale_down_ats_servers(ctx, tx, actions, pctx, installed_servers, installed_count) - .await?; + scale_down_servers_basic( + ctx, + tx, + actions, + pctx, + installed_servers, + installed_count, + ) + .await?; } } PoolType::Pegboard | PoolType::PegboardIsolate => { @@ -350,6 +348,40 @@ async fn scale_servers( Ok(()) } +async fn scale_down_servers_basic< + 'a, + I: Iterator + DoubleEndedIterator + Clone, +>( + ctx: &ActivityCtx, + tx: &mut sqlx::Transaction<'_, sqlx::Postgres>, + actions: &mut Vec, + pctx: &PoolCtx, + installed_servers: I, + installed_count: usize, +) -> GlobalResult<()> { + tracing::info!( + datacenter_id=?pctx.datacenter_id, + desired=%pctx.desired_count, + installed=%installed_count, + "scaling down {}", pctx.pool_type + ); + + let drain_count = installed_count.saturating_sub(pctx.desired_count); + + // Drain servers + if drain_count != 0 { + tracing::info!(count=%drain_count, "draining {} servers", pctx.pool_type); + + let drain_candidates = installed_servers + .take(drain_count) + .map(|server| server.server_id); + + drain_servers(ctx, tx, actions, drain_candidates).await?; + } + + Ok(()) +} + async fn scale_down_job_servers( ctx: &ActivityCtx, tx: &mut sqlx::Transaction<'_, sqlx::Postgres>, @@ -405,71 +437,6 @@ async fn scale_down_job_servers( Ok(()) } -async fn scale_down_gg_servers<'a, I: Iterator + DoubleEndedIterator + Clone>( - ctx: &ActivityCtx, - tx: &mut sqlx::Transaction<'_, sqlx::Postgres>, - actions: &mut Vec, - pctx: &PoolCtx, - installed_servers: I, - installed_count: usize, -) -> GlobalResult<()> { - tracing::info!( - datacenter_id=?pctx.datacenter_id, - desired=%pctx.desired_count, - installed=%installed_count, - "scaling down gg" - ); - - let drain_count = installed_count.saturating_sub(pctx.desired_count); - - // Drain servers - if drain_count != 0 { - tracing::info!(count=%drain_count, "draining gg servers"); - - let drain_candidates = installed_servers - .take(drain_count) - .map(|server| server.server_id); - - drain_servers(ctx, tx, actions, drain_candidates).await?; - } - - Ok(()) -} - -async fn scale_down_ats_servers< - 'a, - I: Iterator + DoubleEndedIterator + Clone, ->( - ctx: &ActivityCtx, - tx: &mut sqlx::Transaction<'_, sqlx::Postgres>, - actions: &mut Vec, - pctx: &PoolCtx, - installed_servers: I, - installed_count: usize, -) -> GlobalResult<()> { - tracing::info!( - datacenter_id=?pctx.datacenter_id, - desired=%pctx.desired_count, - installed=%installed_count, - "scaling down ats" - ); - - let drain_count = installed_count.saturating_sub(pctx.desired_count); - - // Drain servers - if drain_count != 0 { - tracing::info!(count=%drain_count, "draining ats servers"); - - let drain_candidates = installed_servers - .take(drain_count) - .map(|server| server.server_id); - - drain_servers(ctx, tx, actions, drain_candidates).await?; - } - - Ok(()) -} - async fn scale_down_pb_servers( ctx: &ActivityCtx, tx: &mut sqlx::Transaction<'_, sqlx::Postgres>, diff --git a/packages/services/cluster/src/workflows/datacenter/tls_issue.rs b/packages/services/cluster/src/workflows/datacenter/tls_issue.rs index 5596ea0e1b..1700646ed5 100644 --- a/packages/services/cluster/src/workflows/datacenter/tls_issue.rs +++ b/packages/services/cluster/src/workflows/datacenter/tls_issue.rs @@ -55,8 +55,6 @@ pub(crate) async fn cluster_datacenter_tls_issue( "job domain not enabled" ); - ctx.removed::>().await?; - let (gg_cert, job_cert) = ctx .join(( activity(OrderInput { diff --git a/packages/services/cluster/src/workflows/prebake.rs b/packages/services/cluster/src/workflows/prebake.rs index 00cab3909a..e4362b87ee 100644 --- a/packages/services/cluster/src/workflows/prebake.rs +++ b/packages/services/cluster/src/workflows/prebake.rs @@ -46,6 +46,7 @@ pub async fn cluster_prebake(ctx: &mut WorkflowCtx, input: &Input) -> GlobalResu } PoolType::Gg => linode::types::FirewallPreset::Gg, PoolType::Ats => linode::types::FirewallPreset::Ats, + PoolType::Fdb => linode::types::FirewallPreset::Fdb, }, vlan_ip: None, vlan_ip_net: None, diff --git a/packages/services/cluster/src/workflows/server/drain.rs b/packages/services/cluster/src/workflows/server/drain.rs index 7d7c912191..33fb8dad74 100644 --- a/packages/services/cluster/src/workflows/server/drain.rs +++ b/packages/services/cluster/src/workflows/server/drain.rs @@ -35,7 +35,6 @@ pub(crate) async fn cluster_server_drain(ctx: &mut WorkflowCtx, input: &Input) - .send() .await?; } - PoolType::Ats => {} PoolType::Pegboard | PoolType::PegboardIsolate => { let pegboard_client_id = ctx .activity(DrainPegboardClientInput { @@ -51,6 +50,7 @@ pub(crate) async fn cluster_server_drain(ctx: &mut WorkflowCtx, input: &Input) - .await?; } } + PoolType::Ats | PoolType::Fdb => {} } Ok(()) diff --git a/packages/services/cluster/src/workflows/server/install/install_scripts/components/fdb.rs b/packages/services/cluster/src/workflows/server/install/install_scripts/components/fdb.rs new file mode 100644 index 0000000000..f2b6e3e07f --- /dev/null +++ b/packages/services/cluster/src/workflows/server/install/install_scripts/components/fdb.rs @@ -0,0 +1,19 @@ +use chirp_workflow::prelude::*; + +pub fn install(initialize_immediately: bool) -> String { + let mut script = include_str!("../files/fdb_install.sh").replace( + "__PROMETHEUS_PROXY_SCRIPT__", + include_str!("../files/fdp_prometheus_proxy.py"), + ); + + if initialize_immediately { + // Run script immediately + script.push_str("systemctl start --no-block fdb_prometheus_proxy.service"); + } + + script +} + +pub fn configure() -> String { + include_str!("../files/fdb_configure.sh").to_string() +} diff --git a/packages/services/cluster/src/workflows/server/install/install_scripts/components/mod.rs b/packages/services/cluster/src/workflows/server/install/install_scripts/components/mod.rs index 05ac26cbbe..8759452162 100644 --- a/packages/services/cluster/src/workflows/server/install/install_scripts/components/mod.rs +++ b/packages/services/cluster/src/workflows/server/install/install_scripts/components/mod.rs @@ -1,5 +1,6 @@ use indoc::indoc; +pub mod fdb; pub mod nomad; pub mod ok_server; pub mod pegboard; @@ -79,3 +80,9 @@ pub mod cni { include_str!("../files/cni_plugins.sh").to_string() } } + +pub mod python { + pub fn install() -> String { + "apt-get install -y python3 pip".to_string() + } +} diff --git a/packages/services/cluster/src/workflows/server/install/install_scripts/files/fdb_configure.sh b/packages/services/cluster/src/workflows/server/install/install_scripts/files/fdb_configure.sh new file mode 100644 index 0000000000..3772b0d406 --- /dev/null +++ b/packages/services/cluster/src/workflows/server/install/install_scripts/files/fdb_configure.sh @@ -0,0 +1,8 @@ +# Append config +# cat << 'EOF' >> /etc/foundationdb/foundationdb.conf +# [fdbserver] +# EOF + +# TODO: add -t flag for TLS (https://apple.github.io/foundationdb/tls.html#enable-tls) +# Make fdb accessible on VLAN +python3 /usr/lib/foundationdb/make_public.py -a ___VLAN_IP___ diff --git a/packages/services/cluster/src/workflows/server/install/install_scripts/files/fdb_install.sh b/packages/services/cluster/src/workflows/server/install/install_scripts/files/fdb_install.sh new file mode 100644 index 0000000000..a850ae7a55 --- /dev/null +++ b/packages/services/cluster/src/workflows/server/install/install_scripts/files/fdb_install.sh @@ -0,0 +1,50 @@ +sysctl --system + +mkdir -p /etc/foundationdb + +curl -Lf -o /tmp/foundationdb-clients_OFF-1_amd64.deb "https://github.com/apple/foundationdb/releases/download/7.3.43/foundationdb-clients_7.3.43-1_amd64.deb" +dpkg -i /tmp/foundationdb-clients_OFF-1_amd64.deb + +# Verify installation +fdbcli --version + +curl -Lf -o /tmp/foundationdb-server_OFF-1_amd64.deb "https://github.com/apple/foundationdb/releases/download/7.3.43/foundationdb-server_7.3.43-1_amd64.deb" +dpkg -i /tmp/foundationdb-server_OFF-1_amd64.deb + +# Verify installation +fdbserver --version + +# https://apple.github.io/foundationdb/administration.html#administration-running-foundationdb +# Configure redundancy and storage engine +fdbcli --exec "configure perpetual_storage_wiggle=1 storage_migration_type=gradual" +fdbcli --exec "configure single ssd" +service foundationdb stop + + +pip install wheel foundationdb prometheus_client + +cat << 'EOF' > /usr/local/bin/fdb_prometheus_proxy.py +__PROMETHEUS_PROXY_SCRIPT__ +EOF + +# Systemd service +cat << 'EOF' > /etc/systemd/system/fdb_prometheus_proxy.service +[Unit] +Description=FDB Prometheus Proxy +After=network-online.target +Requires=network-online.target + +[Service] +ExecStart=/usr/bin/python3 /usr/local/bin/fdb_prometheus_proxy.py --fdb-cluster-file /etc/foundationdb/fdb.cluster +Restart=always +RestartSec=2 + +[Install] +WantedBy=multi-user.target +EOF + +systemctl daemon-reload +systemctl enable fdb_prometheus_proxy + +# NOTE: we dont have a systemd service for fdbserver because it uses `service`: +# https://apple.github.io/foundationdb/administration.html#administration-running-foundationdb diff --git a/packages/services/cluster/src/workflows/server/install/install_scripts/files/fdp_prometheus_proxy.py b/packages/services/cluster/src/workflows/server/install/install_scripts/files/fdp_prometheus_proxy.py new file mode 100644 index 0000000000..8c5ad3a05c --- /dev/null +++ b/packages/services/cluster/src/workflows/server/install/install_scripts/files/fdp_prometheus_proxy.py @@ -0,0 +1,1019 @@ +# COPIED FROM: https://gist.github.com/tudor/dd17fe94a89b56566738a012d6a3e7a8 + +#!/usr/bin/env python3 + +import fdb +import json +from argparse import ArgumentParser +import logging +import signal +import sys +import tempfile +from prometheus_client import ( + start_http_server, +) +from prometheus_client.core import ( + GaugeMetricFamily, + CounterMetricFamily, + InfoMetricFamily, + Metric, + REGISTRY, + Sample, +) + +logging.basicConfig(format="%(asctime)s %(levelname)s %(message)s") + + +def str_values(d): + return {k: str(v) for k, v in d.items()} + + +BACKUP_STATES = { + "": 0, + "has errored": 1, + "has never been started": 2, + "has been submitted": 3, + "has been started": 4, + "is differential": 5, + "has been completed": 6, + "has been aborted": 7, + "has been partially aborted": 8, +} + + +def backup_state(s): + return BACKUP_STATES.get(s, 0) + + +# The standard Prometheus client does not support quantiles in summaries. +# We aim to remedy this, specifically for the FDB quantiles. +# "commit_latency_statistics" : { +# "count" : 4, +# "max" : 0.0033512099999999999, +# "mean" : 0.0030776900000000001, +# "median" : 0.0030596300000000002, +# "min" : 0.0027711400000000001, +# "p25" : 0.0027711400000000001, +# "p90" : 0.0031287699999999999, +# "p95" : 0.0031287699999999999, +# "p99" : 0.0031287699999999999, +# "p99.9" : 0.0031287699999999999 +# } +FDB_QUANTILES = { + "p25": "0.25", + "median": "0.5", + "p90": "0.9", + "p95": "0.95", + "p99": "0.99", + "p99.9": "0.999", + "max": "1", +} + + +class FDBSummaryMetricFamily(Metric): + def __init__(self, name, documentation, *, labels=None): + Metric.__init__(self, name, documentation, "summary") + self._labelnames = tuple(labels or []) + + def add_metric(self, labels, value, timestamp=None): + count = value["count"] + sum = value["mean"] * count + label_dict = dict(zip(self._labelnames, labels)) + + def add_single(suffix, v): + d = label_dict.copy() + self.samples.append(Sample(self.name + suffix, d, v, timestamp)) + + def add_quantile(q, v): + d = label_dict.copy() + d["quantile"] = q + self.samples.append(Sample(self.name, d, v, timestamp)) + + add_single("_count", count) + add_single("_sum", sum) + for k, q in FDB_QUANTILES.items(): + add_quantile(q, value[k]) + + +def get_path(obj, path, prev_path=None): + prev_path = prev_path or [] + for i, p in enumerate(path): + if type(obj) != dict: + log_path = prev_path + path[:i] + logging.error("Cannot find path {}: not dict".format(".".join(log_path))) + return None + + obj = obj.get(p) + if obj is None: + log_path = prev_path + path[: (i + 1)] + logging.error("Cannot find path {}".format(".".join(log_path))) + return None + return obj + + +def bool_to_int(v): + return int(v) if type(v) == "bool" else v + + +class Info: + def __init__(self, name, doc, path, *, cb=bool_to_int, cls=GaugeMetricFamily): + self.name = name + self.doc = doc + self.path = path + self.cb = cb + self.cls = cls + + def describe(self): + return self.cls("fdb_" + self.name, self.doc) + + def collect(self, status): + g = self.describe() + v = self.cb(get_path(status, self.path)) + if v is not None: + g.add_metric([], v) + return g + + +class LabeledInfo: + def __init__( + self, + name, + doc, + labels, + root_path, + label_paths, + value_path, + *, + cb=bool_to_int, + filter=lambda x: True, + cls=GaugeMetricFamily, + ): + self.name = name + self.doc = doc + self.labels = labels + self.root_path = root_path + self.label_paths = label_paths + self.value_path = value_path + self.cb = cb + self.filter = filter + self.cls = cls + + def describe(self): + return self.cls("fdb_" + self.name, self.doc, labels=self.labels) + + def collect(self, status): + g = self.describe() + items = get_path(status, self.root_path) or [] + for i, item in enumerate(items): + if not self.filter(item): + continue + prev_path = self.root_path + [str(i)] + v = self.cb(get_path(item, self.value_path, prev_path)) + if v is not None: + g.add_metric( + [get_path(item, lp, prev_path) or "" for lp in self.label_paths], + v, + ) + return g + + +class ObjectLabeledInfo: + def __init__( + self, + name, + doc, + labels, + root_path, + label_paths, + value_path, + *, + use_key_as_first_label=True, + cb=bool_to_int, + filter=lambda x: True, + cls=GaugeMetricFamily, + ): + self.name = name + self.doc = doc + self.labels = labels + self.root_path = root_path + self.label_paths = label_paths + self.value_path = value_path + self.use_key_as_first_label = use_key_as_first_label + self.cb = cb + self.filter = filter + self.cls = cls + + def describe(self): + return self.cls("fdb_" + self.name, self.doc, labels=self.labels) + + def collect(self, status): + g = self.describe() + items = get_path(status, self.root_path) or {} + for key, item in items.items(): + if not self.filter(item): + continue + prev_path = self.root_path + [key] + v = self.cb(get_path(item, self.value_path, prev_path)) + if v is not None: + label_values = [key] if self.use_key_as_first_label else [] + label_values += ( + get_path(item, lp, prev_path) or "" for lp in self.label_paths + ) + g.add_metric(label_values, v) + return g + + +class Collector: + def __init__(self, db, *, status_file=None): + self.db = db + self.status_file = status_file + self.metrics = [ + Info( + "client_quorum_reachable", + "Was the proxy able to connect to DB coordinators?", + ["client", "coordinators", "quorum_reachable"], + ), + Info( + "client_count", + "Number of connected clients", + ["cluster", "clients", "count"], + ), + Info( + "cluster_controller_timestamp", + "Timestamp as reported by cluster controller", + ["cluster", "cluster_controller_timestamp"], + ), + Info( + "average_partition_size_bytes", + "Average partition size", + ["cluster", "data", "average_partition_size_bytes"], + ), + Info( + "moving_data_highest_priority", + "Moving data: highest priority (task count?)", + ["cluster", "data", "moving_data", "highest_priority"], + ), + Info( + "moving_data_in_flight_bytes", + "Moving data: bytes in flight", + ["cluster", "data", "moving_data", "in_flight_bytes"], + ), + Info( + "moving_data_in_queue_bytes", + "Moving data: bytes in queue", + ["cluster", "data", "moving_data", "in_queue_bytes"], + ), + Info( + "moving_data_total_written_bytes", + "Moving data: total bytes written", + ["cluster", "data", "moving_data", "total_written_bytes"], + ), + Info( + "partition_count", + "Partition count", + ["cluster", "data", "partitions_count"], + ), + Info( + "total_kv_size_bytes", + "Total KV size", + ["cluster", "data", "total_kv_size_bytes"], + ), + Info( + "total_disk_used_bytes", + "Total disk usage", + ["cluster", "data", "total_disk_used_bytes"], + ), + Info( + "least_operating_space_bytes_log_server", + "Minimum operating space among all log server processes", + ["cluster", "data", "least_operating_space_bytes_log_server"], + ), + Info( + "least_operating_space_bytes_storage_server", + "Minimum operating space among all storage server processes", + [ + "cluster", + "data", + "least_operating_space_bytes_storage_server", + ], + ), + Info( + "database_available", + "Is the database available?", + ["cluster", "database_available"], + ), + Info( + "degraded_process_count", + "Number of degraded processes", + ["cluster", "degraded_processes"], + ), + Info( + "full_replication", + "Is the data fully replicated?", + ["cluster", "full_replication"], + ), + Info( + "generation", + "Cluster generation", + ["cluster", "generation"], + ), + Info( + "incompatible_connection_count", + "Number of incompatible connections", + ["cluster", "incompatible_connections"], + cb=len, + ), + ObjectLabeledInfo( + "machine_cpu_utilization", + "CPU utilization by machine", + ["id", "address"], + ["cluster", "machines"], + [["address"]], + ["cpu", "logical_core_utilization"], + ), + ObjectLabeledInfo( + "machine_memory_committed_bytes", + "Amount of committed (in use) memory by machine", + ["id", "address"], + ["cluster", "machines"], + [["address"]], + ["memory", "committed_bytes"], + ), + ObjectLabeledInfo( + "machine_memory_free_bytes", + "Amount of free memory by machine", + ["id", "address"], + ["cluster", "machines"], + [["address"]], + ["memory", "free_bytes"], + ), + Info( + "page_cache_log_hit_rate", + "Log hit rate", + ["cluster", "page_cache", "log_hit_rate"], + ), + Info( + "page_cache_storage_hit_rate", + "Storage hit rate", + ["cluster", "page_cache", "storage_hit_rate"], + ), + ObjectLabeledInfo( + "process_cpu_utilization", + "CPU utilization", + ["id", "address", "class_type"], + ["cluster", "processes"], + [["address"], ["class_type"]], + ["cpu", "usage_cores"], + ), + ObjectLabeledInfo( + "disk_free_bytes", + "Amount of free disk space", + ["id", "address", "class_type"], + ["cluster", "processes"], + [["address"], ["class_type"]], + ["disk", "free_bytes"], + ), + ObjectLabeledInfo( + "disk_total_bytes", + "Amount of total disk space", + ["id", "address", "class_type"], + ["cluster", "processes"], + [["address"], ["class_type"]], + ["disk", "total_bytes"], + ), + ObjectLabeledInfo( + "disk_busy", + "Disk busyness", + ["id", "address", "class_type"], + ["cluster", "processes"], + [["address"], ["class_type"]], + ["disk", "busy"], + ), + ObjectLabeledInfo( + "disk_read_count", + "Number of disk reads", + ["id", "address", "class_type"], + ["cluster", "processes"], + [["address"], ["class_type"]], + ["disk", "reads", "counter"], + cls=CounterMetricFamily, + ), + ObjectLabeledInfo( + "disk_write_count", + "Number of disk writes", + ["id", "address", "class_type"], + ["cluster", "processes"], + [["address"], ["class_type"]], + ["disk", "writes", "counter"], + cls=CounterMetricFamily, + ), + ObjectLabeledInfo( + "memory_available_bytes", + "Available memory", + ["id", "address", "class_type"], + ["cluster", "processes"], + [["address"], ["class_type"]], + ["memory", "available_bytes"], + ), + ObjectLabeledInfo( + "memory_limit_bytes", + "Memory limit", + ["id", "address", "class_type"], + ["cluster", "processes"], + [["address"], ["class_type"]], + ["memory", "limit_bytes"], + ), + ObjectLabeledInfo( + "unused_allocated_memory_bytes", + "Unused allocated memory", + ["id", "address", "class_type"], + ["cluster", "processes"], + [["address"], ["class_type"]], + ["memory", "unused_allocated_memory"], + ), + ObjectLabeledInfo( + "memory_used_bytes", + "Used memory", + ["id", "address", "class_type"], + ["cluster", "processes"], + [["address"], ["class_type"]], + ["memory", "used_bytes"], + ), + ObjectLabeledInfo( + "run_loop_busy", + "Used memory", + ["id", "address", "class_type"], + ["cluster", "processes"], + [["address"], ["class_type"]], + ["run_loop_busy"], + ), + Info( + "batch_performance_limited_by", + "Reason limiting performance for batch transactions", + ["cluster", "qos", "batch_performance_limited_by"], + cls=InfoMetricFamily, + cb=str_values, + ), + Info( + "performance_limited_by", + "Reason limiting performance for batch transactions", + ["cluster", "qos", "performance_limited_by"], + cls=InfoMetricFamily, + cb=str_values, + ), + Info( + "recovery_state", + "Recovery state", + ["cluster", "recovery_state"], + cls=InfoMetricFamily, + cb=str_values, + ), + Info( + "workload_bytes_read", + "Bytes read", + ["cluster", "workload", "bytes", "read", "counter"], + cls=CounterMetricFamily, + ), + Info( + "workload_bytes_written", + "Bytes written", + ["cluster", "workload", "bytes", "written", "counter"], + cls=CounterMetricFamily, + ), + ObjectLabeledInfo( + "workload_op_count", + "Op count", + ["op"], + ["cluster", "workload", "operations"], + [], + ["counter"], + cls=CounterMetricFamily, + ), + ObjectLabeledInfo( + "workload_transaction_count", + "Transaction count", + ["state"], + ["cluster", "workload", "transactions"], + [], + ["counter"], + cls=CounterMetricFamily, + ), + ] + + roles = [ + "coordinator", + "proxy", + "log", + "storage", + "cluster_controller", + "ratekeeper", + "data_distributor", + "resolver", + "master", + ] + + for r in roles: + self.metrics.append( + ObjectLabeledInfo( + "is_" + r, + "Is this process a {}?".format(r), + ["id", "address", "class_type"], + ["cluster", "processes"], + [["address"], ["class_type"]], + ["roles"], + cb=lambda roles, r=r: int(any(x["role"] == r for x in roles)), + ) + ) + + self.by_role = {r: [] for r in roles} + + self.by_role["proxy"] += [ + LabeledInfo( + "proxy_commit_latency", + "Commit latency statistics", + ["process", "id"], + [], + [["process"], ["id"]], + ["commit_latency_statistics"], + cls=FDBSummaryMetricFamily, + ), + # TODO(tudor): support multiple txn classes, not just "default" + LabeledInfo( + "proxy_grv_latency", + "GetReadVersion latency statistics", + ["process", "id"], + [], + [["process"], ["id"]], + ["grv_latency_statistics", "default"], + cls=FDBSummaryMetricFamily, + ), + ] + + self.by_role["log"] += [ + LabeledInfo( + "log_data_version", + "Data version", + ["process", "id"], + [], + [["process"], ["id"]], + ["data_version"], + ), + LabeledInfo( + "log_durable_bytes", + "Amount of data stored to durable storage (cumulative)", + ["process", "id"], + [], + [["process"], ["id"]], + ["durable_bytes", "counter"], + cls=CounterMetricFamily, + ), + LabeledInfo( + "log_input_bytes", + "Amount of data received (cumulative)", + ["process", "id"], + [], + [["process"], ["id"]], + ["input_bytes", "counter"], + cls=CounterMetricFamily, + ), + LabeledInfo( + "log_kvstore_available_bytes", + "Amount of available kvstore bytes", + ["process", "id"], + [], + [["process"], ["id"]], + ["kvstore_available_bytes"], + ), + LabeledInfo( + "log_kvstore_free_bytes", + "Amount of free kvstore bytes", + ["process", "id"], + [], + [["process"], ["id"]], + ["kvstore_free_bytes"], + ), + LabeledInfo( + "log_kvstore_total_bytes", + "Amount of total kvstore bytes", + ["process", "id"], + [], + [["process"], ["id"]], + ["kvstore_total_bytes"], + ), + LabeledInfo( + "log_kvstore_used_bytes", + "Amount of used kvstore bytes", + ["process", "id"], + [], + [["process"], ["id"]], + ["kvstore_used_bytes"], + ), + LabeledInfo( + "log_queue_disk_available_bytes", + "Amount of available disk bytes for queue", + ["process", "id"], + [], + [["process"], ["id"]], + ["queue_disk_available_bytes"], + ), + LabeledInfo( + "log_queue_disk_free_bytes", + "Amount of free disk bytes for queue", + ["process", "id"], + [], + [["process"], ["id"]], + ["queue_disk_free_bytes"], + ), + LabeledInfo( + "log_queue_disk_total_bytes", + "Amount of total disk bytes for queue", + ["process", "id"], + [], + [["process"], ["id"]], + ["queue_disk_total_bytes"], + ), + LabeledInfo( + "log_queue_disk_used_bytes", + "Amount of used disk bytes for queue", + ["process", "id"], + [], + [["process"], ["id"]], + ["queue_disk_used_bytes"], + ), + ] + + self.by_role["storage"] += [ + LabeledInfo( + "storage_queried_bytes", + "Amount of bytes queried (cumulative)", + ["process", "id"], + [], + [["process"], ["id"]], + ["bytes_queried", "counter"], + cls=CounterMetricFamily, + ), + LabeledInfo( + "storage_data_lag_seconds", + "Data lag (seconds)", + ["process", "id"], + [], + [["process"], ["id"]], + ["data_lag", "seconds"], + ), + LabeledInfo( + "storage_data_lag_versions", + "Data lag (versions)", + ["process", "id"], + [], + [["process"], ["id"]], + ["data_lag", "versions"], + ), + LabeledInfo( + "storage_data_version", + "Highest version", + ["process", "id"], + [], + [["process"], ["id"]], + ["data_version"], + ), + LabeledInfo( + "storage_durability_lag_seconds", + "Durability lag (seconds)", + ["process", "id"], + [], + [["process"], ["id"]], + ["durability_lag", "seconds"], + ), + LabeledInfo( + "storage_durability_lag_versions", + "Durability lag (versions)", + ["process", "id"], + [], + [["process"], ["id"]], + ["durability_lag", "versions"], + ), + LabeledInfo( + "storage_finished_queries", + "Number of finished queries (cumulative)", + ["process", "id"], + [], + [["process"], ["id"]], + ["finished_queries", "counter"], + cls=CounterMetricFamily, + ), + LabeledInfo( + "storage_input_bytes", + "Amount of input bytes (cumulative)", + ["process", "id"], + [], + [["process"], ["id"]], + ["input_bytes", "counter"], + cls=CounterMetricFamily, + ), + LabeledInfo( + "storage_durable_bytes", + "Amount of durable bytes (cumulative)", + ["process", "id"], + [], + [["process"], ["id"]], + ["durable_bytes", "counter"], + cls=CounterMetricFamily, + ), + LabeledInfo( + "storage_keys_queried", + "Number of keys queried (cumulative)", + ["process", "id"], + [], + [["process"], ["id"]], + ["keys_queried", "counter"], + cls=CounterMetricFamily, + ), + LabeledInfo( + "storage_kvstore_available_bytes", + "Amount of available kvstore bytes", + ["process", "id"], + [], + [["process"], ["id"]], + ["kvstore_available_bytes"], + ), + LabeledInfo( + "storage_kvstore_free_bytes", + "Amount of free kvstore bytes", + ["process", "id"], + [], + [["process"], ["id"]], + ["kvstore_free_bytes"], + ), + LabeledInfo( + "storage_kvstore_total_bytes", + "Amount of total kvstore bytes", + ["process", "id"], + [], + [["process"], ["id"]], + ["kvstore_total_bytes"], + ), + LabeledInfo( + "storage_kvstore_used_bytes", + "Amount of used kvstore bytes", + ["process", "id"], + [], + [["process"], ["id"]], + ["kvstore_used_bytes"], + ), + LabeledInfo( + "storage_local_rate", + "Local rate", + ["process", "id"], + [], + [["process"], ["id"]], + ["local_rate"], + ), + LabeledInfo( + "storage_low_priority_queries", + "Number of low priority queries (cumulative)", + ["process", "id"], + [], + [["process"], ["id"]], + ["low_priority_queries", "counter"], + cls=CounterMetricFamily, + ), + LabeledInfo( + "storage_total_queries", + "Number of queries (cumulative)", + ["process", "id"], + [], + [["process"], ["id"]], + ["total_queries", "counter"], + cls=CounterMetricFamily, + ), + LabeledInfo( + "storage_mutation_bytes", + "Amount of bytes mutated (cumulative)", + ["process", "id"], + [], + [["process"], ["id"]], + ["mutation_bytes", "counter"], + cls=CounterMetricFamily, + ), + LabeledInfo( + "storage_mutations", + "Number of mutations (cumulative)", + ["process", "id"], + [], + [["process"], ["id"]], + ["mutations", "counter"], + cls=CounterMetricFamily, + ), + LabeledInfo( + "storage_query_queue_max", + "Number of queries in queue (max)", + ["process", "id"], + [], + [["process"], ["id"]], + ["query_queue_max"], + ), + LabeledInfo( + "storage_read_latency", + "Read latency statistics", + ["process", "id"], + [], + [["process"], ["id"]], + ["read_latency_statistics"], + cls=FDBSummaryMetricFamily, + ), + LabeledInfo( + "storage_log_fetch_count", + "Storage log fetches", + ["process", "id"], + [], + [["process"], ["id"]], + ["fetches_from_logs", "counter"], + cls=CounterMetricFamily, + ), + ] + + layers = ["backup"] + self.by_layer = {l: [] for l in layers} + + self.by_layer["backup"] += [ + ObjectLabeledInfo( + "backup_instance_bytes_sent", + "Bytes sent by backup agent instance", + ["id"], + ["instances"], + [], + ["blob_stats", "total", "bytes_sent"], + cls=CounterMetricFamily, + ), + ObjectLabeledInfo( + "backup_instance_requests_failed", + "Requests failed by backup agent instance", + ["id"], + ["instances"], + [], + ["blob_stats", "total", "requests_failed"], + cls=CounterMetricFamily, + ), + ObjectLabeledInfo( + "backup_instance_requests_successful", + "Requests successful by backup agent instance", + ["id"], + ["instances"], + [], + ["blob_stats", "total", "requests_successful"], + cls=CounterMetricFamily, + ), + ObjectLabeledInfo( + "backup_instance_memory_usage_bytes", + "Memory usage by backup agent instance", + ["id"], + ["instances"], + [], + ["memory_usage"], + ), + ObjectLabeledInfo( + "backup_state", + "Backup state", + ["tag"], + ["tags"], + [], + ["current_status"], + cb=backup_state, + ), + ObjectLabeledInfo( + "backup_running", + "Is backup running?", + ["tag"], + ["tags"], + [], + ["running_backup"], + ), + ObjectLabeledInfo( + "backup_restorable", + "Is backup restorable?", + ["tag"], + ["tags"], + [], + ["running_backup_is_restorable"], + ), + ObjectLabeledInfo( + "backup_restorable_lag", + "Lag from last restorable timestamp", + ["tag"], + ["tags"], + [], + ["last_restorable_seconds_behind"], + ), + ] + + def describe(self): + for g in self.metrics: + yield g.describe() + for r in self.by_role.values(): + for g in r: + yield g.describe() + for r in self.by_layer.values(): + for g in r: + yield g.describe() + + def collect(self): + if self.status_file: + with open(self.status_file, "rb") as f: + status_blob = f.read() + else: + try: + status_blob = self.db[b"\xff\xff/status/json"] + except Exception: + logging.exception("Error retrieving status from DB") + return + + status = json.loads(status_blob) + if not status["client"]["database_status"]["available"]: + messages = "; ".join(m["description"] for m in status["client"]["messages"]) + logging.error( + "DB not available when retrieving status: {}".format(messages) + ) + + for g in self.metrics: + try: + yield g.collect(status) + except Exception: + logging.exception("Error when collecting metric {}".format(g.name)) + + by_role = dict((k, []) for k in self.by_role.keys()) + for pid, proc in status.get("cluster", {}).get("processes", {}).items(): + for role in proc["roles"]: + role_name = role["role"] + if not role_name in self.by_role: + continue + role["process"] = pid + by_role[role_name].append(role) + + for k, metrics in self.by_role.items(): + for g in metrics: + try: + yield g.collect(by_role[k]) + except Exception: + logging.exception( + "Error when collecting by-role metric {}".format(g.name) + ) + + for k, metrics in self.by_layer.items(): + layer = status.get("cluster", {}).get("layers", {}).get(k) + if layer is None: + continue + for g in metrics: + try: + yield g.collect(layer) + except Exception: + logging.exception( + "Error when connecting by-layer metric {}, layer {}", + format(g.name, k), + ) + + +def main(): + ap = ArgumentParser() + ap.add_argument("--prometheus-port", type=int, default=9161, help="Prometheus port") + ap.add_argument("--fdb-cluster-file", help="FDB cluster file") + ap.add_argument("--status-file", help="Use status from local file") + ap.add_argument( + "--copy-cluster-file", + help="Copy cluster file to temp location", + action="store_true", + ) + args = ap.parse_args() + + cluster_file_path = args.fdb_cluster_file + if args.copy_cluster_file: + with open(cluster_file_path, "rb") as old_file: + content = old_file.read() + with tempfile.NamedTemporaryFile(prefix="fdb_cluster", delete=False) as f: + f.write(content) + cluster_file_path = f.name + + db = None + status_file = None + if args.status_file: + status_file = args.status_file + else: + assert args.fdb_cluster_file + fdb.api_version(730) + try: + db = fdb.open(cluster_file=cluster_file_path) + except Exception: + logging.exception( + "Error when connecting to DB, cluster file path = {}".format( + cluster_file_path + ) + ) + sys.exit(1) + + REGISTRY.register(Collector(db, status_file=status_file)) + start_http_server(args.prometheus_port) + while True: + signal.pause() + + +if __name__ == "__main__": + main() diff --git a/packages/services/cluster/src/workflows/server/install/install_scripts/mod.rs b/packages/services/cluster/src/workflows/server/install/install_scripts/mod.rs index b9408b83f5..14f9cd9295 100644 --- a/packages/services/cluster/src/workflows/server/install/install_scripts/mod.rs +++ b/packages/services/cluster/src/workflows/server/install/install_scripts/mod.rs @@ -61,6 +61,10 @@ pub async fn gen_install( script.push(components::cni::plugins()); script.push(components::pegboard::install(config).await?); } + PoolType::Fdb => { + script.push(components::python::install()); + script.push(components::fdb::install(initialize_immediately)); + } } // MARK: Common (post) @@ -155,6 +159,16 @@ pub async fn gen_initialize( }, ); } + PoolType::Fdb => { + script.push(components::fdb::configure()); + prometheus_targets.insert( + "pegboard".into(), + components::vector::PrometheusTarget { + endpoint: "http://127.0.0.1:9161".into(), + scrape_interval: 15, + }, + ); + } } // MARK: Common (post) diff --git a/packages/services/cluster/src/workflows/server/mod.rs b/packages/services/cluster/src/workflows/server/mod.rs index 59de4a83c8..20c7651fa4 100644 --- a/packages/services/cluster/src/workflows/server/mod.rs +++ b/packages/services/cluster/src/workflows/server/mod.rs @@ -108,6 +108,7 @@ pub(crate) async fn cluster_server(ctx: &mut WorkflowCtx, input: &Input) -> Glob } PoolType::Gg => linode::types::FirewallPreset::Gg, PoolType::Ats => linode::types::FirewallPreset::Ats, + PoolType::Fdb => linode::types::FirewallPreset::Fdb, }, vlan_ip: Some(vlan_ip.ip()), vlan_ip_net: Some(vlan_ip.ip_net()), @@ -304,7 +305,13 @@ pub(crate) async fn cluster_server(ctx: &mut WorkflowCtx, input: &Input) -> Glob .await?; } Main::Taint(_) => {} // Only for state - Main::Destroy(_) => break, + Main::Destroy(_) => { + if let PoolType::Fdb = input.pool_type { + bail!("you cant kill fdb you stupid chud"); + } + + break; + }, } } @@ -384,9 +391,26 @@ async fn get_vlan_ip(ctx: &ActivityCtx, input: &GetVlanIpInput) -> GlobalResult< } PoolType::Gg => provision_config.pools.gg.vlan_addr_range(), PoolType::Ats => provision_config.pools.ats.vlan_addr_range(), + PoolType::Fdb => provision_config.pools.fdb.vlan_addr_range(), }; let max_idx = vlan_addr_range.count() as i64; + // HACK: We should be storing `FirewallPreset` in the database and comparing against that instead of pool + // type since certain pool types share vlan ip networks. This converts the actual pool type to the pool + // types that share the ip network. + let shared_net_pool_types = match input.pool_type { + PoolType::Job | PoolType::Pegboard | PoolType::PegboardIsolate => { + vec![ + PoolType::Job as i32, + PoolType::Pegboard as i32, + PoolType::PegboardIsolate as i32, + ] + } + PoolType::Gg => vec![PoolType::Gg as i32], + PoolType::Ats => vec![PoolType::Ats as i32], + PoolType::Fdb => vec![PoolType::Fdb as i32], + }; + let (network_idx,) = sql_fetch_one!( [ctx, (i64,)] " @@ -398,7 +422,7 @@ async fn get_vlan_ip(ctx: &ActivityCtx, input: &GetVlanIpInput) -> GlobalResult< SELECT 1 FROM db_cluster.servers WHERE - pool_type = $3 AND + pool_type = ANY($3) AND -- Technically this should check all servers where their datacenter's provider and -- provider_datacenter_id are the same because VLAN is separated by irl datacenter -- but this is good enough @@ -419,7 +443,7 @@ async fn get_vlan_ip(ctx: &ActivityCtx, input: &GetVlanIpInput) -> GlobalResult< // Choose a random index to start from for better index spread rand::thread_rng().gen_range(0i64..max_idx), max_idx, - input.pool_type as i32, + shared_net_pool_types, input.datacenter_id, input.server_id, ) diff --git a/packages/services/cluster/src/workflows/server/undrain.rs b/packages/services/cluster/src/workflows/server/undrain.rs index 28f03ae52d..72f1295907 100644 --- a/packages/services/cluster/src/workflows/server/undrain.rs +++ b/packages/services/cluster/src/workflows/server/undrain.rs @@ -30,7 +30,6 @@ pub(crate) async fn cluster_server_undrain( .send() .await?; } - PoolType::Ats => {} PoolType::Pegboard | PoolType::PegboardIsolate => { let pegboard_client_id = ctx .activity(UndrainPegboardClientInput { @@ -45,6 +44,7 @@ pub(crate) async fn cluster_server_undrain( .await?; } } + PoolType::Ats | PoolType::Fdb => {} } Ok(()) diff --git a/packages/services/cluster/standalone/metrics-publish/src/lib.rs b/packages/services/cluster/standalone/metrics-publish/src/lib.rs index b8fd8e8be0..73beb2839d 100644 --- a/packages/services/cluster/standalone/metrics-publish/src/lib.rs +++ b/packages/services/cluster/standalone/metrics-publish/src/lib.rs @@ -122,36 +122,15 @@ fn insert_metrics(dc: &Datacenter, servers: &[Server]) -> GlobalResult<()> { let datacenter_id = dc.datacenter_id.to_string(); let cluster_id = dc.cluster_id.to_string(); - let servers_per_pool = [ + let servers_per_pool = [PoolType::Job, PoolType::Gg, PoolType::Ats, PoolType::Pegboard, PoolType::PegboardIsolate, PoolType::Fdb].into_iter().map(|pool_type| { ( - PoolType::Job, + pool_type, servers_in_dc .clone() - .filter(|s| matches!(s.pool_type, PoolType::Job)) + .filter(|s| matches!(s.pool_type, pool_type)) .collect::>(), - ), - ( - PoolType::Gg, - servers_in_dc - .clone() - .filter(|s| matches!(s.pool_type, PoolType::Gg)) - .collect::>(), - ), - ( - PoolType::Ats, - servers_in_dc - .clone() - .filter(|s| matches!(s.pool_type, PoolType::Ats)) - .collect::>(), - ), - ( - PoolType::Pegboard, - servers_in_dc - .clone() - .filter(|s| matches!(s.pool_type, PoolType::Pegboard)) - .collect::>(), - ), - ]; + ) + }).collect::>(); // Aggregate all states per pool type for (pool_type, servers) in servers_per_pool { diff --git a/packages/services/linode/src/types.rs b/packages/services/linode/src/types.rs index 5b7f72ce5e..5e2177b4cb 100644 --- a/packages/services/linode/src/types.rs +++ b/packages/services/linode/src/types.rs @@ -17,6 +17,7 @@ pub enum FirewallPreset { Job, Gg, Ats, + Fdb, } impl FirewallPreset { @@ -32,6 +33,7 @@ impl FirewallPreset { .gg .firewall_rules(&config.server()?.rivet.game_guard), FirewallPreset::Ats => provision_config.pools.ats.firewall_rules(), + FirewallPreset::Fdb => provision_config.pools.fdb.firewall_rules(), }) } } @@ -42,6 +44,7 @@ impl std::fmt::Display for FirewallPreset { FirewallPreset::Job => write!(f, "job"), FirewallPreset::Gg => write!(f, "gg"), FirewallPreset::Ats => write!(f, "ats"), + FirewallPreset::Fdb => write!(f, "fdb"), } } } diff --git a/packages/services/pegboard/src/protocol.rs b/packages/services/pegboard/src/protocol.rs index e8472e0b59..62e0fe6d07 100644 --- a/packages/services/pegboard/src/protocol.rs +++ b/packages/services/pegboard/src/protocol.rs @@ -83,7 +83,6 @@ pub enum Command { #[derive(Debug, Serialize, Deserialize, Clone, Hash)] pub struct ActorConfig { - // pub driver: Driver, pub image: Image, pub root_user_enabled: bool, pub resources: Resources, diff --git a/sdks/full/openapi/openapi.yml b/sdks/full/openapi/openapi.yml index c19aa28808..1b56290d52 100644 --- a/sdks/full/openapi/openapi.yml +++ b/sdks/full/openapi/openapi.yml @@ -9890,15 +9890,206 @@ components: enum: - std_out - std_err - ActorListRegionsResponse: + AdminClustersPoolType: + type: string + enum: + - job + - gg + - ats + - pegboard + - pegboard_isolate + - fdb + AdminClustersProvider: + type: string + enum: + - linode + AdminClustersBuildDeliveryMethod: + type: string + enum: + - traffic_server + - s3_direct + AdminClustersPool: type: object properties: - regions: + pool_type: + $ref: '#/components/schemas/AdminClustersPoolType' + hardware: type: array items: - $ref: '#/components/schemas/ActorRegion' + $ref: '#/components/schemas/AdminClustersHardware' + desired_count: + type: integer + min_count: + type: integer + max_count: + type: integer + drain_timeout_ms: + type: integer + format: int64 required: - - regions + - pool_type + - hardware + - desired_count + - min_count + - max_count + - drain_timeout_ms + AdminClustersHardware: + type: object + properties: + provider_hardware: + type: string + required: + - provider_hardware + AdminClustersCluster: + type: object + properties: + cluster_id: + type: string + format: uuid + name_id: + type: string + create_ts: + type: integer + format: int64 + owner_team_id: + type: string + format: uuid + required: + - cluster_id + - name_id + - create_ts + AdminClustersDatacenter: + type: object + properties: + datacenter_id: + type: string + format: uuid + cluster_id: + type: string + format: uuid + name_id: + type: string + display_name: + type: string + provider: + $ref: '#/components/schemas/AdminClustersProvider' + provider_datacenter_id: + type: string + pools: + type: array + items: + $ref: '#/components/schemas/AdminClustersPool' + build_delivery_method: + $ref: '#/components/schemas/AdminClustersBuildDeliveryMethod' + prebakes_enabled: + type: boolean + required: + - datacenter_id + - cluster_id + - name_id + - display_name + - provider + - provider_datacenter_id + - pools + - build_delivery_method + - prebakes_enabled + AdminClustersServer: + type: object + properties: + server_id: + type: string + format: uuid + datacenter_id: + type: string + format: uuid + pool_type: + $ref: '#/components/schemas/AdminClustersPoolType' + public_ip: + type: string + required: + - server_id + - datacenter_id + - pool_type + AdminClustersPoolUpdate: + type: object + properties: + pool_type: + $ref: '#/components/schemas/AdminClustersPoolType' + hardware: + type: array + items: + $ref: '#/components/schemas/AdminClustersHardware' + desired_count: + type: integer + min_count: + type: integer + max_count: + type: integer + drain_timeout: + type: integer + format: int64 + required: + - pool_type + - hardware + AdminClustersListDatacentersResponse: + type: object + properties: + datacenters: + type: array + items: + $ref: '#/components/schemas/AdminClustersDatacenter' + required: + - datacenters + AdminClustersCreateDatacenterRequest: + type: object + properties: + display_name: + type: string + name_id: + type: string + provider: + $ref: '#/components/schemas/AdminClustersProvider' + provider_datacenter_id: + type: string + build_delivery_method: + $ref: '#/components/schemas/AdminClustersBuildDeliveryMethod' + prebakes_enabled: + type: boolean + required: + - display_name + - name_id + - provider + - provider_datacenter_id + - build_delivery_method + - prebakes_enabled + AdminClustersCreateDatacenterResponse: + type: object + properties: + datacenter_id: + type: string + format: uuid + required: + - datacenter_id + AdminClustersUpdateDatacenterRequest: + type: object + properties: + pools: + type: array + items: + $ref: '#/components/schemas/AdminClustersPoolUpdate' + prebakes_enabled: + type: boolean + required: + - pools + AdminClustersListServersResponse: + type: object + properties: + servers: + type: array + items: + $ref: '#/components/schemas/AdminClustersServer' + required: + - servers AuthCompleteStatus: type: string enum: diff --git a/sdks/full/openapi_compat/openapi.yml b/sdks/full/openapi_compat/openapi.yml index 859919dd42..e828280a28 100644 --- a/sdks/full/openapi_compat/openapi.yml +++ b/sdks/full/openapi_compat/openapi.yml @@ -382,6 +382,247 @@ components: required: - build type: object + AdminClustersBuildDeliveryMethod: + enum: + - traffic_server + - s3_direct + type: string + AdminClustersCluster: + properties: + cluster_id: + format: uuid + type: string + create_ts: + format: int64 + type: integer + name_id: + type: string + owner_team_id: + format: uuid + type: string + required: + - cluster_id + - name_id + - create_ts + type: object + AdminClustersCreateClusterRequest: + properties: + name_id: + type: string + owner_team_id: + format: uuid + type: string + required: + - name_id + type: object + AdminClustersCreateClusterResponse: + properties: + cluster_id: + format: uuid + type: string + required: + - cluster_id + type: object + AdminClustersCreateDatacenterRequest: + properties: + build_delivery_method: + $ref: '#/components/schemas/AdminClustersBuildDeliveryMethod' + display_name: + type: string + name_id: + type: string + prebakes_enabled: + type: boolean + provider: + $ref: '#/components/schemas/AdminClustersProvider' + provider_datacenter_id: + type: string + required: + - display_name + - name_id + - provider + - provider_datacenter_id + - build_delivery_method + - prebakes_enabled + type: object + AdminClustersCreateDatacenterResponse: + properties: + datacenter_id: + format: uuid + type: string + required: + - datacenter_id + type: object + AdminClustersDatacenter: + properties: + build_delivery_method: + $ref: '#/components/schemas/AdminClustersBuildDeliveryMethod' + cluster_id: + format: uuid + type: string + datacenter_id: + format: uuid + type: string + display_name: + type: string + name_id: + type: string + pools: + items: + $ref: '#/components/schemas/AdminClustersPool' + type: array + prebakes_enabled: + type: boolean + provider: + $ref: '#/components/schemas/AdminClustersProvider' + provider_datacenter_id: + type: string + required: + - datacenter_id + - cluster_id + - name_id + - display_name + - provider + - provider_datacenter_id + - pools + - build_delivery_method + - prebakes_enabled + type: object + AdminClustersHardware: + properties: + provider_hardware: + type: string + required: + - provider_hardware + type: object + AdminClustersListClustersResponse: + properties: + clusters: + items: + $ref: '#/components/schemas/AdminClustersCluster' + type: array + required: + - clusters + type: object + AdminClustersListDatacentersResponse: + properties: + datacenters: + items: + $ref: '#/components/schemas/AdminClustersDatacenter' + type: array + required: + - datacenters + type: object + AdminClustersListServersResponse: + properties: + servers: + items: + $ref: '#/components/schemas/AdminClustersServer' + type: array + required: + - servers + type: object + AdminClustersPool: + properties: + desired_count: + type: integer + drain_timeout_ms: + format: int64 + type: integer + hardware: + items: + $ref: '#/components/schemas/AdminClustersHardware' + type: array + max_count: + type: integer + min_count: + type: integer + pool_type: + $ref: '#/components/schemas/AdminClustersPoolType' + required: + - pool_type + - hardware + - desired_count + - min_count + - max_count + - drain_timeout_ms + type: object + AdminClustersPoolType: + enum: + - job + - gg + - ats + - pegboard + - pegboard_isolate + - fdb + type: string + AdminClustersPoolUpdate: + properties: + desired_count: + type: integer + drain_timeout: + format: int64 + type: integer + hardware: + items: + $ref: '#/components/schemas/AdminClustersHardware' + type: array + max_count: + type: integer + min_count: + type: integer + pool_type: + $ref: '#/components/schemas/AdminClustersPoolType' + required: + - pool_type + - hardware + type: object + AdminClustersProvider: + enum: + - linode + type: string + AdminClustersServer: + properties: + datacenter_id: + format: uuid + type: string + pool_type: + $ref: '#/components/schemas/AdminClustersPoolType' + public_ip: + type: string + server_id: + format: uuid + type: string + required: + - server_id + - datacenter_id + - pool_type + type: object + AdminClustersUpdateDatacenterRequest: + properties: + pools: + items: + $ref: '#/components/schemas/AdminClustersPoolUpdate' + type: array + prebakes_enabled: + type: boolean + required: + - pools + type: object + AdminLoginRequest: + properties: + name: + type: string + required: + - name + type: object + AdminLoginResponse: + properties: + url: + type: string + required: + - url + type: object AuthCompleteStatus: description: Represents the state of an external account linking process. enum: diff --git a/sdks/full/typescript/archive.tgz b/sdks/full/typescript/archive.tgz index 4c9ba2ea12..d229be3fc2 100644 --- a/sdks/full/typescript/archive.tgz +++ b/sdks/full/typescript/archive.tgz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:89eb2cf0d1d5eb2611f5699e0b5a634a70b5d0104ce9269dd3707baaf5c43e9a -size 545908 +oid sha256:8632119e515b59e9380e156fb3a1cc4e72f381bde1943ba47bb815b88a26b88d +size 529144 diff --git a/sdks/full/typescript/types/api/resources/admin/resources/clusters/resources/common/types/PoolType.d.ts b/sdks/full/typescript/types/api/resources/admin/resources/clusters/resources/common/types/PoolType.d.ts index 226f12f420..701d67230e 100644 --- a/sdks/full/typescript/types/api/resources/admin/resources/clusters/resources/common/types/PoolType.d.ts +++ b/sdks/full/typescript/types/api/resources/admin/resources/clusters/resources/common/types/PoolType.d.ts @@ -1,11 +1,12 @@ /** * This file was auto-generated by Fern from our API Definition. */ -export declare type PoolType = "job" | "gg" | "ats" | "pegboard" | "pegboard_isolate"; +export declare type PoolType = "job" | "gg" | "ats" | "pegboard" | "pegboard_isolate" | "fdb"; export declare const PoolType: { readonly Job: "job"; readonly Gg: "gg"; readonly Ats: "ats"; readonly Pegboard: "pegboard"; readonly PegboardIsolate: "pegboard_isolate"; + readonly Fdb: "fdb"; }; diff --git a/sdks/full/typescript/types/serialization/resources/admin/resources/clusters/resources/common/types/PoolType.d.ts b/sdks/full/typescript/types/serialization/resources/admin/resources/clusters/resources/common/types/PoolType.d.ts index bccbb9125a..5e8145f9a0 100644 --- a/sdks/full/typescript/types/serialization/resources/admin/resources/clusters/resources/common/types/PoolType.d.ts +++ b/sdks/full/typescript/types/serialization/resources/admin/resources/clusters/resources/common/types/PoolType.d.ts @@ -6,5 +6,5 @@ import * as Rivet from "../../../../../../../../api/index"; import * as core from "../../../../../../../../core"; export declare const PoolType: core.serialization.Schema; export declare namespace PoolType { - type Raw = "job" | "gg" | "ats" | "pegboard" | "pegboard_isolate"; + type Raw = "job" | "gg" | "ats" | "pegboard" | "pegboard_isolate" | "fdb"; } diff --git a/sdks/runtime/typescript/archive.tgz b/sdks/runtime/typescript/archive.tgz index 7a921eb2c1..bc35787370 100644 --- a/sdks/runtime/typescript/archive.tgz +++ b/sdks/runtime/typescript/archive.tgz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1570e51657f74632daeefe262e589c6355d4ce69ce5815392665a5427431fdb7 -size 277122 +oid sha256:5ad033dfb31dd1bb9018a63c120f26cd1fafcea4b0634685af4c457b7775f866 +size 276986