Skip to content

Commit

Permalink
Merge pull request #106 from kubenav/improve-log-and-shell-access
Browse files Browse the repository at this point in the history
Improve log and shell access
  • Loading branch information
ricoberger committed Jun 17, 2020
2 parents 45deefd + 3751f3c commit 38b7f2b
Show file tree
Hide file tree
Showing 8 changed files with 397 additions and 169 deletions.
2 changes: 1 addition & 1 deletion src/components/resources/DetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const DetailsPage: React.FunctionComponent<IDetailsPageProps> = ({ match }: IDet
// useAsyncFn is a custom React hook which wrapps our API call.
const [state, fetch, fetchInit] = useAsyncFn(
async () => await context.request('GET', page.detailsURL(match.params.namespace, match.params.name), ''),
[page],
[page, match.params.namespace, match.params.name],
{ loading: true, error: undefined, value: undefined },
);

Expand Down
6 changes: 5 additions & 1 deletion src/components/resources/misc/DetailsPopover.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { IonButton, IonIcon, IonList, IonPopover } from '@ionic/react';
import { IonButton, IonIcon, IonList, IonPopover, isPlatform } from '@ionic/react';
import { ellipsisHorizontal, ellipsisVertical } from 'ionicons/icons';
import React, { useState } from 'react';

import DeleteItem from './modify/DeleteItem';
import EditItem from './modify/EditItem';
import LogsItem from './modify/LogsItem';
import RestartItem from './modify/RestartItem';
import ScaleItem from './modify/ScaleItem';
import ShellItem from './modify/ShellItem';

interface IDetailsPopoverProps {
type: string;
Expand All @@ -31,6 +33,8 @@ const DetailsPopover: React.FunctionComponent<IDetailsPopoverProps> = ({ type, i
{type === 'deployments' || type === 'statefulsets' || type === 'daemonsets' ? (
<RestartItem activator="item" item={item} url={url} />
) : null}
{isPlatform('hybrid') && type === 'pods' ? <LogsItem activator="item" item={item} url={url} /> : null}
{isPlatform('hybrid') && type === 'pods' ? <ShellItem activator="item" item={item} url={url} /> : null}
<EditItem activator="item" item={item} url={url} />
<DeleteItem activator="item" item={item} url={url} />
</IonList>
Expand Down
120 changes: 120 additions & 0 deletions src/components/resources/misc/modify/LogsItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { ActionSheetButton, IonActionSheet, IonIcon, IonItem, IonLabel } from '@ionic/react';
import { V1Pod } from '@kubernetes/client-node';
import { list } from 'ionicons/icons';
import React, { useContext, useState } from 'react';

import { LOG_TAIL_LINES } from '../../../../utils/constants';
import { IContext, ITerminalContext, TActivator } from '../../../../declarations';
import { AppContext } from '../../../../utils/context';
import { TerminalContext } from '../../../../utils/terminal';
import { addLogs } from '../../../terminal/helpers';

interface ILogsItemProps {
activator: TActivator;
item: V1Pod;
url: string;
}

const LogsItem: React.FunctionComponent<ILogsItemProps> = ({ activator, item, url }: ILogsItemProps) => {
const context = useContext<IContext>(AppContext);
const terminalContext = useContext<ITerminalContext>(TerminalContext);

const generateButtons = (): ActionSheetButton[] => {
const buttons: ActionSheetButton[] = [];

if (item.spec && item.spec.initContainers) {
for (const container of item.spec.initContainers) {
buttons.push({
text: container.name,
handler: () => {
setContainer(container.name);
},
});
}
}

if (item.spec && item.spec.containers) {
for (const container of item.spec.containers) {
buttons.push({
text: container.name,
handler: () => {
setContainer(container.name);
setShowActionSheetOptions(true);
},
});
}
}

return buttons;
};

const buttons = generateButtons();

const [showActionSheetContainer, setShowActionSheetContainer] = useState<boolean>(false);
const [showActionSheetOptions, setShowActionSheetOptions] = useState<boolean>(false);
const [container, setContainer] = useState<string>(
buttons.length === 1 ? (buttons[0].text ? buttons[0].text : '') : '',
);

return (
<React.Fragment>
{activator === 'item' ? (
<IonItem
button={true}
detail={false}
onClick={() => (buttons.length === 1 ? setShowActionSheetOptions(true) : setShowActionSheetContainer(true))}
>
<IonIcon slot="end" color="primary" icon={list} />
<IonLabel>Logs</IonLabel>
</IonItem>
) : null}

<IonActionSheet
isOpen={showActionSheetContainer}
onDidDismiss={() => setShowActionSheetContainer(false)}
header="Select Container"
buttons={buttons}
/>

<IonActionSheet
isOpen={showActionSheetOptions}
onDidDismiss={() => setShowActionSheetOptions(false)}
header="Select Option"
buttons={[
{
text: `Last ${LOG_TAIL_LINES} Log Lines`,
handler: () => {
addLogs(context, terminalContext, url, container, false, LOG_TAIL_LINES, false);
},
},
{
text: 'All Log Lines',
handler: () => {
addLogs(context, terminalContext, url, container, false, 0, false);
},
},
{
text: `Previous Last ${LOG_TAIL_LINES} Log Lines`,
handler: () => {
addLogs(context, terminalContext, url, container, true, LOG_TAIL_LINES, false);
},
},
{
text: 'All Previous Log Lines',
handler: () => {
addLogs(context, terminalContext, url, container, true, 0, false);
},
},
{
text: 'Stream Log Lines',
handler: () => {
addLogs(context, terminalContext, url, container, false, LOG_TAIL_LINES, true);
},
},
]}
/>
</React.Fragment>
);
};

export default LogsItem;
80 changes: 80 additions & 0 deletions src/components/resources/misc/modify/ShellItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { ActionSheetButton, IonActionSheet, IonIcon, IonItem, IonLabel } from '@ionic/react';
import { V1Pod } from '@kubernetes/client-node';
import { terminal } from 'ionicons/icons';
import React, { useContext, useState } from 'react';

import { IContext, ITerminalContext, TActivator } from '../../../../declarations';
import { AppContext } from '../../../../utils/context';
import { TerminalContext } from '../../../../utils/terminal';
import { addShell } from '../../../terminal/helpers';

interface IShellItemProps {
activator: TActivator;
item: V1Pod;
url: string;
}

const ShellItem: React.FunctionComponent<IShellItemProps> = ({ activator, item, url }: IShellItemProps) => {
const context = useContext<IContext>(AppContext);
const terminalContext = useContext<ITerminalContext>(TerminalContext);

const [showActionSheet, setShowActionSheet] = useState<boolean>(false);

const generateButtons = (): ActionSheetButton[] => {
const buttons: ActionSheetButton[] = [];

if (item.spec && item.spec.initContainers) {
for (const container of item.spec.initContainers) {
buttons.push({
text: container.name,
handler: () => {
addShell(context, terminalContext, url, container.name);
},
});
}
}

if (item.spec && item.spec.containers) {
for (const container of item.spec.containers) {
buttons.push({
text: container.name,
handler: () => {
addShell(context, terminalContext, url, container.name);
},
});
}
}

return buttons;
};

const buttons = generateButtons();

return (
<React.Fragment>
{activator === 'item' ? (
<IonItem
button={true}
detail={false}
onClick={() =>
buttons.length === 1
? addShell(context, terminalContext, url, buttons[0].text ? buttons[0].text : '')
: setShowActionSheet(true)
}
>
<IonIcon slot="end" color="primary" icon={terminal} />
<IonLabel>Shell</IonLabel>
</IonItem>
) : null}

<IonActionSheet
isOpen={showActionSheet}
onDidDismiss={() => setShowActionSheet(false)}
header="Select Container"
buttons={buttons}
/>
</React.Fragment>
);
};

export default ShellItem;

0 comments on commit 38b7f2b

Please sign in to comment.