mirror of https://github.com/harness/drone.git
feat: [CDE-243]: fixes cde logs in gitspace details (#2529)
* feat: [CDE-243]: fixes cde logs in gitspace details * feat: [CDE-243]: fixes cde logs in gitspace detailspull/3545/head
parent
54113d4b06
commit
471dead872
|
@ -1,4 +1,4 @@
|
||||||
import React from 'react'
|
import React, { useEffect, useRef, useState } from 'react'
|
||||||
import { PopoverInteractionKind, PopoverPosition } from '@blueprintjs/core'
|
import { PopoverInteractionKind, PopoverPosition } from '@blueprintjs/core'
|
||||||
import { Container, Layout, Button, ButtonVariation } from '@harnessio/uicore'
|
import { Container, Layout, Button, ButtonVariation } from '@harnessio/uicore'
|
||||||
import css from './CDECustomDropdown.module.scss'
|
import css from './CDECustomDropdown.module.scss'
|
||||||
|
@ -7,13 +7,26 @@ interface CDECustomDropdownProps {
|
||||||
leftElement: React.ReactNode
|
leftElement: React.ReactNode
|
||||||
label: React.ReactNode
|
label: React.ReactNode
|
||||||
menu: React.ReactNode
|
menu: React.ReactNode
|
||||||
|
overridePopOverWidth?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CDECustomDropdown = ({ label, menu, leftElement }: CDECustomDropdownProps) => {
|
export const CDECustomDropdown = ({ label, menu, leftElement, overridePopOverWidth }: CDECustomDropdownProps) => {
|
||||||
|
const buttonRef = useRef<HTMLDivElement | null>(null)
|
||||||
|
const [popoverWidth, setPopoverWidth] = useState(0)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
buttonRef?.current?.getBoundingClientRect()?.width &&
|
||||||
|
buttonRef?.current?.getBoundingClientRect()?.width !== popoverWidth
|
||||||
|
) {
|
||||||
|
setPopoverWidth(buttonRef?.current?.getBoundingClientRect()?.width)
|
||||||
|
}
|
||||||
|
}, [buttonRef?.current, popoverWidth])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout.Horizontal className={css.main}>
|
<Layout.Horizontal className={css.main}>
|
||||||
<Container width="70%">{leftElement}</Container>
|
<Container width="70%">{leftElement}</Container>
|
||||||
<Container width="30%">
|
<Container width="30%" ref={buttonRef}>
|
||||||
<Button
|
<Button
|
||||||
height="45px"
|
height="45px"
|
||||||
width="100%"
|
width="100%"
|
||||||
|
@ -29,7 +42,7 @@ export const CDECustomDropdown = ({ label, menu, leftElement }: CDECustomDropdow
|
||||||
popoverClassName: css.popover
|
popoverClassName: css.popover
|
||||||
}}
|
}}
|
||||||
tooltip={
|
tooltip={
|
||||||
<Container className={css.listContainer} width="100%">
|
<Container className={css.listContainer} width={overridePopOverWidth ? '100%' : popoverWidth}>
|
||||||
{menu}
|
{menu}
|
||||||
</Container>
|
</Container>
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ export const CDEIDESelect = ({
|
||||||
<MenuItem
|
<MenuItem
|
||||||
key={item.value}
|
key={item.value}
|
||||||
text={
|
text={
|
||||||
<Layout.Horizontal>
|
<Layout.Horizontal width="90%" flex={{ alignItems: 'center', justifyContent: 'space-between' }}>
|
||||||
<img height={16} width={16} src={item.img} />
|
<img height={16} width={16} src={item.img} />
|
||||||
<Text>{item.label}</Text>
|
<Text>{item.label}</Text>
|
||||||
</Layout.Horizontal>
|
</Layout.Horizontal>
|
||||||
|
|
|
@ -2,10 +2,13 @@ import { Container } from '@harnessio/uicore'
|
||||||
import cx from 'classnames'
|
import cx from 'classnames'
|
||||||
import React, { useEffect, useRef } from 'react'
|
import React, { useEffect, useRef } from 'react'
|
||||||
import { lineElement } from 'components/LogViewer/LogViewer'
|
import { lineElement } from 'components/LogViewer/LogViewer'
|
||||||
|
import { useAppContext } from 'AppContext'
|
||||||
import type { LogData } from '../../hooks/useGetLogStream'
|
import type { LogData } from '../../hooks/useGetLogStream'
|
||||||
|
import { parseLogString } from './ContainerLogs.utils'
|
||||||
import css from './ContainerLogs.module.scss'
|
import css from './ContainerLogs.module.scss'
|
||||||
|
|
||||||
const ContainerLogs = ({ data }: { data: LogData[] }) => {
|
const ContainerLogs = ({ data }: { data: LogData[] }) => {
|
||||||
|
const { standalone } = useAppContext()
|
||||||
const localRef = useRef<HTMLDivElement | null>()
|
const localRef = useRef<HTMLDivElement | null>()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -18,12 +21,15 @@ const ContainerLogs = ({ data }: { data: LogData[] }) => {
|
||||||
localRef.current.innerHTML = ''
|
localRef.current.innerHTML = ''
|
||||||
}
|
}
|
||||||
if (data) {
|
if (data) {
|
||||||
data?.forEach((line: any) => {
|
const logData = standalone ? data : parseLogString(data as unknown as string)
|
||||||
|
logData?.forEach((line: any) => {
|
||||||
const linePos = line.pos + 1
|
const linePos = line.pos + 1
|
||||||
const localDate = new Date(line.time)
|
const localDate = new Date(line.time)
|
||||||
// Format date to a more readable format (local time)
|
// Format date to a more readable format (local time)
|
||||||
const formattedDate = localDate.toLocaleString()
|
const formattedDate = localDate.toLocaleString()
|
||||||
fragment.appendChild(lineElement(`${linePos} ${formattedDate.replace(',', '')} ${line.out}`))
|
fragment.appendChild(
|
||||||
|
lineElement(`${linePos} ${formattedDate.replace(',', '')} ${standalone ? line.out : line.message}`)
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
logContainer.appendChild(fragment)
|
logContainer.appendChild(fragment)
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
export function parseLogString(logString: string) {
|
||||||
|
if (!logString) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
const logEntries = logString.trim().split('\n')
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const parsedLogs: any = []
|
||||||
|
logEntries.forEach((entry, lineIndex) => {
|
||||||
|
// Parse the entry as JSON
|
||||||
|
const jsonEntry = JSON.parse(entry)
|
||||||
|
// Apply the regex to the 'out' field
|
||||||
|
const parts = (jsonEntry?.message).match(/time="([^"]+)" level=([^ ]+) msg="([^"]+)"(.*)/)
|
||||||
|
if (parts) {
|
||||||
|
const [, time, level, message, details, out] = parts
|
||||||
|
const detailParts = details.trim().split(' ')
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const detailDict: any = {}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
detailParts.forEach((part: any) => {
|
||||||
|
if (part.includes('=')) {
|
||||||
|
const [key, value] = part.split('=')
|
||||||
|
detailDict[key.trim()] = value.trim()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
parsedLogs.push({ time, level, message, out, details: detailDict, pos: jsonEntry.pos, logLevel: jsonEntry.level })
|
||||||
|
} else {
|
||||||
|
parsedLogs.push({
|
||||||
|
time: jsonEntry.time,
|
||||||
|
level: jsonEntry.level,
|
||||||
|
message: jsonEntry?.message,
|
||||||
|
pos: lineIndex,
|
||||||
|
logLevel: jsonEntry.level
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return parsedLogs
|
||||||
|
}
|
|
@ -20,7 +20,7 @@ import { Layout } from '@harnessio/uicore'
|
||||||
import { useFormikContext } from 'formik'
|
import { useFormikContext } from 'formik'
|
||||||
import { useParams } from 'react-router-dom'
|
import { useParams } from 'react-router-dom'
|
||||||
import { CDEPathParams, useGetCDEAPIParams } from 'cde-gitness/hooks/useGetCDEAPIParams'
|
import { CDEPathParams, useGetCDEAPIParams } from 'cde-gitness/hooks/useGetCDEAPIParams'
|
||||||
import { OpenapiCreateGitspaceRequest, useListInfraProviderResources } from 'services/cde'
|
import { OpenapiCreateGitspaceRequest, useGetInfraProvider } from 'services/cde'
|
||||||
import { SelectRegion } from '../SelectRegion/SelectRegion'
|
import { SelectRegion } from '../SelectRegion/SelectRegion'
|
||||||
import { SelectMachine } from '../SelectMachine/SelectMachine'
|
import { SelectMachine } from '../SelectMachine/SelectMachine'
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ export const SelectInfraProvider = () => {
|
||||||
// infraProviderConfigIdentifier: 'HARNESS_GCP'
|
// infraProviderConfigIdentifier: 'HARNESS_GCP'
|
||||||
// })
|
// })
|
||||||
|
|
||||||
const { data } = useListInfraProviderResources({
|
const { data } = useGetInfraProvider({
|
||||||
accountIdentifier,
|
accountIdentifier,
|
||||||
projectIdentifier,
|
projectIdentifier,
|
||||||
orgIdentifier,
|
orgIdentifier,
|
||||||
|
@ -41,11 +41,11 @@ export const SelectInfraProvider = () => {
|
||||||
|
|
||||||
const { gitspaceId = '' } = useParams<{ gitspaceId?: string }>()
|
const { gitspaceId = '' } = useParams<{ gitspaceId?: string }>()
|
||||||
|
|
||||||
const optionsList = data && isObject(data) ? data : []
|
const optionsList = data?.resources && isObject(data?.resources) ? data?.resources : []
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (gitspaceId && values.resource_identifier && optionsList.length) {
|
if (gitspaceId && values.resource_identifier && optionsList.length) {
|
||||||
const match = optionsList.find(item => item.id === values.resource_identifier)
|
const match = optionsList.find(item => item.identifier === values.resource_identifier)
|
||||||
if (values?.metadata?.region !== match?.region) {
|
if (values?.metadata?.region !== match?.region) {
|
||||||
onChange('metadata.region', match?.region?.toLowerCase())
|
onChange('metadata.region', match?.region?.toLowerCase())
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ export const SelectInfraProvider = () => {
|
||||||
}, [gitspaceId, values.resource_identifier, values?.metadata?.region, optionsList.map(i => i.name).join('')])
|
}, [gitspaceId, values.resource_identifier, values?.metadata?.region, optionsList.map(i => i.name).join('')])
|
||||||
|
|
||||||
const regionOptions = Object.entries(groupBy(optionsList, 'region')).map(i => {
|
const regionOptions = Object.entries(groupBy(optionsList, 'region')).map(i => {
|
||||||
return { label: i[0], value: i[1] }
|
return { label: i[0] as string, value: i[1] }
|
||||||
})
|
})
|
||||||
|
|
||||||
const machineOptions =
|
const machineOptions =
|
||||||
|
|
|
@ -21,7 +21,7 @@ import { Cpu } from 'iconoir-react'
|
||||||
import { useFormikContext } from 'formik'
|
import { useFormikContext } from 'formik'
|
||||||
import { FontVariation } from '@harnessio/design-system'
|
import { FontVariation } from '@harnessio/design-system'
|
||||||
import { useParams } from 'react-router-dom'
|
import { useParams } from 'react-router-dom'
|
||||||
import type { OpenapiCreateGitspaceRequest, TypesInfraProviderResourceResponse } from 'services/cde'
|
import type { OpenapiCreateGitspaceRequest, TypesInfraProviderResource } from 'services/cde'
|
||||||
import { useStrings } from 'framework/strings'
|
import { useStrings } from 'framework/strings'
|
||||||
import { CDECustomDropdown } from 'cde-gitness/components/CDECustomDropdown/CDECustomDropdown'
|
import { CDECustomDropdown } from 'cde-gitness/components/CDECustomDropdown/CDECustomDropdown'
|
||||||
import css from './SelectMachine.module.scss'
|
import css from './SelectMachine.module.scss'
|
||||||
|
@ -37,8 +37,8 @@ export const labelToMachineId = {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SelectMachineInterface {
|
interface SelectMachineInterface {
|
||||||
options: TypesInfraProviderResourceResponse[]
|
options: TypesInfraProviderResource[]
|
||||||
defaultValue: TypesInfraProviderResourceResponse
|
defaultValue: TypesInfraProviderResource
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SelectMachine = ({ options, defaultValue }: SelectMachineInterface) => {
|
export const SelectMachine = ({ options, defaultValue }: SelectMachineInterface) => {
|
||||||
|
@ -48,9 +48,9 @@ export const SelectMachine = ({ options, defaultValue }: SelectMachineInterface)
|
||||||
const { gitspaceId = '' } = useParams<{ gitspaceId?: string }>()
|
const { gitspaceId = '' } = useParams<{ gitspaceId?: string }>()
|
||||||
|
|
||||||
const machineTypes = options.map(item => {
|
const machineTypes = options.map(item => {
|
||||||
const { cpu, disk, memory, id, name } = item
|
const { cpu, disk, memory, identifier, name } = item
|
||||||
return {
|
return {
|
||||||
id,
|
identifier,
|
||||||
label: name,
|
label: name,
|
||||||
cpu,
|
cpu,
|
||||||
disk,
|
disk,
|
||||||
|
@ -60,15 +60,16 @@ export const SelectMachine = ({ options, defaultValue }: SelectMachineInterface)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (defaultValue && !gitspaceId) {
|
if (defaultValue && !gitspaceId) {
|
||||||
onChange('resource_identifier', defaultValue.id)
|
onChange('resource_identifier', defaultValue.identifier)
|
||||||
}
|
}
|
||||||
}, [defaultValue?.id, gitspaceId])
|
}, [defaultValue?.identifier, gitspaceId])
|
||||||
|
|
||||||
const data = (machineTypes?.find(item => item.id === machine) || {}) as (typeof machineTypes)[0]
|
const data = (machineTypes?.find(item => item.identifier === machine) || {}) as (typeof machineTypes)[0]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<CDECustomDropdown
|
<CDECustomDropdown
|
||||||
|
overridePopOverWidth
|
||||||
leftElement={
|
leftElement={
|
||||||
<Layout.Horizontal>
|
<Layout.Horizontal>
|
||||||
<Cpu height={20} width={20} style={{ marginRight: '8px', alignItems: 'center' }} />
|
<Cpu height={20} width={20} style={{ marginRight: '8px', alignItems: 'center' }} />
|
||||||
|
@ -93,8 +94,8 @@ export const SelectMachine = ({ options, defaultValue }: SelectMachineInterface)
|
||||||
{machineTypes.map(item => {
|
{machineTypes.map(item => {
|
||||||
return (
|
return (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
key={item.id}
|
key={item.identifier}
|
||||||
active={values.resource_identifier === item.id}
|
active={values.resource_identifier === item.identifier}
|
||||||
text={
|
text={
|
||||||
<Layout.Vertical>
|
<Layout.Vertical>
|
||||||
<Text font={{ size: 'normal', weight: 'bold' }}>{item.label?.toUpperCase()}</Text>
|
<Text font={{ size: 'normal', weight: 'bold' }}>{item.label?.toUpperCase()}</Text>
|
||||||
|
@ -112,7 +113,7 @@ export const SelectMachine = ({ options, defaultValue }: SelectMachineInterface)
|
||||||
</Layout.Vertical>
|
</Layout.Vertical>
|
||||||
}
|
}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onChange('resource_identifier', item.id || '')
|
onChange('resource_identifier', item.identifier || '')
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
|
@ -21,7 +21,7 @@ import { Map } from 'iconoir-react'
|
||||||
import { useFormikContext } from 'formik'
|
import { useFormikContext } from 'formik'
|
||||||
import { useStrings } from 'framework/strings'
|
import { useStrings } from 'framework/strings'
|
||||||
import { GitspaceRegion } from 'cde-gitness/constants'
|
import { GitspaceRegion } from 'cde-gitness/constants'
|
||||||
import type { OpenapiCreateGitspaceRequest, TypesInfraProviderResourceResponse } from 'services/cde'
|
import type { OpenapiCreateGitspaceRequest, TypesInfraProviderResource } from 'services/cde'
|
||||||
import { CDECustomDropdown } from 'cde-gitness/components/CDECustomDropdown/CDECustomDropdown'
|
import { CDECustomDropdown } from 'cde-gitness/components/CDECustomDropdown/CDECustomDropdown'
|
||||||
import USWest from './assests/USWest.png'
|
import USWest from './assests/USWest.png'
|
||||||
import USEast from './assests/USEast.png'
|
import USEast from './assests/USEast.png'
|
||||||
|
@ -31,8 +31,8 @@ import Empty from './assests/Empty.png'
|
||||||
|
|
||||||
interface SelectRegionInterface {
|
interface SelectRegionInterface {
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
defaultValue: { label: string; value: TypesInfraProviderResourceResponse[] }
|
defaultValue: { label: string; value: TypesInfraProviderResource[] }
|
||||||
options: { label: string; value: TypesInfraProviderResourceResponse[] }[]
|
options: { label: string; value: TypesInfraProviderResource[] }[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getMapFromRegion = (region: string) => {
|
export const getMapFromRegion = (region: string) => {
|
||||||
|
@ -68,6 +68,7 @@ export const SelectRegion = ({ options, disabled, defaultValue }: SelectRegionIn
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<CDECustomDropdown
|
<CDECustomDropdown
|
||||||
|
overridePopOverWidth
|
||||||
label={
|
label={
|
||||||
<Layout.Horizontal spacing={'small'} flex={{ alignItems: 'center', justifyContent: 'flex-start' }}>
|
<Layout.Horizontal spacing={'small'} flex={{ alignItems: 'center', justifyContent: 'flex-start' }}>
|
||||||
<Layout.Vertical>
|
<Layout.Vertical>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { useGet } from 'restful-react'
|
import { useGet } from 'restful-react'
|
||||||
|
import { useEffect } from 'react'
|
||||||
import { useGetCDEAPIParams } from 'cde-gitness/hooks/useGetCDEAPIParams'
|
import { useGetCDEAPIParams } from 'cde-gitness/hooks/useGetCDEAPIParams'
|
||||||
import { useAppContext } from 'AppContext'
|
import { useAppContext } from 'AppContext'
|
||||||
import { useGetGitspaceInstanceLogs } from 'services/cde'
|
import { useGetGitspaceInstanceLogs } from 'services/cde'
|
||||||
|
@ -22,5 +23,11 @@ export const useGitspacesLogs = ({ gitspaceId }: { gitspaceId: string }) => {
|
||||||
lazy: true
|
lazy: true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!standalone) {
|
||||||
|
cde.refetch()
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
return standalone ? gitness : cde
|
return standalone ? gitness : cde
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,12 @@ const GitspaceDetails = () => {
|
||||||
|
|
||||||
const { data: eventData, refetch: refetchEventData } = useGitspaceEvents({ gitspaceId })
|
const { data: eventData, refetch: refetchEventData } = useGitspaceEvents({ gitspaceId })
|
||||||
|
|
||||||
const { refetch: refetchLogsData, response, error: streamLogsError } = useGitspacesLogs({ gitspaceId })
|
const {
|
||||||
|
data: responseData,
|
||||||
|
refetch: refetchLogsData,
|
||||||
|
response,
|
||||||
|
error: streamLogsError
|
||||||
|
} = useGitspacesLogs({ gitspaceId })
|
||||||
|
|
||||||
const { mutate: actionMutate, loading: mutateLoading } = useGitspaceActions({ gitspaceId })
|
const { mutate: actionMutate, loading: mutateLoading } = useGitspaceActions({ gitspaceId })
|
||||||
|
|
||||||
|
@ -112,6 +117,17 @@ const GitspaceDetails = () => {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
usePolling(
|
||||||
|
async () => {
|
||||||
|
await refetchLogsData()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pollingInterval: 10000,
|
||||||
|
startCondition: (eventData?.[eventData?.length - 1]?.event as string) === 'agent_gitspace_creation_start',
|
||||||
|
stopCondition: pollingCondition
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const startTrigger = async () => {
|
const startTrigger = async () => {
|
||||||
if (redirectFrom && !startTriggred && !mutateLoading) {
|
if (redirectFrom && !startTriggred && !mutateLoading) {
|
||||||
|
@ -135,7 +151,7 @@ const GitspaceDetails = () => {
|
||||||
}
|
}
|
||||||
}, [data?.state, redirectFrom, mutateLoading, startTriggred])
|
}, [data?.state, redirectFrom, mutateLoading, startTriggred])
|
||||||
|
|
||||||
const formattedlogsdata = useGetLogStream({ response })
|
const formattedlogsdata = useGetLogStream(standalone ? { response } : { response: undefined })
|
||||||
|
|
||||||
const confirmDelete = useConfirmAct()
|
const confirmDelete = useConfirmAct()
|
||||||
|
|
||||||
|
@ -392,7 +408,7 @@ const GitspaceDetails = () => {
|
||||||
id="logsCard"
|
id="logsCard"
|
||||||
details={
|
details={
|
||||||
<Container>
|
<Container>
|
||||||
<ContainerLogs data={formattedlogsdata.data} />
|
<ContainerLogs data={standalone ? formattedlogsdata.data : responseData} />
|
||||||
</Container>
|
</Container>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
Loading…
Reference in New Issue