Skip to content

Commit

Permalink
fix(delete-env): delete services before calling cleanupEnvironment
Browse files Browse the repository at this point in the history
This addresses issues where services contain resources that the provider
isn't directly aware of, e.g. when Helm charts deploy cluster-wide resources.

Closes #855
  • Loading branch information
edvald authored and thsig committed Jun 20, 2019
1 parent 9f52ccd commit e98485d
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 11 deletions.
4 changes: 2 additions & 2 deletions docs/reference/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ Examples:

Deletes a running environment.

This will trigger providers to clear up any deployments in a Garden environment and reset it.
When you then run `garden init`, the environment will be reconfigured.
This will delete all services in the specified environment, and trigger providers to clear up any other resources
and reset it. When you then run `garden init` or `garden deploy`, the environment will be reconfigured.

This can be useful if you find the environment to be in an inconsistent state, or need/want to free up
resources.
Expand Down
17 changes: 16 additions & 1 deletion garden-service/src/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -331,11 +331,26 @@ export class ActionHelper implements TypeGuard {
msg: "Deleting...",
status: "active",
})
return this.callServiceHandler({

const status = await this.getServiceStatus({ ...params, hotReload: false })

if (status.state === "missing") {
log.setSuccess({
section: params.service.name,
msg: "Not found",
})
return status
}

const result = this.callServiceHandler({
params: { ...params, log },
actionType: "deleteService",
defaultHandler: dummyDeleteServiceHandler,
})

log.setSuccess()

return result
}

async execInService(params: ServiceActionHelperParams<ExecInServiceParams>): Promise<ExecInServiceResult> {
Expand Down
36 changes: 30 additions & 6 deletions garden-service/src/commands/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ import {
} from "./base"
import { NotFoundError } from "../exceptions"
import dedent = require("dedent")
import { ServiceStatus, getServiceRuntimeContext } from "../types/service"
import { ServiceStatus, getServiceRuntimeContext, ServiceStatusMap } from "../types/service"
import { printHeader } from "../logger/util"
import { DeleteSecretResult } from "../types/plugin/provider/deleteSecret"
import { EnvironmentStatusMap } from "../types/plugin/provider/getEnvironmentStatus"
import chalk from "chalk"

export class DeleteCommand extends Command {
name = "delete"
Expand Down Expand Up @@ -80,26 +81,49 @@ export class DeleteSecretCommand extends Command<typeof deleteSecretArgs> {
}
}

interface DeleteEnvironmentResult {
serviceStatuses: ServiceStatusMap
environmentStatuses: EnvironmentStatusMap
}

export class DeleteEnvironmentCommand extends Command {
name = "environment"
alias = "env"
help = "Deletes a running environment."

description = dedent`
This will trigger providers to clear up any deployments in a Garden environment and reset it.
When you then run \`garden init\`, the environment will be reconfigured.
This will delete all services in the specified environment, and trigger providers to clear up any other resources
and reset it. When you then run \`garden init\` or \`garden deploy\`, the environment will be reconfigured.
This can be useful if you find the environment to be in an inconsistent state, or need/want to free up
resources.
`

async action({ garden, log, headerLog }: CommandParams): Promise<CommandResult<EnvironmentStatusMap>> {
async action({ garden, log, headerLog }: CommandParams): Promise<CommandResult<DeleteEnvironmentResult>> {
printHeader(headerLog, `Deleting ${garden.environmentName} environment`, "skull_and_crossbones")

const actions = await garden.getActionHelper()
const result = await actions.cleanupEnvironment({ log })
const graph = await garden.getConfigGraph()

return { result }
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) => {
const runtimeContext = await getServiceRuntimeContext(garden, graph, service)
serviceStatuses[service.name] = await actions.deleteService({ log: servicesLog, service, runtimeContext })
})

servicesLog.setSuccess()

log.info("")

const envLog = log.info({ msg: chalk.white("Cleaning up environments..."), status: "active" })
const environmentStatuses = await actions.cleanupEnvironment({ log: envLog })
envLog.setSuccess()

return { result: { serviceStatuses, environmentStatuses } }
}
}

Expand Down
34 changes: 32 additions & 2 deletions garden-service/test/unit/src/commands/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,13 @@ describe("DeleteSecretCommand", () => {
})
})

const getServiceStatus = async (): Promise<ServiceStatus> => {
return { state: "ready" }
}

describe("DeleteEnvironmentCommand", () => {
let deletedServices: string[] = []

const testProvider: PluginFactory = () => {
const name = "test-plugin"

Expand All @@ -72,19 +78,35 @@ describe("DeleteEnvironmentCommand", () => {
return testEnvStatuses[name]
}

const deleteService = async ({ service }): Promise<ServiceStatus> => {
deletedServices.push(service.name)
return { state: "missing" }
}

return {
actions: {
cleanupEnvironment,
getEnvironmentStatus,
},
moduleActions: {
test: {
configure: configureTestModule,
getServiceStatus,
deleteService,
},
},
}
}

beforeEach(() => {
deletedServices = []
})

const projectRootB = getDataDir("test-project-b")
const command = new DeleteEnvironmentCommand()
const plugins = { "test-plugin": testProvider }

it("should destroy environment", async () => {
it("should delete environment with services", async () => {
const garden = await Garden.factory(projectRootB, { plugins })
const log = garden.log

Expand All @@ -97,7 +119,14 @@ describe("DeleteEnvironmentCommand", () => {
opts: withDefaultGlobalOpts({}),
})

expect(result!["test-plugin"]["ready"]).to.be.false
expect(result!.environmentStatuses["test-plugin"]["ready"]).to.be.false
expect(result!.serviceStatuses).to.eql({
"service-a": { state: "missing" },
"service-b": { state: "missing" },
"service-c": { state: "missing" },
"service-d": { state: "missing" },
})
expect(deletedServices).to.eql(["service-a", "service-b", "service-c", "service-d"])
})
})

Expand All @@ -122,6 +151,7 @@ describe("DeleteServiceCommand", () => {
moduleActions: {
test: {
configure: configureTestModule,
getServiceStatus,
deleteService,
},
},
Expand Down

0 comments on commit e98485d

Please sign in to comment.