Skip to content

Commit

Permalink
fix(server): some resources missing when deleting app (#875)
Browse files Browse the repository at this point in the history
  • Loading branch information
maslow authored Mar 9, 2023
1 parent 82b963d commit 2b792b0
Show file tree
Hide file tree
Showing 16 changed files with 328 additions and 236 deletions.
277 changes: 86 additions & 191 deletions server/package-lock.json

Large diffs are not rendered by default.

4 changes: 1 addition & 3 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
"dependencies": {
"@aws-sdk/client-s3": "^3.245.0",
"@aws-sdk/client-sts": "^3.226.0",
"@hokify/agenda": "^6.3.0",
"@kubernetes/client-node": "^0.17.1",
"@nestjs/axios": "^1.0.0",
"@nestjs/common": "^9.0.0",
Expand All @@ -47,7 +46,7 @@
"dotenv": "^16.0.3",
"fast-json-patch": "^3.1.1",
"lodash": "^4.17.21",
"mongodb": "^4.12.1",
"mongodb": "^5.1.0",
"mongodb-uri": "^0.9.7",
"nanoid": "^3.3.4",
"npm-package-arg": "^10.1.0",
Expand All @@ -67,7 +66,6 @@
"@types/express": "^4.17.13",
"@types/jest": "28.1.8",
"@types/lodash": "^4.14.191",
"@types/mongodb": "^4.0.7",
"@types/mongodb-uri": "^0.9.1",
"@types/node": "^16.0.0",
"@types/npm-package-arg": "^6.1.1",
Expand Down
8 changes: 4 additions & 4 deletions server/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,9 @@ model Bundle {
}

model ApplicationBundle {
id String @id @default(auto()) @map("_id") @db.ObjectId
appid String @unique
bundleId String @db.ObjectId
id String @id @default(auto()) @map("_id") @db.ObjectId
appid String @unique
bundleId String @db.ObjectId
name String
displayName String
resource BundleResource
Expand Down Expand Up @@ -333,7 +333,7 @@ model DatabasePolicyRule {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
policy DatabasePolicy @relation(fields: [appid, policyName], references: [appid, name])
policy DatabasePolicy @relation(fields: [appid, policyName], references: [appid, name], onDelete: Cascade)
@@unique([appid, policyName, collectionName])
}
Expand Down
115 changes: 93 additions & 22 deletions server/src/application/application-task.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ import { RegionService } from 'src/region/region.service'
import { RuntimeDomainService } from 'src/gateway/runtime-domain.service'
import { ServerConfig, TASK_LOCK_INIT_TIME } from 'src/constants'
import { SystemDatabase } from 'src/database/system-database'
import { TriggerService } from 'src/trigger/trigger.service'
import { FunctionService } from 'src/function/function.service'
import { ApplicationConfigurationService } from './configuration.service'
import { BundleService } from 'src/region/bundle.service'
import { WebsiteService } from 'src/website/website.service'
import { PolicyService } from 'src/database/policy/policy.service'
import { BucketDomainService } from 'src/gateway/bucket-domain.service'

@Injectable()
export class ApplicationTaskService {
Expand All @@ -27,7 +34,14 @@ export class ApplicationTaskService {
private readonly clusterService: ClusterService,
private readonly storageService: StorageService,
private readonly databaseService: DatabaseService,
private readonly gatewayService: RuntimeDomainService,
private readonly runtimeDomainService: RuntimeDomainService,
private readonly bucketDomainService: BucketDomainService,
private readonly triggerService: TriggerService,
private readonly functionService: FunctionService,
private readonly configurationService: ApplicationConfigurationService,
private readonly bundleService: BundleService,
private readonly websiteService: WebsiteService,
private readonly policyService: PolicyService,
) {}

@Cron(CronExpression.EVERY_SECOND)
Expand All @@ -52,7 +66,7 @@ export class ApplicationTaskService {
* Phase `Creating`:
* - create namespace
* - create storage user
* - create gateway domain
* - create runtime domain
* - create database & user
* - move phase `Creating` to `Created`
*/
Expand Down Expand Up @@ -110,15 +124,15 @@ export class ApplicationTaskService {
database = await this.databaseService.create(app.appid)
}

// reconcile gateway
let gateway = await this.gatewayService.findOne(appid)
if (!gateway) {
// reconcile runtime domain
let runtimeDomain = await this.runtimeDomainService.findOne(appid)
if (!runtimeDomain) {
this.logger.log(`Creating gateway for application ${appid}`)
gateway = await this.gatewayService.create(appid)
runtimeDomain = await this.runtimeDomainService.create(appid)
}

// waiting resources' phase to be `Created`
if (gateway?.phase !== DomainPhase.Created) {
if (runtimeDomain?.phase !== DomainPhase.Created) {
return await this.unlock(appid)
}

Expand Down Expand Up @@ -149,7 +163,17 @@ export class ApplicationTaskService {

/**
* Phase `Deleting`:
* - delete namespace, storage, database, gateway
* - delete triggers (k8s cronjob)
* - delete cloud functions
* - delete policies
* - delete application configuration
* - delete application bundle
* - delete website
* - delete runtime domain (apisix route)
* - delete bucket domain (apisix route)
* - delete database (mongo db)
* - delete storage (minio buckets & user)
* - delete namespace
* - move phase `Deleting` to `Deleted`
*/
async handleDeletingPhase() {
Expand Down Expand Up @@ -178,35 +202,82 @@ export class ApplicationTaskService {
const region = await this.regionService.findByAppId(appid)
assert(region, `Region ${region.name} not found`)

// delete namespace (include the instance)
const namespace = await this.clusterService.getAppNamespace(region, appid)
if (namespace) {
await this.clusterService.removeAppNamespace(region, appid)
// delete triggers
const hadTriggers = await this.triggerService.count(appid)
if (hadTriggers > 0) {
await this.triggerService.removeAll(appid)
return await this.unlock(appid)
}

// delete storage
const storage = await this.storageService.findOne(appid)
if (storage) {
await this.storageService.delete(appid)
// delete cloud functions
const hadFunctions = await this.functionService.count(appid)
if (hadFunctions > 0) {
await this.functionService.removeAll(appid)
return await this.unlock(appid)
}

// delete database proxy policies
const hadPolicies = await this.policyService.count(appid)
if (hadPolicies > 0) {
await this.policyService.removeAll(appid)
return await this.unlock(appid)
}

// delete application configuration
const hadConfigurations = await this.configurationService.count(appid)
if (hadConfigurations > 0) {
await this.configurationService.remove(appid)
return await this.unlock(appid)
}

// delete application bundle
const bundle = await this.bundleService.findApplicationBundle(appid)
if (bundle) {
await this.bundleService.deleteApplicationBundle(appid)
return await this.unlock(appid)
}

// delete website
const hadWebsites = await this.websiteService.count(appid)
if (hadWebsites > 0) {
await this.websiteService.removeAll(appid)
return await this.unlock(appid)
}

// delete runtime domain
const runtimeDomain = await this.runtimeDomainService.findOne(appid)
if (runtimeDomain) {
await this.runtimeDomainService.delete(appid)
return await this.unlock(appid)
}

// delete bucket domains
const hadBucketDomains = await this.bucketDomainService.count(appid)
if (hadBucketDomains > 0) {
await this.bucketDomainService.deleteAll(appid)
return await this.unlock(appid)
}

// delete database
// delete application database
const database = await this.databaseService.findOne(appid)
if (database) {
await this.databaseService.delete(database)
return await this.unlock(appid)
}

// delete gateway
const gateway = await this.gatewayService.findOne(appid)
if (gateway) {
await this.gatewayService.delete(appid)
// delete application storage
const storage = await this.storageService.findOne(appid)
if (storage) {
await this.storageService.deleteUsersAndBuckets(appid)
return await this.unlock(appid)
}

// TODO: delete app configuration & bundle
// delete application namespace (include the instance)
const namespace = await this.clusterService.getAppNamespace(region, appid)
if (namespace) {
await this.clusterService.removeAppNamespace(region, appid)
return await this.unlock(appid)
}

// update phase to `Deleted`
const updated = await db.collection<Application>('Application').updateOne(
Expand Down
6 changes: 6 additions & 0 deletions server/src/application/application.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { EnvironmentVariableController } from './environment.controller'
import { StorageModule } from '../storage/storage.module'
import { DatabaseModule } from 'src/database/database.module'
import { GatewayModule } from 'src/gateway/gateway.module'
import { ApplicationConfigurationService } from './configuration.service'
import { TriggerService } from 'src/trigger/trigger.service'
import { WebsiteService } from 'src/website/website.service'

@Module({
imports: [StorageModule, DatabaseModule, GatewayModule],
Expand All @@ -21,6 +24,9 @@ import { GatewayModule } from 'src/gateway/gateway.module'
JwtService,
FunctionService,
EnvironmentVariableService,
ApplicationConfigurationService,
TriggerService,
WebsiteService,
],
exports: [ApplicationService],
})
Expand Down
18 changes: 14 additions & 4 deletions server/src/application/application.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,13 @@ export class ApplicationService {
data.state = dto.state
}

const application = await this.prisma.application.update({
where: { appid },
const application = await this.prisma.application.updateMany({
where: {
appid,
phase: {
notIn: [ApplicationPhase.Deleting, ApplicationPhase.Deleted],
},
},
data,
})

Expand All @@ -135,8 +140,13 @@ export class ApplicationService {

async remove(appid: string) {
try {
const res = await this.prisma.application.update({
where: { appid },
const res = await this.prisma.application.updateMany({
where: {
appid,
phase: {
in: [ApplicationPhase.Started, ApplicationPhase.Stopped],
},
},
data: {
state: ApplicationState.Deleted,
},
Expand Down
25 changes: 25 additions & 0 deletions server/src/application/configuration.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Injectable, Logger } from '@nestjs/common'
import { PrismaService } from 'src/prisma/prisma.service'

@Injectable()
export class ApplicationConfigurationService {
private readonly logger = new Logger(ApplicationConfigurationService.name)

constructor(private readonly prisma: PrismaService) {}

async count(appid: string) {
return this.prisma.applicationConfiguration.count({
where: {
appid,
},
})
}

async remove(appid: string) {
return this.prisma.applicationConfiguration.delete({
where: {
appid,
},
})
}
}
4 changes: 3 additions & 1 deletion server/src/application/dto/update-application.dto.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ApiPropertyOptional } from '@nestjs/swagger'
import { ApplicationState } from '@prisma/client'
import { IsIn } from 'class-validator'
import { IsIn, IsString, Length } from 'class-validator'

const STATES = [
ApplicationState.Running,
Expand All @@ -12,6 +12,8 @@ export class UpdateApplicationDto {
* Application name
*/
@ApiPropertyOptional()
@IsString()
@Length(1, 64)
name?: string

@ApiPropertyOptional({
Expand Down
8 changes: 4 additions & 4 deletions server/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,19 +56,19 @@ export class ServerConfig {
/* switcher of task controllers */

static get DISABLED_INSTANCE_TASK() {
return process.env.DISABLED_INSTANCE_TASK || false
return process.env.DISABLED_INSTANCE_TASK === 'true'
}

static get DISABLED_APPLICATION_TASK() {
return process.env.DISABLED_APPLICATION_TASK || false
return process.env.DISABLED_APPLICATION_TASK === 'true'
}

static get DISABLED_GATEWAY_TASK() {
return process.env.DISABLED_GATEWAY_TASK || false
return process.env.DISABLED_GATEWAY_TASK === 'true'
}

static get DISABLED_STORAGE_TASK() {
return process.env.DISABLED_STORAGE_TASK || false
return process.env.DISABLED_STORAGE_TASK === 'true'
}

static get APPID_LENGTH(): number {
Expand Down
16 changes: 16 additions & 0 deletions server/src/database/policy/policy.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,22 @@ export class PolicyService {
return res
}

async removeAll(appid: string) {
// delete rules first
await this.prisma.databasePolicyRule.deleteMany({
where: {
appid,
},
})

const res = await this.prisma.databasePolicy.deleteMany({
where: {
appid,
},
})
return res
}

async publish(policy: DatabasePolicy & { rules: DatabasePolicyRule[] }) {
const { db, client } = await this.databaseService.findAndConnect(
policy.appid,
Expand Down
7 changes: 7 additions & 0 deletions server/src/function/function.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,13 @@ export class FunctionService {
return res
}

async removeAll(appid: string) {
const res = await this.prisma.cloudFunction.deleteMany({
where: { appid },
})
return res
}

async publish(func: CloudFunction) {
const { db, client } = await this.databaseService.findAndConnect(func.appid)
const session = client.startSession()
Expand Down
Loading

0 comments on commit 2b792b0

Please sign in to comment.