Skip to content

Commit

Permalink
fix(plugins/plugin-kubectl): kubernetes namespace list does not updat…
Browse files Browse the repository at this point in the history
…e after namespace deletion

Fixes #6521
  • Loading branch information
starpit committed Jan 11, 2021
1 parent f6d0a98 commit c28c800
Show file tree
Hide file tree
Showing 10 changed files with 62 additions and 43 deletions.
4 changes: 2 additions & 2 deletions plugins/plugin-kubectl/components/src/CurrentContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ export default class CurrentContext extends React.PureComponent<{}, State> {
return last && now - last < 250
}

private async reportCurrentContext(idx?: Tab | number) {
const tab = getTab(idx)
private async reportCurrentContext(idx?: Tab | number | string) {
const tab = getTab(typeof idx === 'string' ? undefined : idx)
if (!tab || !tab.REPL) {
if (tab && !tab.REPL) {
eventChannelUnsafe.once(`/tab/new/${tab.uuid}`, () => this.reportCurrentContext())
Expand Down
4 changes: 2 additions & 2 deletions plugins/plugin-kubectl/components/src/CurrentNamespace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ export default class CurrentNamespace extends React.PureComponent<{}, State> {
return last && now - last < 250
}

private async reportCurrentNamespace(idx?: Tab | number) {
const tab = getTab(idx)
private async reportCurrentNamespace(idx?: Tab | number | string) {
const tab = getTab(typeof idx === 'string' ? undefined : idx)
if (!tab || !tab.REPL) {
if (tab && !tab.REPL) {
eventChannelUnsafe.once(`/tab/new/${tab.uuid}`, () => this.reportCurrentNamespace())
Expand Down
7 changes: 6 additions & 1 deletion plugins/plugin-kubectl/oc/src/controller/oc/get/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ import {
export default function registerOcProjectGet(registrar: Registrar) {
registrar.listen(`/${commandPrefix}/oc/project`, async args => {
const response = await doExecWithStdout(args, undefined, 'oc')
emitKubectlConfigChangeEvent(args)

const newNamespace = args.argvNoOptions[args.argvNoOptions.indexOf('project') + 1]
if (newNamespace) {
emitKubectlConfigChangeEvent('SetNamespaceOrContext', newNamespace)
}

return response
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ import { KubeOptions, fileOf, getLabel, getNamespace } from '../../kubectl/optio
import status from './status'
import handleErrors from './errors'
import { urlFormatterForArgs } from './url'
import { headersForPlainRequest as headers } from './headers'

import { FinalState } from '../../../lib/model/states'
import { getCommandFromArgs } from '../../../lib/util/util'
import { headersForPlainRequest as headers } from './headers'

const debug = Debug('plugin-kubectl/controller/client/direct/create')

Expand Down
4 changes: 4 additions & 0 deletions plugins/plugin-kubectl/src/controller/client/direct/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import makeWatchable, { DirectWatcher, SingleKindDirectWatcher } from './watch'
import { Explained } from '../../kubectl/explain'
import { KubeOptions } from '../../kubectl/options'
import { isResourceReady } from '../../kubectl/status'
import { emitKubectlConfigChangeEvent } from '../../kubectl/config'

import { FinalState } from '../../../lib/model/states'
import { getCommandFromArgs } from '../../../lib/util/util'
Expand Down Expand Up @@ -203,6 +204,9 @@ export default async function watchMulti(
if (nNotReady === 0) {
// sub-case 1: nothing to watch, as everything is already "ready"
debug('special case: single-group watching, all-ready all ready!', nNotReady, groups[0])
if (groups[0].explainedKind.kind === 'Namespace') {
emitKubectlConfigChangeEvent('CreateOrDeleteNamespace')
}
return tables[0].table
} else {
// sub-case 2: a subset may be done, but we need to fire up a
Expand Down
20 changes: 15 additions & 5 deletions plugins/plugin-kubectl/src/controller/client/direct/watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,18 @@
import Debug from 'debug'
import { Abortable, Arguments, FlowControllable, Row, Table, Watchable, Watcher, WatchPusher } from '@kui-shell/core'

import { toKuiTable } from '../../../lib/view/formatTable'
import { fetchFile, openStream } from '../../../lib/util/fetch-file'
import { KubeOptions, withKubeconfigFrom } from '../../kubectl/options'

import URLFormatter from './url'
import { headersForTableRequest } from './headers'

import { FinalState } from '../../../lib/model/states'
import { isResourceReady } from '../../kubectl/status'
import { toKuiTable } from '../../../lib/view/formatTable'
import { fetchFile, openStream } from '../../../lib/util/fetch-file'
import { MetaTable, isMetaTable } from '../../../lib/model/resource'

import { isResourceReady } from '../../kubectl/status'
import { emitKubectlConfigChangeEvent } from '../../kubectl/config'
import { KubeOptions, withKubeconfigFrom } from '../../kubectl/options'

const debug = Debug('plugin-kubectl/client/direct/watch')

/** The apiServer will emit a stream of these messages */
Expand Down Expand Up @@ -296,6 +298,14 @@ export class SingleKindDirectWatcher extends DirectWatcher implements Abortable,
this.pusher.offline(row.rowKey)
}

if (this.kind === 'Namespace') {
if (update.type === 'ADDED') {
emitKubectlConfigChangeEvent('CreateOrDeleteNamespace', row.name)
} else if (update.type === 'DELETED') {
emitKubectlConfigChangeEvent('CreateOrDeleteNamespace', row.name)
}
}

this.checkIfReady(row, idx, update)
})

Expand Down
8 changes: 7 additions & 1 deletion plugins/plugin-kubectl/src/controller/client/proxy/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,13 @@ function initProxyState() {
const myProxyState = startProxy()
currentProxyState = myProxyState

myProxyState.then(state => onKubectlConfigChangeEvents(state.onQuitHandler))
myProxyState.then(state =>
onKubectlConfigChangeEvents(type => {
if (type === 'SetNamespaceOrContext') {
state.onQuitHandler()
}
})
)
}

return currentProxyState
Expand Down
17 changes: 12 additions & 5 deletions plugins/plugin-kubectl/src/controller/kubectl/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ import { Arguments, Registrar, eventChannelUnsafe } from '@kui-shell/core'

import flags from './flags'
import { doExecWithPty } from './exec'
import { KubeOptions } from './options'
import commandPrefix from '../command-prefix'
import { KubeOptions, getNamespaceAsExpressed } from './options'

const kubectlConfigChangeChannel = '/kubectl/config/change'
type Change = 'NewContext' | 'AlteredContext'
type Handler = (args: Arguments<KubeOptions>) => void
type Handler = (type: 'SetNamespaceOrContext' | 'CreateOrDeleteNamespace', namespace?: string) => void

const mutators = [
'delete-cluster',
Expand All @@ -37,8 +37,15 @@ const mutators = [
'use-context'
]

export function emitKubectlConfigChangeEvent(args: Arguments<KubeOptions>) {
eventChannelUnsafe.emit(kubectlConfigChangeChannel, args)
export function emitKubectlConfigChangeEvent(
type: 'SetNamespaceOrContext' | 'CreateOrDeleteNamespace',
namespace?: string
) {
try {
eventChannelUnsafe.emit(kubectlConfigChangeChannel, type, namespace)
} catch (err) {
console.error('Error in onKubectlConfigChangeEvent handler', err)
}
}

export function onKubectlConfigChangeEvents(handler: Handler) {
Expand Down Expand Up @@ -67,7 +74,7 @@ async function doConfig(args: Arguments<KubeOptions>) {
: undefined

if (change) {
emitKubectlConfigChangeEvent(args)
emitKubectlConfigChangeEvent('SetNamespaceOrContext', getNamespaceAsExpressed(args))
}

return response
Expand Down
14 changes: 8 additions & 6 deletions plugins/plugin-kubectl/src/controller/kubectl/contexts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import { REPL as REPLType, Table, Row, RawResponse, Arguments, Registrar, UsageM

import flags from './flags'
import apiVersion from './apiVersion'
import { KubeOptions } from './options'
import { doExecWithTable } from './exec'
import commandPrefix from '../command-prefix'
import { KubeContext } from '../../lib/model/resource'
Expand Down Expand Up @@ -74,11 +73,14 @@ export async function getCurrentContextName({ REPL }: { REPL: REPLType }) {

/** Extract the namespace from the current context */
let currentDefaultNamespaceCache: string
onKubectlConfigChangeEvents(({ command, parsedOptions }: Pick<Arguments<KubeOptions>, 'command' | 'parsedOptions'>) => {
if (/k(ubectl?)\s+config\s+set-context/.test(command) && parsedOptions.namespace) {
currentDefaultNamespaceCache = parsedOptions.namespace
} else {
currentDefaultNamespaceCache = undefined
onKubectlConfigChangeEvents((type, namespace) => {
if (type === 'SetNamespaceOrContext') {
if (typeof namespace === 'string') {
currentDefaultNamespaceCache = namespace
} else {
// invalidate cache
currentDefaultNamespaceCache = undefined
}
}
})
export async function getCurrentDefaultNamespace({ REPL }: { REPL: REPLType }) {
Expand Down
24 changes: 4 additions & 20 deletions plugins/plugin-kubectl/src/test/k8s/contexts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,7 @@ import * as assert from 'assert'

import { expandHomeDir } from '@kui-shell/core'
import { Common, CLI, ReplExpect, SidecarExpect, Selectors } from '@kui-shell/test'
import {
waitForGreen,
waitForRed,
createNS,
waitTillNone,
defaultModeForGet
} from '@kui-shell/plugin-kubectl/tests/lib/k8s/utils'
import { waitForGreen, createNS, defaultModeForGet } from '@kui-shell/plugin-kubectl/tests/lib/k8s/utils'

const synonyms = ['kubectl']

Expand All @@ -49,19 +43,9 @@ Common.localDescribe('kubectl context switching', function(this: Common.ISuite)

synonyms.forEach(kubectl => {
/** delete the given namespace */
const deleteIt = (name: string, errOk = false) => {
const deleteIt = (name: string, context: string, kubeconfig: string) => {
it(`should delete the namespace ${name} via ${kubectl}`, () => {
return CLI.command(`${kubectl} delete namespace ${name}`, this.app)
.then(
ReplExpect.okWithCustom<string>({ selector: Selectors.BY_NAME(name), errOk })
) // FIXME
.then(selector => waitForRed(this.app, selector))
.then(() => waitTillNone('namespace', undefined, name))
.catch(err => {
if (!errOk) {
return Common.oops(this, true)(err)
}
})
execSync(`kubectl delete namespace ${name} --context ${context} --kubeconfig ${kubeconfig}`)
})
}

Expand Down Expand Up @@ -259,6 +243,6 @@ Common.localDescribe('kubectl context switching', function(this: Common.ISuite)
getPodInSidecar('nginx', ns, `--kubeconfig ${initialKubeConfig}`)
switchToContextByCommand('holla')
listPodsAndExpectOne('nginx')
deleteIt(ns)
deleteIt(ns, initialContext, initialKubeConfig)
})
})

0 comments on commit c28c800

Please sign in to comment.