Skip to content

Commit

Permalink
Merge pull request #604 from SquirrelCorporation/598-feature-history-…
Browse files Browse the repository at this point in the history
…of-output-for-ansible-playbook-executions

[CHORE] Add task event logs retrieval and display in the UI
  • Loading branch information
SquirrelDeveloper authored Dec 25, 2024
2 parents 2f43396 + fdf07b6 commit 931f825
Show file tree
Hide file tree
Showing 10 changed files with 210 additions and 17 deletions.
13 changes: 13 additions & 0 deletions client/src/components/LiveLogs/LiveLogs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,19 @@ const LiveLogs = React.forwardRef<LiveLogsHandles, LiveLogsProps>(
.emitWithAck(SsmEvents.Logs.GET_LOGS, { containerId: id, from })
.then((response) => {
if (response.status === 'OK') {
terminalRef?.current?.onDataIn(
'---\n' +
'# ,;;:;,\n' +
'# ;;;;;\n' +
"# ,:;;:; ,'=.\n" +
"# ;:;:;' .=\" ,'_\\\n" +
"# ':;:;,/ ,__:=@\n" +
"# ';;:; =./)_\n" +
'# `"=\\_ )_"`\n' +
'# ``\'"`\n' +
'# Squirrel Servers Manager Container Live Logs\n' +
'---\n',
);
socket.on(SsmEvents.Logs.NEW_LOGS, onNewLogs);
} else {
handleConnectionError(`(${response.status} - ${response.error})`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,19 @@ const PlaybookExecutionTerminalModal = React.forwardRef<
const pollingCallback = () => terminalHandler.pollingCallback(execId);

const startPolling = () => {
terminalRef?.current?.onDataIn(
'---\n' +
'# ,;;:;,\n' +
'# ;;;;;\n' +
"# ,:;;:; ,'=.\n" +
"# ;:;:;' .=\" ,'_\\\n" +
"# ':;:;,/ ,__:=@\n" +
"# ';;:; =./)_\n" +
'# `"=\\_ )_"`\n' +
'# ``\'"`\n' +
'# Squirrel Servers Manager Playbook Executor\n' +
'---\n',
);
// pollingCallback(); // To immediately start fetching data
// Polling every 30 seconds
// @ts-ignore
Expand Down
31 changes: 29 additions & 2 deletions client/src/pages/Admin/Logs/TaskLogsColumns.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
import DeviceQuickActionDropDown from '@/components/DeviceComponents/DeviceQuickAction/DeviceQuickActionDropDown';
import TaskLogsTerminalModal from '@/pages/Admin/Logs/TaskLogsTerminalModal';
import { ProColumns } from '@ant-design/pro-components';
import { Tag } from 'antd';
import { Tag, Typography } from 'antd';
import React from 'react';
import { API, SsmAnsible } from 'ssm-shared-lib';

const { Text } = Typography;

const EllipsisMiddle: React.FC<{ suffixCount: number; children: string }> = ({
suffixCount,
children,
}) => {
const start = children.slice(0, children.length - suffixCount);
const suffix = children.slice(-suffixCount).trim();
return (
<Text style={{ maxWidth: '100%' }} ellipsis={{ suffix }} code>
{start}
</Text>
);
};

const TaskLogsColumns: ProColumns<API.Task>[] = [
{
title: 'Created At',
Expand Down Expand Up @@ -48,7 +65,17 @@ const TaskLogsColumns: ProColumns<API.Task>[] = [
title: 'Command',
dataIndex: 'cmd',
key: 'cmd',
valueType: 'code',
render: (_, entity) => {
return <EllipsisMiddle suffixCount={12}>{entity.cmd}</EllipsisMiddle>;
},
},
{
dataIndex: 'option',
valueType: 'option',
hideInSearch: true,
render: (_, record) => [
<TaskLogsTerminalModal key="terminal" task={record} />,
],
},
];

Expand Down
90 changes: 90 additions & 0 deletions client/src/pages/Admin/Logs/TaskLogsTerminalModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import TerminalCoreModal, {
PlaybookExecutionTerminalModalHandles,
} from '@/components/PlaybookExecutionModal/PlaybookExecutionTerminalModal';
import TerminalCore, {
TerminalCoreHandles,
} from '@/components/Terminal/TerminalCore';
import { getTaskEventsLogs } from '@/services/rest/logs';
import { Modal } from 'antd';
import React, { useEffect, useRef, useState } from 'react';
import { API } from 'ssm-shared-lib';

type TaskLogsTerminalModalProps = {
task: API.Task;
};

const TaskLogsTerminalModal: React.FC<TaskLogsTerminalModalProps> = ({
task,
}) => {
const terminalRef = useRef<TerminalCoreHandles>(null);
const [isOpen, setIsOpen] = useState<boolean>(false);

useEffect(() => {
if (isOpen) {
terminalRef?.current?.resetTerminalContent();
getTaskEventsLogs(task.ident).then((res) => {
if (!res.data || res.data.length === 0) {
terminalRef?.current?.onDataIn(
'No logs to show.\nSelected a higher retention period for "Ansible tasks & statuses retention in seconds" in Settings > General Settings',
true,
);
return;
}
terminalRef?.current?.onDataIn(
'---\n' +
'# ,;;:;,\n' +
'# ;;;;;\n' +
"# ,:;;:; ,'=.\n" +
"# ;:;:;' .=\" ,'_\\\n" +
"# ':;:;,/ ,__:=@\n" +
"# ';;:; =./)_\n" +
'# `"=\\_ )_"`\n' +
'# ``\'"`\n' +
'# Squirrel Servers Manager Playbook Executor\n' +
'---\n',
);
for (const line of res.data) {
if (line.stdout) {
terminalRef?.current?.onDataIn(line.stdout, true);
}
}
});
}
}, [isOpen, task.ident]);

return (
<>
<a
key="view"
onClick={() => {
setIsOpen(true);
}}
>
Show logs
</a>
<Modal
open={isOpen}
title={''}
onOk={() => {
setIsOpen(false);
}}
onCancel={() => {
setIsOpen(false);
}}
width={1000}
>
<div style={{ height: '500px' }}>
<TerminalCore
ref={terminalRef}
disableStdin={true}
rows={35}
cols={130}
convertEol={true}
/>
</div>
</Modal>
</>
);
};

export default TaskLogsTerminalModal;
31 changes: 17 additions & 14 deletions client/src/pages/Admin/Logs/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const Index: React.FC = () => {
const [form] = ProForm.useForm<any>();
const [searchParams] = useSearchParams();
const location = useLocation();
const [currentRow, setCurrentRow] = useState<API.ServerLog | undefined>();

const [columnsStateMap, setColumnsStateMap] = useState<
Record<string, ColumnsState>
Expand Down Expand Up @@ -95,20 +96,22 @@ const Index: React.FC = () => {
}, [location.hash]);

return (
<PageContainer
header={{
title: (
<Title.MainTitle
title={'Logs'}
backgroundColor={TitleColors.LOGS}
icon={<UnorderedListOutlined />}
/>
),
}}
tabList={logsTabItems}
onTabChange={handleTabChange}
tabActiveKey={location.hash.replace('#', '') || logsTabItems[0].key}
/>
<>
<PageContainer
header={{
title: (
<Title.MainTitle
title={'Logs'}
backgroundColor={TitleColors.LOGS}
icon={<UnorderedListOutlined />}
/>
),
}}
tabList={logsTabItems}
onTabChange={handleTabChange}
tabActiveKey={location.hash.replace('#', '') || logsTabItems[0].key}
/>
</>
);
};

Expand Down
15 changes: 15 additions & 0 deletions client/src/pages/Devices/DeviceSSHTerminal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,22 @@ const DeviceSSHTerminal = () => {
const setupSocket = (
onDataIn: (value: string, newLine?: boolean) => void,
) => {
terminalRef?.current?.resetTerminalContent();
socket.connect();
terminalRef?.current?.onDataIn('---', true);
terminalRef?.current?.onDataIn('# ,;;:;,', true);
terminalRef?.current?.onDataIn('# ;;;;;', true);
terminalRef?.current?.onDataIn("# ,:;;:; ,'=.", true);
terminalRef?.current?.onDataIn("# ;:;:;' .=\" ,'_\\", true);
terminalRef?.current?.onDataIn("# ':;:;,/ ,__:=@", true);
terminalRef?.current?.onDataIn("# ';;:; =./)_", true);
terminalRef?.current?.onDataIn('# `"=\\_ )_"`', true);
terminalRef?.current?.onDataIn('# ``\'"`', true);
terminalRef?.current?.onDataIn(
'# Squirrel Servers Manager Remote SSH Terminal',
true,
);
terminalRef?.current?.onDataIn('---', true);
onDataIn('🛜 Connecting...', true);
socket
.emitWithAck(SsmEvents.SSH.START_SESSION, { deviceUuid: id, rows, cols })
Expand Down
14 changes: 14 additions & 0 deletions client/src/services/rest/logs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,17 @@ export async function getServerLogs(
...(options || {}),
});
}

export async function getTaskEventsLogs(
id: string,
params?: API.PageParams,
options?: { [key: string]: any },
) {
return request<API.Tasks>(`/api/logs/tasks/${id}/events`, {
method: 'GET',
params: {
...params,
},
...(options || {}),
});
}
12 changes: 12 additions & 0 deletions server/src/controllers/rest/logs/task.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { parse } from 'url';
import { API } from 'ssm-shared-lib';
import AnsibleLogsRepo from '../../../data/database/repository/AnsibleLogsRepo';
import AnsibleTaskRepo from '../../../data/database/repository/AnsibleTaskRepo';
import { filterByFields, filterByQueryParams } from '../../../helpers/query/FilterHelper';
import { paginate } from '../../../helpers/query/PaginationHelper';
import { sortByFields } from '../../../helpers/query/SorterHelper';
import logger from '../../../logger';
import { NotFoundError } from '../../../middlewares/api/ApiError';
import { SuccessResponse } from '../../../middlewares/api/ApiResponse';

export const getTaskLogs = async (req, res) => {
Expand Down Expand Up @@ -40,3 +42,13 @@ export const getTaskLogs = async (req, res) => {
current: parseInt(`${params.current}`, 10) || 1,
}).send(res);
};

export const getTaskEvents = async (req, res) => {
const { id } = req.params;
if (!id) {
throw new NotFoundError('No id');
}
const events = await AnsibleLogsRepo.findAllByIdent(id);

new SuccessResponse('Get task logs successful', events).send(res);
};
4 changes: 4 additions & 0 deletions server/src/controllers/rest/logs/task.validator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { param } from 'express-validator';
import validator from '../../../middlewares/Validator';

export const getTaskEventsValidator = [param('id').exists().notEmpty().isUUID(), validator];
4 changes: 3 additions & 1 deletion server/src/routes/logs.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import express from 'express';
import passport from 'passport';
import { getServerLogs } from '../controllers/rest/logs/server';
import { getTaskLogs } from '../controllers/rest/logs/task';
import { getTaskEvents, getTaskLogs } from '../controllers/rest/logs/task';
import { getTaskEventsValidator } from '../controllers/rest/logs/task.validator';

const router = express.Router();

router.use(passport.authenticate('jwt', { session: false }));

router.get(`/server`, getServerLogs);
router.get(`/tasks`, getTaskLogs);
router.get(`/tasks/:id/events`, getTaskEventsValidator, getTaskEvents);

export default router;

0 comments on commit 931f825

Please sign in to comment.