Skip to content

Commit

Permalink
chore: Rework delete tasks (#2331)
Browse files Browse the repository at this point in the history
Signed-off-by: Anatolii Bazko <[email protected]>

Signed-off-by: Anatolii Bazko <[email protected]>
  • Loading branch information
tolusha authored Oct 20, 2022
1 parent 41d1964 commit 9f191aa
Show file tree
Hide file tree
Showing 12 changed files with 224 additions and 329 deletions.
118 changes: 54 additions & 64 deletions src/api/kube.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,27 +140,6 @@ export class KubeHelper {
}
}

async deleteAllServices(namespace: string): Promise<void> {
const k8sApi = this.kubeConfig.makeApiClient(CoreV1Api)
try {
const res = await k8sApi.listNamespacedService(namespace)
if (res && res.response && res.response.statusCode === 200) {
const serviceList = res.body
await serviceList.items.forEach(async service => {
try {
await k8sApi.deleteNamespacedService(service.metadata!.name!, namespace)
} catch (error: any) {
if (error.response.statusCode !== 404) {
throw error
}
}
})
}
} catch (e: any) {
throw this.wrapK8sClientError(e)
}
}

async applyResource(yamlPath: string, opts = ''): Promise<void> {
const command = `kubectl apply -f ${yamlPath} ${opts}`
await execa(command, { timeout: 60000, shell: true })
Expand Down Expand Up @@ -568,10 +547,10 @@ export class KubeHelper {
}
}

