Просмотр исходного кода

Status page using new component working without sockets

jnfrati 4 лет назад
Родитель
Сommit
4d32f95b3b

+ 2 - 2
dashboard/src/main/home/onboarding/steps/ProvisionResources/ProvisionResources.tsx

@@ -12,7 +12,7 @@ import ProviderSelector, {
 import FormFlowWrapper from "./forms/FormFlow";
 import FormFlowWrapper from "./forms/FormFlow";
 import ConnectExternalCluster from "./forms/_ConnectExternalCluster";
 import ConnectExternalCluster from "./forms/_ConnectExternalCluster";
 import backArrow from "assets/back_arrow.png";
 import backArrow from "assets/back_arrow.png";
-import { SharedStatus } from "./forms/SharedStatus";
+import { SharedStatus, StatusPage } from "./forms/SharedStatus";
 import { useSnapshot } from "valtio";
 import { useSnapshot } from "valtio";
 import { OFState } from "../../state";
 import { OFState } from "../../state";
 
 
@@ -108,7 +108,7 @@ const ProvisionResources: React.FC<Props> = () => {
       case "status":
       case "status":
         return (
         return (
           <>
           <>
-            <SharedStatus
+            <StatusPage
               project_id={project?.id}
               project_id={project?.id}
               filter={getFilterOpts()}
               filter={getFilterOpts()}
               setInfraStatus={setInfraStatus}
               setInfraStatus={setInfraStatus}

+ 264 - 5
dashboard/src/main/home/onboarding/steps/ProvisionResources/forms/SharedStatus.tsx

@@ -3,7 +3,7 @@ import ProvisionerStatus, {
   TFResource,
   TFResource,
   TFResourceError,
   TFResourceError,
 } from "components/ProvisionerStatus";
 } from "components/ProvisionerStatus";
-import React, { useEffect, useState } from "react";
+import React, { useEffect, useMemo, useRef, useState } from "react";
 import api from "shared/api";
 import api from "shared/api";
 import { useWebsockets } from "shared/hooks/useWebsockets";
 import { useWebsockets } from "shared/hooks/useWebsockets";
 
 
@@ -23,8 +23,8 @@ export const SharedStatus: React.FC<{
   const [isLoadingState, setIsLoadingState] = useState(true);
   const [isLoadingState, setIsLoadingState] = useState(true);
 
 
   const updateTFModules = (
   const updateTFModules = (
-    index: number,
-    addedResources: TFResource[],
+    index: number, // TF module index
+    addedResources: TFResource[], //
     erroredResources: TFResource[],
     erroredResources: TFResource[],
     globalErrors: TFResourceError[],
     globalErrors: TFResourceError[],
     gotDesired?: boolean
     gotDesired?: boolean
@@ -281,11 +281,11 @@ export const SharedStatus: React.FC<{
 
 
             // convert current state to a lookup table
             // convert current state to a lookup table
             var currentMap: Map<string, string> = new Map();
             var currentMap: Map<string, string> = new Map();
-
+            debugger;
             current?.resources?.forEach((val: any) => {
             current?.resources?.forEach((val: any) => {
               currentMap.set(val?.type + "." + val?.name, "");
               currentMap.set(val?.type + "." + val?.name, "");
             });
             });
-
+            console.log(current);
             mergeCurrentAndDesired(index, desired, currentMap);
             mergeCurrentAndDesired(index, desired, currentMap);
           })
           })
           .catch((err) => {
           .catch((err) => {
@@ -363,3 +363,262 @@ export const SharedStatus: React.FC<{
     </>
     </>
   );
   );
 };
 };
+
+type Props = {
+  setInfraStatus: (status: { hasError: boolean; description?: string }) => void;
+  project_id: number;
+  filter: string[];
+};
+
+const infra = [
+  {
+    id: 2758,
+    created_at: "2021-11-09T19:24:00.485269Z",
+    updated_at: "2021-11-09T19:24:17.820528Z",
+    project_id: 2380,
+    kind: "docr",
+    status: "created",
+    do_integration_id: 1717,
+    last_applied: { docr_name: "aide", docr_subscription_tier: "basic" },
+  },
+  {
+    id: 2759,
+    created_at: "2021-11-09T19:24:00.66398Z",
+    updated_at: "2021-11-09T19:33:39.913885Z",
+    project_id: 2380,
+    kind: "doks",
+    status: "created",
+    do_integration_id: 1717,
+    last_applied: { cluster_name: "aide-cluster", do_region: "nyc1" },
+  },
+];
+
+type Infra = {
+  id: number;
+  created_at: string;
+  updated_at: string;
+  project_id: number;
+  kind: string;
+  status: string;
+  last_applied: any;
+};
+
+interface TMPTFModule {
+  id: number;
+  kind: string;
+  status: string;
+  created_at: string;
+  global_errors?: TFResourceError[];
+  got_desired: boolean;
+  // optional resources, if not created
+  resources?: TFResource[];
+  desired?: TFResource[];
+}
+
+const desiredExample = {
+  addr: "google_compute_address.lb",
+  errored: { errored_out: false },
+  implied_provider: "google",
+  resource: "google_compute_address.lb",
+  resource_name: "lb",
+  resource_type: "google_compute_address",
+};
+
+type Desired = {
+  addr: string;
+  errored:
+    | { errored_out: false }
+    | { errored_out: true; error_context: string };
+  implied_provider: string;
+  resource: string;
+  resource_name: string;
+  resource_type: string;
+};
+
+export const StatusPage = ({
+  filter: infraFilters,
+  project_id,
+  setInfraStatus,
+}: Props) => {
+  const {
+    newWebsocket,
+    openWebsocket,
+    closeWebsocket,
+    closeAllWebsockets,
+  } = useWebsockets();
+  const [isLoading, setIsLoading] = useState(true);
+  const [_tfModules, setTFModules] = useState<TFModule[]>([]);
+  const {
+    tfModules,
+    initModule,
+    updateDesired,
+    updateModuleResources,
+  } = useTFModules();
+
+  const infraExistsOnFilter = (currentInfra: Infra) => {
+    if (!Array.isArray(infraFilters) || !infraFilters?.length) {
+      return true;
+    }
+
+    if (infraFilters.includes(currentInfra.kind)) {
+      return true;
+    }
+    return false;
+  };
+
+  const getInfras = async () => {
+    try {
+      const res = await api.getInfra<Infra[]>(
+        "<token>",
+        {},
+        { project_id: project_id }
+      );
+      const matchedInfras = res.data.filter(infraExistsOnFilter);
+
+      // Check if all infras are created then enable continue button
+      if (matchedInfras.every((infra) => infra.status === "created")) {
+        setInfraStatus({
+          hasError: false,
+        });
+      }
+
+      // Init tf modules based on matched infras
+      matchedInfras.forEach((infra) => {
+        initModule(infra);
+        getDesiredState(infra.id);
+      });
+    } catch (error) {}
+  };
+
+  const getDesiredState = async (infra_id: number) => {
+    try {
+      const desired = await api
+        .getInfraDesired("<token>", {}, { project_id, infra_id })
+        .then((res) => res?.data);
+
+      updateDesired(infra_id, desired);
+      getProvisionedModules(infra_id);
+    } catch (error) {
+      console.error(error);
+      setTimeout(() => {
+        getDesiredState(infra_id);
+      }, 500);
+    }
+  };
+
+  const getProvisionedModules = async (infra_id: number) => {
+    try {
+      const current = await api
+        .getInfraCurrent("<token>", {}, { project_id, infra_id })
+        .then((res) => res?.data);
+      console.log(current);
+      const provisionedResources = current?.resources?.map((resource: any) => {
+        return {
+          addr: `${resource?.type}.${resource?.name}`,
+        };
+      });
+      console.log(provisionedResources);
+      updateModuleResources(infra_id, provisionedResources);
+    } catch (error) {}
+  };
+
+  useEffect(() => {
+    getInfras();
+  }, []);
+
+  const sortedModules = tfModules.sort((a, b) =>
+    b.id < a.id ? -1 : b.id > a.id ? 1 : 0
+  );
+
+  return <ProvisionerStatus modules={sortedModules} />;
+};
+
+type TFModulesState = {
+  [key: number]: TFModule;
+};
+
+const useTFModules = () => {
+  const modules = useRef<TFModulesState>({});
+  const [tfModules, setTfModules] = useState<TFModule[]>([]);
+
+  const updateTFModules = (): void => {
+    if (typeof modules.current !== "object") {
+      setTfModules([]);
+    }
+
+    const sortedModules = Object.values(modules.current).sort((a, b) =>
+      b.id < a.id ? -1 : b.id > a.id ? 1 : 0
+    );
+    setTfModules(sortedModules);
+  };
+
+  const initModule = (infra: Infra) => {
+    const module: TFModule = {
+      id: infra.id,
+      kind: infra.kind,
+      status: infra.status,
+      got_desired: false,
+      created_at: infra.created_at,
+    };
+    modules.current[infra.id] = module;
+  };
+
+  const setModule = (infraId: number, module: TFModule) => {
+    modules.current = {
+      ...modules.current,
+      [infraId]: module,
+    };
+    updateTFModules();
+  };
+
+  const getModule = (infraId: number) => {
+    return { ...modules.current[infraId] };
+  };
+
+  const updateDesired = (infraId: number, desired: Desired[]) => {
+    const selectedModule = getModule(infraId);
+
+    if (!Array.isArray(selectedModule?.resources)) {
+      selectedModule.resources = [];
+    }
+
+    selectedModule.resources = desired.map((d) => {
+      return {
+        addr: d.addr,
+        errored: d.errored,
+        provisioned: false,
+      };
+    });
+
+    setModule(infraId, selectedModule);
+  };
+
+  const updateModuleResources = (
+    infraId: number,
+    provisionedResources: { addr: string }[]
+  ) => {
+    const selectedModule = getModule(infraId);
+    debugger;
+    const updatedResources = selectedModule.resources.map((resource) => {
+      const resourceWasProvisioned = !!provisionedResources.find(
+        (pr) => pr.addr === resource.addr
+      );
+
+      return {
+        ...resource,
+        provisioned: resourceWasProvisioned,
+      };
+    });
+
+    selectedModule.resources = updatedResources;
+
+    setModule(infraId, selectedModule);
+  };
+
+  return {
+    tfModules,
+    initModule,
+    updateDesired,
+    updateModuleResources,
+  };
+};