Skip to content

Commit

Permalink
Prevent download of empty files and view raw logs in Pipeline Logs
Browse files Browse the repository at this point in the history
  • Loading branch information
manaswinidas committed Dec 19, 2023
1 parent 5b8f4c5 commit 617082d
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,10 @@ test('Does not show run content', async ({ page }) => {
// expect nodes dont open drawer on click
await page.getByText('flip-coin').click();
await expect(page.getByTestId('pipeline-run-drawer-right-content')).toHaveCount(1);

//expect drawer to open on clicking node
await expect(page.getByText('Input / Output')).toHaveCount(1);
await expect(page.getByText('Details')).toHaveCount(2);
await expect(page.getByText('Volumes')).toHaveCount(1);
await expect(page.getByText('Logs')).toHaveCount(1);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React from 'react';
import {
Dropdown,
DropdownItem,
DropdownToggle,
KebabToggle,
} from '@patternfly/react-core/deprecated';
import { Truncate } from '@patternfly/react-core';

type DownloadDropdownProps = {
onDownload: () => void;
onDownloadAll: () => void;
isSmallScreen: boolean;
isSingleStepLogsEmpty: boolean;
};

const DownloadDropdown: React.FC<DownloadDropdownProps> = ({
onDownload,
onDownloadAll,
isSmallScreen,
isSingleStepLogsEmpty,
}) => {
const [isDownloadDropdownOpen, setIsDownloadDropdownOpen] = React.useState(false);

return isSmallScreen ? (
<Dropdown
toggle={<KebabToggle onToggle={() => setIsDownloadDropdownOpen(!isDownloadDropdownOpen)} />}
isOpen={isDownloadDropdownOpen}
isPlain={isSmallScreen}
dropdownItems={[
<DropdownItem
isDisabled={isSingleStepLogsEmpty}
key="current-container-logs"
onClick={onDownload}
>
<Truncate content={'Download current step log'} />
</DropdownItem>,
<DropdownItem key="all-container-logs" onClick={onDownloadAll}>
<Truncate content={'Download all step logs'} />
</DropdownItem>,
]}
/>
) : (
<Dropdown
toggle={
<DropdownToggle
id="download-steps-logs-toggle"
onToggle={() => setIsDownloadDropdownOpen(!isDownloadDropdownOpen)}
>
Download
</DropdownToggle>
}
isOpen={isDownloadDropdownOpen}
dropdownItems={[
<DropdownItem
isDisabled={isSingleStepLogsEmpty}
key="current-container-logs"
onClick={onDownload}
>
<Truncate content={'Current step log'} />
</DropdownItem>,
<DropdownItem key="all-container-logs" onClick={onDownloadAll}>
<Truncate content={'All step logs'} />
</DropdownItem>,
]}
/>
);
};
export default DownloadDropdown;
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,12 @@ import {
Split,
SplitItem,
Bullseye,
Truncate,
} from '@patternfly/react-core';
import {
Dropdown,
DropdownItem,
DropdownToggle,
KebabToggle,
} from '@patternfly/react-core/deprecated';
import OutlinedPlayCircleIcon from '@patternfly/react-icons/dist/esm/icons/outlined-play-circle-icon';
import PauseIcon from '@patternfly/react-icons/dist/esm/icons/pause-icon';
import PlayIcon from '@patternfly/react-icons/dist/esm/icons/play-icon';
import DownloadIcon from '@patternfly/react-icons/dist/esm/icons/download-icon';
import { OutlinedWindowRestoreIcon } from '@patternfly/react-icons';
import { PipelineRunTaskDetails } from '~/concepts/pipelines/content/types';
import SimpleDropdownSelect from '~/components/SimpleDropdownSelect';
import useFetchLogs from '~/concepts/k8s/pods/useFetchLogs';
Expand All @@ -34,6 +28,7 @@ import DashboardCodeEditor from '~/concepts/dashboard/codeEditor/DashboardCodeEd
import useCodeEditorAsLogs from '~/concepts/dashboard/codeEditor/useCodeEditorAsLogs';
import { downloadCurrentStepLog, downloadAllStepLogs } from '~/concepts/k8s/pods/utils';
import { usePipelinesAPI } from '~/concepts/pipelines/context';
import DownloadDropdown from '~/concepts/pipelines/content/pipelinesDetails/pipelineRun/runLogs/DownloadDropdown';

// TODO: If this gets large enough we should look to make this its own component file
const LogsTab: React.FC<{ task: PipelineRunTaskDetails }> = ({ task }) => {
Expand Down Expand Up @@ -68,7 +63,6 @@ const LogsTabForPodName: React.FC<{ podName: string }> = ({ podName }) => {
const [downloading, setDownloading] = React.useState(false);
const [downloadError, setDownloadError] = React.useState<Error | undefined>();
const { scrollToBottom, onMount, editorOptions } = useCodeEditorAsLogs();
const [isDownloadDropdownOpen, setIsDownloadDropdownOpen] = React.useState(false);
const { isSmallScreen } = useWindowResize();

React.useEffect(() => {
Expand Down Expand Up @@ -111,7 +105,7 @@ const LogsTabForPodName: React.FC<{ podName: string }> = ({ podName }) => {
} else if (logs) {
data = logs;
} else {
data = 'No content';
data = 'No logs available';
}

return (
Expand All @@ -121,6 +115,7 @@ const LogsTabForPodName: React.FC<{ podName: string }> = ({ podName }) => {
<LogsTabStatus
loaded={loaded}
error={error}
isLogsNotAvailable={logs === null || logs === ''}
refresh={refreshLogs}
onDownload={onDownloadAll}
/>
Expand All @@ -130,18 +125,18 @@ const LogsTabForPodName: React.FC<{ podName: string }> = ({ podName }) => {
<Split hasGutter isWrappable>
<SplitItem>
<Split hasGutter>
{isSmallScreen() ? null : (
{!isSmallScreen() ? (
<SplitItem>
<Bullseye>
<TextContent>
<TextListItem component={TextListItemVariants.dt}>Step</TextListItem>
</TextContent>
</Bullseye>
</SplitItem>
)}
) : null}
<SplitItem>
<SimpleDropdownSelect
isDisabled={podContainers.length === 0}
isDisabled={podContainers.length <= 1}
options={podContainers.map((container) => ({
key: container.name,
label: container.name,
Expand All @@ -163,73 +158,62 @@ const LogsTabForPodName: React.FC<{ podName: string }> = ({ podName }) => {
onClick={() => setIsPaused(!isPaused)}
isDisabled={!!error}
>
{error ? (
'Error loading logs'
) : !logsLoaded || podStatus?.podInitializing ? (
<>
<Spinner size="sm" /> {isSmallScreen() ? 'Loading' : 'Loading log'}
</>
) : isPaused ? (
<>
<PlayIcon /> {isSmallScreen() ? 'Resume' : 'Resume refreshing'}
</>
) : (
<>
<PauseIcon /> {isSmallScreen() ? 'Pause' : 'Pause refreshing'}
</>
)}
{!error &&
(!logsLoaded ? (
<>
<Spinner size="sm" /> {isSmallScreen() ? 'Loading' : 'Loading log'}
</>
) : isPaused ? (
<>
<PlayIcon /> {isSmallScreen() ? 'Resume' : 'Resume refreshing'}
</>
) : (
<>
<PauseIcon /> {isSmallScreen() ? 'Pause' : 'Pause refreshing'}
</>
))}
</Button>
)}
</SplitItem>
<SplitItem isFilled style={{ textAlign: 'right' }}>
<Tooltip position="top" content={<div>View raw logs</div>}>
<Button
onClick={() => {
const newTab = window.open();
newTab?.document.write(`<pre>${logs}</pre>`);
}}
variant="link"
aria-label="View raw logs"
icon={<OutlinedWindowRestoreIcon />}
isDisabled={podContainers.length === 1 && (logs === null || logs === '')}
>
{!isSmallScreen() ? 'Raw' : null}
</Button>
</Tooltip>
{downloading ? (
<>
<Spinner size="sm" />{' '}
</>
) : null}
{podContainers.length !== 0 ? (
<Dropdown
toggle={
isSmallScreen() ? (
<KebabToggle
onToggle={() => setIsDownloadDropdownOpen(!isDownloadDropdownOpen)}
/>
) : (
<DropdownToggle
id="download-steps-logs-toggle"
onToggle={() => setIsDownloadDropdownOpen(!isDownloadDropdownOpen)}
>
Download
</DropdownToggle>
)
}
isOpen={isDownloadDropdownOpen}
isPlain={isSmallScreen()}
dropdownItems={[
<DropdownItem key="current-container-logs" onClick={onDownload}>
<Truncate
content={isSmallScreen() ? 'Download current step log' : 'Current step log'}
/>
</DropdownItem>,
<DropdownItem key="all-container-logs" onClick={onDownloadAll}>
<Truncate
content={isSmallScreen() ? 'Download all step logs' : 'All step logs'}
/>
</DropdownItem>,
]}
/>
) : (
{podContainers.length <= 1 ? (
<Tooltip position="top" content={<div>Download current step log</div>}>
<Button
onClick={onDownload}
variant="link"
aria-label="Download current step log"
icon={<DownloadIcon />}
isDisabled={!canDownload}
isDisabled={!canDownload || logs === null || logs === ''}
>
Download
{!isSmallScreen() ? 'Download' : null}
</Button>
</Tooltip>
) : (
<DownloadDropdown
onDownload={onDownload}
onDownloadAll={onDownloadAll}
isSmallScreen={isSmallScreen()}
isSingleStepLogsEmpty={logs === null || logs === ''}
/>
)}
</SplitItem>
</Split>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,19 @@ import {

type LogsTabStatusProps = {
error?: Error;
isLogsNotAvailable?: boolean;
loaded: boolean;
refresh: () => void;
onDownload: () => void;
};

const LogsTabStatus: React.FC<LogsTabStatusProps> = ({ error, loaded, refresh, onDownload }) => {
const LogsTabStatus: React.FC<LogsTabStatusProps> = ({
error,
isLogsNotAvailable,
loaded,
refresh,
onDownload,
}) => {
if (error) {
return (
<Alert
Expand All @@ -38,7 +45,7 @@ const LogsTabStatus: React.FC<LogsTabStatusProps> = ({ error, loaded, refresh, o
latest {LOG_TAIL_LINES} lines. Exceptionally long lines are abridged. To view the full log
for this task, you can{' '}
<Button
isDisabled={!onDownload}
isDisabled={!onDownload || isLogsNotAvailable}
variant="link"
isInline
component="span"
Expand Down

0 comments on commit 617082d

Please sign in to comment.