Skip to content

Commit

Permalink
Lookoutv2: add sidebar displaying useful commands (#3354)
Browse files Browse the repository at this point in the history
  • Loading branch information
robertdavidsmith committed Feb 1, 2024
1 parent 95572c8 commit 4b587f9
Show file tree
Hide file tree
Showing 11 changed files with 119 additions and 1 deletion.
5 changes: 5 additions & 0 deletions config/lookoutv2/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,8 @@ uiConfig:
armadaApiBaseUrl: "http://armada-server:8080"
userAnnotationPrefix: "armadaproject.io/"
binocularsBaseUrlPattern: "http://armada-binoculars:8080"
commandSpecs:
- name: Logs
template: "kubectl --context {{ runs[runs.length - 1].cluster }} -n {{ namespace }} logs armada-{{ jobId }}-0"
- name: Exec
template: "kubectl --context {{ runs[runs.length - 1].cluster }} -n {{ namespace }} exec -it armada-{{ jobId }}-0 /bin/sh"
3 changes: 3 additions & 0 deletions internal/lookout/ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { ICordonService } from "./services/lookoutV2/CordonService"
import { IGetJobSpecService } from "./services/lookoutV2/GetJobSpecService"
import { IGetRunErrorService } from "./services/lookoutV2/GetRunErrorService"
import { ILogService } from "./services/lookoutV2/LogService"
import { CommandSpec } from "./utils"
import { OidcConfig } from "./utils"

import "./App.css"
Expand Down Expand Up @@ -76,6 +77,7 @@ type AppProps = {
jobSetsAutoRefreshMs: number | undefined
jobsAutoRefreshMs: number | undefined
debugEnabled: boolean
commandSpecs: CommandSpec[]
}

function OidcCallback(): JSX.Element {
Expand Down Expand Up @@ -130,6 +132,7 @@ export function App(props: AppProps): JSX.Element {
cordonService={props.v2CordonService}
debug={props.debugEnabled}
autoRefreshMs={props.jobsAutoRefreshMs}
commandSpecs={props.commandSpecs}
/>
}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ describe("Sidebar", () => {
sidebarWidth={600}
onClose={onClose}
onWidthChange={() => undefined}
commandSpecs={[]}
/>
</SnackbarProvider>,
)
Expand Down
15 changes: 15 additions & 0 deletions internal/lookout/ui/src/components/lookoutV2/sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Job, JobState } from "models/lookoutV2Models"

import styles from "./Sidebar.module.css"
import { SidebarHeader } from "./SidebarHeader"
import { SidebarTabJobCommands } from "./SidebarTabJobCommands"
import { SidebarTabJobDetails } from "./SidebarTabJobDetails"
import { SidebarTabJobLogs } from "./SidebarTabJobLogs"
import { SidebarTabJobRuns } from "./SidebarTabJobRuns"
Expand All @@ -14,12 +15,14 @@ import { ICordonService } from "../../../services/lookoutV2/CordonService"
import { IGetJobSpecService } from "../../../services/lookoutV2/GetJobSpecService"
import { IGetRunErrorService } from "../../../services/lookoutV2/GetRunErrorService"
import { ILogService } from "../../../services/lookoutV2/LogService"
import { CommandSpec } from "../../../utils"

enum SidebarTab {
JobDetails = "JobDetails",
JobRuns = "JobRuns",
Yaml = "Yaml",
Logs = "Logs",
Commands = "Commands",
}

type ResizeState = {
Expand All @@ -35,6 +38,7 @@ export interface SidebarProps {
logService: ILogService
cordonService: ICordonService
sidebarWidth: number
commandSpecs: CommandSpec[]
onClose: () => void
onWidthChange: (width: number) => void
}
Expand All @@ -49,6 +53,7 @@ export const Sidebar = memo(
sidebarWidth,
onClose,
onWidthChange,
commandSpecs,
}: SidebarProps) => {
const [openTab, setOpenTab] = useState<SidebarTab>(SidebarTab.JobDetails)

Expand Down Expand Up @@ -168,6 +173,12 @@ export const Sidebar = memo(
sx={{ minWidth: "50px" }}
disabled={job.state === JobState.Queued}
></Tab>
<Tab
label="Commands"
value={SidebarTab.Commands}
sx={{ minWidth: "50px" }}
disabled={job.state === JobState.Queued}
></Tab>
</Tabs>

<TabPanel value={SidebarTab.JobDetails} className={styles.sidebarTabPanel}>
Expand All @@ -185,6 +196,10 @@ export const Sidebar = memo(
<TabPanel value={SidebarTab.Logs} className={styles.sidebarTabPanel}>
<SidebarTabJobLogs job={job} jobSpecService={jobSpecService} logService={logService} />
</TabPanel>

<TabPanel value={SidebarTab.Commands} className={styles.sidebarTabPanel}>
<SidebarTabJobCommands job={job} commandSpecs={commandSpecs} />
</TabPanel>
</TabContext>
</div>
</Box>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.commandsText {
width: 100%;
white-space: pre-wrap;
font-family: monospace;
word-wrap: break-word;
background: #1b1b1b none;
color: #fff;
padding: 5px;
border-radius: 5px;
position: relative;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React, { useCallback } from "react"

import { ContentCopy } from "@mui/icons-material"
import { IconButton, Link } from "@mui/material"
import { template, templateSettings } from "lodash"
import { Job } from "models/lookoutV2Models"
import validator from "validator"

import styles from "./SidebarTabJobCommands.module.css"
import { useCustomSnackbar } from "../../../hooks/useCustomSnackbar"
import { CommandSpec } from "../../../utils"

export interface SidebarTabJobCommandsProps {
job: Job
commandSpecs: CommandSpec[]
}

function getCommandText(job: Job, commandSpec: CommandSpec): string {
templateSettings.interpolate = /{{([\s\S]+?)}}/g
const compiledTemplate = template(commandSpec.template)
return compiledTemplate(job)
}

export const SidebarTabJobCommands = ({ job, commandSpecs }: SidebarTabJobCommandsProps) => {
const openSnackbar = useCustomSnackbar()

const copyCommand = useCallback(async (commandText: string) => {
await navigator.clipboard.writeText(commandText)
openSnackbar("Copied command to clipboard!", "info", {
autoHideDuration: 3000,
preventDuplicate: true,
})
}, [])

return (
<div style={{ width: "100%", height: "100%" }}>
{job.runs?.length ? (
<div>
{commandSpecs.map((c, i) => (
<>
<div>
{i > 0 ? <br /> : undefined}
{c.name}
<IconButton size="small" title="Copy to clipboard" onClick={() => copyCommand(getCommandText(job, c))}>
<ContentCopy />
</IconButton>
</div>
{validator.isURL(getCommandText(job, c)) ? (
<Link href={getCommandText(job, c)} target="_blank">
<div>{getCommandText(job, c)}</div>
</Link>
) : (
<div className={styles.commandsText}>{getCommandText(job, c)}</div>
)}
</>
))}
</div>
) : undefined}
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ describe("JobsTableContainer", () => {
cordonService={new FakeCordonService()}
debug={false}
autoRefreshMs={30000}
commandSpecs={[]}
/>
</SnackbarProvider>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ import { ICordonService } from "../../services/lookoutV2/CordonService"
import { CustomViewsService } from "../../services/lookoutV2/CustomViewsService"
import { IGetJobSpecService } from "../../services/lookoutV2/GetJobSpecService"
import { ILogService } from "../../services/lookoutV2/LogService"
import { getErrorMessage, waitMillis } from "../../utils"
import { getErrorMessage, waitMillis, CommandSpec } from "../../utils"
import { EmptyInputError, ParseError } from "../../utils/resourceUtils"

const PAGE_SIZE_OPTIONS = [5, 25, 50, 100]
Expand All @@ -88,6 +88,7 @@ interface JobsTableContainerProps {
cordonService: ICordonService
debug: boolean
autoRefreshMs: number | undefined
commandSpecs: CommandSpec[]
}

export type LookoutColumnFilter = {
Expand Down Expand Up @@ -132,6 +133,7 @@ export const JobsTableContainer = ({
cordonService,
debug,
autoRefreshMs,
commandSpecs,
}: JobsTableContainerProps) => {
const openSnackbar = useCustomSnackbar()

Expand Down Expand Up @@ -846,6 +848,7 @@ export const JobsTableContainer = ({
sidebarWidth={sidebarWidth}
onClose={sideBarClose}
onWidthChange={setSidebarWidth}
commandSpecs={commandSpecs}
/>
)}
</Box>
Expand Down
1 change: 1 addition & 0 deletions internal/lookout/ui/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import "./index.css"
jobSetsAutoRefreshMs={uiConfig.jobSetsAutoRefreshMs}
jobsAutoRefreshMs={uiConfig.jobsAutoRefreshMs}
debugEnabled={uiConfig.debugEnabled}
commandSpecs={uiConfig.commandSpecs}
/>,
document.getElementById("root"),
)
Expand Down
11 changes: 11 additions & 0 deletions internal/lookout/ui/src/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ export interface OidcConfig {
clientId: string
scope: string
}
export interface CommandSpec {
name: string
template: string
}

interface UIConfig {
armadaApiBaseUrl: string
Expand All @@ -19,6 +23,7 @@ interface UIConfig {
customTitle: string
oidcEnabled: boolean
oidc?: OidcConfig
commandSpecs: CommandSpec[]
}

export type RequestStatus = "Loading" | "Idle"
Expand Down Expand Up @@ -46,6 +51,7 @@ export async function getUIConfig(): Promise<UIConfig> {
customTitle: "",
oidcEnabled: false,
oidc: undefined,
commandSpecs: [],
}

try {
Expand All @@ -64,6 +70,11 @@ export async function getUIConfig(): Promise<UIConfig> {
clientId: json.Oidc.ClientId,
scope: json.Oidc.Scope,
}
if (json.CommandSpecs) {
config.commandSpecs = json.CommandSpecs.map((c: { Name: string; Template: string }) => {
return { name: c.Name, template: c.Template }
})
}
}
} catch (e) {
console.error(e)
Expand Down
6 changes: 6 additions & 0 deletions internal/lookoutv2/configuration/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ type PrunerConfig struct {
BatchSize int
}

type CommandSpec struct {
Name string
Template string
}

type UIConfig struct {
CustomTitle string

Expand All @@ -51,4 +56,5 @@ type UIConfig struct {

JobSetsAutoRefreshMs int `json:",omitempty"`
JobsAutoRefreshMs int `json:",omitempty"`
CommandSpecs []CommandSpec
}

0 comments on commit 4b587f9

Please sign in to comment.