Skip to content

Commit

Permalink
feat: add workspace:delete command (#586)
Browse files Browse the repository at this point in the history
* Add `workspace:delete` command

Signed-off-by: Anatoliy Bazko <[email protected]>
  • Loading branch information
tolusha authored Mar 19, 2020
1 parent 2ae158b commit a3c72dc
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 22 deletions.
36 changes: 30 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ USAGE
* [`chectl server:stop`](#chectl-serverstop)
* [`chectl server:update`](#chectl-serverupdate)
* [`chectl update [CHANNEL]`](#chectl-update-channel)
* [`chectl workspace:delete WORKSPACE`](#chectl-workspacedelete-workspace)
* [`chectl workspace:inject`](#chectl-workspaceinject)
* [`chectl workspace:list`](#chectl-workspacelist)
* [`chectl workspace:logs`](#chectl-workspacelogs)
Expand Down Expand Up @@ -411,6 +412,31 @@ USAGE

_See code: [@oclif/plugin-update](https://github.com/oclif/plugin-update/blob/v1.3.9/src/commands/update.ts)_

## `chectl workspace:delete WORKSPACE`

delete a user's workspace

```
USAGE
$ chectl workspace:delete WORKSPACE
ARGUMENTS
WORKSPACE The workspace id to delete
OPTIONS
-h, --help show CLI help
-n, --chenamespace=chenamespace [default: che] Kubernetes namespace where Eclipse Che server is supposed to be
deployed
--access-token=access-token Eclipse Che OIDC Access Token
--delete-namespace Indicates that a Kubernetes namespace where workspace was created will be deleted as
well
```

_See code: [src/commands/workspace/delete.ts](https://github.com/che-incubator/chectl/blob/v0.0.2/src/commands/workspace/delete.ts)_

## `chectl workspace:inject`

inject configurations and tokens in a workspace
Expand Down Expand Up @@ -448,14 +474,12 @@ USAGE
$ chectl workspace:list
OPTIONS
-h, --help show CLI help
-h, --help show CLI help
-n, --chenamespace=chenamespace [default: che] Kubernetes namespace where Eclipse Che server is supposed to
be deployed
-n, --chenamespace=chenamespace [default: che] Kubernetes namespace where Eclipse Che server is supposed to be
deployed
--access-token=access-token Eclipse Che OIDC Access Token
--listr-renderer=default|silent|verbose [default: default] Listr renderer
--access-token=access-token Eclipse Che OIDC Access Token
```

_See code: [src/commands/workspace/list.ts](https://github.com/che-incubator/chectl/blob/v0.0.2/src/commands/workspace/list.ts)_
Expand Down
60 changes: 51 additions & 9 deletions src/api/che.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,44 @@ export class CheHelper {
}
}

/**
* Get workspace.
*/
async getWorkspace(cheUrl: string, workspaceId: string, accessToken = ''): Promise<any> {
const endpoint = `${cheUrl}/api/workspace/${workspaceId}`
const headers: any = { 'Content-Type': 'text/yaml' }
if (accessToken && accessToken.length > 0) {
headers.Authorization = `${accessToken}`
}

try {
const response = await this.axios.get(endpoint, { headers })
return response.data
} catch (error) {
throw this.getCheApiError(error, endpoint)
}
}

/**
* Deletes workspace.
*/
async deleteWorkspace(cheUrl: string, workspaceId: string, accessToken = ''): Promise<void> {
const endpoint = `${cheUrl}/api/workspace/${workspaceId}`
const headers: any = {}
if (accessToken) {
headers.Authorization = `${accessToken}`
}

try {
await this.axios.delete(endpoint, { headers })
} catch (error) {
if (error.response.status === 409) {
throw new Error(`Workspace '${workspaceId}' not found`)
}
throw this.getCheApiError(error, endpoint)
}
}

/**
* Indicates if pod matches given labels.
*/
Expand Down Expand Up @@ -444,16 +482,20 @@ export class CheHelper {
}

private getCheApiError(error: any, endpoint: string): Error {
if (error.response && error.response.status === 403) {
return new Error(`E_CHE_API_FORBIDDEN - Endpoint: ${endpoint} - Message: ${JSON.stringify(error.response.data.message)}`)
}
if (error.response && error.response.status === 401) {
return new Error(`E_CHE_API_UNAUTHORIZED - Endpoint: ${endpoint} - Message: ${JSON.stringify(error.response.data)}`)
}
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
return new Error(`E_CHE_API_UNKNOWN_ERROR - Endpoint: ${endpoint} -Status: ${error.response.status}`)
const status = error.response.status
if (status === 403) {
return new Error(`E_CHE_API_FORBIDDEN - Endpoint: ${endpoint} - Message: ${JSON.stringify(error.response.data.message)}`)
} else if (status === 401) {
return new Error(`E_CHE_API_UNAUTHORIZED - Endpoint: ${endpoint} - Message: ${JSON.stringify(error.response.data)}`)
} else if (status === 404) {
return new Error(`E_CHE_API_NOTFOUND - Endpoint: ${endpoint} - Message: ${JSON.stringify(error.response.data)}`)
} else {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
return new Error(`E_CHE_API_UNKNOWN_ERROR - Endpoint: ${endpoint} -Status: ${error.response.status}`)
}

} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
Expand Down
9 changes: 9 additions & 0 deletions src/api/kube.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1211,6 +1211,15 @@ export class KubeHelper {
}
}

async deleteNamespace(namespace: string): Promise<void> {
const k8sCoreApi = this.kc.makeApiClient(CoreV1Api)
try {
await k8sCoreApi.deleteNamespace(namespace)
} catch (e) {
throw this.wrapK8sClientError(e)
}
}

async clusterIssuerExists(name: string): Promise<boolean> {
const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi)

Expand Down
117 changes: 117 additions & 0 deletions src/commands/workspace/delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*********************************************************************
* Copyright (c) 2019 Red Hat, Inc.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/

import { Command, flags } from '@oclif/command'
import { cli } from 'cli-ux'
import * as Listrq from 'listr'
import * as notifier from 'node-notifier'

import { CheHelper } from '../../api/che'
import { KubeHelper } from '../../api/kube'
import { accessToken, cheNamespace } from '../../common-flags'
import { CheTasks } from '../../tasks/che'
import { ApiTasks } from '../../tasks/platforms/api'

export default class Delete extends Command {
static description = 'delete a user\'s workspace'

static flags = {
help: flags.help({ char: 'h' }),
chenamespace: cheNamespace,
'delete-namespace': flags.boolean({
description: 'Indicates that a Kubernetes namespace where workspace was created will be deleted as well',
default: false
}),
'access-token': accessToken
}
static args = [
{
name: 'workspace',
description: 'The workspace id to delete',
required: true
}
]

async run() {
const { flags } = this.parse(Delete)
const { args } = this.parse(Delete)
const ctx: any = {}
ctx.workspaces = []

const apiTasks = new ApiTasks()
const cheTasks = new CheTasks(flags)
const cheHelper = new CheHelper(flags)
const kubeHelper = new KubeHelper(flags)
const tasks = new Listrq(undefined, { renderer: 'silent' })

tasks.add(apiTasks.testApiTasks(flags, this))
tasks.add(cheTasks.verifyCheNamespaceExistsTask(flags, this))
tasks.add(cheTasks.retrieveEclipseCheUrl(flags))
tasks.add(cheTasks.checkEclipseCheStatus())
tasks.add({
title: `Get workspace with id '${args.workspace}'`,
task: async (ctx, task) => {
const workspace = await cheHelper.getWorkspace(ctx.cheURL, args.workspace, flags['access-token'])
ctx.infrastructureNamespace = workspace.attributes.infrastructureNamespace
task.title = `${task.title}... done`
}
})
tasks.add({
title: `Delete workspace with id '${args.workspace}'`,
task: async (ctx, task) => {
await cheHelper.deleteWorkspace(ctx.cheURL, args.workspace, flags['access-token'])
cli.log(`Workspace with id '${args.workspace}' deleted.`)
task.title = `${task.title}... done`
}
})
tasks.add({
title: 'Verify if namespace exists',
enabled: () => flags['delete-namespace'],
task: async (ctx, task) => {
task.title = `${task.title} '${ctx.infrastructureNamespace}'`
if (ctx.infrastructureNamespace === flags.chenamespace) {
cli.warn(`It is not possible to delete namespace '${ctx.infrastructureNamespace}' since it is used for Eclipse Che deployment.`)
return
}

ctx.infrastructureNamespaceExists = await kubeHelper.namespaceExist(ctx.infrastructureNamespace)
if (ctx.infrastructureNamespaceExists) {
task.title = `${task.title}... found`
} else {
task.title = `${task.title}... not found`
}
}
})
tasks.add({
title: 'Delete namespace',
skip: ctx => !ctx.infrastructureNamespaceExists,
enabled: () => flags['delete-namespace'],
task: async (ctx, task) => {
task.title = `${task.title} '${ctx.infrastructureNamespace}'`
await kubeHelper.deleteNamespace(ctx.infrastructureNamespace)
cli.log(`Namespace '${ctx.infrastructureNamespace}' deleted.`)
task.title = `${task.title}... done`
}
})

try {
await tasks.run(ctx)
} catch (error) {
this.error(error)
}

notifier.notify({
title: 'chectl',
message: 'Command workspace:delete has completed successfully.'
})

this.exit(0)
}
}
7 changes: 3 additions & 4 deletions src/commands/workspace/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { cli } from 'cli-ux'
import * as Listrq from 'listr'

import { CheHelper } from '../../api/che'
import { accessToken, cheNamespace, listrRenderer } from '../../common-flags'
import { accessToken, cheNamespace } from '../../common-flags'
import { CheTasks } from '../../tasks/che'
import { ApiTasks } from '../../tasks/platforms/api'

Expand All @@ -23,8 +23,7 @@ export default class List extends Command {
static flags = {
help: flags.help({ char: 'h' }),
chenamespace: cheNamespace,
'access-token': accessToken,
'listr-renderer': listrRenderer
'access-token': accessToken
}

async run() {
Expand All @@ -34,7 +33,7 @@ export default class List extends Command {

const apiTasks = new ApiTasks()
const cheTasks = new CheTasks(flags)
const tasks = new Listrq(undefined, { renderer: flags['listr-renderer'] as any })
const tasks = new Listrq(undefined, { renderer: 'silent' })

tasks.add(apiTasks.testApiTasks(flags, this))
tasks.add(cheTasks.verifyCheNamespaceExistsTask(flags, this))
Expand Down
2 changes: 1 addition & 1 deletion src/tasks/che.ts
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ export class CheTasks {

verifyCheNamespaceExistsTask(flags: any, command: Command): ReadonlyArray<Listr.ListrTask> {
return [{
title: `Verify if namespace ${flags.chenamespace} exists`,
title: `Verify if namespace '${flags.chenamespace}' exists`,
task: async () => {
if (!await this.che.cheNamespaceExist(flags.chenamespace)) {
command.error(`E_BAD_NS - Namespace does not exist.\nThe Kubernetes Namespace "${flags.chenamespace}" doesn't exist. The configuration cannot be injected.\nFix with: verify the namespace where workspace is running (kubectl get --all-namespaces deployment | grep workspace)`, { code: 'EBADNS' })
Expand Down
4 changes: 2 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1517,11 +1517,11 @@ ecc-jsbn@~0.1.1:

"eclipse-che-operator@git://github.com/eclipse/che-operator#master":
version "0.0.0"
resolved "git://github.com/eclipse/che-operator#016e7f11cd7011db496425c3a0a357116d440cb0"
resolved "git://github.com/eclipse/che-operator#0952fa99a3aa2b88e798047c9e86eb42e9bbd124"

"eclipse-che@git://github.com/eclipse/che#master":
version "0.0.0"
resolved "git://github.com/eclipse/che#59f9e41be62586c174d10da0485e3ba66588ef33"
resolved "git://github.com/eclipse/che#9eab046a344a6a5f3a25691dbc3ac038a78cf576"

editorconfig@^0.15.0:
version "0.15.3"
Expand Down

0 comments on commit a3c72dc

Please sign in to comment.