Skip to content

Commit

Permalink
feat: pause and resume watchable jobs
Browse files Browse the repository at this point in the history
Fixes #1747
  • Loading branch information
myan9 authored and starpit committed Feb 2, 2021
1 parent a4bde92 commit 279ceea
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 10 deletions.
8 changes: 7 additions & 1 deletion packages/core/src/core/jobs/job.ts
Expand Up @@ -24,5 +24,11 @@ export interface FlowControllable {
write(data: string): void
}

export type Suspendable = Omit<FlowControllable, 'write'>

/** in the future, a WatchableJob may be more than Abortable, e.g. Suspendable */
export type WatchableJob = Abortable
export type WatchableJob = Abortable & Partial<Suspendable>

export function isSuspendable(watch: Partial<Suspendable>) {
return watch.xon && watch.xoff
}
4 changes: 2 additions & 2 deletions packages/core/src/core/jobs/watchable.ts
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import { Abortable } from './job'
import { WatchableJob } from './job'
import { Entity } from '../../models/entity'
import { Row } from '../../webapp/models/table'

Expand All @@ -28,7 +28,7 @@ export interface Watcher {
}

export interface Watchable {
watch: Watcher & Abortable
watch: Watcher & WatchableJob
}

/** callbacks to indicate state changes */
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/index.ts
Expand Up @@ -102,7 +102,7 @@ export {
isMetadataBearing as isResourceWithMetadata
} from './models/entity'
export { isWatchable, Watchable, Watcher, WatchPusher } from './core/jobs/watchable'
export { Abortable, FlowControllable } from './core/jobs/job'
export { Abortable, FlowControllable, Suspendable, isSuspendable } from './core/jobs/job'
import { Tab } from './webapp/tab'
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { getHistoryForTab } from './models/history'
Expand Down
4 changes: 4 additions & 0 deletions packages/test/src/api/selectors.ts
Expand Up @@ -198,6 +198,10 @@ export const TABLE_CELL = (rowKey: string, cellKey: string) => `tbody [data-name
export const TABLE_SHOW_AS_GRID = (N: number) => `${OUTPUT_N(N)} .kui--toolbar-button-as-grid`
export const TABLE_SHOW_AS_SEQUENCE = (N: number) => `${OUTPUT_N(N)} .kui--toolbar-button-as-sequence`
export const TABLE_SHOW_AS_LIST = (N: number) => `${OUTPUT_N(N)} .kui--toolbar-button-as-list`
export const WATCH_LIVE_BUTTON = (N: number, splitIndex = 1) =>
`${OUTPUT_N(N, splitIndex)} .kui--toolbar-button-watch[data-online="true"]`
export const WATCH_OFFLINE_BUTTON = (N: number, splitIndex = 1) =>
`${OUTPUT_N(N, splitIndex)} .kui--toolbar-button-watch[data-online="false"]`
export const TABLE_PAGINATION_FORWARD = (N: number) =>
`${OUTPUT_N(N)} .kui--data-table-toolbar-pagination button.bx--pagination__button--forward`
export const TABLE_PAGINATION_BACKWARD = (N: number) =>
Expand Down
2 changes: 2 additions & 0 deletions plugins/plugin-client-common/i18n/resources_en_US.json
Expand Up @@ -59,5 +59,7 @@
"Settings": "Settings",
"Switch theme": "Switch theme",
"Show as table in terminal": "Show as table in terminal",
"Pause watcher": "Pause this watcher",
"Resume watcher": "Resume this watcher",
"Close watcher": "Close and terminate this watcher"
}
Expand Up @@ -15,7 +15,7 @@
*/

import React from 'react'
import { i18n, Table as KuiTable, Row as KuiRow, Watchable } from '@kui-shell/core'
import { i18n, Table as KuiTable, Row as KuiRow, Watchable, isSuspendable } from '@kui-shell/core'

import Icons from '../../spi/Icons'
import kuiHeaderFromBody from './kuiHeaderFromBody'
Expand Down Expand Up @@ -91,14 +91,52 @@ export default class LivePaginatedTable extends PaginatedTable<LiveProps, LiveSt
this.props.response.watch.init({ update, batchUpdateDone, offline, done, allOffline, header, footer })
}

private pauseWatch() {
if (this.props.response.watch.xoff) {
this.props.response.watch.xoff()
this.setState({ isWatching: false })
}
}

private resumeWatch() {
if (this.props.response.watch.xon) {
this.props.response.watch.xon()
this.setState({ isWatching: true })
}
}

protected watchControll() {
if (this.state.isWatching) {
this.pauseWatch()
} else {
this.resumeWatch()
}
}

/** E.g. last updated time for live tables */
protected caption() {
if (this.state.lastUpdatedMillis) {
const icon = this.state.isWatching ? 'Eye' : 'EyeSlash'
const iconColor = this.state.isWatching ? 'green-text' : 'red-text'
const watchControlDescription = this.state.isWatching ? strings('Pause watcher') : strings('Resume watcher')

return (
<React.Fragment>
<Icons icon={icon} className={'small-right-pad ' + iconColor} />
{!isSuspendable(this.props.response.watch) ? (
<Icons icon={icon} className={'small-right-pad ' + iconColor} />
) : (
<a
href="#"
className="kui--toolbar-button-watch"
data-online={this.state.isWatching}
onClick={this.watchControll.bind(this)}
onMouseDown={evt => evt.preventDefault()}
title={watchControlDescription}
aria-label={watchControlDescription}
>
<Icons icon={icon} className={'small-right-pad ' + iconColor} />
</a>
)}
{strings('Last updated', new Date(this.state.lastUpdatedMillis).toLocaleTimeString())}
</React.Fragment>
)
Expand Down
8 changes: 4 additions & 4 deletions plugins/plugin-kubectl/src/controller/client/direct/watch.ts
Expand Up @@ -15,7 +15,7 @@
*/

import Debug from 'debug'
import { Abortable, Arguments, FlowControllable, Row, Table, Watchable, Watcher, WatchPusher } from '@kui-shell/core'
import { Abortable, Arguments, Row, Suspendable, Table, Watchable, Watcher, WatchPusher } from '@kui-shell/core'

import { Group, isObjectInGroup } from './group'
import columnsOf from './columns'
Expand All @@ -42,12 +42,12 @@ interface WatchUpdate {
object: MetaTable
}

export abstract class DirectWatcher implements Watcher, Abortable, Omit<FlowControllable, 'write'> {
export abstract class DirectWatcher implements Watcher, Abortable, Suspendable {
/** The table push API */
protected pusher: WatchPusher

/** The current stream jobs. These will be aborted/flow-controlled as directed by the associated view. */
protected jobs: (Abortable & FlowControllable)[] = []
protected jobs: (Abortable & Suspendable)[] = []

abstract init(pusher: WatchPusher): void

Expand Down Expand Up @@ -274,7 +274,7 @@ export class SingleKindDirectWatcher extends DirectWatcher implements Abortable,
}

/** The streamer is almost ready. We give it back a stream to push data to */
public onInitForBodyUpdates(job: Abortable & FlowControllable) {
public onInitForBodyUpdates(job: Abortable & Suspendable) {
this.jobs.push(job)
return this.onData.bind(this)
}
Expand Down
Expand Up @@ -148,6 +148,19 @@ const watchNS = function(this: Common.ISuite, kubectl: string) {

// and, conversely, that watch had better eventually show Offline
await this.app.client.$(watchBadgeButOffline).then(_ => _.waitForExist())

// hit the pause watcher button in get -w
await this.app.client.$(Selectors.WATCH_LIVE_BUTTON(testWatch.count)).then(_ => _.click())
await this.app.client.$(Selectors.WATCH_OFFLINE_BUTTON(testWatch.count))
// create again
await waitForOnline(await CLI.command(`${kubectl} create ns ${nsNameForIter}`, this.app))
// get -w should stay red
await this.app.client.$(watchBadgeButOffline).then(_ => _.waitForExist())
// hit the resume watcher button in get -w
await this.app.client.$(Selectors.WATCH_OFFLINE_BUTTON(testWatch.count)).then(_ => _.click())
await this.app.client.$(Selectors.WATCH_LIVE_BUTTON(testWatch.count))
// get -w should be green
await this.app.client.$(watchBadge).then(_ => _.waitForExist())
} catch (err) {
await Common.oops(this, true)(err)
}
Expand Down

0 comments on commit 279ceea

Please sign in to comment.