Skip to content

Commit

Permalink
fix: fix destroy env command after kubernetes-client upgrade
Browse files Browse the repository at this point in the history
  • Loading branch information
eysi09 committed May 15, 2018
1 parent eeadf16 commit 200fd01
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 85 deletions.
66 changes: 3 additions & 63 deletions src/commands/environment/destroy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,85 +6,25 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import { every, reduce } from "lodash"
import { PluginContext } from "../../plugin-context"

import { Command } from "../base"
import { EntryStyle } from "../../logger/types"
import { EnvironmentStatus, EnvironmentStatusMap } from "../../types/plugin"
import { LogEntry } from "../../logger"
import { sleep } from "../../util"
import { TimeoutError } from "../../exceptions"

const WAIT_FOR_SHUTDOWN_TIMEOUT = 600

export type LogEntryMap = { [key: string]: LogEntry }

const providersTerminated = (status: EnvironmentStatusMap): boolean => every(status, s => s.configured === false)
import { EnvironmentStatusMap } from "../../types/plugin"

export class EnvironmentDestroyCommand extends Command {
name = "destroy"
alias = "d"
help = "Destroy environment"

async action(ctx: PluginContext) {
async action(ctx: PluginContext): Promise<EnvironmentStatusMap> {
const { name } = ctx.getEnvironment()
ctx.log.header({ emoji: "skull_and_crossbones", command: `Destroying ${name} environment` })

let result: EnvironmentStatusMap
let logEntries: LogEntryMap = {}

result = await ctx.destroyEnvironment()

if (!providersTerminated(result)) {
ctx.log.info("Waiting for providers to terminate")
logEntries = reduce(result, (acc: LogEntryMap, status: EnvironmentStatus, provider: string) => {
if (status.configured) {
acc[provider] = ctx.log.info({
section: provider,
msg: "Terminating",
entryStyle: EntryStyle.activity,
})
}
return acc
}, {})

result = await this.waitForShutdown(ctx, name, logEntries)
}
const result = await ctx.destroyEnvironment()

ctx.log.finish()

return result
}

async waitForShutdown(ctx: PluginContext, name: string, logEntries: LogEntryMap) {
const startTime = new Date().getTime()
let result: EnvironmentStatusMap

while (true) {
await sleep(2000)

result = await ctx.getEnvironmentStatus()

Object.keys(result).forEach(key => {
if (result[key].configured && logEntries[key]) {
logEntries[key].setSuccess("Terminated")
}
})

if (providersTerminated(result)) {
break
}

const now = new Date().getTime()
if (now - startTime > WAIT_FOR_SHUTDOWN_TIMEOUT * 1000) {
throw new TimeoutError(
`Timed out waiting for ${name} delete to complete`,
{ environmentStatus: result },
)
}
}

return result
}
}
36 changes: 30 additions & 6 deletions src/plugins/kubernetes/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import * as inquirer from "inquirer"
import * as Joi from "joi"

import { DeploymentError, NotFoundError } from "../../exceptions"
import { DeploymentError, NotFoundError, TimeoutError } from "../../exceptions"
import {
ConfigureEnvironmentParams,
DeleteConfigParams,
Expand All @@ -34,7 +34,7 @@ import {
ContainerModule,
} from "../container"
import { values, every, uniq } from "lodash"
import { deserializeKeys, prompt, serializeKeys, splitFirst } from "../../util"
import { deserializeKeys, prompt, serializeKeys, splitFirst, sleep } from "../../util"
import { ServiceStatus } from "../../types/service"
import { joiIdentifier } from "../../types/common"
import {
Expand All @@ -49,6 +49,7 @@ import {
getAllAppNamespaces,
} from "./namespace"
import {
KUBECTL_DEFAULT_TIMEOUT,
kubectl,
} from "./kubectl"
import { DEFAULT_TEST_TIMEOUT } from "../../constants"
Expand Down Expand Up @@ -134,22 +135,45 @@ export async function getServiceStatus(params: GetServiceStatusParams<ContainerM
return await checkDeploymentStatus(params)
}

export async function destroyEnvironment({ ctx, provider }: DestroyEnvironmentParams) {
const context = provider.config.context
export async function destroyEnvironment({ ctx, provider, env }: DestroyEnvironmentParams) {
const { context } = provider.config
const namespace = await getAppNamespace(ctx, provider)
const entry = ctx.log.info({
section: "kubernetes",
msg: `Deleting namespace ${namespace}`,
entryStyle: EntryStyle.activity,
})

try {
await coreApi(context).namespace(namespace).delete(namespace)
entry.setSuccess("Finished")
// Note: Need to call the delete method with an empty object
await coreApi(context).namespaces(namespace).delete({})
} catch (err) {
entry.setError(err.message)
const availableNamespaces = await getAllAppNamespaces(context)
throw new NotFoundError(err, { namespace, availableNamespaces })
}

// Wait until namespace has been deleted
const startTime = new Date().getTime()
while (true) {
await sleep(2000)

const status = await getEnvironmentStatus({ ctx, provider, env })

if (!status.configured) {
entry.setSuccess("Finished")
break
}

const now = new Date().getTime()
if (now - startTime > KUBECTL_DEFAULT_TIMEOUT * 1000) {
throw new TimeoutError(
`Timed out waiting for namespace ${namespace} delete to complete`,
{ status },
)
}
}

}

export async function getServiceOutputs({ service }: GetServiceOutputsParams<ContainerModule>) {
Expand Down
1 change: 0 additions & 1 deletion src/plugins/kubernetes/namespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

import { PluginContext } from "../../plugin-context"
import {
apiGetOrNull,
coreApi,
} from "./api"
import { KubernetesProvider } from "./index"
Expand Down
15 changes: 0 additions & 15 deletions test/src/commands/environment/destroy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,4 @@ describe("EnvironmentDestroyCommand", () => {
expect(result["test-plugin"]["configured"]).to.be.false
})

it("should wait until each provider is no longer configured", async () => {
const garden = await Garden.factory(projectRootB, { plugins: [testProvider] })

td.replace(
command,
"waitForShutdown",
async () => ({
"test-plugin": { configured: false },
}),
)

const result = await command.action(garden.pluginContext)

expect(result["test-plugin"]["configured"]).to.be.false
})
})

0 comments on commit 200fd01

Please sign in to comment.