async listConfigMaps(namespace: string): Promise<V1ConfigMap[]> {
async listConfigMaps(namespace: string, labelSelector: string | undefined): Promise<V1ConfigMap[]> {
const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api)
try {
const { body } = await k8sCoreApi.listNamespacedConfigMap(namespace)
const { body } = await k8sCoreApi.listNamespacedConfigMap(namespace, undefined, undefined, undefined, undefined, labelSelector)
return body.items
} catch (e: any) {
throw this.wrapK8sClientError(e)
Expand Down Expand Up @@ -679,7 +658,7 @@ export class KubeHelper {
}
}

async patchCustomResource(name: string, patch: any, resourceAPIGroup: string, resourceAPIVersion: string, resourcePlural: string): Promise<any | undefined> {
async patchClusterCustomObject(name: string, patch: any, resourceAPIGroup: string, resourceAPIVersion: string, resourcePlural: string): Promise<any | undefined> {
const k8sCoreApi = this.kubeConfig.makeApiClient(CustomObjectsApi)

// It is required to patch content-type, otherwise request will be rejected with 415 (Unsupported media type) error.
Expand Down Expand Up @@ -1030,6 +1009,18 @@ export class KubeHelper {
}
}

async deletePod(name: string, namespace: string): Promise<void> {
const k8sApi = this.kubeConfig.makeApiClient(CoreV1Api)
try {
await k8sApi.deleteNamespacedPod(name, namespace)
} catch (e: any) {
if (e.response && e.response.statusCode === 404) {
return
}
throw this.wrapK8sClientError(e)
}
}

async replaceDeployment(yamlDeployment: V1Deployment): Promise<void> {
// updating restartedAt to make sure that rollout will be restarted
let annotations = yamlDeployment.spec!.template!.metadata!.annotations
Expand Down Expand Up @@ -1067,15 +1058,6 @@ export class KubeHelper {
}
}

async deleteAllDeployments(namespace: string): Promise<void> {
const k8sAppsApi = this.kubeConfig.makeApiClient(AppsV1Api)
try {
await k8sAppsApi.deleteCollectionNamespacedDeployment(namespace)
} catch (e: any) {
throw this.wrapK8sClientError(e)
}
}

async getDeployment(name: string, namespace: string): Promise<V1Deployment | undefined> {
const k8sAppsApi = this.kubeConfig.makeApiClient(AppsV1Api)
try {
Expand Down Expand Up @@ -1115,16 +1097,7 @@ export class KubeHelper {
}
}

async deleteAllIngresses(namespace: string): Promise<void> {
const networkingV1Api = this.kubeConfig.makeApiClient(NetworkingV1Api)
try {
await networkingV1Api.deleteCollectionNamespacedIngress(namespace)
} catch (e: any) {
throw this.wrapK8sClientError(e)
}
}

async createCrdFromFile(crd: V1CustomResourceDefinition): Promise<void> {
async createCrd(crd: V1CustomResourceDefinition): Promise<void> {
const k8sApi = this.kubeConfig.makeApiClient(ApiextensionsV1Api)
try {
await k8sApi.createCustomResourceDefinition(crd)
Expand All @@ -1133,7 +1106,7 @@ export class KubeHelper {
}
}

async replaceCrdFromFile(crd: V1CustomResourceDefinition): Promise<void> {
async replaceCustomResourceDefinition(crd: V1CustomResourceDefinition): Promise<void> {
const k8sApi = this.kubeConfig.makeApiClient(ApiextensionsV1Api)
try {
const response = await k8sApi.readCustomResourceDefinition(crd.metadata!.name!)
Expand All @@ -1145,7 +1118,7 @@ export class KubeHelper {
}
}

async getCrd(name: string): Promise<any | undefined> {
async getCustomResourceDefinition(name: string): Promise<any | undefined> {
const k8sApi = this.kubeConfig.makeApiClient(ApiextensionsV1Api)
try {
const { body } = await k8sApi.readCustomResourceDefinition(name)
Expand All @@ -1154,7 +1127,6 @@ export class KubeHelper {
if (e.response && e.response.statusCode === 404) {
return
}

throw this.wrapK8sClientError(e)
}
}
Expand Down Expand Up @@ -1262,7 +1234,7 @@ export class KubeHelper {
async getAllCheClusters(): Promise<any[]> {
for (let i = 0; i < 30; i++) {
try {
return await this.listCustomResources(CHE_CLUSTER_API_GROUP, CHE_CLUSTER_API_VERSION_V2, CHE_CLUSTER_KIND_PLURAL)
return await this.listClusterCustomObject(CHE_CLUSTER_API_GROUP, CHE_CLUSTER_API_VERSION_V2, CHE_CLUSTER_KIND_PLURAL)
} catch (e: any) {
if (isWebhookAvailabilityError(e)) {
await sleep(5 * 1000)
Expand All @@ -1275,10 +1247,27 @@ export class KubeHelper {
return []
}

async deleteAllCustomResources(apiGroup: string, version: string, plural: string): Promise<void> {
async deleteAllCustomResourcesAndCrd(crdName: string, apiGroup: string, version: string, plural: string): Promise<void> {
const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi)

let resources = await this.listCustomResources(apiGroup, version, plural)
const crd = await this.getCustomResourceDefinition(crdName)
if (!crd) {
return
}

// 1. Disable conversion webhook
await this.patchClusterCustomObject(crdName, {spec: {conversion: null}}, 'apiextensions.k8s.io', 'v1', 'customresourcedefinitions')

// 2. Patch CRD to unblock potential invalid resource
for (let i = 0; i < crd.spec.versions.length; i++) {
if (crd.spec.versions[i].schema?.openAPIV3Schema?.properties?.spec) {
crd.spec.versions[i].schema.openAPIV3Schema.properties.spec = {type: 'object', properties: {}}
}
}
await this.replaceCustomResourceDefinition(crd)

// 3. Delete resources
let resources = await this.listClusterCustomObject(apiGroup, version, plural)
for (const resource of resources) {
const name = resource.metadata.name
const namespace = resource.metadata.namespace
Expand All @@ -1289,34 +1278,39 @@ export class KubeHelper {
}
}

// wait and check
for (let i = 0; i < 12; i++) {
await cli.wait(5 * 1000)
const resources = await this.listCustomResources(apiGroup, version, plural)
await cli.wait(5000)
const resources = await this.listClusterCustomObject(apiGroup, version, plural)
if (resources.length === 0) {
return
break
}
}

// remove finalizers
// 4. Remove finalizers
resources = await this.listClusterCustomObject(apiGroup, version, plural)
for (const resource of resources) {
const name = resource.metadata.name
const namespace = resource.metadata.namespace
try {
await this.patchNamespacedCustomResource(name, namespace, { metadata: { finalizers: null } }, apiGroup, version, plural)
} catch (error) {
if (!await this.getCustomResource(name, namespace, apiGroup, version, plural)) {
continue // successfully removed
if (error.cause?.body?.reason === 'NotFound') {
continue
}
throw error
}
}

// wait for some time and check again
// wait and delete CRD
await cli.wait(5000)
await this.deleteCustomResourceDefinition(crdName)

resources = await this.listCustomResources(apiGroup, version, plural)
// wait and check
await cli.wait(5000)
resources = await this.listClusterCustomObject(apiGroup, version, plural)
if (resources.length !== 0) {
throw new Error(`Failed to remove Custom Resource ${apiGroup}/${version}, ${resources.length} left.`)
throw new Error(`Failed to remove Custom Resources: ${plural}${apiGroup}, ${resources.length} resource(s) left.`)
}
}

Expand Down Expand Up @@ -1365,7 +1359,7 @@ export class KubeHelper {
/**
* Returns all custom resources
*/
async listCustomResources(resourceAPIGroup: string, resourceAPIVersion: string, resourcePlural: string): Promise<any[]> {
async listClusterCustomObject(resourceAPIGroup: string, resourceAPIVersion: string, resourcePlural: string): Promise<any[]> {
const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi)
try {
const { body } = await customObjectsApi.listClusterCustomObject(resourceAPIGroup, resourceAPIVersion, resourcePlural)
Expand All @@ -1379,10 +1373,6 @@ export class KubeHelper {
}
}

async deleteAllCheClusters(namespace: string): Promise<void> {
return this.deleteCustomResource(namespace, CHE_CLUSTER_API_GROUP, CHE_CLUSTER_API_VERSION_V2, CHE_CLUSTER_KIND_PLURAL)
}

/**
* Deletes custom resources in the given namespace.
*/
Expand Down Expand Up @@ -1734,7 +1724,7 @@ export class KubeHelper {
}
}

async deleteCrd(name: string): Promise<void> {
async deleteCustomResourceDefinition(name: string): Promise<void> {
const k8sApi = this.kubeConfig.makeApiClient(ApiextensionsV1Api)
try {
await k8sApi.deleteCustomResourceDefinition(name)
Expand Down
6 changes: 0 additions & 6 deletions src/api/openshift.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,4 @@ export class OpenShiftHelper {
const { stdout } = await execa(command, args, { timeout: 60000 })
return stdout.trim().includes(name)
}

async deleteAllRoutes(namespace = '') {
const command = 'oc'
const args = ['delete', 'route', '--all', '--namespace', namespace]
await execa(command, args, { timeout: 60000 })
}
}
42 changes: 22 additions & 20 deletions src/commands/server/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,30 +85,29 @@ export default class Delete extends Command {
title: 'Uninstall Dev Workspace Operator',
task: async (_ctx: any, _task: any) => {
if (flags['delete-all']) {
const olmDevWorkspaceTasks = new OLMDevWorkspaceTasks(flags)
const devWorkspaceTasks = new DevWorkspaceTasks(flags)

const tasks = new Listrq([], ctx.listrOptions)

tasks.add({
title: 'Delete Custom Resources',
task: () => new Listr(devWorkspaceTasks.getDeleteCRsTasks()),
title: 'Delete operator resources',
task: () => {
const devWorkspaceTasks = new DevWorkspaceTasks(flags)
if (ctx[ChectlContext.IS_OPENSHIFT]) {
return new Listr(devWorkspaceTasks.getDeleteTasks(OPENSHIFT_OPERATORS_NAMESPACE))
} else {
return new Listr(devWorkspaceTasks.getDeleteTasks(WORKSPACE_CONTROLLER_NAMESPACE))
}
},
})

let devWorkspaceNamespace = WORKSPACE_CONTROLLER_NAMESPACE
if (await olmDevWorkspaceTasks.isDevWorkspaceOperatorInstalledViaOLM()) {
devWorkspaceNamespace = OPENSHIFT_OPERATORS_NAMESPACE

if (ctx[ChectlContext.IS_OPENSHIFT]) {
tasks.add({
title: 'Delete OLM resources',
task: () => new Listr(olmDevWorkspaceTasks.getDeleteTasks()),
task: () => {
const olmDevWorkspaceTasks = new OLMDevWorkspaceTasks(flags)
return new Listr(olmDevWorkspaceTasks.getDeleteTasks())
},
})
}

tasks.add({
title: 'Delete operator resources',
task: () => new Listr(devWorkspaceTasks.getDeleteTasks(devWorkspaceNamespace)),
})

return tasks
}
},
Expand All @@ -117,14 +116,15 @@ export default class Delete extends Command {
tasks.add({
title: 'Uninstall Eclipse Che Operator',
task: async (ctx: any, _task: any) => {
const operatorTasks = new OperatorInstaller(flags)
const cheTasks = new CheTasks(flags)

const tasks = new Listrq([], ctx.listrOptions)
tasks.add({
title: 'Delete operator resources',
task: () => new Listr(operatorTasks.getDeleteTasks()),
task: () => {
const operatorTasks = new OperatorInstaller(flags)
return new Listr(operatorTasks.getDeleteTasks())
},
})

if (ctx[ChectlContext.IS_OPENSHIFT]) {
let olmInstaller: Installer
if (getProjectName() === DSC_PROJECT_NAME) {
Expand All @@ -137,6 +137,8 @@ export default class Delete extends Command {
task: () => new Listr(olmInstaller.getDeleteTasks()),
})
}

const cheTasks = new CheTasks(flags)
tasks.add({
title: 'Wait until all pods are deleted',
task: () => new Listr(cheTasks.getWaitPodsDeletedTasks()),
Expand Down
14 changes: 8 additions & 6 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,11 @@ export const CHE_CLUSTER_API_VERSION_V2 = 'v2'
export const CHE_CLUSTER_KIND_PLURAL = 'checlusters'

export const DEVFILE_WORKSPACE_API_GROUP = 'workspace.devfile.io'
export const DEVFILE_WORKSPACE_API_VERSION = 'v1alpha1'
export const DEVFILE_WORKSPACE_KIND_PLURAL = 'devworkspaces'

export const DEVFILE_WORKSPACE_ROUTINGS_API_GROUP = 'controller.devfile.io'
export const DEVFILE_WORKSPACE_ROUTINGS_VERSION = 'v1alpha1'
export const DEVFILE_WORKSPACE_ROUTINGS_KIND_PLURAL = 'devworkspaceroutings'
export const DEVFILE_WORKSPACE_API_VERSION = 'v1alpha2'
export const DEVWORKSPACES_KIND_PLURAL = 'devworkspaces'
export const DEVWORKSPACETEMPLATES_KIND_PLURAL = 'devworkspacetemplates'

export const DEVFILE_CONTROLLER_API_GROUP = 'controller.devfile.io'
export const DEVFILE_CONTROLLER_API_VERSION = 'v1alpha1'
export const DEVWORKSPACEROUTINGS_KIND_PLURAL = 'devworkspaceroutings'
export const DEVWORKSPACEOPERATORCONFIGS_KIND_PLURAL = 'devworkspaceoperatorconfigs'
2 changes: 1 addition & 1 deletion src/tasks/components/cert-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export class CertManagerTasks {
{
title: 'Install Cert Manager',
task: async (ctx: any, task: any) => {
const certManagerCrd = await this.kubeHelper.getCrd('certificates.cert-manager.io')
const certManagerCrd = await this.kubeHelper.getCustomResourceDefinition('certificates.cert-manager.io')
if (certManagerCrd) {
task.title = `${task.title}...[Exists]`
} else {
Expand Down
Loading

0 comments on commit 9f191aa

Please sign in to comment.