Skip to content

Commit

Permalink
Merge pull request #216 from amosproj/feat/last-minute
Browse files Browse the repository at this point in the history
Feat/last minute
  • Loading branch information
vringar authored Jul 17, 2023
2 parents 44832e4 + 0aae1ef commit f37b723
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 41 deletions.
4 changes: 2 additions & 2 deletions DB/kubernetes-schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,10 @@ CREATE TABLE nodes(
CREATE TABLE "container_states"(
"id" int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
"kind" text,
"started_at" time,
"started_at" timestamp,
"container_id" text,
"exit_code" int,
"finished_at" time,
"finished_at" timestamp,
"message" text,
"reason" text,
"signal" int
Expand Down
2 changes: 1 addition & 1 deletion Explorer/src/app/containers/[id]/page.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jest.mock("../../../lib/db", () => ({
return Promise.resolve({
container_event_id: 1,
container_id: 1,
timestamp: "2021-08-01 00:00:00",
timestamp: new Date("2021-08-01 00:00:00"),
pod_id: 1,
name: "Container 1",
image: "Image 1",
Expand Down
127 changes: 102 additions & 25 deletions Explorer/src/components/container_detail_page.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
"use client";

import { Table } from "flowbite-react";
import { H1 } from "@/components/style_elements";
import { H1, H2 } from "@/components/style_elements";
import { HealthIndicatorBadge } from "@/components/health_indicators";
import { Container } from "@/lib/types/Container";
import Link from "next/link";
import { Container, ContainerStates } from "@/lib/types/Container";
import { ReactNode } from "react";

export default function ContainerDetailPage({
container_details,
container_details: container,
}: {
container_details: Container;
}): JSX.Element {
return (
<div>
<div className="flex">
<H1 content={"Container " + container_details.name} />
<HealthIndicatorBadge status={container_details.status} />
<H1 content={"Container " + container.name} />
<HealthIndicatorBadge status={container.status} />
</div>
<div className="flex">
<div className="w-1/4 w-max">
<ContainerDetailsWidget container_data={container_details} />
<ContainerDetailsWidget container_data={container} />
</div>
{/* <div className="w-1/2 w-max px-8">
<ContainerWorkLoad />
Expand Down Expand Up @@ -88,7 +89,7 @@ function _ContainerChangelogWidget({
<div className="p-0">
<section>
<header>
<h2 className="mt-2 mb-3 text-2xl font-bold">Changelog</h2>
<H2 className="mt-2 mb-3 text-2xl font-bold" content="Changelog" />
</header>
<div className="">
<Table>
Expand Down Expand Up @@ -145,7 +146,7 @@ function ContainerDetailsWidget({
}): JSX.Element {
return (
<div className="p-0 w-max">
<h2 className="mt-2 mb-3 text-2xl font-bold">Details</h2>
<H2 content={"Details"} />
<Table>
<Table.Head>
<Table.HeadCell className="!py-2 bg-gray-50 dark:bg-gray-800">
Expand All @@ -156,12 +157,8 @@ function ContainerDetailsWidget({
</Table.HeadCell>
</Table.Head>
<Table.Body className="divide-y">
{Object.entries(container_data).map(([name, value], index) => {
if (value instanceof Date) {
value = value.toUTCString();
} else if (typeof value === "boolean") {
value = value ? "true" : "false";
}
{Object.keys(container_data).map((key, index) => {
const name = key as keyof Container;
return (
<Table.Row
key={index}
Expand All @@ -171,17 +168,7 @@ function ContainerDetailsWidget({
{name.toUpperCase()}
</Table.Cell>
<Table.Cell className="!py-1 whitespace-nowrap font-medium bg-gray-30 dark:bg-gray-600">
{name === "pod_id" ? (
<Link
href={`/pods/${encodeURIComponent(value)}`}
className="text-decoration-none text-blue-800"
id="list"
>
{value}
</Link>
) : (
value
)}
{computeValue(name, container_data)}
</Table.Cell>
</Table.Row>
);
Expand All @@ -191,3 +178,93 @@ function ContainerDetailsWidget({
</div>
);
}

function computeValue(
key: keyof Container,
container_data: Container
): ReactNode {
switch (key) {
case "current_state":
case "last_fail_state":
return (
<ContainerStatesWidget
state={container_data[key]}
></ContainerStatesWidget>
);
case "timestamp":
return container_data[key].toUTCString();
case "pod_id": {
const value = container_data[key];
return (
<Link
href={`/pods/${encodeURIComponent(value)}`}
className="text-decoration-none text-blue-800"
id="list"
>
{value}
</Link>
);
}
case "ready":
case "started":
return container_data[key] ? "true" : "false";
default:
return container_data[key];
}
}

function ContainerStatesWidget({
state,
}: {
state: ContainerStates | undefined;
}): JSX.Element {
if (state === undefined) {
return <div></div>;
}
let elem: JSX.Element;
switch (state.kind) {
case "running":
elem = (
<div>
<p>Started at: {state.started_at.toUTCString()}</p>
</div>
);
break;
case "waiting":
elem = (
<div>
<p>Reason: {state.reason}</p>
<p>Message: {state.message}</p>
</div>
);
break;
case "terminated":
elem = (
<div>
<p>
Container ID:
<Link
href={`/containers/${encodeURIComponent(state.container_id)}`}
className="text-decoration-none text-blue-800"
id="list"
>
{state.container_id}
</Link>
</p>
<p>Exit code: {state.exit_code}</p>
<p>Signal: {state.signal}</p>
<p>Reason: {state.reason}</p>
<p>Started at: {state.started_at.toUTCString()}</p>
<p>Finished at: {state.finished_at.toUTCString()}</p>
<p>Finished at: {state.finished_at.toUTCString()}</p>
<p>Message: {state.message}</p>
</div>
);
}
return (
<div>
<p>State: {state.kind}</p>
{elem}
</div>
);
}
6 changes: 3 additions & 3 deletions Explorer/src/components/pod_detail_page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";
import Link from "next/link";
import { Table } from "flowbite-react";
import { H1 } from "@/components/style_elements";
import { H1, H2 } from "@/components/style_elements";
import { HealthIndicatorBadge } from "@/components/health_indicators";
import { Pod } from "@/lib/types/Pod";
import { Container, ContainerList } from "@/lib/types/Container";
Expand Down Expand Up @@ -36,7 +36,7 @@ export default function PodDetailPage({
function PodDetailsWidget({ pod_data }: { pod_data: Pod }): JSX.Element {
return (
<div className="p-0 w-max">
<h2 className="mt-2 mb-3 text-2xl font-bold">Details</h2>
<H2 content={"Details"} />
<Table className="details-table">
<Table.Head>
<Table.HeadCell className="!py-2 details-table bg-gray-50 dark:bg-gray-800">
Expand Down Expand Up @@ -80,7 +80,7 @@ function ChildContainerWidget({
}): JSX.Element {
return (
<div className="p-0 w-max">
<h2 className="mt-2 mb-3 text-2xl font-bold">Child Containers</h2>
<H2 content={"Child Containers"} />
<Table>
<Table.Head>
<Table.HeadCell
Expand Down
4 changes: 2 additions & 2 deletions Explorer/src/components/style_elements.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ export function H1({ content }: any): JSX.Element {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function H2({ content }: any): JSX.Element {
return (
<h1 className="mb-4 my-2 text-3xl font-bold leading-none tracking-tight text-gray-900 dark:text-white">
<h2 className="mb-4 my-2 text-3xl font-bold leading-none tracking-tight text-gray-900 dark:text-white">
{content}
</h1>
</h2>
);
}
64 changes: 61 additions & 3 deletions Explorer/src/lib/db.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import "server-only";
import { Container, ContainerList } from "./types/Container";
import { Container, ContainerList, ContainerStates } from "./types/Container";
import { Pool } from "pg";
import { Pod, PodList } from "./types/Pod";

Expand All @@ -14,14 +14,72 @@ const pool = new Pool({
export async function getContainerDetails(
container_id: string
): Promise<Container | undefined> {
return (
const container_row = (
await pool.query(
"SELECT * FROM containers c WHERE container_id = $1 order by timestamp DESC limit 1",
"SELECT * FROM containers c WHERE c.container_id = $1 order by timestamp DESC limit 1",
[container_id]
)
).rows[0];
if (!container_row) {
// No container found
return undefined;
}
const current_state = await getContainerState(container_row.state_id);
if (!current_state) {
return undefined;
}
const last_fail_state = await getContainerState(container_row.last_state_id);
const container: Container = {
id: container_row.id,
timestamp: container_row.timestamp,
container_id: container_row.container_id,
pod_id: container_row.pod_id,
name: container_row.name,
image: container_row.image,
status: container_row.status,
// ports: 0,
image_id: container_row.image_id,
ready: container_row.ready,
restart_count: container_row.restart_count,
started: container_row.started,
// state_id: 0,
// last_state_id: 0,
current_state,
last_fail_state,
};
return container;
}

async function getContainerState(
state_id: number
): Promise<ContainerStates | undefined> {
const row = (
await pool.query("SELECT * FROM container_states cs WHERE cs.id = $1", [
state_id,
])
).rows[0];
if (!row) {
return undefined;
}

switch (row.kind) {
case "Waiting":
return { kind: "waiting", message: row.message, reason: row.reason };
case "Terminated":
return {
kind: "terminated",
container_id: row.container_id,
reason: row.reason,
message: row.message,
started_at: row.started_at,
finished_at: row.finished_at,
exit_code: row.exit_code,
signal: row.signal,
};
case "Running":
return { kind: "running", started_at: row.started_at };
}
}
export async function getContainerList(): Promise<ContainerList> {
const res = await pool.query(
// returns entry with the last timestamp for every unique (name, pod_id)
Expand Down
18 changes: 15 additions & 3 deletions Explorer/src/lib/types/Container.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
import { Status } from "./Status";
export type ContainerStates =
| { kind: "running"; started_at: Date }
| {
kind: "terminated";
container_id: number;
reason: string;
message: string;
started_at: Date;
finished_at: Date;
exit_code: number;
signal: number;
}
| { kind: "waiting"; message: string; reason: string };

export type Container = {
id: number;
Expand All @@ -8,12 +21,11 @@ export type Container = {
name: string;
image: string;
status: Status;
ports: number;
image_id: string;
ready: boolean;
restart_count: number;
started: boolean;
state_id: number;
last_state_id: number;
current_state: ContainerStates;
last_fail_state: ContainerStates | undefined;
};
export type ContainerList = Container[];
4 changes: 2 additions & 2 deletions Proxy/internal/database/model/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,10 @@ type Container struct {
type ContainerState struct {
ID int `bun:"id,autoincrement,pk"`
Kind string `bun:"kind,type:text"`
StartedAt time.Time `bun:"started_at,type:time"`
StartedAt time.Time `bun:"started_at,type:timestamp"`
ContainerID string `bun:"container_id,type:text"`
ExitCode int `bun:"exit_code,type:int"`
FinishedAt time.Time `bun:"finished_at,type:time"`
FinishedAt time.Time `bun:"finished_at,type:timestamp"`
Message string `bun:"message,type:text"`
Reason string `bun:"reason,type:text"`
Signal int `bun:"signal,type:int"`
Expand Down

0 comments on commit f37b723

Please sign in to comment.