Sfoglia il codice sorgente

Merge pull request #181 from porter-dev/master

Merge master into staging
abelanger5 5 anni fa
parent
commit
fd1bf7ac70

BIN
dashboard/src/assets/warning.png


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

@@ -30,6 +30,7 @@ type StateType = {
   showWelcome: boolean,
   showWelcome: boolean,
   currentView: string,
   currentView: string,
   viewData: any[],
   viewData: any[],
+  forceRefreshClusters: boolean, // For updating ClusterSection from modal on deletion
 
 
   // Track last project id for refreshing clusters on project change
   // Track last project id for refreshing clusters on project change
   prevProjectId: number | null,
   prevProjectId: number | null,
@@ -41,7 +42,8 @@ export default class Home extends Component<PropsType, StateType> {
     showWelcome: false,
     showWelcome: false,
     currentView: 'dashboard',
     currentView: 'dashboard',
     prevProjectId: null as number | null,
     prevProjectId: null as number | null,
-    viewData: null as any
+    viewData: null as any,
+    forceRefreshClusters: false,
   }
   }
 
 
   // Possibly consolidate into context (w/ ProjectSection + NewProject)
   // Possibly consolidate into context (w/ ProjectSection + NewProject)
@@ -189,6 +191,8 @@ export default class Home extends Component<PropsType, StateType> {
           setWelcome={(x: boolean) => this.setState({ showWelcome: x })}
           setWelcome={(x: boolean) => this.setState({ showWelcome: x })}
           setCurrentView={this.setCurrentView}
           setCurrentView={this.setCurrentView}
           currentView={this.state.currentView}
           currentView={this.state.currentView}
+          forceRefreshClusters={this.state.forceRefreshClusters}
+          setRefreshClusters={(x: boolean) => this.setState({ forceRefreshClusters: x })}
         />
         />
       );
       );
     }
     }
