Skip to content

Commit

Permalink
Wait workspace active state
Browse files Browse the repository at this point in the history
Signed-off-by: Anatolii Bazko <abazko@redhat.com>
  • Loading branch information
tolusha committed Dec 3, 2020
1 parent d87017f commit 63ad677
Show file tree
Hide file tree
Showing 10 changed files with 44 additions and 42 deletions.
25 changes: 19 additions & 6 deletions src/api/che.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { che as chetypes } from '@eclipse-che/api'
import { CoreV1Api, V1Pod, Watch } from '@kubernetes/client-node'
import axios, { AxiosInstance } from 'axios'
import * as cp from 'child_process'
import { cli } from 'cli-ux'
import * as commandExists from 'command-exists'
import * as fs from 'fs-extra'
import * as https from 'https'
Expand Down Expand Up @@ -101,7 +102,7 @@ export class CheHelper {
}

async cheURL(namespace = ''): Promise<string> {
if (!await this.cheNamespaceExist(namespace)) {
if (!await this.kube.getNamespace(namespace)) {
throw new Error(`ERR_NAMESPACE_NO_EXIST - No namespace ${namespace} is found`)
}

Expand All @@ -119,7 +120,7 @@ export class CheHelper {
return this.flags['plugin-registry-url']
}
// check
if (!await this.cheNamespaceExist(namespace)) {
if (!await this.kube.getNamespace(namespace)) {
throw new Error(`ERR_NAMESPACE_NO_EXIST - No namespace ${namespace} is found`)
}

Expand Down Expand Up @@ -278,10 +279,6 @@ export class CheHelper {
throw new Error(`ERR_ROUTE_NO_EXIST - No route ${route_names} in namespace ${namespace}`)
}

async cheNamespaceExist(namespace = '') {
return this.kube.namespaceExist(namespace)
}

async createWorkspaceFromDevfile(cheApiEndpoint: string, devfilePath: string, workspaceName?: string, accessToken?: string): Promise<chetypes.workspace.Workspace> {
let devfile: string | undefined
try {
Expand Down Expand Up @@ -421,6 +418,22 @@ export class CheHelper {
() => { })
}

/**
* Wait until workspace is in 'Active` state.
*/
async waitNamespaceActive(namespaceName: string, intervalMs = 500, timeoutMs = 60000) {
const iterations = timeoutMs / intervalMs
for (let index = 0; index < iterations; index++) {
const namespace = await this.kube.getNamespace(namespaceName)
if (namespace && namespace.status && namespace.status.phase && namespace.status.phase === 'Active') {
return
}
await cli.wait(intervalMs)
}

throw new Error(`ERR_TIMEOUT: ${namespaceName} is not 'Active'.`)
}

/**
* Indicates if pod matches given labels.
*/
Expand Down
15 changes: 4 additions & 11 deletions src/api/kube.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/

import { ApiextensionsV1beta1Api, ApisApi, AppsV1Api, AuthorizationV1Api, BatchV1Api, CoreV1Api, CustomObjectsApi, ExtensionsV1beta1Api, ExtensionsV1beta1IngressList, KubeConfig, Log, PortForward, RbacAuthorizationV1Api, V1beta1CustomResourceDefinition, V1ClusterRole, V1ClusterRoleBinding, V1ConfigMap, V1ConfigMapEnvSource, V1Container, V1ContainerStateTerminated, V1ContainerStateWaiting, V1Deployment, V1DeploymentList, V1DeploymentSpec, V1EnvFromSource, V1Job, V1JobSpec, V1LabelSelector, V1NamespaceList, V1ObjectMeta, V1PersistentVolumeClaimList, V1Pod, V1PodCondition, V1PodList, V1PodSpec, V1PodTemplateSpec, V1PolicyRule, V1Role, V1RoleBinding, V1RoleRef, V1Secret, V1SelfSubjectAccessReview, V1SelfSubjectAccessReviewSpec, V1Service, V1ServiceAccount, V1ServiceList, V1Subject, Watch } from '@kubernetes/client-node'
import { ApiextensionsV1beta1Api, ApisApi, AppsV1Api, AuthorizationV1Api, BatchV1Api, CoreV1Api, CustomObjectsApi, ExtensionsV1beta1Api, ExtensionsV1beta1IngressList, KubeConfig, Log, PortForward, RbacAuthorizationV1Api, V1beta1CustomResourceDefinition, V1ClusterRole, V1ClusterRoleBinding, V1ConfigMap, V1ConfigMapEnvSource, V1Container, V1ContainerStateTerminated, V1ContainerStateWaiting, V1Deployment, V1DeploymentList, V1DeploymentSpec, V1EnvFromSource, V1Job, V1JobSpec, V1LabelSelector, V1Namespace, V1NamespaceList, V1ObjectMeta, V1PersistentVolumeClaimList, V1Pod, V1PodCondition, V1PodList, V1PodSpec, V1PodTemplateSpec, V1PolicyRule, V1Role, V1RoleBinding, V1RoleRef, V1Secret, V1SelfSubjectAccessReview, V1SelfSubjectAccessReviewSpec, V1Service, V1ServiceAccount, V1ServiceList, V1Subject, Watch } from '@kubernetes/client-node'
import { Cluster, Context } from '@kubernetes/client-node/dist/config_types'
import axios, { AxiosRequestConfig } from 'axios'
import { cli } from 'cli-ux'
Expand Down Expand Up @@ -581,19 +581,12 @@ export class KubeHelper {
}
}

async namespaceExist(namespace: string) {
async getNamespace(namespace: string): Promise<V1Namespace | undefined> {
const k8sApi = KubeHelper.KUBE_CONFIG.makeApiClient(CoreV1Api)
try {
const res = await k8sApi.readNamespace(namespace)
if (res && res.body &&
res.body.metadata && res.body.metadata.name
&& res.body.metadata.name === namespace) {
return true
} else {
return false
}
const { body } = await k8sApi.readNamespace(namespace)
return body
} catch {
return false
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/commands/cacert/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export default class Export extends Command {
if (!await kube.hasReadPermissionsForNamespace(flags.chenamespace)) {
throw new Error(`E_PERM_DENIED - Permission denied: no read access to '${flags.chenamespace}' namespace`)
}
if (!await cheHelper.cheNamespaceExist(flags.chenamespace)) {
if (!await kube.getNamespace(flags.chenamespace)) {
throw new Error(`E_BAD_NS - Namespace ${flags.chenamespace} does not exist. Please specify it with --chenamespace flag`)
}

Expand Down
3 changes: 0 additions & 3 deletions src/commands/server/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -396,9 +396,6 @@ export default class Deploy extends Command {
await postInstallTasks.run(ctx)
this.log(getCommandSuccessMessage())
}

await postInstallTasks.run(ctx)
this.log(getCommandSuccessMessage())
} catch (err) {
this.error(getCommandErrorMessage(err))
}
Expand Down
2 changes: 1 addition & 1 deletion src/commands/workspace/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export default class Delete extends Command {
}

const kube = new KubeHelper(flags)
if (await kube.namespaceExist(infrastructureNamespace)) {
if (await kube.getNamespace(infrastructureNamespace)) {
try {
await kube.deleteNamespace(infrastructureNamespace)
cli.log(`Namespace '${infrastructureNamespace}' deleted.`)
Expand Down
9 changes: 3 additions & 6 deletions src/tasks/che.ts
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ export class CheTasks {
return [{
title: `Delete namespace ${flags.chenamespace}`,
task: async (task: any) => {
const namespaceExist = await this.kube.namespaceExist(flags.chenamespace)
const namespaceExist = await this.kube.getNamespace(flags.chenamespace)
if (namespaceExist) {
await this.kube.deleteNamespace(flags.chenamespace)
}
Expand All @@ -508,7 +508,7 @@ export class CheTasks {
return [{
title: `Verify if namespace '${flags.chenamespace}' exists`,
task: async () => {
if (!await this.che.cheNamespaceExist(flags.chenamespace)) {
if (!await this.kube.getNamespace(flags.chenamespace)) {
command.error(`E_BAD_NS - Namespace does not exist.\nThe Kubernetes Namespace "${flags.chenamespace}" doesn't exist.`, { code: 'EBADNS' })
}
}
Expand Down Expand Up @@ -636,10 +636,7 @@ export class CheTasks {

const cheUrl = await this.che.cheURL(flags.chenamespace)
messages.push(`Users Dashboard : ${cheUrl}`)
const cheCluster = await this.kube.getCheCluster(flags.chenamespace)
if (cheCluster && cheCluster.spec.auth && cheCluster.spec.auth.updateAdminPassword) {
messages.push('Admin user login : "admin:admin". NOTE: must change after first login.')
}
messages.push('Admin user login : "admin:admin". NOTE: must change after first login.')
messages.push(OUTPUT_SEPARATOR)

const cheConfigMap = await this.kube.getConfigMap('che', flags.chenamespace)
Expand Down
2 changes: 1 addition & 1 deletion src/tasks/component-installers/cert-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class CertManagerTasks {
title: 'Check Cert Manager deployment',
task: async (ctx: any, task: any) => {
// Check only one CRD of cert-manager assuming that it is installed or not.
ctx.certManagerInstalled = await this.kubeHelper.namespaceExist(CERT_MANAGER_NAMESPACE_NAME) && await this.kubeHelper.crdExist('certificates.cert-manager.io')
ctx.certManagerInstalled = await this.kubeHelper.getNamespace(CERT_MANAGER_NAMESPACE_NAME) && await this.kubeHelper.crdExist('certificates.cert-manager.io')
if (ctx.certManagerInstalled) {
task.title = `${task.title}...already deployed`
} else {
Expand Down
8 changes: 6 additions & 2 deletions src/tasks/installers/common-tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,15 @@ export function createNamespaceTask(namespaceName: string, labels: {}): Listr.Li
title: `Create Namespace (${namespaceName})`,
task: async (_ctx: any, task: any) => {
const kube = new KubeHelper()
const exist = await kube.namespaceExist(namespaceName)
if (exist) {
const che = new CheHelper({})

const namespace = await kube.getNamespace(namespaceName)
if (namespace) {
await che.waitNamespaceActive(namespaceName)
task.title = `${task.title}...It already exists.`
} else {
await kube.createNamespace(namespaceName, labels)
await che.waitNamespaceActive(namespaceName)
task.title = `${task.title}...Done.`
}
}
Expand Down
6 changes: 2 additions & 4 deletions src/tasks/installers/helm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import { copy, mkdirp, remove } from 'fs-extra'
import * as Listr from 'listr'
import * as path from 'path'

import { CheHelper } from '../../api/che'
import { KubeHelper } from '../../api/kube'
import { VersionHelper } from '../../api/version'
import { CHE_ROOT_CA_SECRET_NAME, CHE_TLS_SECRET_NAME, DEFAULT_CHE_IMAGE } from '../../constants'
Expand Down Expand Up @@ -65,9 +64,8 @@ export class HelmTasks {
{
title: `Create Namespace (${flags.chenamespace})`,
task: async (_ctx: any, task: any) => {
const che = new CheHelper(flags)
const exist = await che.cheNamespaceExist(flags.chenamespace)
if (exist) {
const kube = new KubeHelper(flags)
if (await kube.getNamespace(flags.chenamespace)) {
task.title = `${task.title}...does already exist.`
} else {
await execa(`kubectl create namespace ${flags.chenamespace}`, { shell: true })
Expand Down
14 changes: 7 additions & 7 deletions test/api/che.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ let k8sApi = new CoreV1Api()
describe('Eclipse Che helper', () => {
describe('cheURL', () => {
fancy
.stub(ch, 'cheNamespaceExist', () => true)
.stub(kube, 'getNamespace', () => ({}))
.stub(kube, 'ingressExist', () => true)
.stub(kube, 'getIngressProtocol', () => 'https')
.stub(kube, 'getIngressHost', () => 'example.org')
Expand All @@ -39,13 +39,13 @@ describe('Eclipse Che helper', () => {
expect(cheURL).to.equals('https://example.org')
})
fancy
.stub(ch, 'cheNamespaceExist', () => true)
.stub(kube, 'getNamespace', () => ({}))
.stub(kube, 'ingressExist', () => false)
.do(() => ch.cheURL('che-namespace'))
.catch(err => expect(err.message).to.match(/ERR_INGRESS_NO_EXIST/))
.it('fails fetching Eclipse Che URL when ingress does not exist')
fancy
.stub(ch, 'cheNamespaceExist', () => true)
.stub(kube, 'getNamespace', () => ({}))
.stub(ctx, 'get', () => ({ isOpenShift: true }))
.stub(oc, 'routeExist', () => true)
.stub(oc, 'getRouteProtocol', () => 'https')
Expand All @@ -55,14 +55,14 @@ describe('Eclipse Che helper', () => {
expect(cheURL).to.equals('https://example.org')
})
fancy
.stub(ch, 'cheNamespaceExist', () => true)
.stub(kube, 'getNamespace', () => ({}))
.stub(ctx, 'get', () => ({ isOpenShift: true }))
.stub(oc, 'routeExist', () => false)
.do(() => ch.cheURL('che-namespace'))
.catch(/ERR_ROUTE_NO_EXIST/)
.it('fails fetching Eclipse Che URL when route does not exist')
fancy
.stub(ch, 'cheNamespaceExist', () => false)
.stub(kube, 'getNamespace', () => undefined)
.do(() => ch.cheURL('che-namespace'))
.catch(err => expect(err.message).to.match(/ERR_NAMESPACE_NO_EXIST/))
.it('fails fetching Eclipse Che URL when namespace does not exist')
Expand All @@ -72,14 +72,14 @@ describe('Eclipse Che helper', () => {
.stub(KubeHelper.KUBE_CONFIG, 'makeApiClient', () => k8sApi)
.stub(k8sApi, 'readNamespace', jest.fn().mockImplementation(() => { throw new Error() }))
.it('founds out that a namespace doesn\'t exist', async () => {
const res = await ch.cheNamespaceExist(namespace)
const res = !!await kube.getNamespace(namespace)
expect(res).to.equal(false)
})
fancy
.stub(KubeHelper.KUBE_CONFIG, 'makeApiClient', () => k8sApi)
.stub(k8sApi, 'readNamespace', () => ({ response: '', body: { metadata: { name: `${namespace}` } } }))
.it('founds out that a namespace does exist', async () => {
const res = await ch.cheNamespaceExist(namespace)
const res = !!await kube.getNamespace(namespace)
expect(res).to.equal(true)
})
})
Expand Down

0 comments on commit 63ad677

Please sign in to comment.