Skip to main content

docbox_management/
interface.rs

1use docbox_core::{
2    database::{DbErr, models::tenant::Tenant, sqlx::types::Uuid},
3    storage::StorageLayerError,
4    tenant::tenant_options_ext::TenantOptionsExt,
5};
6use docbox_management_interface::{
7    CheckRootOutput, CreateTenantInput, CreateTenantOutput, DeleteTenantInput, DeleteTenantOutput,
8    DocboxManagementInterface, DocboxServiceError, FailedTenantMigration, GetTenantInput,
9    GetTenantOutput, GetTenantPendingMigrationsInput, GetTenantPendingMigrationsOutput,
10    GetTenantsInput, GetTenantsOutput, ManagedTenant, ManagementError, ManagementTenantTarget,
11    MigrateTenantIAMInput, MigrateTenantIAMOutput, MigrateTenantInput, MigrateTenantOutput,
12    PendingMigration, SetTenantAllowedCorsOriginsInput, TenantMigrationService, async_trait,
13    error::DynServiceError,
14};
15use thiserror::Error;
16
17use crate::{
18    config::ServerConfigData,
19    server::ManagedServer,
20    tenant::{
21        MigrateTenantsOutcome, TenantTarget,
22        create_tenant::CreateTenantConfig,
23        delete_tenant::{DeleteTenant, DeleteTenantOptions},
24        migrate_tenants::MigrateTenantsConfig,
25        migrate_tenants_search::MigrateTenantsSearchConfig,
26        migrate_tenants_storage::MigrateTenantsStorageConfig,
27    },
28};
29
30pub struct ManagedServerInterface {
31    pub server: ManagedServer,
32    pub config: ServerConfigData,
33}
34
35#[derive(Debug, Error)]
36#[error(transparent)]
37pub struct ManagementDbErr(DbErr);
38
39impl DocboxServiceError for ManagementDbErr {}
40
41#[derive(Debug, Error)]
42#[error(transparent)]
43pub struct ManagementStorageLayerErr(StorageLayerError);
44
45impl DocboxServiceError for ManagementStorageLayerErr {}
46
47#[derive(Debug, Error)]
48#[error("tenant not found")]
49pub struct TenantNotFoundError;
50
51impl DocboxServiceError for TenantNotFoundError {}
52
53impl DocboxServiceError for crate::root::initialize::InitializeError {}
54
55impl DocboxServiceError for crate::root::migrate_root::MigrateRootError {}
56
57impl DocboxServiceError for crate::tenant::flush_tenant_cache::FlushTenantCacheError {}
58
59impl DocboxServiceError for crate::tenant::delete_tenant::DeleteTenantError {}
60
61impl DocboxServiceError for crate::tenant::create_tenant::CreateTenantError {}
62
63impl DocboxServiceError for crate::tenant::migrate_tenants::MigrateTenantsError {}
64
65impl DocboxServiceError for crate::tenant::migrate_tenants_search::MigrateTenantsSearchError {}
66
67impl DocboxServiceError for crate::tenant::migrate_tenants_storage::MigrateTenantsStorageError {}
68
69impl DocboxServiceError for crate::tenant::migrate_tenant_secret_to_iam::MigrateIAMError {}
70
71impl DocboxServiceError
72    for crate::tenant::get_pending_tenant_search_migrations::GetPendingTenantMigrationsError
73{
74}
75
76impl DocboxServiceError
77    for crate::tenant::get_pending_tenant_storage_migrations::GetPendingTenantMigrationsError
78{
79}
80
81impl From<TenantNotFoundError> for ManagementError {
82    fn from(value: TenantNotFoundError) -> Self {
83        ManagementError::Service(DynServiceError::from(value))
84    }
85}
86
87impl From<ManagementDbErr> for ManagementError {
88    fn from(value: ManagementDbErr) -> Self {
89        ManagementError::Service(DynServiceError::from(value))
90    }
91}
92
93impl From<ManagementStorageLayerErr> for ManagementError {
94    fn from(value: ManagementStorageLayerErr) -> Self {
95        ManagementError::Service(DynServiceError::from(value))
96    }
97}
98
99fn map_managed_tenant(tenant: Tenant) -> ManagedTenant {
100    ManagedTenant {
101        env: tenant.env,
102        id: tenant.id,
103        name: tenant.name,
104        db_name: tenant.db_name,
105        db_secret_name: tenant.db_secret_name,
106        db_iam_user_name: tenant.db_iam_user_name,
107        s3_name: tenant.s3_name,
108        os_index_name: tenant.os_index_name,
109        event_queue_url: tenant.event_queue_url,
110    }
111}
112
113#[async_trait]
114impl DocboxManagementInterface for ManagedServerInterface {
115    async fn check_root(&self) -> Result<CheckRootOutput, ManagementError> {
116        let initialized = crate::root::initialize::is_initialized(&self.server.db_provider)
117            .await
118            .map_err(ManagementDbErr)
119            .map_err(DynServiceError::from)?;
120
121        Ok(CheckRootOutput { initialized })
122    }
123
124    async fn create_root(&self) -> Result<(), ManagementError> {
125        if self.config.database.root_iam {
126            crate::root::initialize::initialize_iam(&self.server.db_provider)
127                .await
128                .map_err(DynServiceError::from)?;
129        } else if let Some(root_secret_name) = self.config.database.root_secret_name.as_ref() {
130            crate::root::initialize::initialize(
131                &self.server.db_provider,
132                &self.server.secrets,
133                root_secret_name,
134            )
135            .await
136            .map_err(DynServiceError::from)?;
137        }
138
139        Ok(())
140    }
141
142    async fn create_tenant(
143        &self,
144        input: CreateTenantInput,
145    ) -> Result<CreateTenantOutput, ManagementError> {
146        let tenant = crate::tenant::create_tenant::create_tenant(
147            &self.server.db_provider,
148            &self.server.search,
149            &self.server.storage,
150            &self.server.secrets,
151            CreateTenantConfig {
152                id: input.id,
153                name: input.name,
154                env: input.env,
155                db_name: input.db_name,
156                db_role_name: input.db_role_name,
157                db_secret_name: input.db_secret_name,
158                db_iam_user: input.db_iam_user,
159                storage_bucket_name: input.storage_bucket_name,
160                storage_cors_origins: input.storage_cors_origins,
161                storage_s3_queue_arn: input.storage_s3_queue_arn,
162                search_index_name: input.search_index_name,
163                event_queue_url: input.event_queue_url,
164            },
165        )
166        .await
167        .map_err(DynServiceError::from)?;
168
169        Ok(CreateTenantOutput {
170            tenant: map_managed_tenant(tenant),
171        })
172    }
173
174    async fn get_tenant(&self, input: GetTenantInput) -> Result<GetTenantOutput, ManagementError> {
175        let tenant = crate::tenant::get_tenant::get_tenant(
176            &self.server.db_provider,
177            &input.env,
178            input.tenant_id,
179        )
180        .await
181        .map_err(ManagementDbErr)
182        .map_err(DynServiceError::from)?;
183
184        Ok(GetTenantOutput {
185            tenant: tenant.map(map_managed_tenant),
186        })
187    }
188
189    async fn delete_tenant(
190        &self,
191        input: DeleteTenantInput,
192    ) -> Result<DeleteTenantOutput, ManagementError> {
193        let tenant = crate::tenant::get_tenant::get_tenant(
194            &self.server.db_provider,
195            &input.env,
196            input.tenant_id,
197        )
198        .await
199        .map_err(ManagementDbErr)?
200        .ok_or(TenantNotFoundError)?;
201
202        // Must close the connections in advance to ensure the tenant
203        // database can be deleted
204        self.server.db_cache.close_tenant_pool(&tenant).await;
205
206        // Tell the API server to flush and close its database pools
207        self.flush_tenant_cache().await?;
208
209        crate::tenant::delete_tenant::delete_tenant(
210            &self.server.db_provider,
211            &self.server.search,
212            &self.server.storage,
213            &self.server.events,
214            &self.server.secrets,
215            DeleteTenant {
216                env: input.env,
217                tenant_id: input.tenant_id,
218                options: DeleteTenantOptions {
219                    delete_contents: input.options.delete_contents,
220                    delete_database: input.options.delete_database,
221                    delete_search: input.options.delete_search,
222                    delete_storage: input.options.delete_storage,
223                    permanently_delete_secret: input.options.permanently_delete_secret,
224                },
225            },
226        )
227        .await
228        .map_err(DynServiceError::from)?;
229
230        Ok(DeleteTenantOutput {})
231    }
232
233    async fn get_tenants(
234        &self,
235        input: GetTenantsInput,
236    ) -> Result<GetTenantsOutput, ManagementError> {
237        let mut tenants = crate::tenant::get_tenants::get_tenants(&self.server.db_provider)
238            .await
239            .map_err(ManagementDbErr)?;
240
241        if let Some(env) = input.env {
242            tenants.retain(|tenant| tenant.env.eq(&env));
243        }
244
245        Ok(GetTenantsOutput {
246            tenants: tenants.into_iter().map(map_managed_tenant).collect(),
247        })
248    }
249
250    async fn set_tenant_allowed_cors_origins(
251        &self,
252        input: SetTenantAllowedCorsOriginsInput,
253    ) -> Result<(), ManagementError> {
254        let tenant = crate::tenant::get_tenant::get_tenant(
255            &self.server.db_provider,
256            &input.env,
257            input.tenant_id,
258        )
259        .await
260        .map_err(ManagementDbErr)?
261        .ok_or(TenantNotFoundError)?;
262
263        let storage = self
264            .server
265            .storage
266            .create_layer(tenant.storage_layer_options());
267
268        storage
269            .set_bucket_cors_origins(input.origins)
270            .await
271            .map_err(|err| DynServiceError::from(ManagementStorageLayerErr(err)))?;
272
273        Ok(())
274    }
275
276    async fn migrate_root(&self) -> Result<(), ManagementError> {
277        crate::root::migrate_root::migrate_root(&self.server.db_provider, None)
278            .await
279            .map_err(DynServiceError::from)?;
280
281        Ok(())
282    }
283
284    async fn migrate_tenant(
285        &self,
286        input: MigrateTenantInput,
287    ) -> Result<MigrateTenantOutput, ManagementError> {
288        let target = ApplyServiceMigrationTarget {
289            env: input.env,
290            tenant_id: input.tenant_id,
291            name: input.name,
292            skip_failed: input.skip_failed,
293        };
294
295        if let Some(service) = input.service {
296            return self.apply_service_migration(service, &target).await;
297        }
298
299        let database_outcome = self
300            .apply_service_migration(TenantMigrationService::Database, &target)
301            .await?;
302
303        let search_outcome = self
304            .apply_service_migration(TenantMigrationService::Search, &target)
305            .await?;
306
307        let storage_outcome = self
308            .apply_service_migration(TenantMigrationService::Storage, &target)
309            .await?;
310
311        let applied_tenants: Vec<ManagementTenantTarget> = database_outcome
312            .applied_tenants
313            .into_iter()
314            .chain(search_outcome.applied_tenants)
315            .chain(storage_outcome.applied_tenants)
316            .collect();
317
318        let failed_tenants: Vec<FailedTenantMigration> = database_outcome
319            .failed_tenants
320            .into_iter()
321            .chain(search_outcome.failed_tenants)
322            .chain(storage_outcome.failed_tenants)
323            .collect();
324
325        Ok(MigrateTenantOutput {
326            applied_tenants,
327            failed_tenants,
328        })
329    }
330
331    async fn migrate_tenant_iam(
332        &self,
333        input: MigrateTenantIAMInput,
334    ) -> Result<MigrateTenantIAMOutput, ManagementError> {
335        let mut tenants = crate::tenant::get_tenants::get_tenants(&self.server.db_provider)
336            .await
337            .map_err(ManagementDbErr)?;
338
339        tenants.retain(|tenant| {
340            tenant.env.eq(&input.env) && input.tenant_id.is_none_or(|id| tenant.id.eq(&id))
341        });
342
343        let mut migrated_tenants = Vec::new();
344
345        for mut tenant in tenants {
346            if tenant.db_iam_user_name.is_some() {
347                tracing::debug!(?tenant, "skipping tenant with iam user name already set");
348                continue;
349            }
350
351            crate::tenant::migrate_tenant_secret_to_iam::migrate_tenant_secret_to_iam(
352                &self.server.db_provider,
353                &self.server.secrets,
354                &mut tenant,
355            )
356            .await
357            .map_err(DynServiceError::from)?;
358
359            migrated_tenants.push(ManagementTenantTarget {
360                id: tenant.id,
361                name: tenant.name,
362                env: tenant.env,
363            });
364        }
365
366        Ok(MigrateTenantIAMOutput {
367            applied_tenants: migrated_tenants,
368        })
369    }
370
371    async fn get_pending_root_migrations(&self) -> Result<Vec<String>, ManagementError> {
372        let pending_migrations =
373            crate::root::get_pending_root_migrations::get_pending_root_migrations(
374                &self.server.db_provider,
375            )
376            .await
377            .map_err(ManagementDbErr)?;
378        Ok(pending_migrations)
379    }
380
381    async fn get_tenant_pending_migrations(
382        &self,
383        input: GetTenantPendingMigrationsInput,
384    ) -> Result<GetTenantPendingMigrationsOutput, ManagementError> {
385        let tenant = crate::tenant::get_tenant::get_tenant(
386            &self.server.db_provider,
387            &input.env,
388            input.tenant_id,
389        )
390        .await
391        .map_err(ManagementDbErr)?
392        .ok_or(TenantNotFoundError)?;
393
394        if let Some(service) = input.service {
395            let migrations = self.get_service_migrations(service, &tenant).await?;
396            return Ok(GetTenantPendingMigrationsOutput { migrations });
397        }
398
399        let database_migrations = self
400            .get_service_migrations(TenantMigrationService::Database, &tenant)
401            .await?;
402        let search_migrations = self
403            .get_service_migrations(TenantMigrationService::Search, &tenant)
404            .await?;
405        let storage_migrations = self
406            .get_service_migrations(TenantMigrationService::Storage, &tenant)
407            .await?;
408
409        let migrations = database_migrations
410            .into_iter()
411            .chain(search_migrations)
412            .chain(storage_migrations)
413            .collect();
414
415        Ok(GetTenantPendingMigrationsOutput { migrations })
416    }
417
418    async fn flush_tenant_cache(&self) -> Result<(), ManagementError> {
419        crate::tenant::flush_tenant_cache::flush_tenant_cache(&self.config.api)
420            .await
421            .map_err(DynServiceError::from)?;
422
423        Ok(())
424    }
425}
426
427struct ApplyServiceMigrationTarget {
428    env: Option<String>,
429    tenant_id: Option<Uuid>,
430    name: Option<String>,
431    skip_failed: bool,
432}
433
434fn map_tenant_target(tenant: TenantTarget) -> ManagementTenantTarget {
435    ManagementTenantTarget {
436        env: tenant.env,
437        id: tenant.tenant_id,
438        name: tenant.name,
439    }
440}
441
442fn map_migrate_tenants_outcome(outcome: MigrateTenantsOutcome) -> MigrateTenantOutput {
443    MigrateTenantOutput {
444        applied_tenants: outcome
445            .applied_tenants
446            .into_iter()
447            .map(map_tenant_target)
448            .collect(),
449        failed_tenants: outcome
450            .failed_tenants
451            .into_iter()
452            .map(|(error, target)| FailedTenantMigration {
453                error,
454                target: map_tenant_target(target),
455            })
456            .collect(),
457    }
458}
459
460impl ManagedServerInterface {
461    async fn apply_service_migration(
462        &self,
463        service: TenantMigrationService,
464        target: &ApplyServiceMigrationTarget,
465    ) -> Result<MigrateTenantOutput, ManagementError> {
466        match service {
467            TenantMigrationService::Database => {
468                let outcome = crate::tenant::migrate_tenants::migrate_tenants(
469                    &self.server.db_provider,
470                    MigrateTenantsConfig {
471                        env: target.env.clone(),
472                        tenant_id: target.tenant_id,
473                        skip_failed: target.skip_failed,
474                        target_migration_name: target.name.clone(),
475                    },
476                )
477                .await
478                .map_err(DynServiceError::from)?;
479
480                Ok(map_migrate_tenants_outcome(outcome))
481            }
482            TenantMigrationService::Search => {
483                let outcome = crate::tenant::migrate_tenants_search::migrate_tenants_search(
484                    &self.server.db_provider,
485                    &self.server.search,
486                    MigrateTenantsSearchConfig {
487                        env: target.env.clone(),
488                        tenant_id: target.tenant_id,
489                        skip_failed: target.skip_failed,
490                        target_migration_name: target.name.clone(),
491                    },
492                )
493                .await
494                .map_err(DynServiceError::from)?;
495
496                Ok(map_migrate_tenants_outcome(outcome))
497            }
498            TenantMigrationService::Storage => {
499                let outcome = crate::tenant::migrate_tenants_storage::migrate_tenants_storage(
500                    &self.server.db_provider,
501                    &self.server.storage,
502                    MigrateTenantsStorageConfig {
503                        env: target.env.clone(),
504                        tenant_id: target.tenant_id,
505                        skip_failed: target.skip_failed,
506                        target_migration_name: target.name.clone(),
507                    },
508                )
509                .await
510                .map_err(DynServiceError::from)?;
511
512                Ok(map_migrate_tenants_outcome(outcome))
513            }
514        }
515    }
516
517    async fn get_service_migrations(
518        &self,
519        service: TenantMigrationService,
520        tenant: &Tenant,
521    ) -> Result<Vec<PendingMigration>, ManagementError> {
522        match service {
523            TenantMigrationService::Database => {
524                let pending_migrations =
525                    crate::tenant::get_pending_tenant_migrations::get_pending_tenant_migrations(
526                        &self.server.db_provider,
527                        tenant,
528                    )
529                    .await
530                    .map_err(ManagementDbErr)?;
531
532                Ok(pending_migrations
533                    .into_iter()
534                    .map(|name| PendingMigration { name, service })
535                    .collect())
536            }
537            TenantMigrationService::Search => {
538                let pending_migrations = crate::tenant::get_pending_tenant_storage_migrations::get_pending_tenant_storage_migrations(
539                    &self.server.db_provider,
540                    &self.server.storage,
541                    tenant,
542                )
543                .await
544                .map_err(DynServiceError::from)?;
545
546                Ok(pending_migrations
547                    .into_iter()
548                    .map(|name| PendingMigration { name, service })
549                    .collect())
550            }
551            TenantMigrationService::Storage => {
552                let pending_migrations = crate::tenant::get_pending_tenant_search_migrations::get_pending_tenant_search_migrations(
553                    &self.server.db_provider,
554                    &self.server.search,
555                    tenant,
556                )
557                .await    .map_err(DynServiceError::from)?;
558
559                Ok(pending_migrations
560                    .into_iter()
561                    .map(|name| PendingMigration { name, service })
562                    .collect())
563            }
564        }
565    }
566}