Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify cloud build log output #132

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion radlab-ui/webapp/public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,7 @@
"publish-module": "Modules are getting ready to be published...",
"refreshing": "Refreshing...",
"loading": "Loading",
"no-modules-message":"If your not seeing the module you want, ask your Admin"
"no-modules-message":"If your not seeing the module you want, ask your Admin",
"build-status":"Build Status",
"history":"History"
}
6 changes: 4 additions & 2 deletions radlab-ui/webapp/public/locales/es/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,7 @@
"publish-module": "Los módulos se están preparando para ser publicados...",
"refreshing": "Refrescante...",
"loading": "Cargando",
"no-modules-message":"Si no ve el módulo que desea, pregúntele a su administrador"
}
"no-modules-message": "Si no ve el módulo que desea, pregúntele a su administrador",
"build-status": "Estado de construcción",
"history": "Historia"
}
117 changes: 84 additions & 33 deletions radlab-ui/webapp/src/components/logs/ModuleLogs.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
import Loading from "@/navigation/Loading"
import { ILogHeader, URL, URLData } from "@/utils/types"
import { ILogHeader, URL, URLData, DEPLOYMENT_STATUS } from "@/utils/types"
import axios from "axios"
import { useEffect, useState } from "react"

import { textColorFromDeployStatus } from "@/utils/deployments"
import { userStore } from "@/store"
import { classNames } from "@/utils/dom"

interface IModuleLogs {
deploymentId: string
tableHeaders: ILogHeader[]
}

