sunguroku 5 лет назад
Родитель
Сommit
2b22ba0f4b

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

@@ -19,7 +19,7 @@ type StateType = {
 
 export default class Chart extends Component<PropsType, StateType> {
   getAvailability = (kind: string, c: any) => {
-    switch (kind.toLowerCase()) {
+    switch (kind?.toLowerCase()) {
       case "deployment":
       case "replicaset":
         return (c.status.availableReplicas == c.status.replicas)
@@ -67,7 +67,7 @@ export default class Chart extends Component<PropsType, StateType> {
     if (chartStatus === 'deployed') {
       for (var uid in this.state.controllers) {
         if (!this.state.controllers[uid]) {
-          return 'updating'
+          return 'not ready'
         }
       }
       return 'deployed'

Разница между файлами не показана из-за своего большого размера
+ 1 - 3
dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedChart.tsx


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

@@ -1,9 +1,8 @@
 import React, { Component } from 'react';
 import styled from 'styled-components';
-import api from '../../../../shared/api';
 
 import { Context } from '../../../../shared/Context';
-import { ResourceType, StorageType, ChartType } from '../../../../shared/types';
+import { ResourceType, ChartType } from '../../../../shared/types';
 
 import ResourceItem from './ResourceItem';
 import Loading from '../../../../components/Loading';

+ 0 - 152
dashboard/src/main/home/cluster-dashboard/expanded-chart/log/LogSection.tsx

@@ -1,152 +0,0 @@
-import React, { Component } from 'react';
-import styled from 'styled-components';
-import api from '../../../../../shared/api';
-import Logs from './Logs';
-import { Context } from '../../../../../shared/Context';
-
-interface Pod {
-  namespace?: string;
-  name?: string;
-}
-
-type PropsType = {
-  selectors: string[],
-};
-
-type StateType = {
-  logs: string[]
-  pods: Pod[],
-  selectedPod: Pod,
-};
-
-export default class LogSection extends Component<PropsType, StateType> {
-  state = {
-    logs: [] as string[],
-    pods: [] as Pod[],
-    selectedPod: {} as Pod,
-  }
-
-  renderLogs = () => {
-    return <Logs key={this.state.selectedPod?.name} selectedPod={this.state.selectedPod} />
-  }
-
-  renderPodTabs = () => {
-    return this.state.pods.map((pod, i) => {
-      return (
-        <Tab
-          key={i}
-          selected={(this.state.selectedPod == pod)}
-          onClick={() => {
-          this.setState({selectedPod: pod})
-          }
-        }>
-          {pod.name}
-        </Tab>
-      );
-    });
-  }
-
-  renderLogSection = () => {
-    if (this.state.pods.length > 0) {
-      return (
-        <div>
-          <TabWrapper>
-            {this.renderPodTabs()}
-          </TabWrapper>
-          {this.renderLogs()}
-        </div>
-      )
-    } else {
-      return (
-        <NoPods> <i className="material-icons">category</i> No pods to display. </NoPods>
-      )
-    }
-  }
-
-  componentDidMount() {
-    const { selectors } = this.props;
-    let { currentCluster, currentProject, setCurrenterror } = this.context;
-
-    api.getMatchingPods('<token>', { 
-      cluster_id: currentCluster.id,
-      service_account_id: currentCluster.service_account_id,
-      selectors,
-    }, {
-      id: currentProject.id
-    }, (err: any, res: any) => {
-      if (err) {
-        console.log(err)
-        setCurrenterror(JSON.stringify(err))
-        return
-      }
-      let pods = res?.data?.map((pod: any) => {
-        return {
-          namespace: pod?.metadata?.namespace, 
-          name: pod?.metadata?.name
-        }
-      })
-      this.setState({ pods , selectedPod: pods[0]})
-    })
-  }
-
-  render() {
-    return (
-      <StyledLogSection>
-        {this.renderLogSection()}
-      </StyledLogSection>
-    );
-  }
-}
-
-LogSection.contextType = Context;
-
-const TabWrapper = styled.div`
-  display: flex;
-  flex-direction: column;
-  overflow: hidden;
-  width: 30%;
-  float: left;
-`
-
-const Tab = styled.div`
-  align-items: center;
-  color: ${(props: {selected: boolean}) => props.selected ? 'white' : '#ffffff66'};
-  background: ${(props: {selected: boolean}) => props.selected ? '#ffffff18' : '##ffffff11'};
-  height: 100%;
-  justify-content: center;
-  font-size: 13px;
-  padding: 15px 13px;
-  margin-right: 10px;
-  border-radius: 5px;
-  text-shadow: 0px 0px 8px none;
-  cursor: pointer;
-  :hover {
-    color: white;
-    background: #ffffff18;
-  }
-`;
-
-const StyledLogSection = styled.span`
-  width: 100%;
-  height: 100%;
-  position: relative;
-  font-size: 13px;
-  padding: 0px;
-  user-select: text;
-`;
-
-const NoPods = styled.div`
-  padding-top: 20%;
-  position: relative;
-  width: 100%;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  color: #ffffff44;
-  font-size: 14px;
-
-  > i {
-    font-size: 18px;
-    margin-right: 12px;
-  }
-`;

+ 277 - 0
dashboard/src/main/home/cluster-dashboard/expanded-chart/status/ControllerTab.tsx

@@ -0,0 +1,277 @@
+import React, { Component } from 'react';
+import styled from 'styled-components';
+import { kindToIcon } from '../../../../../shared/rosettaStone';
+import api from '../../../../../shared/api';
+import { Context } from '../../../../../shared/Context';
+
+interface Pod {
+  namespace?: string;
+  name?: string;
+
+}
+
+type PropsType = {
+  controller: any,
+  selectedPod: Pod,
+  selectPod: Function,
+};
+
+type StateType = {
+  expanded: boolean,
+  pods: Pod[],
+};
+
+// Controller tab in log section that displays list of pods on click.
+export default class ControllerTab extends Component<PropsType, StateType> {
+  state = {
+    expanded: false,
+    pods: [] as Pod[],
+  }
+
+  // Handle previewing old revisions
+//   componentDidUpdate(prevProps: PropsType) {
+//     if (prevProps.resource.RawYAML !== this.props.resource.RawYAML) {
+    //   this.setState({ RawYAML: yaml.dump(this.props.resource.RawYAML) });
+//     }
+//   }
+
+  renderIcon = (kind: string) => {
+
+    let icon = 'tonality';
+    if (Object.keys(kindToIcon).includes(kind)) {
+      icon = kindToIcon[kind]; 
+    }
+    
+    return (
+      <IconWrapper>
+        <i className="material-icons">{icon}</i>
+      </IconWrapper>
+    );
+  }
+
+  renderExpanded = () => {
+    if (this.state.expanded) {
+      return (
+        <ExpandWrapper>
+            {
+              this.state.pods.map((pod) => {
+                return (
+                  <Tab 
+                    key={pod.name}
+                    selected={(this.props.selectedPod.name === pod.name)}
+                    onClick={() => {this.props.selectPod(pod)}}
+                  > 
+                    {pod.name} 
+                  </Tab>)
+              })
+            }
+        </ExpandWrapper>
+      );
+    }
+  }
+
+  componentDidMount() {
+    let { currentCluster, currentProject, setCurrentError } = this.context;
+    let { controller } = this.props;
+
+    let selectors = [] as string[]
+    let ml = controller?.spec?.selector?.matchLabels || controller?.spec?.selector
+    let i = 1;
+    let selector = ''
+    for (var key in ml) {
+      selector += key + '=' + ml[key]
+      if (i != Object.keys(ml).length) {
+        selector += ','
+      }
+      i += 1;
+    }
+    selectors.push(selector)
+    
+    api.getMatchingPods('<token>', { 
+      cluster_id: currentCluster.id,
+      service_account_id: currentCluster.service_account_id,
+      selectors,
+    }, {
+      id: currentProject.id
+    }, (err: any, res: any) => {
+      if (err) {
+        console.log(err)
+        setCurrentError(JSON.stringify(err))
+        return
+      }
+      let pods = res?.data?.map((pod: any) => {
+        return {
+          namespace: pod?.metadata?.namespace, 
+          name: pod?.metadata?.name
+        }
+      })
+      console.log(res.data)
+      this.setState({ pods })
+    })
+  }
+
+  render() {
+    let { controller } = this.props;
+    console.log(controller)
+    let status = 'running'
+    return (
+      <StyledResourceItem>
+        <ResourceHeader
+          expanded={this.state.expanded}
+          onClick={() => this.setState({ expanded: !this.state.expanded })}
+        >
+          <DropdownIcon expanded={this.state.expanded}>
+            <i className="material-icons">arrow_right</i>
+          </DropdownIcon>
+          <Info>
+          <Metadata>
+            {this.renderIcon(controller.kind)}
+            {`${controller.kind}`}
+            <ResourceName
+              showKindLabels={true}
+            >
+              {controller.metadata.name}
+            </ResourceName>
+          </Metadata>
+          <Status>
+            <StatusColor status={status} />
+            3/{controller.status.replicas}
+          </Status>
+          </Info>
+        </ResourceHeader>
+        {this.renderExpanded()}
+      </StyledResourceItem>
+    );
+  }
+}
+
+ControllerTab.contextType = Context;
+
+const StyledResourceItem = styled.div`
+  width: 100%;
+`;
+
+const ExpandWrapper = styled.div`
+  overflow: hidden;
+`;
+
+const ResourceHeader = styled.div`
+  width: 100%;
+  height: 60px;
+  display: flex;
+  align-items: center;
+  color: #ffffff66;
+  padding: 8px 13px;
+  text-transform: capitalize;
+  cursor: pointer;
+  background: ${(props: { expanded: boolean }) => props.expanded ? '#ffffff11' : ''};
+  :hover {
+    background: #ffffff18;
+
+    > i {
+      background: #ffffff22;
+    }
+  }
+`;
+
+const Info = styled.div`
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+  align-items: center;
+  width: 100%;
+  height: 100%;
+`;
+
+const Metadata = styled.div`
+  display: flex;
+  align-items: center;
+  width: 85%;
+`;
+
+const Status = styled.div`
+  display: flex;
+  font-size: 13px;
+  flex-direction: row;
+  text-transform: capitalize;
+  align-items: center;
+  font-family: 'Hind Siliguri', sans-serif;
+  color: #aaaabb;
+  animation: fadeIn 0.5s;
+  @keyframes fadeIn {
+    from { opacity: 0 }
+    to { opacity: 1 }
+  }
+`;
+
+const StatusColor = styled.div`
+  margin-bottom: 1px;
+  margin-right: 5px;
+  width: 8px;
+  height: 8px;
+  background: ${(props: { status: string }) => (props.status === 'running' ? '#4797ff' : props.status === 'failed' ? "#ed5f85" : "#f5cb42")};
+  border-radius: 20px;
+`;
+
+const Tab = styled.div`
+  align-items: center;
+  color: ${(props: {selected: boolean}) => props.selected ? 'white' : '#ffffff66'};
+  background: ${(props: {selected: boolean}) => props.selected ? '#ffffff18' : '##ffffff11'};
+  height: 100%;
+  justify-content: center;
+  font-size: 13px;
+  padding: 25px 13px;
+  text-shadow: 0px 0px 8px none;
+  cursor: pointer;
+  :hover {
+    color: white;
+    background: #ffffff18;
+  }
+`;
+
+const ResourceName = styled.div`
+  color: #ffffff;
+  margin-left: ${(props: { showKindLabels: boolean }) => props.showKindLabels ? '10px' : ''};
+  text-transform: none;
+  max-width: 60%;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+
+  :hover {
+    overflow: visible;
+  }
+`;
+
+const IconWrapper = styled.div`
+  width: 25px;
+  height: 25px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+
+  > i {
+    font-size: 16px;
+    color: #ffffff;
+    margin-right: 14px;
+  }
+`;
+
+const DropdownIcon = styled.div`
+  > i {
+    margin-right: 13px;
+    font-size: 20px;
+    color: #ffffff66;
+    cursor: pointer;
+    border-radius: 20px;
+    background: ${(props: { expanded: boolean }) => props.expanded ? '#ffffff18' : ''};
+    transform: ${(props: { expanded: boolean }) => props.expanded ? 'rotate(180deg)' : ''};
+    animation: ${(props: { expanded: boolean }) => props.expanded ? 'quarterTurn 0.3s' : ''};
+    animation-fill-mode: forwards;
+
+    @keyframes quarterTurn {
+      from { transform: rotate(0deg) }
+      to { transform: rotate(90deg) }
+    }
+  }
+`;

+ 8 - 3
dashboard/src/main/home/cluster-dashboard/expanded-chart/log/Logs.tsx → dashboard/src/main/home/cluster-dashboard/expanded-chart/status/Logs.tsx

@@ -30,6 +30,10 @@ export default class Logs extends Component<PropsType, StateType> {
   }
 
   renderLogs = () => {
+    let { selectedPod } = this.props;
+    if (!selectedPod.name) {
+      return <div>no bueno, select pod pl0x</div>
+    }
     return this.state.logs.map((log, i) => {
         return <div key={i}>{log}</div>
     })
@@ -38,7 +42,7 @@ export default class Logs extends Component<PropsType, StateType> {
   componentDidMount() {
     let { currentCluster, currentProject } = this.context;
     let { selectedPod } = this.props;
-    if (!this.props.selectedPod) return
+    if (!selectedPod.name) return
 
     let ws = new WebSocket(`ws://localhost:8080/api/projects/${currentProject.id}/k8s/${selectedPod.namespace}/pod/${selectedPod.name}/logs?cluster_id=${currentCluster.id}&service_account_id=${currentCluster.service_account_id}`)
 
@@ -79,10 +83,11 @@ export default class Logs extends Component<PropsType, StateType> {
 Logs.contextType = Context;
 
 const LogStream = styled.div`
-  width: 70%;
+  overflow: auto;
+  width: 65%;
+  float: right;
   height: 100%;
   background: #202227;
-  position: relative;
   padding: 25px;
   user-select: text;
   overflow: auto;

+ 151 - 0
dashboard/src/main/home/cluster-dashboard/expanded-chart/status/StatusSection.tsx

@@ -0,0 +1,151 @@
+import React, { Component } from 'react';
+import styled from 'styled-components';
+import api from '../../../../../shared/api';
+import Logs from './Logs';
+import ControllerTab from './ControllerTab';
+import { Context } from '../../../../../shared/Context';
+import { ChartType, StorageType } from '../../../../../shared/types';
+
+interface Pod {
+  namespace?: string;
+  name?: string;
+}
+
+type PropsType = {
+  selectors: string[],
+  currentChart: ChartType,
+};
+
+type StateType = {
+  logs: string[]
+  pods: Pod[],
+  selectedPod: Pod,
+  controllers: any[],
+};
+
+export default class StatusSection extends Component<PropsType, StateType> {
+  state = {
+    logs: [] as string[],
+    pods: [] as Pod[],
+    selectedPod: {} as Pod,
+    controllers: [] as any[],
+  }
+
+  renderLogs = () => {
+    return <Logs key={this.state.selectedPod?.name} selectedPod={this.state.selectedPod} />
+  }
+
+  selectPod = (pod: Pod) => {
+    let select = { 
+      namespace: pod.namespace, 
+      name: pod.name,
+    }
+    this.setState({
+      selectedPod: select
+    })
+  }
+
+  renderTabs = () => {
+    return this.state.controllers.map((c) => {
+      return (
+        <ControllerTab 
+          key={c.metadata.uid} 
+          selectedPod={this.state.selectedPod} 
+          selectPod={this.selectPod.bind(this)}
+          controller={c}
+        />
+      )
+    })
+  }
+
+  renderStatusSection = () => {
+    if (this.state.controllers.length > 0) {
+      return (
+        <Wrapper>
+          <TabWrapper>
+            {this.renderTabs()}
+          </TabWrapper>
+          {this.renderLogs()}
+        </Wrapper>
+      )
+    } else {
+      return (
+        <NoControllers> 
+          <i className="material-icons">category</i> 
+          No objects to display. This might happen while your app is still deploying.
+        </NoControllers>
+      )
+    }
+  }
+
+  componentDidMount() {
+    const { selectors, currentChart } = this.props;
+    let { currentCluster, currentProject, setCurrentError } = this.context;
+    api.getChartControllers('<token>', {
+      namespace: currentChart.namespace,
+      cluster_id: currentCluster.id,
+      service_account_id: currentCluster.service_account_id,
+      storage: StorageType.Secret
+    }, {
+      id: currentProject.id,
+      name: currentChart.name,
+      revision: currentChart.version
+    }, (err: any, res: any) => {
+      if (err) {
+        setCurrentError(JSON.stringify(err));
+        return
+      }
+      this.setState({ controllers: res.data })
+    });
+  }
+
+  render() {
+    return (
+      <StyledStatusSection>
+        {this.renderStatusSection()}
+      </StyledStatusSection>
+    );
+  }
+}
+
+StatusSection.contextType = Context;
+
+const TabWrapper = styled.div`
+  display: flex;
+  flex-direction: column;
+  overflow: auto;
+  width: 35%;
+  float: left;
+  max-height: 100%;
+  background: #ffffff11;
+`
+
+const StyledStatusSection = styled.div`
+  width: 100%;
+  height: 100%;
+  position: relative;
+  font-size: 13px;
+  padding: 0px;
+  user-select: text;
+`;
+
+const Wrapper = styled.div`
+  width: 100%;
+  height: 100%;
+`;
+
+const NoControllers = styled.div`
+  padding-top: 20%;
+  position: relative;
+  width: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  color: #ffffff44;
+  font-size: 14px;
+
+  > i {
+    font-size: 18px;
+    margin-right: 12px;
+  }
+`;

Некоторые файлы не были показаны из-за большого количества измененных файлов