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

job and chart resource-level routing

jusrhee 5 лет назад
Родитель
Сommit
29704395ee

+ 57 - 0
dashboard/src/main/home/PageNotFound.tsx → dashboard/src/components/PageNotFound.tsx

@@ -13,6 +13,61 @@ class PageNotFound extends Component<PropsType, StateType> {
 
   render() {
     let { pathname } = this.props.location;
+    let params = this.props.match.params as any;
+    let { baseRoute } = params;
+    if (baseRoute === "applications") {
+      return (
+        <StyledPageNotFound>
+          <Mega>
+            404
+            <Inside>Application Not Found</Inside>
+          </Mega>
+          <Flex>
+            <BackButton
+              width="140px"
+              onClick={() =>
+                pushFiltered(this.props, "/applications", ["project_id"])
+              }
+            >
+              <i className="material-icons">arrow_back</i>
+              Applications
+            </BackButton>
+            {pathname && (
+              <>
+                <Splitter>|</Splitter>
+                <Helper>Could not find "{pathname}"</Helper>
+              </>
+            )}
+          </Flex>
+        </StyledPageNotFound>
+      );
+    } else if (baseRoute === "jobs") {
+      return (
+        <StyledPageNotFound>
+          <Mega>
+            404
+            <Inside>Job Not Found</Inside>
+          </Mega>
+          <Flex>
+            <BackButton
+              width="90px"
+              onClick={() =>
+                pushFiltered(this.props, "/jobs", ["project_id"])
+              }
+            >
+              <i className="material-icons">arrow_back</i>
+              Jobs
+            </BackButton>
+            {pathname && (
+              <>
+                <Splitter>|</Splitter>
+                <Helper>Could not find "{pathname}"</Helper>
+              </>
+            )}
+          </Flex>
+        </StyledPageNotFound>
+      );
+    }
     return (
       <StyledPageNotFound>
         <Mega>
@@ -58,6 +113,8 @@ const Flex = styled.div`
 
 const Helper = styled.div`
   font-size: 15px;
+  max-width: 550px;
+  margin-right: -50px;
 `;
 
 const BackButton = styled.div`

+ 0 - 1
dashboard/src/components/values-form/UploadArea.tsx

@@ -35,7 +35,6 @@ export default class UploadArea extends Component<PropsType, StateType> {
 
   render() {
     let { label, placeholder } = this.props;
-    console.log(this.state.fileName);
     if (this.state.fileName) {
       placeholder = `Uploaded ${this.state.fileName}`;
     }

+ 1 - 1
dashboard/src/main/Main.tsx

@@ -180,7 +180,7 @@ export default class Main extends Component<PropsType, StateType> {
           }}
         />
         <Route
-          path={`/:baseRoute`}
+          path={`/:baseRoute/:cluster?/:namespace?`}
           render={(routeProps) => {
             const baseRoute = routeProps.match.params.baseRoute;
             if (

+ 6 - 1
dashboard/src/main/home/Home.tsx

@@ -23,7 +23,7 @@ import Navbar from "./navbar/Navbar";
 import NewProject from "./new-project/NewProject";
 import ProjectSettings from "./project-settings/ProjectSettings";
 import Sidebar from "./sidebar/Sidebar";
-import PageNotFound from "./PageNotFound";
+import PageNotFound from "components/PageNotFound";
 
 type PropsType = RouteComponentProps & {
   logOut: () => void;
@@ -231,6 +231,11 @@ class Home extends Component<PropsType, StateType> {
   };
 
   componentDidMount() {
+    let { match } = this.props;
+    let params = match.params as any;
+    let { cluster } = params;
+    console.log("cluster is", cluster);
+
     let { user } = this.context;
 
     // Initialize Highlight

+ 17 - 22
dashboard/src/main/home/cluster-dashboard/ClusterDashboard.tsx

@@ -13,7 +13,7 @@ import EnvGroupDashboard from "./env-groups/EnvGroupDashboard";
 import NamespaceSelector from "./NamespaceSelector";
 import SortSelector from "./SortSelector";
 import ExpandedChart from "./expanded-chart/ExpandedChart";
-import ExpandedJobChartWrapper from "./expanded-chart/ExpandedJobChartWrapper";
+import ExpandedChartWrapper from "./expanded-chart/ExpandedChartWrapper";
 import { RouteComponentProps, withRouter } from "react-router";
 
 import api from "shared/api";
@@ -44,7 +44,12 @@ class ClusterDashboard extends Component<PropsType, StateType> {
 
   componentDidMount() {
     let { currentCluster, currentProject } = this.context;
-    pushQueryParams(this.props, { cluster: currentCluster.name });
+    let params = this.props.match.params as any;
+    let pathClusterName = params.cluster;
+    // Don't add cluster as query param if present in path
+    if (!pathClusterName) {
+      pushQueryParams(this.props, { cluster: currentCluster.name });
+    }
     api
       .getPrometheusIsInstalled(
         "<token>",
@@ -84,9 +89,9 @@ class ClusterDashboard extends Component<PropsType, StateType> {
           sortType: "Newest",
           currentChart: null,
         },
-        () =>
+        () => 
           pushQueryParams(this.props, {
-            namespace: this.state.namespace || "ALL",
+            namespace: this.state.namespace === null ? "default" : this.state.namespace,
           })
       );
     }
@@ -125,10 +130,11 @@ class ClusterDashboard extends Component<PropsType, StateType> {
             />
             <NamespaceSelector
               setNamespace={(namespace) =>
-                this.setState({ namespace }, () =>
+                this.setState({ namespace }, () => {
                   pushQueryParams(this.props, {
                     namespace: this.state.namespace || "ALL",
                   })
+                }
                 )
               }
               namespace={this.state.namespace}
@@ -148,18 +154,7 @@ class ClusterDashboard extends Component<PropsType, StateType> {
 
   renderContents = () => {
     let { currentCluster, setSidebar, currentView } = this.props;
-    if (this.state.currentChart) {
-      return (
-        <ExpandedChart
-          namespace={this.state.namespace}
-          currentCluster={this.props.currentCluster}
-          currentChart={this.state.currentChart}
-          closeChart={() => this.setState({ currentChart: null })}
-          isMetricsInstalled={this.state.isMetricsInstalled}
-          setSidebar={setSidebar}
-        />
-      );
-    } else if (currentView === "env-groups") {
+    if (currentView === "env-groups") {
       return <EnvGroupDashboard currentCluster={this.props.currentCluster} />;
     }
 
@@ -190,11 +185,11 @@ class ClusterDashboard extends Component<PropsType, StateType> {
     let { setSidebar } = this.props;
     return (
       <Switch>
-        <Route path="/jobs/:clusterName/:namespace/:chartName">
-          <ExpandedJobChartWrapper setSidebar={setSidebar} />
-        </Route>
-        <Route path="/applications/:application">
-          <h1>Application!</h1>
+        <Route path="/:baseRoute/:clusterName+/:namespace/:chartName">
+          <ExpandedChartWrapper 
+            setSidebar={setSidebar}
+            isMetricsInstalled={this.state.isMetricsInstalled}
+          />
         </Route>
         <Route path={["/jobs", "/applications", "/env-groups"]}>
           {this.renderContents()}

+ 1 - 1
dashboard/src/main/home/cluster-dashboard/NamespaceSelector.tsx

@@ -61,7 +61,7 @@ export default class NamespaceSelector extends Component<PropsType, StateType> {
             }
           );
           this.setState({ namespaceOptions }, () => {
-            if (urlNamespace === "") {
+            if (urlNamespace === "" || defaultNamespace === "") {
               this.props.setNamespace("");
             } else if (this.props.namespace !== defaultNamespace) {
               this.props.setNamespace(defaultNamespace);

+ 1 - 2
dashboard/src/main/home/cluster-dashboard/chart/Chart.tsx

@@ -55,8 +55,7 @@ class Chart extends Component<PropsType, StateType> {
           let { location, match } = this.props;
           let urlParams = new URLSearchParams(location.search);
           let cluster = urlParams.get("cluster");
-          let namespace = urlParams.get("namespace");
-          let route = `${match.url}/${cluster}/${namespace}/${chart.name}`;
+          let route = `${match.url}/${cluster}/${chart.namespace}/${chart.name}`;
           pushFiltered(this.props, route, ["project_id"]);
         }}
       >

+ 1 - 1
dashboard/src/main/home/cluster-dashboard/env-groups/EnvGroupDashboard.tsx

@@ -78,7 +78,7 @@ class EnvGroupDashboard extends Component<PropsType, StateType> {
                 setNamespace={(namespace) =>
                   this.setState({ namespace }, () =>
                     pushQueryParams(this.props, {
-                      namespace: this.state.namespace || "ALL",
+                      namespace: this.state.namespace === null ? "default" : this.state.namespace,
                     })
                   )
                 }

+ 145 - 0
dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedChartWrapper.tsx

@@ -0,0 +1,145 @@
+import React, { Component } from "react";
+import styled from "styled-components";
+import { Context } from "shared/Context";
+import { RouteComponentProps, withRouter } from "react-router";
+
+import {
+  ResourceType,
+  ChartType,
+  StorageType,
+  ClusterType,
+} from "shared/types";
+import api from "shared/api";
+import { PorterUrl, pushQueryParams, pushFiltered } from "shared/routing";
+import ExpandedJobChart from "./ExpandedJobChart";
+import ExpandedChart from "./ExpandedChart";
+import Loading from "components/Loading";
+import PageNotFound from "components/PageNotFound";
+
+type PropsType = RouteComponentProps & {
+  setSidebar: (x: boolean) => void;
+  isMetricsInstalled: boolean;
+};
+
+type StateType = {
+  loading: boolean;
+  currentChart: ChartType;
+};
+
+class ExpandedChartWrapper extends Component<PropsType, StateType> {
+  state = {
+    loading: true,
+    currentChart: null as ChartType,
+  };
+
+  // Retrieve full chart data (includes form and values)
+  getChartData = () => {
+    let { match } = this.props;
+    let { namespace, chartName } = match.params as any;
+    let { currentProject, currentCluster } = this.context;
+    if (currentProject && currentCluster) {
+      // TODO: add query for retrieving max revision #
+      api
+        .getRevisions(
+          "<token>",
+          {
+            namespace: namespace,
+            cluster_id: currentCluster.id,
+            storage: StorageType.Secret,
+          },
+          { id: currentProject.id, name: chartName }
+        )
+        .then((res) => {
+          res.data.sort((a: ChartType, b: ChartType) => {
+            return -(a.version - b.version);
+          });
+          let maxVersion = res.data[0].version;
+          api
+            .getChart(
+              "<token>",
+              {
+                namespace: namespace,
+                cluster_id: currentCluster.id,
+                storage: StorageType.Secret,
+              },
+              {
+                name: chartName,
+                revision: maxVersion,
+                id: currentProject.id,
+              }
+            )
+            .then((res) => {
+              this.setState({ currentChart: res.data, loading: false });
+            })
+            .catch((err) => {
+              console.log("err", err.response.data);
+              this.setState({ loading: false });
+            });
+        })
+      .catch((err) => {
+        console.log("err", err.response.data);
+        this.setState({ loading: false });
+      });
+    }
+  };
+
+  componentDidMount() {
+    this.setState({ loading: true });
+    this.getChartData();
+  }
+
+  render() {
+    let { setSidebar, location, match } = this.props;
+    let { baseRoute, namespace } = match.params as any;
+    let { loading, currentChart } = this.state;
+    if (loading) {
+      return <Loading />
+    } else if (currentChart && baseRoute === "jobs") {
+      return (
+        <ExpandedJobChart
+          namespace={namespace}
+          currentChart={currentChart}
+          currentCluster={this.context.currentCluster}
+          closeChart={() => 
+            pushFiltered(this.props, "/jobs", ["project_id"], {
+              cluster: this.context.currentCluster.name,
+              namespace: namespace,
+            })
+          }
+          setSidebar={setSidebar}
+        />
+      );
+    } else if (currentChart && baseRoute === "applications") {
+      return (
+        <ExpandedChart
+          namespace={namespace}
+          isMetricsInstalled={this.props.isMetricsInstalled}
+          currentChart={currentChart}
+          currentCluster={this.context.currentCluster}
+          closeChart={() => 
+            pushFiltered(this.props, "/jobs", ["project_id"], {
+              cluster: this.context.currentCluster.name,
+              namespace: namespace,
+            })
+          }
+          setSidebar={setSidebar}
+        />
+      )
+    }
+    return (
+      <PageNotFound />
+    )
+  }
+}
+
+ExpandedChartWrapper.contextType = Context;
+
+export default withRouter(ExpandedChartWrapper);
+
+const NotFoundPlaceholder = styled.div`
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 100%;
+  height: 100%;
+`;

+ 0 - 59
dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedJobChartWrapper.tsx

@@ -1,59 +0,0 @@
-import React, { Component } from "react";
-import styled from "styled-components";
-import { RouteComponentProps, withRouter } from "react-router";
-
-import api from "shared/api";
-import { PorterUrl, pushQueryParams, pushFiltered } from "shared/routing";
-import ExpandedJobChart from "./ExpandedJobChart";
-import Loading from "components/Loading";
-
-type PropsType = RouteComponentProps & {
-  setSidebar: (x: boolean) => void;
-};
-
-type StateType = {
-  loading: boolean;
-};
-
-class ExpandedJobChartWrapper extends Component<PropsType, StateType> {
-  state = {
-    loading: true,
-  };
-
-  getClusterFromName = (clusterName: string) => {
-    api
-      .getClusters("<token>", {}, { id: currentProject.id })
-      .then((res) => {
-        window.analytics.identify(user.userId, {
-          currentProject,
-          clusters: res.data,
-        });
-
-        this.props.setWelcome(false);
-        // TODO: handle uninitialized kubeconfig
-        if (res.data) {
-          let clusters = res.data;
-  }
-
-  getChartFromName = (chartName: string) => {
-
-  }
-
-  componentDidMount() {
-    let { setSidebar, location, match } = this.props;
-    let { clusterName, namespace, chartName } = match.params as any;
-    console.log(clusterName, namespace, chartName);
-  } 
-
-  render() {
-    let { loading } = this.state;
-    if (loading) {
-      return <Loading />
-    }
-    return (
-      <h1>lil ol me</h1>
-    );
-  }
-}
-
-export default withRouter(ExpandedJobChartWrapper);

+ 13 - 6
dashboard/src/main/home/sidebar/ClusterSection.tsx

@@ -60,15 +60,22 @@ class ClusterSection extends Component<PropsType, StateType> {
           let clusters = res.data;
           clusters.sort((a: any, b: any) => a.id - b.id);
           if (clusters.length > 0) {
-            // Set cluster from URL if specified
             let queryString = window.location.search;
             let urlParams = new URLSearchParams(queryString);
-            let clusterName = urlParams.get("cluster");
-            let defaultCluster = null;
-            if (clusterName) {
+            let paramClusterName = urlParams.get("cluster");
+            let params = this.props.match.params as any;
+            let pathClusterName = params.cluster;
+
+            // Set cluster from URL if in path or params
+            let defaultCluster = null as ClusterType;
+            if (paramClusterName || pathClusterName) {
               clusters.forEach((cluster: ClusterType) => {
-                if (cluster.name === clusterName) {
-                  defaultCluster = cluster;
+                if (!defaultCluster) {
+                  if (cluster.name === pathClusterName) {
+                    defaultCluster = cluster;
+                  } else if (cluster.name === paramClusterName) {
+                    defaultCluster = cluster;
+                  }
                 }
               });
             }

+ 48 - 15
dashboard/src/main/home/sidebar/Sidebar.tsx

@@ -106,33 +106,66 @@ class Sidebar extends Component<PropsType, StateType> {
         <>
           <NavButton
             selected={currentView === "applications"}
-            onClick={() =>
-              pushFiltered(this.props, "/applications", ["project_id"], {
-                cluster: currentCluster.name,
-              })
-            }
+            onClick={() => {
+              let params = this.props.match.params as any;
+              let pathNamespace = params.namespace;
+
+              // If namespace is currently only in path (ex: ExpandedChart) set to param
+              if (pathNamespace) {
+                pushFiltered(this.props, "/applications", ["project_id", "cluster", "namespace"], {
+                  cluster: currentCluster.name,
+                  namespace: pathNamespace,
+                })
+              } else {
+                pushFiltered(this.props, "/applications", ["project_id", "cluster", "namespace"], {
+                  cluster: currentCluster.name,
+                })
+              }
+            }}
           >
             <Img src={monoweb} />
             Applications
           </NavButton>
           <NavButton
             selected={currentView === "jobs"}
-            onClick={() =>
-              pushFiltered(this.props, "/jobs", ["project_id"], {
-                cluster: currentCluster.name,
-              })
-            }
+            onClick={() => {
+              let params = this.props.match.params as any;
+              let pathNamespace = params.namespace;
+
+              // If namespace is currently only in path (ex: ExpandedChart) set to param
+              if (pathNamespace) {
+                pushFiltered(this.props, "/jobs", ["project_id", "cluster", "namespace"], {
+                  cluster: currentCluster.name,
+                  namespace: pathNamespace,
+                })
+              } else {
+                pushFiltered(this.props, "/jobs", ["project_id", "cluster", "namespace"], {
+                  cluster: currentCluster.name,
+                })
+              }
+            }}
           >
             <Img src={monojob} />
             Jobs
           </NavButton>
           <NavButton
             selected={currentView === "env-groups"}
-            onClick={() =>
-              pushFiltered(this.props, "/env-groups", ["project_id"], {
-                cluster: currentCluster.name,
-              })
-            }
+            onClick={() => {
+              let params = this.props.match.params as any;
+              let pathNamespace = params.namespace;
+
+              // If namespace is currently only in path (ex: ExpandedChart) set to param
+              if (pathNamespace) {
+                pushFiltered(this.props, "/env-groups", ["project_id", "cluster", "namespace"], {
+                  cluster: currentCluster.name,
+                  namespace: pathNamespace,
+                })
+              } else {
+                pushFiltered(this.props, "/env-groups", ["project_id", "cluster", "namespace"], {
+                  cluster: currentCluster.name,
+                })
+              }
+            }}
           >
             <Img src={sliders} />
             Env Groups