Kaynağa Gözat

Moved to events folder on components and integrated

jnfrati 4 yıl önce
ebeveyn
işleme
97a2b2cd8c

+ 0 - 12
dashboard/src/components/EventLogs.tsx

@@ -1,12 +0,0 @@
-import { Event } from "main/home/cluster-dashboard/expanded-chart/events/EventsTab";
-import React from "react";
-
-type EventLogsProps = {
-  event: Event;
-};
-
-const EventLogs: React.FunctionComponent<EventLogsProps> = ({}) => {
-  return <div>Show logs</div>;
-};
-
-export default EventLogs;

+ 1 - 1
dashboard/src/components/EventCard.tsx → dashboard/src/components/events/EventCard.tsx

@@ -1,6 +1,6 @@
 import React, { useState } from "react";
 import styled from "styled-components";
-import { Event } from "main/home/cluster-dashboard/expanded-chart/events/EventsTab";
+import { Event } from "./EventsContext";
 
 type CardProps = {
   event: Event;

+ 56 - 0
dashboard/src/components/events/EventLogs.tsx

@@ -0,0 +1,56 @@
+import React, { useContext } from "react";
+import styled from "styled-components";
+import backArrow from "assets/back_arrow.png";
+import { EventContext } from "./EventsContext";
+
+type EventLogsProps = {};
+
+const EventLogs: React.FunctionComponent<EventLogsProps> = ({}) => {
+  const { clearSelectedEvent } = useContext(EventContext);
+  return (
+    <>
+      <ControlRow>
+        <div>
+          <BackButton onClick={clearSelectedEvent}>
+            <BackButtonImg src={backArrow} />
+          </BackButton>
+        </div>
+      </ControlRow>
+      <div>Show logs</div>
+    </>
+  );
+};
+
+export default EventLogs;
+
+const ControlRow = styled.div`
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 35px;
+  padding-left: 0px;
+`;
+
+const BackButton = styled.div`
+  display: flex;
+  width: 36px;
+  cursor: pointer;
+  height: 36px;
+  align-items: center;
+  justify-content: center;
+  border: 1px solid #ffffff55;
+  border-radius: 100px;
+  background: #ffffff11;
+
+  :hover {
+    background: #ffffff22;
+    > img {
+      opacity: 1;
+    }
+  }
+`;
+
+const BackButtonImg = styled.img`
+  width: 16px;
+  opacity: 0.75;
+`;

+ 215 - 0
dashboard/src/components/events/EventsContext.tsx

@@ -0,0 +1,215 @@
+import React, { createContext, useContext, useEffect, useState } from "react";
+import api from "shared/api";
+import { Context } from "shared/Context";
+
+export type Event = {
+  id: number;
+  project_id: number;
+  cluster_id: number;
+  owner_name: string;
+  owner_type: string;
+  event_type: "critical" | "normal";
+  resource_type: string;
+  name: string;
+  namespace: string;
+  message: string;
+  reason: string;
+  timestamp: string;
+};
+
+type EventsContextType = {
+  isPorterAgentInstalled: boolean;
+  isPorterAgentInstalling: boolean;
+  isLoading: boolean;
+  eventList: Event[];
+  selectedEvent: Event | null;
+  selectEvent: (id: number) => void;
+  clearSelectedEvent: () => void;
+  setLimit: (limit: number) => void;
+  setResourceType: (newResourceType: "pod" | "hpa" | "node") => void;
+  installPorterAgent: () => Promise<void>;
+};
+
+const defaultEventContext: EventsContextType = {
+  eventList: [],
+  isPorterAgentInstalled: false,
+  isPorterAgentInstalling: false,
+  isLoading: true,
+  selectedEvent: null,
+  selectEvent: () => {},
+  clearSelectedEvent: () => {},
+  setLimit: () => {},
+  setResourceType: () => {},
+  installPorterAgent: async () => {},
+};
+
+export const EventContext = createContext<EventsContextType>(
+  defaultEventContext
+);
+
+type EventController = { type: string; name: string };
+
+type Props = {
+  controllers: EventController[];
+  enableNodeEvents: boolean;
+};
+
+const EventsContextProvider: React.FC<Props> = ({
+  children,
+  controllers,
+  enableNodeEvents,
+}) => {
+  // Porter agent related
+  const [isPorterAgentInstalled, setIsPorterAgentInstalled] = useState<boolean>(
+    false
+  );
+  const [
+    isPorterAgentInstalling,
+    setIsPorterAgentInstalling,
+  ] = useState<boolean>(false);
+  const [isLoading, setIsLoading] = useState<boolean>(false);
+
+  // Event related
+  const [eventList, setEventList] = useState<Event[]>([]);
+  const [selectedEvent, setSelectedEvent] = useState<Event | null>(null);
+  const [selectedController, setSelectedController] = useState<EventController>(
+    () => controllers[0] || undefined
+  );
+
+  // Pagination related
+  const [limit, setLimit] = useState<number>(10);
+  const [resourceType, setResourceType] = useState<"pod" | "hpa" | "node">(
+    "pod"
+  );
+  // Currently only implemented one sort type
+  const [sortBy] = useState<"timestamp">("timestamp");
+
+  const { currentCluster, currentProject, setCurrentError } = useContext(
+    Context
+  );
+
+  useEffect(() => {
+    checkIfPorterAgentIsInstalled();
+  }, [currentCluster, currentProject]);
+
+  useEffect(() => {
+    if (!isPorterAgentInstalling) {
+      return () => {};
+    }
+    const interval = setInterval(() => {
+      checkIfPorterAgentIsInstalled();
+    }, 500);
+
+    return () => clearInterval(interval);
+  }, [isPorterAgentInstalling]);
+
+  useEffect(() => {
+    setIsLoading(true);
+    // Clear out event list if the resource type or the selected controller changed
+    if (
+      resourceType !== eventList[0]?.resource_type ||
+      selectedController.name !== eventList[0].name
+    ) {
+      setEventList([]);
+    }
+
+    getEventList();
+  }, [isPorterAgentInstalled, selectedController, resourceType, sortBy]);
+
+  const checkIfPorterAgentIsInstalled = async () => {
+    try {
+      await api.getPorterAgentIsInstalled(
+        "<token>",
+        {
+          cluster_id: currentCluster.id,
+        },
+        {
+          project_id: currentProject.id,
+        }
+      );
+      setIsPorterAgentInstalled(true);
+    } catch (error) {
+      setIsPorterAgentInstalled(false);
+      setCurrentError(JSON.stringify(error));
+    }
+  };
+
+  const installPorterAgent = async () => {
+    try {
+      await api.installPorterAgent(
+        "<token>",
+        {
+          cluster_id: currentCluster.id,
+        },
+        {
+          project_id: currentProject.id,
+        }
+      );
+      setIsPorterAgentInstalling(true);
+    } catch (error) {}
+  };
+
+  const removeDuplicatedEvents = (events: Event[]) => {
+    return events.reduce<Event[]>((prev, event, arr) => {
+      if (prev.find((e) => e.id === event.id)) {
+        return prev;
+      }
+      return [...prev, event];
+    }, []);
+  };
+
+  const getEventList = async () => {
+    try {
+      const res = await api.getEvents(
+        "<token>",
+        {
+          limit,
+          skip: eventList.length,
+          type: resourceType,
+          sort_by: sortBy,
+          owner_name: selectedController.name,
+          owner_type: selectedController.type,
+        },
+        {
+          cluster_id: currentCluster.id,
+          project_id: currentProject.id,
+        }
+      );
+      const newEventList = removeDuplicatedEvents([...eventList, ...res.data]);
+      setEventList(newEventList);
+    } catch (error) {
+      setEventList([]);
+      setCurrentError(JSON.stringify(error));
+    }
+  };
+
+  const selectEvent = (id: number) => {
+    const event = eventList.find((e) => e.id === id);
+    setSelectedEvent(event);
+  };
+
+  const clearSelectedEvent = () => {
+    setSelectedEvent(null);
+  };
+
+  return (
+    <EventContext.Provider
+      value={{
+        isPorterAgentInstalled,
+        isPorterAgentInstalling,
+        isLoading,
+        eventList,
+        selectedEvent,
+        selectEvent,
+        clearSelectedEvent,
+        setLimit,
+        setResourceType,
+        installPorterAgent,
+      }}
+    >
+      {children}
+    </EventContext.Provider>
+  );
+};
+
+export default EventsContextProvider;

+ 142 - 0
dashboard/src/components/events/EventsList.tsx

@@ -0,0 +1,142 @@
+import React, { useContext, useEffect, useState } from "react";
+import styled from "styled-components";
+import Dropdown from "components/Dropdown";
+import { Event, EventContext } from "./EventsContext";
+import EventCard from "./EventCard";
+
+const mockData = [
+  {
+    id: 1,
+    project_id: 1,
+    cluster_id: 6,
+    owner_name: "pod-test",
+    owner_type: "deployment",
+    event_type: "critical",
+    resource_type: "pod",
+    name: "pod-test-1",
+    namespace: "default",
+    message: "",
+    reason: "OOM killed",
+    timestamp: "2021-06-30T21:48:23Z",
+  },
+  {
+    id: 2,
+    project_id: 1,
+    cluster_id: 6,
+    owner_name: "pod-test",
+    owner_type: "deployment",
+    event_type: "normal",
+    resource_type: "pod",
+    name: "pod-test-2",
+    namespace: "default",
+    message: "",
+    reason: "OOM killed",
+    timestamp: "2021-06-30T21:48:23Z",
+  },
+  {
+    id: 3,
+    project_id: 1,
+    cluster_id: 6,
+    owner_name: "pod-test",
+    owner_type: "deployment",
+    event_type: "critical",
+    resource_type: "pod",
+    name: "pod-test-2",
+    namespace: "default",
+    message: "",
+    reason: "OOM killed",
+    timestamp: "2021-06-30T21:48:23Z",
+  },
+  {
+    id: 4,
+    project_id: 1,
+    cluster_id: 6,
+    owner_name: "pod-test",
+    owner_type: "deployment",
+    event_type: "critical",
+    resource_type: "pod",
+    name: "pod-test-2",
+    namespace: "default",
+    message: "",
+    reason: "OOM killed",
+    timestamp: "2021-06-30T21:48:23Z",
+  },
+  {
+    id: 5,
+    project_id: 1,
+    cluster_id: 6,
+    owner_name: "pod-test",
+    owner_type: "deployment",
+    event_type: "critical",
+    resource_type: "pod",
+    name: "pod-test-2",
+    namespace: "default",
+    message: "",
+    reason: "OOM killed",
+    timestamp: "2021-06-30T21:48:23Z",
+  },
+  {
+    id: 6,
+    project_id: 1,
+    cluster_id: 6,
+    owner_name: "pod-test",
+    owner_type: "deployment",
+    event_type: "critical",
+    resource_type: "pod",
+    name: "pod-test-2",
+    namespace: "default",
+    message: "",
+    reason: "OOM killed",
+    timestamp: "2021-06-30T21:48:23Z",
+  },
+];
+
+const EventsList: React.FunctionComponent = ({}) => {
+  const { eventList, selectEvent, setResourceType } = useContext(EventContext);
+
+  const handleEventTypeSelection = (option: {
+    label: string;
+    value: "pod" | "hpa";
+  }) => {
+    setResourceType(option.value);
+  };
+
+  return (
+    <div>
+      <ControlRow>
+        <div>
+          <Dropdown
+            options={[
+              { label: "Pods", value: "pod" },
+              { label: "HPA", value: "hpa" },
+            ]}
+            onSelect={handleEventTypeSelection}
+          />
+        </div>
+      </ControlRow>
+      <EventsGrid>
+        {eventList.map((event) => {
+          return (
+            <EventCard key={event.id} event={event} selectEvent={selectEvent} />
+          );
+        })}
+      </EventsGrid>
+    </div>
+  );
+};
+
+export default EventsList;
+
+const ControlRow = styled.div`
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 35px;
+  padding-left: 0px;
+`;
+
+const EventsGrid = styled.div`
+  display: grid;
+  grid-row-gap: 15px;
+  grid-template-columns: 1;
+`;

+ 25 - 217
dashboard/src/main/home/cluster-dashboard/expanded-chart/events/EventsTab.tsx

@@ -1,232 +1,40 @@
-import React, { useEffect, useState } from "react";
+import React, { useContext, useEffect, useState } from "react";
 import styled from "styled-components";
-import backArrow from "assets/back_arrow.png";
-import Dropdown from "components/Dropdown";
-import EventCard from "components/EventCard";
-import EventLogs from "components/EventLogs";
 
-const mockData = [
-  {
-    id: 1,
-    project_id: 1,
-    cluster_id: 6,
-    owner_name: "pod-test",
-    owner_type: "deployment",
-    event_type: "critical",
-    resource_type: "pod",
-    name: "pod-test-1",
-    namespace: "default",
-    message: "",
-    reason: "OOM killed",
-    timestamp: "2021-06-30T21:48:23Z",
-  },
-  {
-    id: 2,
-    project_id: 1,
-    cluster_id: 6,
-    owner_name: "pod-test",
-    owner_type: "deployment",
-    event_type: "normal",
-    resource_type: "pod",
-    name: "pod-test-2",
-    namespace: "default",
-    message: "",
-    reason: "OOM killed",
-    timestamp: "2021-06-30T21:48:23Z",
-  },
-  {
-    id: 3,
-    project_id: 1,
-    cluster_id: 6,
-    owner_name: "pod-test",
-    owner_type: "deployment",
-    event_type: "critical",
-    resource_type: "pod",
-    name: "pod-test-2",
-    namespace: "default",
-    message: "",
-    reason: "OOM killed",
-    timestamp: "2021-06-30T21:48:23Z",
-  },
-  {
-    id: 4,
-    project_id: 1,
-    cluster_id: 6,
-    owner_name: "pod-test",
-    owner_type: "deployment",
-    event_type: "critical",
-    resource_type: "pod",
-    name: "pod-test-2",
-    namespace: "default",
-    message: "",
-    reason: "OOM killed",
-    timestamp: "2021-06-30T21:48:23Z",
-  },
-  {
-    id: 5,
-    project_id: 1,
-    cluster_id: 6,
-    owner_name: "pod-test",
-    owner_type: "deployment",
-    event_type: "critical",
-    resource_type: "pod",
-    name: "pod-test-2",
-    namespace: "default",
-    message: "",
-    reason: "OOM killed",
-    timestamp: "2021-06-30T21:48:23Z",
-  },
-  {
-    id: 6,
-    project_id: 1,
-    cluster_id: 6,
-    owner_name: "pod-test",
-    owner_type: "deployment",
-    event_type: "critical",
-    resource_type: "pod",
-    name: "pod-test-2",
-    namespace: "default",
-    message: "",
-    reason: "OOM killed",
-    timestamp: "2021-06-30T21:48:23Z",
-  },
-];
-
-export type Event = {
-  id: number;
-  project_id: number;
-  cluster_id: number;
-  owner_name: string;
-  owner_type: string;
-  event_type: "critical" | "normal";
-  resource_type: string;
-  name: string;
-  namespace: string;
-  message: string;
-  reason: string;
-  timestamp: string;
-};
+import EventLogs from "components/events/EventLogs";
+import EventsList from "components/events/EventsList";
+import EventsContextProvider, {
+  EventContext,
+} from "components/events/EventsContext";
 
 type EventsTabProps = {};
 
 const EventsTab: React.FunctionComponent<EventsTabProps> = () => {
-  const [eventList, setEventList] = useState<Event[]>([]);
-  const [selectedEvent, setSelectedEvent] = useState<Event>(null);
-  const [currentFilter, setCurrentFilter] = useState<string>("all");
-
-  useEffect(() => {
-    setTimeout(() => {
-      setEventList(
-        (mockData as Event[]).filter(
-          (e) => currentFilter === "all" || e.resource_type === currentFilter
-        )
-      );
-    }, 500);
-  }, [currentFilter]);
-
-  const selectEvent = (id: number) => {
-    const event = eventList.find((e) => e.id === id);
-    setSelectedEvent(event);
-  };
-
-  const clearSelectedEvent = () => {
-    setSelectedEvent(null);
-  };
-
-  const handleEventTypeSelection = (option: {
-    label: string;
-    value: string;
-  }) => {
-    console.log(option);
-    setCurrentFilter(option.value);
-  };
-
   return (
-    <NamespaceListWrapper>
-      {!selectedEvent && (
-        <>
-          <ControlRow>
-            <div>
-              <Dropdown
-                options={[
-                  { label: "All", value: "all" },
-                  { label: "Pods", value: "pod" },
-                  { label: "HPA", value: "HPA" },
-                ]}
-                onSelect={handleEventTypeSelection}
-              />
-            </div>
-          </ControlRow>
-          <EventsGrid>
-            {eventList.map((event) => {
-              return (
-                <EventCard
-                  key={event.id}
-                  event={event}
-                  selectEvent={selectEvent}
-                />
-              );
-            })}
-          </EventsGrid>
-        </>
-      )}
-      {selectedEvent && (
-        <>
-          <ControlRow>
-            <div>
-              <BackButton onClick={clearSelectedEvent}>
-                <BackButtonImg src={backArrow} />
-              </BackButton>
-            </div>
-          </ControlRow>
-          <EventLogs event={selectedEvent} />
-        </>
-      )}
-    </NamespaceListWrapper>
+    <EventsContextProvider controllers={[]} enableNodeEvents={false}>
+      <EventContext.Consumer>
+        {({ selectedEvent }) => (
+          <EventsPageWrapper>
+            {!selectedEvent && (
+              <>
+                <EventsList />
+              </>
+            )}
+            {selectedEvent && (
+              <>
+                <EventLogs />
+              </>
+            )}
+          </EventsPageWrapper>
+        )}
+      </EventContext.Consumer>
+    </EventsContextProvider>
   );
 };
 
 export default EventsTab;
 
-const NamespaceListWrapper = styled.div`
+const EventsPageWrapper = styled.div`
   margin-top: 35px;
   padding-bottom: 80px;
 `;
-
-const ControlRow = styled.div`
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  margin-bottom: 35px;
-  padding-left: 0px;
-`;
-
-const EventsGrid = styled.div`
-  display: grid;
-  grid-row-gap: 15px;
-  grid-template-columns: 1;
-`;
-
-const BackButton = styled.div`
-  display: flex;
-  width: 36px;
-  cursor: pointer;
-  height: 36px;
-  align-items: center;
-  justify-content: center;
-  border: 1px solid #ffffff55;
-  border-radius: 100px;
-  background: #ffffff11;
-
-  :hover {
-    background: #ffffff22;
-    > img {
-      opacity: 1;
-    }
-  }
-`;
-
-const BackButtonImg = styled.img`
-  width: 16px;
-  opacity: 0.75;
-`;

+ 54 - 0
dashboard/src/shared/api.tsx

@@ -1005,6 +1005,56 @@ const createWebhookToken = baseApi<
     `/api/projects/${project_id}/releases/${chart_name}/webhook_token?namespace=${namespace}&cluster_id=${cluster_id}&storage=${storage}`
 );
 
+const getPorterAgentIsInstalled = baseApi<
+  {
+    cluster_id: number;
+  },
+  { project_id: number }
+>("GET", (pathParams) => {
+  return `/api/projects/${pathParams.project_id}/k8s/agent/detect`;
+});
+
+const installPorterAgent = baseApi<
+  {
+    cluster_id: number;
+  },
+  { project_id: number }
+>("GET", (pathParams) => {
+  return `/api/projects/${pathParams.project_id}/k8s/agent/deploy`;
+});
+
+const getEvents = baseApi<
+  {
+    limit: number;
+    skip: number;
+    type: "pod" | "node" | "hpa";
+    owner_type: string;
+    owner_name: string;
+    sort_by: "timestamp";
+  },
+  {
+    project_id: number;
+    cluster_id: number;
+  }
+>(
+  "GET",
+  (pathParams) =>
+    `api/projects/${pathParams.project_id}/clusters/${pathParams.cluster_id}/events`
+);
+
+const getEventById = baseApi<
+  {},
+  {
+    project_id: number;
+    cluster_id: number;
+    event_id: number;
+  }
+>(
+  "GET",
+  ({ project_id, cluster_id, event_id }) =>
+    `/api/projects/${project_id}/clusters/${cluster_id}/events/${event_id}`
+);
+
 // Bundle export to allow default api import (api.<method> is more readable)
 export default {
   checkAuth,
@@ -1108,4 +1158,8 @@ export default {
   removeCollaborator,
   getPolicyDocument,
   createWebhookToken,
+  getPorterAgentIsInstalled,
+  installPorterAgent,
+  getEvents,
+  getEventById,
 };