Skip to content

Commit

Permalink
improvement(core): delete services in dep order
Browse files Browse the repository at this point in the history
The delete environment command now deletes services in dependency order.

That is, a service's dependants are deleted before it itself is deleted.

Added a new task class, DeleteServiceTask to accomplish this.
  • Loading branch information
thsig authored and edvald committed Nov 6, 2019
1 parent f69551e commit 7895c92
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 14 deletions.
23 changes: 18 additions & 5 deletions garden-service/src/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ import { ConfigureProviderParams, ConfigureProviderResult } from "./types/plugin
import { Task } from "./types/task"
import { ConfigureModuleParams, ConfigureModuleResult } from "./types/plugin/module/configure"
import { PluginContext } from "./plugin-context"
import { DeleteServiceTask, deletedServiceStatuses } from "./tasks/delete-service"

type TypeGuard = {
readonly [P in keyof (PluginActionParams | ModuleActionParams<any>)]: (...args: any[]) => Promise<any>
Expand Down Expand Up @@ -368,7 +369,10 @@ export class ActionRouter implements TypeGuard {
defaultHandler: dummyDeleteServiceHandler,
})

log.setSuccess()
log.setSuccess({
msg: chalk.green(`Done (took ${log.getDuration(1)} sec)`),
append: true,
})

return result
}
Expand Down Expand Up @@ -524,11 +528,20 @@ export class ActionRouter implements TypeGuard {
const servicesLog = log.info({ msg: chalk.white("Deleting services..."), status: "active" })

const services = await graph.getServices()
const serviceStatuses: { [key: string]: ServiceStatus } = {}

await Bluebird.map(services, async (service) => {
serviceStatuses[service.name] = await this.deleteService({ log: servicesLog, service })
})
const deleteResults = await this.garden.processTasks(
services.map((service) => {
return new DeleteServiceTask({
garden: this.garden,
graph,
service,
log: servicesLog,
includeDependants: true,
})
})
)

const serviceStatuses = deletedServiceStatuses(deleteResults)

servicesLog.setSuccess()

Expand Down
16 changes: 7 additions & 9 deletions garden-service/src/commands/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import Bluebird from "bluebird"
import { Command, CommandResult, CommandParams, StringParameter, StringsParameter } from "./base"
import { NotFoundError } from "../exceptions"
import dedent = require("dedent")
import { ServiceStatus, ServiceStatusMap } from "../types/service"
import dedent from "dedent"
import { ServiceStatusMap } from "../types/service"
import { printHeader } from "../logger/util"
import { DeleteSecretResult } from "../types/plugin/provider/deleteSecret"
import { EnvironmentStatusMap } from "../types/plugin/provider/getEnvironmentStatus"
import { DeleteServiceTask, deletedServiceStatuses } from "../tasks/delete-service"

export class DeleteCommand extends Command {
name = "delete"
Expand Down Expand Up @@ -133,14 +133,12 @@ export class DeleteServiceCommand extends Command {

printHeader(headerLog, "Delete service", "skull_and_crossbones")

const result: { [key: string]: ServiceStatus } = {}

const actions = await garden.getActionRouter()

await Bluebird.map(services, async (service) => {
result[service.name] = await actions.deleteService({ log, service })
const deleteServiceTasks = services.map((service) => {
return new DeleteServiceTask({ garden, graph, log, service })
})

const result = deletedServiceStatuses(await garden.processTasks(deleteServiceTasks))

return { result }
}
}
1 change: 1 addition & 0 deletions garden-service/src/tasks/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { splitLast } from "../util/util"

export type TaskType =
| "build"
| "delete-service"
| "deploy"
| "get-service-status"
| "get-task-result"
Expand Down
89 changes: 89 additions & 0 deletions garden-service/src/tasks/delete-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright (C) 2018 Garden Technologies, Inc. <info@garden.io>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import { LogEntry } from "../logger/log-entry"
import { BaseTask, TaskType } from "./base"
import { Service, ServiceStatus } from "../types/service"
import { Garden } from "../garden"
import { ConfigGraph } from "../config-graph"
import { TaskResults, TaskResult } from "../task-graph"

export interface DeleteServiceTaskParams {
garden: Garden
graph: ConfigGraph
service: Service
log: LogEntry
includeDependants?: boolean
}

export class DeleteServiceTask extends BaseTask {
type: TaskType = "delete-service"

private graph: ConfigGraph
private service: Service
private includeDependants: boolean

constructor({ garden, graph, log, service, includeDependants = false }: DeleteServiceTaskParams) {
super({ garden, log, force: false, version: service.module.version })
this.graph = graph
this.service = service
this.includeDependants = includeDependants
}

async getDependencies() {
if (!this.includeDependants) {
return []
}

// Note: We delete in _reverse_ dependency order, so we query for dependants
const deps = await this.graph.getDependants("service", this.getName(), false)

return deps.service.map((service) => {
return new DeleteServiceTask({
garden: this.garden,
graph: this.graph,
log: this.log,
service,
includeDependants: this.includeDependants,
})
})
}

getName() {
return this.service.name
}

getDescription() {
return `deleting service '${this.service.name}' (from module '${this.service.module.name}')`
}

async process(): Promise<ServiceStatus> {
const actions = await this.garden.getActionRouter()
let status: ServiceStatus

try {
status = await actions.deleteService({ log: this.log, service: this.service })
} catch (err) {
this.log.setError()
throw err
}

return status
}
}

export function deletedServiceStatuses(results: TaskResults): { [serviceName: string]: ServiceStatus } {
const deleted = <TaskResult[]>Object.values(results).filter((r) => r && r.type === "delete-service")
const statuses = {}

for (const res of deleted) {
statuses[res.name] = res.output
}

return statuses
}

0 comments on commit 7895c92

Please sign in to comment.