mirror of https://github.com/harness/drone.git
feat: [CDE-141]: handle failure to parse container logs (#2215)
* feat: [CDE-141]: fixed time format in event timeline * feat: [CDE-141]: handle failure to parse container logs * feat: [CDE-141]: handle failure to parse container logsunified-ui
parent
b9d3bb4d2b
commit
227cb55749
|
@ -7,7 +7,7 @@ import { useStrings } from 'framework/strings'
|
|||
import { formatTimestamp } from './EventTimeline.utils'
|
||||
import css from './EventTimeline.module.scss'
|
||||
|
||||
const EventTimeline = ({ data }: { data?: TypesGitspaceEventResponse[] | null; polling?: boolean }) => {
|
||||
const EventTimeline = ({ data }: { data?: TypesGitspaceEventResponse[] | null }) => {
|
||||
const localRef = useRef<HTMLDivElement | null>(null)
|
||||
const scrollContainerRef = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
|
@ -50,7 +50,7 @@ const EventTimeline = ({ data }: { data?: TypesGitspaceEventResponse[] | null; p
|
|||
<Container ref={localRef}>
|
||||
{data?.map((item, index) => {
|
||||
return (
|
||||
<Layout.Horizontal background={Color.GREY_50} key={item.query_key}>
|
||||
<Layout.Horizontal background={Color.GREY_50} key={`${item.query_key}_${item.timestamp}`}>
|
||||
<Container
|
||||
background={Color.GREY_50}
|
||||
width={'8%'}
|
||||
|
|
|
@ -3,8 +3,17 @@ import moment from 'moment'
|
|||
import { Text, Layout } from '@harnessio/uicore'
|
||||
import { Color } from '@harnessio/design-system'
|
||||
|
||||
export const convertToMilliSecs = (timestamp: number) => {
|
||||
if (timestamp > 1e12) {
|
||||
timestamp = Math.floor(timestamp / 1e6)
|
||||
}
|
||||
return timestamp
|
||||
}
|
||||
|
||||
export const formatTimestamp = (timestamp: number) => {
|
||||
const inputDate = moment(timestamp)
|
||||
const convertedTimeStamp = convertToMilliSecs(timestamp)
|
||||
|
||||
const inputDate = moment(convertedTimeStamp)
|
||||
const currentDate = moment()
|
||||
|
||||
if (inputDate.isSame(currentDate, 'day')) {
|
||||
|
|
|
@ -3,9 +3,11 @@ import moment from 'moment'
|
|||
import { Container, Text, Layout } from '@harnessio/uicore'
|
||||
import { Color, FontVariation } from '@harnessio/design-system'
|
||||
import { useStrings } from 'framework/strings'
|
||||
import { convertToMilliSecs } from '../EventTimeline/EventTimeline.utils'
|
||||
|
||||
const EventTimelineSummary = ({ timestamp, message }: { timestamp?: number; message?: string }) => {
|
||||
const { getString } = useStrings()
|
||||
const convertedTimeStamp = convertToMilliSecs(timestamp || 0)
|
||||
return (
|
||||
<Container width="100%" flex={{ alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<Text font={{ variation: FontVariation.CARD_TITLE }} margin={{ left: 'large' }}>
|
||||
|
@ -17,7 +19,7 @@ const EventTimelineSummary = ({ timestamp, message }: { timestamp?: number; mess
|
|||
{message}
|
||||
</Text>
|
||||
<Text margin={{ left: 'large' }} font={{ size: 'small' }}>
|
||||
{moment(timestamp).format('DD MMM, YYYY hh:mma')}
|
||||
{moment(convertedTimeStamp).format('DD MMM, YYYY hh:mma')}
|
||||
</Text>
|
||||
</Layout.Horizontal>
|
||||
)}
|
||||
|
|
|
@ -539,8 +539,10 @@ export const RenderActions = ({ row, refreshList }: RenderActionsProps) => {
|
|||
|
||||
const handleDelete = async (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
||||
confirmDelete({
|
||||
title: getString('cde.deleteGitspaceTitle'),
|
||||
message: getString('cde.deleteGitspaceText', { name: name }),
|
||||
intent: 'danger',
|
||||
title: getString('cde.deleteGitspaceTitle', { name: name }),
|
||||
message: getString('cde.deleteGitspaceText'),
|
||||
confirmText: getString('delete'),
|
||||
action: async () => {
|
||||
try {
|
||||
e.preventDefault()
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
.formFields {
|
||||
div[data-id='branch-1'],
|
||||
div[data-id='code_repo_url-0'],
|
||||
[class*='repoInput'],
|
||||
[class*='branchDropdown'] {
|
||||
width: 100% !important;
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { get } from 'lodash-es'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useStrings, type UseStringsReturn } from 'framework/strings'
|
||||
|
||||
export interface LogData {
|
||||
pos: number
|
||||
|
@ -6,24 +8,29 @@ export interface LogData {
|
|||
time: number
|
||||
}
|
||||
|
||||
export function parseLog(log: string): LogData[] {
|
||||
export function parseLog(log: string, getString: UseStringsReturn['getString']): LogData[] {
|
||||
const logLines = log.trim().split('\n\n')
|
||||
const parsedData: LogData[] = []
|
||||
|
||||
logLines.forEach(line => {
|
||||
const dataMatch = line.match(/data: (.+)/)
|
||||
try {
|
||||
logLines.forEach(line => {
|
||||
const dataMatch = line.match(/data: (.+)/)
|
||||
|
||||
if (dataMatch && dataMatch[1] !== 'eof') {
|
||||
const eventData: LogData = JSON.parse(dataMatch[1])
|
||||
if (dataMatch && dataMatch[1] !== 'eof') {
|
||||
const eventData: LogData = JSON.parse(dataMatch[1])
|
||||
|
||||
parsedData.push(eventData)
|
||||
}
|
||||
})
|
||||
parsedData.push(eventData)
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
parsedData.push({ pos: 1, out: get(error, 'message') || getString('cde.details.logsFailed'), time: 0 })
|
||||
}
|
||||
|
||||
return parsedData
|
||||
}
|
||||
|
||||
export const useGetLogStream = ({ response }: { response: any }) => {
|
||||
const { getString } = useStrings()
|
||||
const [data, setData] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -50,5 +57,5 @@ export const useGetLogStream = ({ response }: { response: any }) => {
|
|||
}
|
||||
}, [response])
|
||||
|
||||
return { data: parseLog(data) }
|
||||
return { data: parseLog(data, getString) }
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ import { useAppContext } from 'AppContext'
|
|||
import { useStrings } from 'framework/strings'
|
||||
import EventTimelineAccordion from 'cde-gitness/components/EventTimelineAccordion/EventTimelineAccordion'
|
||||
import { DetailsCard } from 'cde-gitness/components/DetailsCard/DetailsCard'
|
||||
import type { TypesGitspaceConfig, TypesGitspaceEventResponse } from 'cde-gitness/services'
|
||||
import type { EnumGitspaceStateType, TypesGitspaceConfig, TypesGitspaceEventResponse } from 'cde-gitness/services'
|
||||
import { GitspaceActionType, GitspaceStatus } from 'cde/constants'
|
||||
import { useQueryParams } from 'hooks/useQueryParams'
|
||||
import { useUpdateQueryParams } from 'hooks/useUpdateQueryParams'
|
||||
|
@ -33,6 +33,7 @@ import vscodeIcon from 'cde/icons/VSCode.svg?url'
|
|||
import pauseIcon from 'cde-gitness/assests/pause.svg?url'
|
||||
import { StandaloneIDEType } from 'cde-gitness/constants'
|
||||
import homeIcon from 'cde-gitness/assests/home.svg?url'
|
||||
import { useConfirmAct } from 'hooks/useConfirmAction'
|
||||
import ContainerLogs from '../../components/ContainerLogs/ContainerLogs'
|
||||
import { useGetLogStream } from '../../hooks/useGetLogStream'
|
||||
import css from './GitspaceDetails.module.scss'
|
||||
|
@ -41,9 +42,10 @@ export const GitspaceDetails = () => {
|
|||
const space = useGetSpaceParam()
|
||||
const { getString } = useStrings()
|
||||
const { routes } = useAppContext()
|
||||
const { showError } = useToaster()
|
||||
const { showError, showSuccess } = useToaster()
|
||||
const history = useHistory()
|
||||
const [startTriggred, setStartTriggred] = useState<boolean>(false)
|
||||
const [triggerPollingOnStart, setTriggerPollingOnStart] = useState<EnumGitspaceStateType>()
|
||||
const { gitspaceId = '' } = useParams<{ gitspaceId?: string }>()
|
||||
|
||||
const [isStreamingLogs, setIsStreamingLogs] = useState(false)
|
||||
|
@ -60,7 +62,11 @@ export const GitspaceDetails = () => {
|
|||
debounce: 500
|
||||
})
|
||||
|
||||
const { refetch: refetchLogsData, response } = useGet<any>({
|
||||
const {
|
||||
refetch: refetchLogsData,
|
||||
response,
|
||||
error: streamLogsError
|
||||
} = useGet<any>({
|
||||
path: `api/v1/gitspaces/${space}/${gitspaceId}/+/logs/stream`,
|
||||
debounce: 500,
|
||||
lazy: true
|
||||
|
@ -79,12 +85,11 @@ export const GitspaceDetails = () => {
|
|||
const { updateQueryParams } = useUpdateQueryParams<{ redirectFrom?: string }>()
|
||||
const { redirectFrom = '' } = useQueryParams<{ redirectFrom?: string }>()
|
||||
|
||||
const pollingCondition = [
|
||||
GitspaceStatus.RUNNING,
|
||||
GitspaceStatus.STOPPED,
|
||||
GitspaceStatus.ERROR,
|
||||
GitspaceStatus.UNINITIALIZED
|
||||
].includes(data?.state as GitspaceStatus)
|
||||
const pollingCondition = triggerPollingOnStart
|
||||
? false
|
||||
: [GitspaceStatus.RUNNING, GitspaceStatus.STOPPED, GitspaceStatus.ERROR, GitspaceStatus.UNINITIALIZED].includes(
|
||||
data?.state as GitspaceStatus
|
||||
)
|
||||
|
||||
const disabledActionButtons = [GitspaceStatus.STARTING, GitspaceStatus.STOPPING].includes(
|
||||
data?.state as GitspaceStatus
|
||||
|
@ -99,15 +104,21 @@ export const GitspaceDetails = () => {
|
|||
if (disabledActionButtons && filteredEvent?.length && !isStreamingLogs) {
|
||||
refetchLogsData()
|
||||
setIsStreamingLogs(true)
|
||||
} else if (filteredEvent?.length && !disabledActionButtons && isStreamingLogs) {
|
||||
} else if (
|
||||
(filteredEvent?.length && !disabledActionButtons && isStreamingLogs) ||
|
||||
(isStreamingLogs && streamLogsError)
|
||||
) {
|
||||
setIsStreamingLogs(false)
|
||||
}
|
||||
}, [eventData, data?.instance?.updated, disabledActionButtons])
|
||||
}, [eventData, data?.instance?.updated, disabledActionButtons, streamLogsError])
|
||||
|
||||
usePolling(
|
||||
async () => {
|
||||
await refetchEventData()
|
||||
await refetch()
|
||||
if (triggerPollingOnStart) {
|
||||
setTriggerPollingOnStart(undefined)
|
||||
}
|
||||
},
|
||||
{
|
||||
pollingInterval: 10000,
|
||||
|
@ -120,7 +131,11 @@ export const GitspaceDetails = () => {
|
|||
if (redirectFrom && !startTriggred && !mutateLoading) {
|
||||
try {
|
||||
setStartTriggred(true)
|
||||
await actionMutate({ action: GitspaceActionType.START })
|
||||
const resp = await actionMutate({ action: GitspaceActionType.START })
|
||||
if (resp?.state === GitspaceStatus.STARTING) {
|
||||
setTriggerPollingOnStart(resp.state)
|
||||
}
|
||||
await refetchEventData()
|
||||
await refetch()
|
||||
updateQueryParams({ redirectFrom: undefined })
|
||||
} catch (err) {
|
||||
|
@ -136,6 +151,28 @@ export const GitspaceDetails = () => {
|
|||
|
||||
const formattedlogsdata = useGetLogStream({ response })
|
||||
|
||||
const confirmDelete = useConfirmAct()
|
||||
|
||||
const handleDelete = async (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
||||
confirmDelete({
|
||||
intent: 'danger',
|
||||
title: getString('cde.deleteGitspaceTitle', { name: data?.name }),
|
||||
message: getString('cde.deleteGitspaceText'),
|
||||
confirmText: getString('delete'),
|
||||
action: async () => {
|
||||
try {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
await deleteGitspace({})
|
||||
showSuccess(getString('cde.deleteSuccess'))
|
||||
history.push(routes.toCDEGitspaces({ space }))
|
||||
} catch (exception) {
|
||||
showError(getErrorMessage(exception))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Page.Header
|
||||
|
@ -223,7 +260,7 @@ export const GitspaceDetails = () => {
|
|||
}}
|
||||
/>
|
||||
<MenuItem
|
||||
onClick={deleteGitspace as Unknown as () => void}
|
||||
onClick={handleDelete as Unknown as () => void}
|
||||
text={
|
||||
<Layout.Horizontal
|
||||
spacing="small"
|
||||
|
|
|
@ -1160,8 +1160,8 @@ cde:
|
|||
stopingGitspace: Stopping Gitspace
|
||||
sessionDuration: Last Started
|
||||
lastActivated: LAST ACTIVATED
|
||||
deleteGitspaceTitle: Delete Gitspace
|
||||
deleteGitspaceText: "Are you sure to delete the gitspace, '{{name}}'?"
|
||||
deleteGitspaceTitle: Delete Gitspace '{{name}}'
|
||||
deleteGitspaceText: 'This action cannot be undone. Are you sure you want to proceed to delete?'
|
||||
deleteSuccess: Gitspace deleted succesfully
|
||||
repositoryAndBranch: Repository & Branch
|
||||
importInto: Import Repository into Gitness
|
||||
|
|
Loading…
Reference in New Issue