Skip to content
This repository was archived by the owner on Jul 30, 2025. It is now read-only.

Commit f6138cc

Browse files
starpitk8s-ci-robot
authored andcommitted
fix: PTY jobs are not resizable
This PR exposes a new Resizable trait from Jobs, and does some minor cleanup on the Job type. It updates pty/client.ts to pass this through to the underlying PTY, and updates the plugin-kubectl ExecIntoPod view to use it! part of #7473
1 parent 648a2fe commit f6138cc

File tree

7 files changed

+47
-30
lines changed

7 files changed

+47
-30
lines changed

packages/core/src/core/jobs/job.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ export interface Abortable {
1818
abort(): void
1919
}
2020

21+
export interface Resizable {
22+
resize(rows: number, cols: number): void
23+
}
24+
25+
export function isResizable(job: Partial<Resizable>): job is Resizable {
26+
return typeof job.resize === 'function'
27+
}
28+
2129
export interface FlowControllable {
2230
xon(): void
2331
xoff(): void
@@ -32,3 +40,5 @@ export type WatchableJob = Abortable & Partial<Suspendable>
3240
export function isSuspendable(watch: Partial<Suspendable>) {
3341
return watch.xon && watch.xoff
3442
}
43+
44+
export type Job = Abortable & FlowControllable & Partial<Resizable>

packages/core/src/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,9 @@ export {
109109
isMetadataBearing as isResourceWithMetadata
110110
} from './models/entity'
111111
export { isWatchable, Watchable, Watcher, WatchPusher } from './core/jobs/watchable'
112-
export { Abortable, FlowControllable, Suspendable, isSuspendable } from './core/jobs/job'
112+
113+
export { Abortable, FlowControllable, Job, Resizable, isResizable, Suspendable, isSuspendable } from './core/jobs/job'
114+
113115
import { Tab } from './webapp/tab'
114116
// eslint-disable-next-line @typescript-eslint/no-unused-vars
115117
import { getHistoryForTab } from './models/history'

packages/core/src/models/execOptions.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { ExecType } from './command'
1818
import { Tab } from '../webapp/tab'
1919
import { Stream, Streamable, StreamableFactory } from './streamable'
2020
import { Block } from '../webapp/models/block'
21-
import { Abortable, FlowControllable } from '../core/jobs/job'
21+
import { Job } from '../core/jobs/job'
2222

2323
export interface ExecOptions {
2424
/** force execution in a given tab? */
@@ -91,10 +91,10 @@ export interface ExecOptions {
9191
stderr?: (str: string) => any // eslint-disable-line @typescript-eslint/no-explicit-any
9292

9393
/** on job init, pass the job, and get back a stdout; i.e. just before the PTY is brought up */
94-
onInit?: (job: Abortable & FlowControllable) => Stream | Promise<Stream>
94+
onInit?: (job: Job) => Stream | Promise<Stream>
9595

9696
/** on job ready, i.e. after the PTY is up, but before any data has been processed */
97-
onReady?: (job: Abortable & FlowControllable) => void | Promise<void>
97+
onReady?: (job: Job) => void | Promise<void>
9898

9999
/** on job exit, pass the exitCode */
100100
onExit?: (exitCode: number) => void

plugins/plugin-bash-like/src/pty/client.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,9 @@ export const doExec = (
913913
debug('xoff requested')
914914
ws.send(JSON.stringify({ type: 'xoff', uuid: ourUUID }))
915915
},
916+
resize: (rows: number, cols: number) => {
917+
ws.send(JSON.stringify({ type: 'resize', rows, cols, uuid: ourUUID }))
918+
},
916919
abort: () => {
917920
debug('abort requested')
918921
ws.send(JSON.stringify({ type: 'kill', uuid: ourUUID }))

plugins/plugin-kubectl/src/lib/util/fetch-file.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ export async function openStream<T extends object>(
103103
},
104104
xon: () => stream.resume(),
105105
xoff: () => stream.pause(),
106+
resize: () => {
107+
console.error('Unsupported Operation: resize')
108+
},
106109
write: () => {
107110
console.error('Unsupported Operation: write')
108111
}

plugins/plugin-kubectl/src/lib/view/modes/ContainerCommon.tsx

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,7 @@
1616

1717
import React from 'react'
1818
import { DropDown, Icons } from '@kui-shell/plugin-client-common'
19-
import {
20-
Abortable,
21-
Arguments,
22-
Button,
23-
FlowControllable,
24-
ParsedOptions,
25-
Tab,
26-
ToolbarProps,
27-
ToolbarText,
28-
i18n
29-
} from '@kui-shell/core'
19+
import { Arguments, Button, ParsedOptions, Job, Tab, ToolbarProps, ToolbarText, i18n } from '@kui-shell/core'
3020

3121
import { Pod } from '../../model/resource'
3222

@@ -39,8 +29,6 @@ const strings = i18n('plugin-kubectl')
3929
*/
4030
export const HYSTERESIS = 1500
4131

42-
export type Job = Abortable & FlowControllable
43-
4432
export type StreamingStatus = 'Live' | 'Paused' | 'Stopped' | 'Error' | 'Idle'
4533

4634
export interface ContainerProps {

plugins/plugin-kubectl/src/lib/view/modes/ExecIntoPod.tsx

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import { v4 as uuid } from 'uuid'
2020
import {
2121
Arguments,
2222
ModeRegistration,
23+
Job,
24+
isResizable,
2325
Streamable,
2426
Tab,
2527
ToolbarProps,
@@ -39,7 +41,7 @@ import { getCommandFromArgs } from '../../util/util'
3941
import { KubeResource, Pod, isPod } from '../../model/resource'
4042
import { KubeOptions, getContainer, withKubeconfigFrom } from '../../../controller/kubectl/options'
4143

42-
import { ContainerProps, ContainerState, ContainerComponent, HYSTERESIS, Job, StreamingStatus } from './ContainerCommon'
44+
import { ContainerProps, ContainerState, ContainerComponent, HYSTERESIS, StreamingStatus } from './ContainerCommon'
4345

4446
import '../../../../web/scss/components/Terminal/Terminal.scss'
4547

@@ -450,6 +452,27 @@ export class Terminal<S extends TerminalState = TerminalState> extends Container
450452
return
451453
}
452454

455+
if (isResizable(job)) {
456+
xterm.onResize(({ rows, cols }) => {
457+
job.resize(rows, cols)
458+
})
459+
460+
const { doResize } = this.state
461+
if (doResize) {
462+
// resize once on init
463+
this.state.doResize()
464+
465+
const observer = new ResizeObserver(function observer(observed) {
466+
// re: the if guard, see https://github.com/IBM/kui/issues/6585
467+
if (observed.every(_ => _.contentRect.width > 0 && _.contentRect.height > 0)) {
468+
setTimeout(doResize)
469+
}
470+
})
471+
observer.observe(this.state.dom)
472+
this.state.perTerminalCleaners.push(() => observer.disconnect())
473+
}
474+
}
475+
453476
xterm.onData((data: string) => {
454477
if (!this._unmounted && this.state.streamUUID === streamUUID) {
455478
job.write(data)
@@ -545,18 +568,6 @@ export class Terminal<S extends TerminalState = TerminalState> extends Container
545568
}
546569
}
547570

548-
// resize once on init
549-
doResize()
550-
551-
const observer = new ResizeObserver(function observer(observed) {
552-
// re: the if guard, see https://github.com/IBM/kui/issues/6585
553-
if (observed.every(_ => _.contentRect.width > 0 && _.contentRect.height > 0)) {
554-
setTimeout(doResize)
555-
}
556-
})
557-
observer.observe(dom)
558-
perTerminalCleaners.push(() => observer.disconnect())
559-
560571
return {
561572
xterm,
562573
doResize,

0 commit comments

Comments
 (0)