@@ -214,6 +218,16 @@ export default class Home extends Component<PropsType, StateType> {
         >
         >
           <UpdateProjectModal />
           <UpdateProjectModal />
         </ReactModal>
         </ReactModal>
+        <ReactModal
+          isOpen={currentModal === 'UpdateClusterModal'}
+          onRequestClose={() => setCurrentModal(null, null)}
+          style={ProjectModalStyles}
+          ariaHideApp={false}
+        >
+          <UpdateClusterModal 
+            setRefreshClusters={(x: boolean) => this.setState({ forceRefreshClusters: x })} 
+          />
+        </ReactModal>
         <ReactModal
         <ReactModal
           isOpen={currentModal === 'IntegrationsModal'}
           isOpen={currentModal === 'IntegrationsModal'}
           onRequestClose={() => setCurrentModal(null, null)}
           onRequestClose={() => setCurrentModal(null, null)}

+ 9 - 2
dashboard/src/main/home/cluster-dashboard/ClusterDashboard.tsx

@@ -74,7 +74,14 @@ export default class ClusterDashboard extends Component<PropsType, StateType> {
         <TitleSection>
         <TitleSection>
           {this.renderDashboardIcon()}
           {this.renderDashboardIcon()}
           <Title>{currentCluster.name}</Title>
           <Title>{currentCluster.name}</Title>
-          <i className="material-icons">more_vert</i>
+          <i 
+            className="material-icons"
+            onClick={() => this.context.setCurrentModal('UpdateClusterModal', { 
+              setCurrentView: this.props.setCurrentView,
+            })}
+          >
+            more_vert
+          </i>
         </TitleSection>
         </TitleSection>
 
 
         <InfoSection>
         <InfoSection>
@@ -280,7 +287,7 @@ const TitleSection = styled.div`
 
 
   > i {
   > i {
     margin-left: 10px;
     margin-left: 10px;
-    cursor: not-allowed;
+    cursor: pointer;
     font-size 18px;
     font-size 18px;
     color: #858FAAaa;
     color: #858FAAaa;
     padding: 5px;
     padding: 5px;

+ 38 - 27
dashboard/src/main/home/modals/UpdateClusterModal.tsx

@@ -11,6 +11,7 @@ import InputRow from '../../../components/values-form/InputRow';
 import ConfirmOverlay from '../../../components/ConfirmOverlay';
 import ConfirmOverlay from '../../../components/ConfirmOverlay';
 
 
 type PropsType = {
 type PropsType = {
+  setRefreshClusters: (x: boolean) => void,
 };
 };
 
 
 type StateType = {
 type StateType = {
@@ -25,20 +26,39 @@ export default class UpdateClusterModal extends Component<PropsType, StateType>
     status: null as string | null,
     status: null as string | null,
     showDeleteOverlay: false,
     showDeleteOverlay: false,
   };
   };
-  
+
   handleDelete = () => {
   handleDelete = () => {
     let { currentProject, currentCluster } = this.context;
     let { currentProject, currentCluster } = this.context;
     this.setState({ status: 'loading' });
     this.setState({ status: 'loading' });
+
     api.deleteCluster('<token>', {}, { 
     api.deleteCluster('<token>', {}, { 
       project_id: currentProject.id,
       project_id: currentProject.id,
       cluster_id: currentCluster.id,
       cluster_id: currentCluster.id,
     }, (err: any, res: any) => {
     }, (err: any, res: any) => {
       if (err) {
       if (err) {
         this.setState({ status: 'error' });
         this.setState({ status: 'error' });
-        // console.log(err)
+        console.log(err)
       } else {
       } else {
-        alert('nice');
+
+        // Handle destroying infra we've provisioned
+        if (currentCluster.infra_id) {
+          console.log('destroying provisioned infra...');
+          api.destroyCluster('<token>', {}, { 
+            project_id: currentProject.id,
+            infra_id: currentCluster.infra_id,
+          }, (err: any, res: any) => {
+            if (err) {
+              this.setState({ status: 'error' });
+              console.log(err)
+            } else {
+              console.log('destroyed provisioned infra.');
+            }
+          });
+        }
+
+        this.props.setRefreshClusters(true);
         this.setState({ status: 'successful', showDeleteOverlay: false });
         this.setState({ status: 'successful', showDeleteOverlay: false });
+        this.context.setCurrentModal(null, null);
       }
       }
     });
     });
   }
   }
@@ -58,10 +78,9 @@ export default class UpdateClusterModal extends Component<PropsType, StateType>
         </Subtitle>
         </Subtitle>
 
 
         <InputWrapper>
         <InputWrapper>
-          <ProjectIcon>
-            <ProjectImage src={gradient} />
-            <Letter>{this.state.clusterName ? this.state.clusterName[0].toUpperCase() : '-'}</Letter>
-          </ProjectIcon>
+          <DashboardIcon>
+            <i className="material-icons">device_hub</i>
+          </DashboardIcon>
           <InputRow
           <InputRow
             disabled={true}
             disabled={true}
             type='string'
             type='string'
@@ -81,7 +100,7 @@ export default class UpdateClusterModal extends Component<PropsType, StateType>
 
 
         <ConfirmOverlay
         <ConfirmOverlay
           show={this.state.showDeleteOverlay}
           show={this.state.showDeleteOverlay}
-          message={`Are you sure you want to delete ${this.state.clusterName}?`}
+          message={`Are you sure you want to delete this cluster?`}
           onYes={this.handleDelete}
           onYes={this.handleDelete}
           onNo={() => this.setState({ showDeleteOverlay: false })}
           onNo={() => this.setState({ showDeleteOverlay: false })}
         />
         />
@@ -92,25 +111,7 @@ export default class UpdateClusterModal extends Component<PropsType, StateType>
 
 
 UpdateClusterModal.contextType = Context;
 UpdateClusterModal.contextType = Context;
 
 
-const Letter = styled.div`
-  height: 100%;
-  width: 100%;
-  position: absolute;
-  background: #00000028;
-  top: 0;
-  left: 0;
-  display: flex;
-  color: white;
-  align-items: center;
-  justify-content: center;
-`;
-
-const ProjectImage = styled.img`
-  width: 100%;
-  height: 100%;
-`;
-
-const ProjectIcon = styled.div`
+const DashboardIcon = styled.div`
   width: 25px;
   width: 25px;
   min-width: 25px;
   min-width: 25px;
   height: 25px;
   height: 25px;
@@ -120,6 +121,16 @@ const ProjectIcon = styled.div`
   margin-right: 10px;
   margin-right: 10px;
   font-weight: 400;
   font-weight: 400;
   margin-top: 14px;
   margin-top: 14px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background: #676C7C;
+  border: 2px solid #8e94aa;
+  color: white;
+
+  > i {
+    font-size: 13px;
+  }
 `;
 `;
 
 
 const InputWrapper = styled.div`
 const InputWrapper = styled.div`

+ 27 - 0
dashboard/src/main/home/modals/UpdateProjectModal.tsx

@@ -5,6 +5,7 @@ import gradient from '../../../assets/gradient.jpg';
 
 
 import api from '../../../shared/api';
 import api from '../../../shared/api';
 import { Context } from '../../../shared/Context';
 import { Context } from '../../../shared/Context';
+import { ClusterType } from '../../../shared/types';
 
 
 import SaveButton from '../../../components/SaveButton';
 import SaveButton from '../../../components/SaveButton';
 import InputRow from '../../../components/values-form/InputRow';
 import InputRow from '../../../components/values-form/InputRow';
@@ -57,6 +58,32 @@ export default class UpdateProjectModal extends Component<PropsType, StateType>
         this.setState({ status: 'successful', showDeleteOverlay: false });
         this.setState({ status: 'successful', showDeleteOverlay: false });
       }
       }
     });
     });
