Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add workspace:delete command #586

Merged
merged 7 commits into from
Mar 19, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 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`](#chectl-workspacedelete)
* [`chectl workspace:inject`](#chectl-workspaceinject)
* [`chectl workspace:list`](#chectl-workspacelist)
* [`chectl workspace:logs`](#chectl-workspacelogs)
Expand Down Expand Up @@ -411,6 +412,29 @@ USAGE

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

## `chectl workspace:delete`

delete workspace

```
USAGE
$ chectl workspace:delete

OPTIONS
-h, --help show CLI help

-n, --chenamespace=chenamespace [default: che] Kubernetes namespace where Eclipse Che server is supposed to
be deployed

-w, --workspace=workspace (required) The workspace id to delete

--access-token=access-token Eclipse Che OIDC Access Token

--listr-renderer=default|silent|verbose [default: default] Listr renderer
```

_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
64 changes: 53 additions & 11 deletions src/api/che.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ import * as fs from 'fs-extra'
import * as https from 'https'
import * as yaml from 'js-yaml'
import * as path from 'path'

import { OpenShiftHelper } from '../api/openshift'

import { Devfile } from './devfile'
import { KubeHelper } from './kube'



export class CheHelper {
defaultCheResponseTimeoutMs = 3000
kc = new KubeConfig()
Expand Down 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
102 changes: 102 additions & 0 deletions src/commands/workspace/delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*********************************************************************
* 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 * as Listrq from 'listr'
import * as notifier from 'node-notifier'

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

export default class Delete extends Command {
static description = 'delete workspace'

static flags = {
help: flags.help({ char: 'h' }),
chenamespace: cheNamespace,
workspace: flags.string({
char: 'w',
description: 'The workspace id to delete',
required: true
}),
'access-token': accessToken,
'listr-renderer': listrRenderer
}

async run() {
const { flags } = 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: flags['listr-renderer'] as any })

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 '${flags.workspace}'`,
task: async (ctx, task) => {
const workspace = await cheHelper.getWorkspace(ctx.cheURL, flags.workspace, flags['access-token'])
ctx.infrastructureNamespace = workspace.attributes.infrastructureNamespace
task.title = `${task.title}... done`
}
})
tasks.add({
title: `Delete workspace with id '${flags.workspace}'`,
task: async (ctx, task) => {
await cheHelper.deleteWorkspace(ctx.cheURL, flags.workspace, flags['access-token'])
task.title = `${task.title}... done`
}
})
tasks.add({
title: 'Verify if namespace exists',
skip: ctx => ctx.infrastructureNamespace === flags.chenamespace,
task: async (ctx, task) => {
task.title = `${task.title} '${ctx.infrastructureNamespace}'`
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',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello. @tolusha workspace can be configured to start workspace with the same namespace with Che...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes. that's why I check

skip: ctx => ctx.infrastructureNamespace === flags.chenamespace,

if it is the same namespace then I won't delete it

skip: ctx => !ctx.infrastructureNamespaceExists,
task: async (ctx, task) => {
task.title = `${task.title} '${ctx.infrastructureNamespace}'`
await kubeHelper.deleteNamespace(ctx.infrastructureNamespace)
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)
}
}
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
2 changes: 1 addition & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1469,7 +1469,7 @@ ecc-jsbn@~0.1.1:

"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#27714b57f96c1fc5052574dafd0a9b688f70332d"

editorconfig@^0.15.0:
version "0.15.3"
Expand Down