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

Lookout: Support state rejected and job errors #3706

Merged
merged 4 commits into from
Jun 24, 2024
Merged
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: 2 additions & 2 deletions internal/lookout/ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import NavBar from "./components/NavBar"
import JobSetsContainer from "./containers/JobSetsContainer"
import { UserManagerContext, useUserManager } from "./oidc"
import { ICordonService } from "./services/lookoutV2/CordonService"
import { IGetJobSpecService } from "./services/lookoutV2/GetJobSpecService"
import { IGetJobInfoService } from "./services/lookoutV2/GetJobInfoService"
import { IGetRunInfoService } from "./services/lookoutV2/GetRunInfoService"
import { ILogService } from "./services/lookoutV2/LogService"
import { CommandSpec } from "./utils"
Expand Down Expand Up @@ -69,7 +69,7 @@ type AppProps = {
v2GetJobsService: IGetJobsService
v2GroupJobsService: IGroupJobsService
v2RunInfoService: IGetRunInfoService
v2JobSpecService: IGetJobSpecService
v2JobSpecService: IGetJobInfoService
v2LogService: ILogService
v2UpdateJobsService: UpdateJobsService
v2UpdateJobSetsService: UpdateJobSetsService
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { KeyValuePairTable } from "./KeyValuePairTable"
import { useCustomSnackbar } from "../../../hooks/useCustomSnackbar"
import { useJobSpec } from "../../../hooks/useJobSpec"
import { Job } from "../../../models/lookoutV2Models"
import { IGetJobSpecService } from "../../../services/lookoutV2/GetJobSpecService"
import { IGetJobInfoService } from "../../../services/lookoutV2/GetJobInfoService"

export interface ContainerData {
name: string
Expand All @@ -19,7 +19,7 @@ export interface ContainerData {

interface ContainerDetailsProps {
job: Job
jobSpecService: IGetJobSpecService
jobSpecService: IGetJobInfoService
}

const getContainerData = (container: any): ContainerData => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { makeTestJob } from "utils/fakeJobsUtils"

import { Sidebar } from "./Sidebar"
import { FakeCordonService } from "../../../services/lookoutV2/mocks/FakeCordonService"
import FakeGetJobSpecService from "../../../services/lookoutV2/mocks/FakeGetJobSpecService"
import FakeGetJobInfoService from "../../../services/lookoutV2/mocks/FakeGetJobInfoService"
import { FakeGetRunInfoService } from "../../../services/lookoutV2/mocks/FakeGetRunInfoService"
import { FakeLogService } from "../../../services/lookoutV2/mocks/FakeLogService"

Expand Down Expand Up @@ -44,7 +44,7 @@ describe("Sidebar", () => {
<Sidebar
job={job}
runInfoService={new FakeGetRunInfoService()}
jobSpecService={new FakeGetJobSpecService()}
jobSpecService={new FakeGetJobInfoService()}
logService={new FakeLogService()}
cordonService={new FakeCordonService()}
sidebarWidth={600}
Expand All @@ -71,7 +71,7 @@ describe("Sidebar", () => {
const run = job.runs[0]

// Switch to runs tab
await userEvent.click(getByRole("tab", { name: /Runs/ }))
await userEvent.click(getByRole("tab", { name: /Result/ }))

// First run should already be expanded
within(getByRole("row", { name: /Run ID/ })).getByText(run.runId)
Expand All @@ -84,7 +84,7 @@ describe("Sidebar", () => {
run.exitCode = 137

// Switch to runs tab
await userEvent.click(getByRole("tab", { name: /Runs/ }))
await userEvent.click(getByRole("tab", { name: /Result/ }))

// First run should already be expanded
within(getByRole("row", { name: /Run ID/ })).getByText(run.runId)
Expand All @@ -96,7 +96,7 @@ describe("Sidebar", () => {
const { getByRole, getByText } = renderComponent()

// Switch to runs tab
await userEvent.click(getByRole("tab", { name: /Runs/ }))
await userEvent.click(getByRole("tab", { name: /Result/ }))

getByText("This job has not run.")
})
Expand Down
19 changes: 12 additions & 7 deletions internal/lookout/ui/src/components/lookoutV2/sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ import { SidebarHeader } from "./SidebarHeader"
import { SidebarTabJobCommands } from "./SidebarTabJobCommands"
import { SidebarTabJobDetails } from "./SidebarTabJobDetails"
import { SidebarTabJobLogs } from "./SidebarTabJobLogs"
import { SidebarTabJobRuns } from "./SidebarTabJobRuns"
import { SidebarTabJobResult } from "./SidebarTabJobResult"
import { SidebarTabJobYaml } from "./SidebarTabJobYaml"
import { ICordonService } from "../../../services/lookoutV2/CordonService"
import { IGetJobSpecService } from "../../../services/lookoutV2/GetJobSpecService"
import { IGetJobInfoService } from "../../../services/lookoutV2/GetJobInfoService"
import { IGetRunInfoService } from "../../../services/lookoutV2/GetRunInfoService"
import { ILogService } from "../../../services/lookoutV2/LogService"
import { CommandSpec } from "../../../utils"

enum SidebarTab {
JobDetails = "JobDetails",
JobRuns = "JobRuns",
JobResult = "JobResult",
Yaml = "Yaml",
Logs = "Logs",
Commands = "Commands",
Expand All @@ -34,7 +34,7 @@ type ResizeState = {
export interface SidebarProps {
job: Job
runInfoService: IGetRunInfoService
jobSpecService: IGetJobSpecService
jobSpecService: IGetJobInfoService
logService: ILogService
cordonService: ICordonService
sidebarWidth: number
Expand Down Expand Up @@ -165,7 +165,7 @@ export const Sidebar = memo(
<TabContext value={openTab}>
<Tabs value={openTab} onChange={handleTabChange} className={styles.sidebarTabs}>
<Tab label="Details" value={SidebarTab.JobDetails} sx={{ minWidth: "50px" }}></Tab>
<Tab label="Runs" value={SidebarTab.JobRuns} sx={{ minWidth: "50px" }}></Tab>
<Tab label="Result" value={SidebarTab.JobResult} sx={{ minWidth: "50px" }}></Tab>
<Tab label="Yaml" value={SidebarTab.Yaml} sx={{ minWidth: "50px" }}></Tab>
<Tab
label="Logs"
Expand All @@ -185,8 +185,13 @@ export const Sidebar = memo(
<SidebarTabJobDetails job={job} jobSpecService={jobSpecService} />
</TabPanel>

<TabPanel value={SidebarTab.JobRuns} className={styles.sidebarTabPanel}>
<SidebarTabJobRuns job={job} runInfoService={runInfoService} cordonService={cordonService} />
<TabPanel value={SidebarTab.JobResult} className={styles.sidebarTabPanel}>
<SidebarTabJobResult
job={job}
jobInfoService={jobSpecService}
runInfoService={runInfoService}
cordonService={cordonService}
/>
</TabPanel>

<TabPanel value={SidebarTab.Yaml} className={styles.sidebarTabPanel}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { Job } from "models/lookoutV2Models"

import { ContainerDetails } from "./ContainerDetails"
import { KeyValuePairTable } from "./KeyValuePairTable"
import { IGetJobSpecService } from "../../../services/lookoutV2/GetJobSpecService"
import { IGetJobInfoService } from "../../../services/lookoutV2/GetJobInfoService"
import { formatBytes, formatCpu } from "../../../utils/resourceUtils"

export interface SidebarTabJobDetailsProps {
job: Job
jobSpecService: IGetJobSpecService
jobSpecService: IGetJobInfoService
}

export const SidebarTabJobDetails = ({ job, jobSpecService }: SidebarTabJobDetailsProps) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ import styles from "./SidebarTabJobLogs.module.css"
import { useCustomSnackbar } from "../../../hooks/useCustomSnackbar"
import { useJobSpec } from "../../../hooks/useJobSpec"
import { getAccessToken, useUserManager } from "../../../oidc"
import { IGetJobSpecService } from "../../../services/lookoutV2/GetJobSpecService"
import { IGetJobInfoService } from "../../../services/lookoutV2/GetJobInfoService"
import { ILogService, LogLine } from "../../../services/lookoutV2/LogService"
import { getErrorMessage, RequestStatus } from "../../../utils"

export interface SidebarTabJobLogsProps {
job: Job
jobSpecService: IGetJobSpecService
jobSpecService: IGetJobInfoService
logService: ILogService
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,49 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"
import { ExpandMore } from "@mui/icons-material"
import {
Accordion,
AccordionSummary,
Typography,
AccordionDetails,
AccordionSummary,
Button,
CircularProgress,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
DialogContent,
DialogTitle,
Tooltip,
Typography,
} from "@mui/material"
import { Button, Tooltip } from "@mui/material"
import { Job, JobRun } from "models/lookoutV2Models"
import { Job, JobRun, JobState } from "models/lookoutV2Models"
import { formatJobRunState, formatTimeSince, formatUtcDate } from "utils/jobsTableFormatters"

import { CodeBlock } from "./CodeBlock"
import { KeyValuePairTable } from "./KeyValuePairTable"
import styles from "./SidebarTabJobRuns.module.css"
import styles from "./SidebarTabJobResult.module.css"
import { useCustomSnackbar } from "../../../hooks/useCustomSnackbar"
import { getAccessToken, useUserManager } from "../../../oidc"
import { ICordonService } from "../../../services/lookoutV2/CordonService"
import { IGetJobInfoService } from "../../../services/lookoutV2/GetJobInfoService"
import { IGetRunInfoService } from "../../../services/lookoutV2/GetRunInfoService"
import { getErrorMessage } from "../../../utils"

export interface SidebarTabJobRunsProps {
export interface SidebarTabJobResultProps {
job: Job
runInfoService: IGetRunInfoService
jobInfoService: IGetJobInfoService
cordonService: ICordonService
}

type LoadState = "Idle" | "Loading"

export const SidebarTabJobRuns = ({ job, runInfoService, cordonService }: SidebarTabJobRunsProps) => {
export const SidebarTabJobResult = ({
job,
jobInfoService,
runInfoService,
cordonService,
}: SidebarTabJobResultProps) => {
const mounted = useRef(false)
const openSnackbar = useCustomSnackbar()
const runsNewestFirst = useMemo(() => [...job.runs].reverse(), [job])
const [jobError, setJobError] = useState<string>("")
const [runErrorMap, setRunErrorMap] = useState<Map<string, string>>(new Map<string, string>())
const [runErrorLoadingMap, setRunErrorLoadingMap] = useState<Map<string, LoadState>>(new Map<string, LoadState>())
const [runDebugMessageMap, setRunDebugMessageMap] = useState<Map<string, string>>(new Map<string, string>())
Expand All @@ -45,6 +54,29 @@ export const SidebarTabJobRuns = ({ job, runInfoService, cordonService }: Sideba
)
const [open, setOpen] = useState(false)

const fetchJobError = useCallback(async () => {
if (job.state != JobState.Failed && job.state != JobState.Rejected) {
setJobError("")
return
}
const getJobErrorResultPromise = jobInfoService.getJobError(job.jobId)
getJobErrorResultPromise
.then((errorString) => {
if (!mounted.current) {
return
}
setJobError(errorString)
})
.catch(async (e) => {
const errMsg = await getErrorMessage(e)
console.error(errMsg)
if (!mounted.current) {
return
}
openSnackbar("Failed to retrieve Job error for Job with ID: " + job.jobId + ": " + errMsg, "error")
})
}, [job])

const fetchRunErrors = useCallback(async () => {
const newRunErrorLoadingMap = new Map<string, LoadState>()
for (const run of job.runs) {
Expand Down Expand Up @@ -134,9 +166,25 @@ export const SidebarTabJobRuns = ({ job, runInfoService, cordonService }: Sideba
}
}, [job])

let topLevelError = ""
let topLevelErrorTitle = ""
if (jobError != "") {
topLevelError = jobError
topLevelErrorTitle = "Job Error"
} else {
for (const run of job.runs) {
const runErr = runErrorMap.get(run.runId) ?? ""
if (runErr != "") {
topLevelError = runErr
topLevelErrorTitle = "Last Job Run Error"
}
}
}

useEffect(() => {
mounted.current = true
fetchRunErrors()
fetchJobError()
fetchRunDebugMessages()
return () => {
mounted.current = false
Expand Down Expand Up @@ -167,9 +215,15 @@ export const SidebarTabJobRuns = ({ job, runInfoService, cordonService }: Sideba
openSnackbar("Failed to cordon node " + node + ": " + errMsg, "error")
}
}

return (
<div style={{ width: "100%", height: "100%" }}>
{topLevelError !== "" ? (
<>
<Typography variant="subtitle2">{topLevelErrorTitle}:</Typography>
<CodeBlock text={topLevelError} />
</>
) : null}
<Typography variant="subtitle2">Runs:</Typography>
{runsNewestFirst.map((run, i) => {
return (
<Accordion key={run.runId} defaultExpanded={i === 0}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import { Job } from "models/lookoutV2Models"
import styles from "./SidebarTabJobYaml.module.css"
import { useCustomSnackbar } from "../../../hooks/useCustomSnackbar"
import { useJobSpec } from "../../../hooks/useJobSpec"
import { IGetJobSpecService } from "../../../services/lookoutV2/GetJobSpecService"
import { IGetJobInfoService } from "../../../services/lookoutV2/GetJobInfoService"

export interface SidebarTabJobYamlProps {
job: Job
jobSpecService: IGetJobSpecService
jobSpecService: IGetJobInfoService
}

function toJobSubmissionYaml(jobSpec: Record<string, any>): string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import FakeGroupJobsService from "services/lookoutV2/mocks/FakeGroupJobsService"
import { v4 as uuidv4 } from "uuid"

import { JobsTableContainer } from "./JobsTableContainer"
import { IGetJobSpecService } from "../../services/lookoutV2/GetJobSpecService"
import { IGetJobInfoService } from "../../services/lookoutV2/GetJobInfoService"
import { IGetRunInfoService } from "../../services/lookoutV2/GetRunInfoService"
import { ILogService } from "../../services/lookoutV2/LogService"
import { FakeCordonService } from "../../services/lookoutV2/mocks/FakeCordonService"
import FakeGetJobSpecService from "../../services/lookoutV2/mocks/FakeGetJobSpecService"
import FakeGetJobInfoService from "../../services/lookoutV2/mocks/FakeGetJobInfoService"
import { FakeGetRunInfoService } from "../../services/lookoutV2/mocks/FakeGetRunInfoService"
import { FakeLogService } from "../../services/lookoutV2/mocks/FakeLogService"

Expand Down Expand Up @@ -57,7 +57,7 @@ describe("JobsTableContainer", () => {
let getJobsService: IGetJobsService,
groupJobsService: IGroupJobsService,
runErrorService: IGetRunInfoService,
jobSpecService: IGetJobSpecService,
jobSpecService: IGetJobInfoService,
logService: ILogService,
updateJobsService: UpdateJobsService

Expand All @@ -69,7 +69,7 @@ describe("JobsTableContainer", () => {
beforeEach(() => {
setUp([])
runErrorService = new FakeGetRunInfoService(false)
jobSpecService = new FakeGetJobSpecService(false)
jobSpecService = new FakeGetJobInfoService(false)
logService = new FakeLogService()
localStorage.clear()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ import styles from "./JobsTableContainer.module.css"
import { useCustomSnackbar } from "../../hooks/useCustomSnackbar"
import { ICordonService } from "../../services/lookoutV2/CordonService"
import { CustomViewsService } from "../../services/lookoutV2/CustomViewsService"
import { IGetJobSpecService } from "../../services/lookoutV2/GetJobSpecService"
import { IGetJobInfoService } from "../../services/lookoutV2/GetJobInfoService"
import { ILogService } from "../../services/lookoutV2/LogService"
import { getErrorMessage, waitMillis, CommandSpec } from "../../utils"
import { EmptyInputError, ParseError } from "../../utils/resourceUtils"
Expand All @@ -83,7 +83,7 @@ interface JobsTableContainerProps {
groupJobsService: IGroupJobsService
updateJobsService: UpdateJobsService
runInfoService: IGetRunInfoService
jobSpecService: IGetJobSpecService
jobSpecService: IGetJobInfoService
logService: ILogService
cordonService: ICordonService
debug: boolean
Expand Down
4 changes: 2 additions & 2 deletions internal/lookout/ui/src/hooks/useJobSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useEffect, useState } from "react"

import { OpenSnackbarFn } from "./useCustomSnackbar"
import { Job } from "../models/lookoutV2Models"
import { IGetJobSpecService } from "../services/lookoutV2/GetJobSpecService"
import { IGetJobInfoService } from "../services/lookoutV2/GetJobInfoService"
import { getErrorMessage, RequestStatus } from "../utils"

export type JobSpecState = {
Expand All @@ -12,7 +12,7 @@ export type JobSpecState = {

export const useJobSpec = (
job: Job,
jobSpecService: IGetJobSpecService,
jobSpecService: IGetJobInfoService,
openSnackbar: OpenSnackbarFn,
): JobSpecState => {
const [jobSpecState, setJobSpecState] = useState<JobSpecState>({
Expand Down
Loading