+
+    // Loop through and delete infra of all clusters we've provisioned
+    api.getClusters('<token>', {}, { id: currentProject.id }, (err: any, res: any) => {
+      if (err) {
+        console.log(err);
+      } else {
+        res.data.forEach((cluster: ClusterType) => {
+
+          // Handle destroying infra we've provisioned
+          if (cluster.infra_id) {
+            console.log('destroying provisioned infra...', cluster.infra_id);
+            api.destroyCluster('<token>', {}, { 
+              project_id: currentProject.id,
+              infra_id: cluster.infra_id,
+            }, (err: any, res: any) => {
+              if (err) {
+                this.setState({ status: 'error' });
+                console.log(err)
+              } else {
+                console.log('destroyed provisioned infra:', cluster.infra_id);
+              }
+            });
+          }
+        });
+      }
+    });
   }
   }
 
 
   render() {
   render() {

+ 101 - 20
dashboard/src/main/home/new-project/NewProject.tsx

@@ -16,10 +16,11 @@ import SaveButton from '../../../components/SaveButton';
 const providers = ['aws', 'gcp', 'do',];
 const providers = ['aws', 'gcp', 'do',];
 
 
 type PropsType = {
 type PropsType = {
-  setCurrentView: (x: string, data: any) => void,
+  setCurrentView: (x: string, data?: any) => void,
 };
 };
 
 
 type StateType = {
 type StateType = {
+  projectExists: boolean,
   projectName: string,
   projectName: string,
   selectedProvider: string | null,
   selectedProvider: string | null,
   awsRegion: string | null,
   awsRegion: string | null,
@@ -30,6 +31,7 @@ type StateType = {
 
 
 export default class NewProject extends Component<PropsType, StateType> {
 export default class NewProject extends Component<PropsType, StateType> {
   state = {
   state = {
+    projectExists: false,
     projectName: '',
     projectName: '',
     selectedProvider: null as string | null,
     selectedProvider: null as string | null,
     awsRegion: '' as string | null,
     awsRegion: '' as string | null,
@@ -66,6 +68,7 @@ export default class NewProject extends Component<PropsType, StateType> {
     });
     });
   }
   }
 
 
+  // TODO: split this out into a separate component
   renderProvisioners = () => {
   renderProvisioners = () => {
     if (this.state.selectedProvider === 'aws') {
     if (this.state.selectedProvider === 'aws') {
       return (
       return (
@@ -173,7 +176,13 @@ export default class NewProject extends Component<PropsType, StateType> {
         {this.renderProvisioners()}
         {this.renderProvisioners()}
         <Helper>
         <Helper>
           Already have a Kubernetes cluster? 
           Already have a Kubernetes cluster? 
-          <Highlight onClick={() => this.setState({ selectedProvider: 'skipped' })}>
+          <Highlight onClick={() => {
+            if (this.state.projectExists) {
+              this.props.setCurrentView('dashboard');
+            } else {
+              this.setState({ selectedProvider: 'skipped' });
+            }
+          }}>
             Skip
             Skip
           </Highlight>
           </Highlight>
         </Helper>
         </Helper>
@@ -212,10 +221,8 @@ export default class NewProject extends Component<PropsType, StateType> {
       }, {id: proj.id}, (err: any, ecr:any) => {
       }, {id: proj.id}, (err: any, ecr:any) => {
         if (err) {
         if (err) {
           this.setState({ 
           this.setState({ 
-            status: 'Please provide valid AWS credentials.',
-            awsRegion: '',
-            awsAccessId: '',
-            awsSecretKey: '', 
+            projectExists: true,
+            status: 'Please provide valid credentials.',
           });
           });
           return;
           return;
         }
         }
@@ -247,10 +254,8 @@ export default class NewProject extends Component<PropsType, StateType> {
       }, { id: proj.id}, (err: any, eks: any) => {
       }, { id: proj.id}, (err: any, eks: any) => {
         if (err) {
         if (err) {
           this.setState({ 
           this.setState({ 
-            status: 'Please provide valid AWS credentials.',
-            awsRegion: '',
-            awsAccessId: '',
-            awsSecretKey: '', 
+            projectExists: true,
+            status: 'Please provide valid credentials.',
           });
           });
           return;
           return;
         }
         }
@@ -293,14 +298,60 @@ export default class NewProject extends Component<PropsType, StateType> {
       }
       }
     });
     });
   }
   }
-  
-  render() {
+
+  createInfra = () => {
+    this.setState({ status: 'loading' });
+    let { user } = this.context;
+    api.getProjects('<token>', {}, { id: user.userId }, (err: any, res: any) => {
+      if (err) {
+        console.log(err)
+      } else if (res.data) {
+        this.context.setProjects(res.data);
+        if (res.data.length > 0) {
+          let proj = res.data.find((el: ProjectType) => el.name === this.state.projectName);
+          this.context.setCurrentProject(proj);
+          
+          if (this.state.selectedProvider === 'aws') {
+            this.provisionECR(proj, this.provisionEKS)
+          } else {
+            this.props.setCurrentView('dashboard', null);
+          }
+        } 
+      }
+    });
+  }
+
+  renderHeaderSection = () => {
+    if (this.state.projectExists) {
+      return (
+        <>
+          <TitleSection>
+            <Title>Configure Hosting</Title>
+          </TitleSection>
+          <Helper>     
+            <Warning highlight={true} makeFlush={true}>
+              There was an issue configuring your cloud provider.
+            </Warning>
+          </Helper>
+          <Helper>     
+            You can refer to our docs for instructions on 
+            <Link 
+              href="https://docs.getporter.dev/docs/getting-started-with-porter-on-aws"
+              target="_blank"
+            >
+              creating AWS credentials for Porter
+            </Link>.
+          </Helper>
+          <br />
+        </>
+      );
+    }
+
     return (
     return (
-      <StyledNewProject height={this.state.selectedProvider === 'aws' ? '700px' : '600px'}>
+      <>
         <TitleSection>
         <TitleSection>
           <Title>New Project</Title>
           <Title>New Project</Title>
         </TitleSection>
         </TitleSection>
-
         <Helper>
         <Helper>
           Project name
           Project name
           <Warning highlight={!this.isAlphanumeric(this.state.projectName) && this.state.projectName !== ''}>
           <Warning highlight={!this.isAlphanumeric(this.state.projectName) && this.state.projectName !== ''}>
@@ -321,17 +372,42 @@ export default class NewProject extends Component<PropsType, StateType> {
             width='470px'
             width='470px'
           />
           />
         </InputWrapper>
         </InputWrapper>
+      </>
+    );
+  }
 
 
-        {this.renderHostingSection()}
-
+  renderButton = () => {
+    if (this.state.projectExists) {
+      return (
         <SaveButton
         <SaveButton
-          text='Create Project'
+          text='Submit'
           disabled={!this.validateForm()}
           disabled={!this.validateForm()}
-          onClick={this.createProject}
+          onClick={this.createInfra}
           makeFlush={true}
           makeFlush={true}
           helper='Note: Provisioning can take up to 15 minutes'
           helper='Note: Provisioning can take up to 15 minutes'
           status={this.state.status}
           status={this.state.status}
         />
         />
+      );
+    }
+
+    return (
+      <SaveButton
+        text='Create Project'
+        disabled={!this.validateForm()}
+        onClick={this.createProject}
+        makeFlush={true}
+        helper='Note: Provisioning can take up to 15 minutes'
+        status={this.state.status}
+      />
+    );
+  }
+  
+  render() {
+    return (
+      <StyledNewProject height={this.state.selectedProvider === 'aws' ? '700px' : '600px'}>
+        {this.renderHeaderSection()}
+        {this.renderHostingSection()}
+        {this.renderButton()}
       </StyledNewProject>
       </StyledNewProject>
     );
     );
   }
   }
@@ -339,6 +415,11 @@ export default class NewProject extends Component<PropsType, StateType> {
 
 
 NewProject.contextType = Context;
 NewProject.contextType = Context;
 
 
+const Link = styled.a`
+  cursor: pointer;
+  margin-left: 5px;
+`;
+
 const GuideButton = styled.a`
 const GuideButton = styled.a`
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
@@ -492,8 +573,8 @@ const InputWrapper = styled.div`
 `;
 `;
 
 
 const Warning = styled.span`
 const Warning = styled.span`
-  color: ${(props: { highlight: boolean }) => props.highlight ? '#f5cb42' : ''};
-  margin-left: 5px;
+  color: ${(props: { highlight: boolean, makeFlush?: boolean }) => props.highlight ? '#f5cb42' : ''};
+  margin-left: ${(props: { highlight: boolean, makeFlush?: boolean }) => props.makeFlush ? '' : '5px'};
 `;
 `;
 
 
 const Icon = styled.img`
 const Icon = styled.img`

+ 57 - 18
dashboard/src/main/home/new-project/Provisioner.tsx

@@ -6,6 +6,7 @@ import { Context } from '../../../shared/Context';
 import ansiparse from '../../../shared/ansiparser'
 import ansiparse from '../../../shared/ansiparser'
 import { integrationList } from '../../../shared/common';
 import { integrationList } from '../../../shared/common';
 import loading from '../../../assets/loading.gif';
 import loading from '../../../assets/loading.gif';
+import warning from '../../../assets/warning.png';
 
 
 import Helper from '../../../components/values-form/Helper';
 import Helper from '../../../components/values-form/Helper';
 import { eventNames } from 'process';
 import { eventNames } from 'process';
@@ -17,6 +18,7 @@ type PropsType = {
 };
 };
 
 
 type StateType = {
 type StateType = {
+  error: boolean,
   logs: string[],
   logs: string[],
   websockets: any[],
   websockets: any[],
   maxStep : Record<string, number>,
   maxStep : Record<string, number>,
@@ -25,6 +27,7 @@ type StateType = {
 
 
 export default class Provisioner extends Component<PropsType, StateType> {
 export default class Provisioner extends Component<PropsType, StateType> {
   state = {
   state = {
+    error: false,
     logs: [] as string[],
     logs: [] as string[],
     websockets : [] as any[],
     websockets : [] as any[],
     maxStep: {} as Record<string, any>,
     maxStep: {} as Record<string, any>,
@@ -57,34 +60,32 @@ export default class Provisioner extends Component<PropsType, StateType> {
       }
       }
 
 
       ws.onmessage = (evt: MessageEvent) => {
       ws.onmessage = (evt: MessageEvent) => {
-        let event = JSON.parse(evt.data)
-        console.log(event)
-        let validEvents = [] as any[]
-        let err = null
+        let event = JSON.parse(evt.data);
+        let validEvents = [] as any[];
+        let err = null;
         
         
         for (var i = 0; i < event.length; i++) {
         for (var i = 0; i < event.length; i++) {
-          let msg = event[i]
+          let msg = event[i];
           if (msg["Values"] && msg["Values"]["data"] && this.isJSON(msg["Values"]["data"])) { 
           if (msg["Values"] && msg["Values"]["data"] && this.isJSON(msg["Values"]["data"])) { 
-            let d = JSON.parse(msg["Values"]["data"])
+            let d = JSON.parse(msg["Values"]["data"]);
   
   
             if (d["kind"] == "error") {
             if (d["kind"] == "error") {
-              err = d["log"]
+              err = d["log"];
               break;
               break;
             }
             }
   
   
             // add only valid events
             // add only valid events
             if (d["log"] != null && d["created_resources"] != null && d["total_resources"] != null) {
             if (d["log"] != null && d["created_resources"] != null && d["total_resources"] != null) {
-              validEvents.push(d)
+              validEvents.push(d);
             }
             }
           }
           }
         }
         }
   
   
         if (err) {
         if (err) {
           let e = ansiparse(err).map((el: any) => {
           let e = ansiparse(err).map((el: any) => {
-            return el.text
+            return el.text;
           })
           })
-          console.log("error", e)
-          this.setState({ logs: e })
+          this.setState({ logs: e, error: true });
           return;
           return;
         }
         }
   
   
@@ -150,6 +151,38 @@ export default class Provisioner extends Component<PropsType, StateType> {
       return <div key={i}>{log}</div>
       return <div key={i}>{log}</div>
     });
     });
   }
   }
+
+  renderHeadingSection = () => {
+    if (this.state.error) {
+      return (
+        <>
+          <TitleSection>
+            <Title><img src={warning} /> Provisioning Error</Title>
+          </TitleSection>
+
+          <Helper>
+            Porter encountered an error while provisioning.
+            <Link onClick={() => this.props.setCurrentView('dashboard')}>
+              Exit to dashboard
+            </Link> 
+            to try again with new credentials.
+          </Helper>
+        </>
+      );
+    }
+
+    return (
+      <>
+        <TitleSection>
+          <Title><img src={loading} /> Setting Up Porter</Title>
+        </TitleSection>
+
+        <Helper>
+          Porter is currently being provisioned to your AWS account:
+        </Helper>
+      </>
+    )
+  }
   
   
   render() {
   render() {
     let maxStep = 0;
     let maxStep = 0;
@@ -171,13 +204,7 @@ export default class Provisioner extends Component<PropsType, StateType> {
 
 
     return (
     return (
       <StyledProvisioner>
       <StyledProvisioner>
-        <TitleSection>
-          <Title><img src={loading} /> Setting Up Porter</Title>
-        </TitleSection>
-
-        <Helper>
-          Porter is currently being provisioned to your AWS account:
-        </Helper>
+        {this.renderHeadingSection()}
 
 
         <LoadingBar>
         <LoadingBar>
           <Loaded progress={((currentStep / (maxStep == 0 ? 1 : maxStep)) * 100).toString() + '%'} />
           <Loaded progress={((currentStep / (maxStep == 0 ? 1 : maxStep)) * 100).toString() + '%'} />
@@ -199,6 +226,18 @@ export default class Provisioner extends Component<PropsType, StateType> {
 
 
 Provisioner.contextType = Context;
 Provisioner.contextType = Context;
 
 
+const Link = styled.a`
+  cursor: pointer;
+  margin-left: 5px;
+  margin-right: 5px;
+`;
+
+const Warning = styled.span`
+  color: ${(props: { highlight: boolean, makeFlush?: boolean }) => props.highlight ? '#f5cb42' : ''};
+  margin-left: ${(props: { highlight: boolean, makeFlush?: boolean }) => props.makeFlush ? '' : '5px'};
+  margin-right: 5px;
+`;
+
 const Wrapper = styled.div`
 const Wrapper = styled.div`
   width: 100%;
   width: 100%;
   height: 100%;
   height: 100%;

+ 7 - 1
dashboard/src/main/home/sidebar/ClusterSection.tsx

@@ -13,7 +13,9 @@ type PropsType = {
   releaseDrawer: () => void,
   releaseDrawer: () => void,
   setWelcome: (x: boolean) => void,
   setWelcome: (x: boolean) => void,
   setCurrentView: (x: string) => void,
   setCurrentView: (x: string) => void,
-  isSelected: boolean
+  isSelected: boolean,
+  forceRefreshClusters: boolean,
+  setRefreshClusters: (x: boolean) => void,
 };
 };
 
 
 type StateType = {
 type StateType = {
@@ -56,6 +58,7 @@ export default class ClusterSection extends Component<PropsType, StateType> {
           } else {
           } else {
             this.setState({ clusters: [] });
             this.setState({ clusters: [] });
             setCurrentCluster(null);
             setCurrentCluster(null);
+            this.props.setCurrentView('dashboard');
           }
           }
         }
         }
       }
       }
@@ -74,6 +77,9 @@ export default class ClusterSection extends Component<PropsType, StateType> {
       if (this.state.prevProjectId !== this.context.currentProject.id) {
       if (this.state.prevProjectId !== this.context.currentProject.id) {
         this.updateClusters();
         this.updateClusters();
         this.setState({ prevProjectId: this.context.currentProject.id });
         this.setState({ prevProjectId: this.context.currentProject.id });
+      } else if (this.props.forceRefreshClusters === true) {
+        this.updateClusters();
+        this.props.setRefreshClusters(false);
       }
       }
 
 
       if (this.props.forceCloseDrawer && this.state.showDrawer) {
       if (this.props.forceCloseDrawer && this.state.showDrawer) {

+ 4 - 0
dashboard/src/main/home/sidebar/Sidebar.tsx

@@ -15,6 +15,8 @@ type PropsType = {
   setWelcome: (x: boolean) => void,
   setWelcome: (x: boolean) => void,
   setCurrentView: (x: string, viewData?: any) => void,
   setCurrentView: (x: string, viewData?: any) => void,
   currentView: string,
   currentView: string,
+  forceRefreshClusters: boolean,
+  setRefreshClusters: (x: boolean) => void,
 };
 };
 
 
 type StateType = {
 type StateType = {
@@ -132,6 +134,8 @@ export default class Sidebar extends Component<PropsType, StateType> {
             setWelcome={this.props.setWelcome}
             setWelcome={this.props.setWelcome}
             setCurrentView={this.props.setCurrentView}
             setCurrentView={this.props.setCurrentView}
             isSelected={this.props.currentView === 'cluster-dashboard'}
             isSelected={this.props.currentView === 'cluster-dashboard'}
+            forceRefreshClusters={this.props.forceRefreshClusters}
+            setRefreshClusters={this.props.setRefreshClusters}
           />
           />
         </>
         </>
       );
       );

+ 2 - 2
dashboard/src/shared/api.tsx

@@ -281,7 +281,7 @@ const destroyCluster = baseApi<{
   project_id: number,
   project_id: number,
   infra_id: number,
   infra_id: number,
 }>('POST', pathParams => {
 }>('POST', pathParams => {
-  return `/projects/${pathParams.project_id}/infra/${pathParams.infra_id}/eks/destroy`;
+  return `/api/projects/${pathParams.project_id}/infra/${pathParams.infra_id}/eks/destroy`;
 });
 });
 
 
 const deleteCluster = baseApi<{
 const deleteCluster = baseApi<{
@@ -289,7 +289,7 @@ const deleteCluster = baseApi<{
   project_id: number,
   project_id: number,
   cluster_id: number,
   cluster_id: number,
 }>('DELETE', pathParams => {
 }>('DELETE', pathParams => {
-  return `/projects/${pathParams.project_id}/clusters/${pathParams.cluster_id}`;
+  return `/api/projects/${pathParams.project_id}/clusters/${pathParams.cluster_id}`;
 })
 })
 
 
 // Bundle export to allow default api import (api.<method> is more readable)
 // Bundle export to allow default api import (api.<method> is more readable)

+ 1 - 0
dashboard/src/shared/types.tsx

@@ -3,6 +3,7 @@ export interface ClusterType {
   name: string,
   name: string,
   server: string,
   server: string,
   service_account_id: number
   service_account_id: number
+  infra_id?: number
 }
 }
 
 
 export interface ChartType {
 export interface ChartType {

+ 2 - 0
go.sum

@@ -670,6 +670,8 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
 github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
 github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
 github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
 github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI=
 github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI=
 github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
 github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=