mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-21 00:02:30 +00:00
Merge commit 'eab084b36fe14ecee3ecc9a78fb079760de133b9' into HEAD
# Conflicts: # backend/windmill-common/src/lib.rs
This commit is contained in:
@@ -1 +1 @@
|
||||
4de90447da1e166f8d22997a1ced3ff219d5898e
|
||||
bf8f77b076599124ad8bb1ebb8f28f1509b7d70e
|
||||
@@ -1678,6 +1678,8 @@ struct CustomInstanceDb {
|
||||
tag: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
used_by_workspaces: Vec<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
workspace_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Serialize, Default)]
|
||||
@@ -1914,12 +1916,31 @@ async fn setup_custom_instance_pg_database(
|
||||
Path(dbname): Path<String>,
|
||||
Json(body): Json<SetupCustomInstanceDbBody>,
|
||||
) -> JsonResult<CustomInstanceDb> {
|
||||
// Before anything is recorded: the status written below replaces the registry entry, and with it
|
||||
// the workspace a fork copy is reserved for.
|
||||
require_super_admin(&db, &authed).await?;
|
||||
// A re-run keeps the fork reservation: without it, the workspace the copy was made for could no
|
||||
// longer import into it or finish its fork.
|
||||
let workspace_id = sqlx::query_scalar::<_, Option<String>>(
|
||||
"SELECT value->'databases'->$1->>'workspace_id' FROM global_settings
|
||||
WHERE name = 'custom_instance_pg_databases'",
|
||||
)
|
||||
.bind(&dbname)
|
||||
.fetch_optional(&db)
|
||||
.await?
|
||||
.flatten();
|
||||
let mut logs = CustomInstanceDbLogs::default();
|
||||
let result = setup_custom_instance_pg_database_inner(authed, &db, &dbname, &mut logs).await;
|
||||
let success = result.is_ok();
|
||||
let error = result.err().map(|e| e.to_string());
|
||||
let status =
|
||||
CustomInstanceDb { logs, success, error, tag: body.tag, used_by_workspaces: vec![] };
|
||||
let status = CustomInstanceDb {
|
||||
logs,
|
||||
success,
|
||||
error,
|
||||
tag: body.tag,
|
||||
used_by_workspaces: vec![],
|
||||
workspace_id,
|
||||
};
|
||||
let status_json = serde_json::to_value(&status).map_err(to_anyhow)?;
|
||||
// Save that the database was setup successfully
|
||||
sqlx::query!(
|
||||
|
||||
@@ -34090,6 +34090,9 @@ components:
|
||||
items:
|
||||
type: string
|
||||
description: Workspaces that reference this database via a ducklake catalog or datatable database with resource_type 'instance'. Computed at request time, not persisted.
|
||||
workspace_id:
|
||||
type: string
|
||||
description: The workspace a member created this database for as a fork copy. Only that workspace can import into it or point a fork at it.
|
||||
|
||||
NewSqsTrigger:
|
||||
type: object
|
||||
|
||||
@@ -41,6 +41,21 @@ pub struct ExternalInstancePgState {
|
||||
pub databases: BTreeMap<String, CustomInstanceDb>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub last_setup: Option<ExternalInstancePgSetupReport>,
|
||||
/// The cluster ([`external_instance_pg_address`]) the last successful setup converged. Databases
|
||||
/// are only created on a cluster setup succeeded on: the passwords above exist as soon as setup
|
||||
/// first runs, whether or not the cluster accepted them.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub set_up_for: Option<String>,
|
||||
}
|
||||
|
||||
/// What identifies the cluster a configuration points at. Other fields (admin login, sslmode) can
|
||||
/// change without it becoming another cluster.
|
||||
pub fn external_instance_pg_address(config: &ExternalInstancePg) -> String {
|
||||
format!(
|
||||
"{}:{}",
|
||||
config.host.trim().to_lowercase(),
|
||||
config.port.unwrap_or(5432)
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
@@ -125,6 +140,10 @@ pub async fn external_instance_pg_status(db: &DB) -> Result<ExternalInstancePgSt
|
||||
}
|
||||
|
||||
/// The databases Windmill created on the external cluster, without the passwords kept beside them.
|
||||
///
|
||||
/// Authorization: names every database across all workspaces, and the workspace each fork copy is
|
||||
/// reserved for, and checks nothing. Callers MUST be superadmin or an internal authorization or
|
||||
/// lifecycle path that does not return the names to a workspace caller.
|
||||
pub async fn external_instance_databases(db: &DB) -> Result<BTreeMap<String, CustomInstanceDb>> {
|
||||
Ok(read_external_instance_pg_state(db).await?.databases)
|
||||
}
|
||||
@@ -355,8 +374,7 @@ async fn ensure_external_instance_pg_not_repointed(
|
||||
let Ok(desired) = serde_json::from_value::<ExternalInstancePg>(value.clone()) else {
|
||||
return Ok(());
|
||||
};
|
||||
let address = |c: &ExternalInstancePg| (c.host.trim().to_lowercase(), c.port.unwrap_or(5432));
|
||||
if address(¤t) == address(&desired) {
|
||||
if external_instance_pg_address(¤t) == external_instance_pg_address(&desired) {
|
||||
return Ok(());
|
||||
}
|
||||
let state = read_external_instance_pg_state(db).await?;
|
||||
|
||||
@@ -1664,6 +1664,10 @@ pub async fn create_custom_instance_database(
|
||||
/// its external instance counterpart) and nothing uses it yet. The `wm_fork_` prefix is no
|
||||
/// authorization: every database of a cluster answers to the same `custom_instance_user`, so a name
|
||||
/// is all it takes to reach another workspace's copy.
|
||||
///
|
||||
/// Authorization: reads the global registries and every workspace's settings, and names other
|
||||
/// workspaces in its refusal. Callers MUST have authorized `w_id` for the caller first — a member
|
||||
/// of it forking or importing there — and MUST NOT call it on a workspace the caller is not in.
|
||||
pub async fn ensure_fork_database_available_to(
|
||||
db: &DB,
|
||||
kind: workspaces::DataTableCatalogResourceType,
|
||||
|
||||
Reference in New Issue
Block a user