const ModuleLogs: React.FC<IModuleLogs> = ({ deploymentId, tableHeaders }) => {
const [lines, setLines] = useState<string[] | null>(null)
type IBuildSteps = {
id: string
status: DEPLOYMENT_STATUS
logsContent?: string[]
}

const ModuleLogs: React.FC<IModuleLogs> = ({ deploymentId }) => {
const [loading, setLoading] = useState(true)
const [buildSteps, setBuildSteps] = useState<IBuildSteps[] | null>(null)
const { isAdmin } = userStore((state) => state)

const fetchData = async () => {
await axios
return await axios
.get(`/api/deployments/${deploymentId}/logs`)
.then((res) => {
const urlPath = URL.parse(res.data)
Expand All @@ -22,47 +33,87 @@ const ModuleLogs: React.FC<IModuleLogs> = ({ deploymentId, tableHeaders }) => {
.then((res) => {
const urlData = URLData.parse(res)
const lines: string[] = urlData.data.split("\n")
setLines(lines)
return lines || []
})
.catch((error) => {
console.error(error)
return []
})
.finally(() => {
setLoading(false)
})
}

const fetchBuildStatus = async () => {
try {
const statusCheck = await axios.get(
`/api/deployments/${deploymentId}/status`,
)
const { buildSteps } = statusCheck.data
const linesData = isAdmin ? await fetchData() : []

const logStepDetails = buildSteps.map(
(steps: IBuildSteps, index: Number) => {
let filterVar = `Step #${index}`
const logResult = linesData?.filter((line) => {
return line.includes(filterVar)
})
return { ...steps, logsContent: logResult }
},
)

setBuildSteps(logStepDetails)
setLoading(false)
} catch (error) {
console.error(error)
}
}

useEffect(() => {
fetchData()
fetchBuildStatus()
}, [])

if (loading) return <Loading />

return (
<div className="w-full card card-actions bg-base-100 overflow-x-auto rounded-sm">
<table className="w-full divide-y divide-base-200 border-2 border-base-300 rounded-lg block max-h-screen overflow-auto">
<thead className="bg-base-300 sticky top-0">
<tr className="border-base-300">
{tableHeaders.map((tableHeader) => (
<th
key={tableHeader.header}
className="px-4 py-3 text-sm font-medium text-center text-dim font-bold"
>
{tableHeader.header}
</th>
))}
</tr>
</thead>
<tbody className="bg-base-100 divide-y-2 divide-base-300 overflow-y-scroll w-full">
{lines?.map((line, index) => (
<tr key={line + index}>
<td className="border border-base-300 px-1 py-2 text-xs font-semibold text-faint">
{line}
</td>
</tr>
))}
</tbody>
</table>
<div className="w-full card card-actions bg-base-100 rounded-sm max-h-full">
{buildSteps?.map((stepsData: IBuildSteps, indexStep) => (
<div
key={indexStep}
tabIndex={indexStep}
className={classNames(
"border border-base-300 bg-base-100 rounded-box w-full",
isAdmin
? "collapse collapse-arrow"
: "cursor-not-allowed pointer-events-none bg-base-300",
)}
>
<div className="collapse-title text-sm text-dim font-medium">
{stepsData.id}:
<span
className={`text-${textColorFromDeployStatus(
stepsData.status,
)} font-semibold ml-1`}
>
{stepsData.status}
</span>
</div>
<div className="collapse-content">
<div className="max-h-80 overflow-auto">
{isAdmin && (
<table className="table w-full divide-y divide-base-200 border-2 border-base-300 rounded-lg block">
<tbody className="bg-base-100 divide-y-2 divide-base-300 w-full">
{stepsData.logsContent?.map((line, index) => (
<tr key={line + index}>
<td className="border border-base-300 px-1 py-2 text-xs font-semibold text-faint">
{line}
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
</div>
</div>
))}
</div>
)
}
Expand Down
8 changes: 5 additions & 3 deletions radlab-ui/webapp/src/components/outputs/ModuleOutputs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ const ModuleOutputs: React.FC<ModuleOutputsProps> = ({
)

const shouldShowOutputs = () =>
status === DEPLOYMENT_STATUS.SUCCESS && !deployment.deletedAt
status !== DEPLOYMENT_STATUS.WORKING && !deployment.deletedAt

const fetchOutputs = async () => {
await axios
Expand All @@ -114,7 +114,9 @@ const ModuleOutputs: React.FC<ModuleOutputsProps> = ({
const { buildStatus } = TFStatus.parse(statusCheck.data)
setStatus(buildStatus)
} catch (error) {
setError("Failed to load Build Id. Did the deploy complete successfully?")
console.error(error)
setLoading(false)
const errorStatus: any = error
if (errorStatus.response.status !== 404) {
setAlert({
Expand All @@ -131,12 +133,12 @@ const ModuleOutputs: React.FC<ModuleOutputsProps> = ({
fetchStatus()
}, [deployment])

if (!shouldShowOutputs()) return <LoadingRow title={t("output-progress")} />

if (loading) return <Loading />

if (error) return <div className="text-center text-error">{error}</div>

if (!shouldShowOutputs()) return <LoadingRow title={t("output-progress")} />

return (
<div className="bg-base-100 -mt-4">
{Object.entries(outputs || {}).map(([variable, output]) => (
Expand Down
13 changes: 11 additions & 2 deletions radlab-ui/webapp/src/pages/api/deployments/[id]/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import {
} from "@/utils/Api_SeverSideCon"
import { getBuildStatus } from "@/utils/api"
import { withAuth } from "@/utils/middleware"
import { AuthedNextApiHandler, IBuild, IDeployment } from "@/utils/types"
import {
AuthedNextApiHandler,
IBuild,
IBuildSteps,
IDeployment,
} from "@/utils/types"
import { NextApiResponse } from "next"

const getDeploymentStatus = async (
Expand Down Expand Up @@ -38,12 +43,15 @@ const getDeploymentStatus = async (
}

let tfState = ""
cloudBuild.steps.forEach((step: Record<string, any>) => {
cloudBuild.steps.forEach((step: IBuildSteps) => {
if (step.id === "Apply") {
tfState = step.status
return
}
})
const buildSteps = cloudBuild.steps.map((step: IBuildSteps) => {
return { id: step.id, status: step.status }
})
const updateBuilds = {
status: cloudBuild.status,
buildId: mostRecentBuild.buildId,
Expand All @@ -56,6 +64,7 @@ const getDeploymentStatus = async (
res.status(200).json({
buildStatus: cloudBuild.status,
tfApplyState: tfState,
buildSteps,
})
}

Expand Down
8 changes: 5 additions & 3 deletions radlab-ui/webapp/src/routes/DeploymentDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,11 @@ const DeploymentDetails: React.FC<IDeploymentDetails> = ({}) => {
</div>
<ModuleOverview deployment={deployment} />
<div className="tabs mt-8 rounded-t-lg">
{renderTabHeader(TAB_DETAILS.OUTPUTS, t("outputs"), tabStatus)}
{renderTabHeader(TAB_DETAILS.LOG, t("logs"), tabStatus)}
{renderTabHeader(TAB_DETAILS.BUILDS, t("builds"), tabStatus)}
{deployment.status !== DEPLOYMENT_STATUS.WORKING &&
!deployment.deletedAt &&
renderTabHeader(TAB_DETAILS.OUTPUTS, t("outputs"), tabStatus)}
{renderTabHeader(TAB_DETAILS.LOG, t("build-status"), tabStatus)}
{renderTabHeader(TAB_DETAILS.BUILDS, t("history"), tabStatus)}
</div>
<div className="p-8 bg-base-100 rounded-b-lg rounded-tr-lg shadow-lg">
{!isLoading ? (
Expand Down
9 changes: 9 additions & 0 deletions radlab-ui/webapp/src/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,13 @@
.text-normal {
@apply opacity-100;
}
.collapse-arrow .collapse-title:after{
@apply -rotate-45
}
.collapse-arrow .collapse-title:after{
@apply -rotate-45
}
.collapse-arrow:focus:not(.collapse-close) .collapse-title:after{
@apply rotate-45
}
}
7 changes: 7 additions & 0 deletions radlab-ui/webapp/src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,13 @@ export interface Dictionary<T> {
[index: string]: T
}

export const BuildSteps = z.object({
id: z.string(),
status: z.string(),
})

export type IBuildSteps = z.infer<typeof BuildSteps>

export type AuthedUser = DecodedIdToken & { isAdmin: boolean; isUser: boolean }

export interface AuthedNextApiHandler extends NextApiRequest {
Expand Down