Sfoglia il codice sorgente

fixed redis conf merge issue

Alexander Belanger 5 anni fa
parent
commit
b6264a162f

+ 23 - 0
.github/workflows/gcr.yaml

@@ -0,0 +1,23 @@
+name: Build, Push to GCR.
+on:
+  push:
+    branches:
+      - staging
+jobs:
+  login-build-push:
+  runs-on: ubuntu-latest
+  steps:
+  - name: Set up Cloud SDK
+    uses: google-github-actions/setup-gcloud@master
+    with:
+      project_id: ${{ secrets.GCP_PROJECT_ID }}
+      service_account_key: ${{ secrets.GCP_SA_KEY }}
+      export_default_credentials: true
+  - name: Log in to gcloud CLI
+    run: gcloud auth configure-docker
+  - name: Checkout
+    uses: actions/checkout@v2.3.4
+  - name: Build
+    run: |
+      docker build . -t gcr.io/porter-dev-273614/porter-prov:latest -f ./docker/Dockerfile
+      docker push gcr.io/porter-dev-273614/porter-prov:latest

+ 20 - 2
dashboard/src/main/home/Home.tsx

@@ -4,7 +4,7 @@ import ReactModal from 'react-modal';
 
 
 import { Context } from '../../shared/Context';
 import { Context } from '../../shared/Context';
 import api from '../../shared/api';
 import api from '../../shared/api';
-import { ProjectType } from '../../shared/types';
+import { InfraType } from '../../shared/types';
 
 
 import Sidebar from './sidebar/Sidebar';
 import Sidebar from './sidebar/Sidebar';
 import Dashboard from './dashboard/Dashboard';
 import Dashboard from './dashboard/Dashboard';
@@ -48,11 +48,29 @@ export default class Home extends Component<PropsType, StateType> {
     let { user, currentProject, projects, setProjects } = this.context;
     let { user, currentProject, projects, setProjects } = this.context;
     api.getProjects('<token>', {}, { id: user.userId }, (err: any, res: any) => {
     api.getProjects('<token>', {}, { id: user.userId }, (err: any, res: any) => {
       if (err) {
       if (err) {
-        console.log(err)
+        console.log(err);
       } else if (res.data) {
       } else if (res.data) {
         setProjects(res.data);
         setProjects(res.data);
         if (res.data.length > 0 && !currentProject) {
         if (res.data.length > 0 && !currentProject) {
           this.context.setCurrentProject(res.data[0]);
           this.context.setCurrentProject(res.data[0]);
+
+          // Check if current project is provisioning
+          api.getInfra('<token>', {}, { project_id: res.data[0].id }, (err: any, res: any) => {
+            if (err) {
+              console.log(err);
+            } else if (res.data) {
+              
+              // TODO: separately handle non meta-provisioning case
+              res.data.forEach((el: InfraType) => {
+                if (el.status === 'creating') {
+                  this.setState({ currentView: 'provisioner', viewData: {
+                    infra_id: el.id,
+                    kind: el.kind,
+                  }});
+                }
+              });
+            }
+          });
         } else if (res.data.length === 0) {
         } else if (res.data.length === 0) {
           this.setState({ currentView: 'new-project' });
           this.setState({ currentView: 'new-project' });
         }
         }

+ 60 - 27
dashboard/src/main/home/new-project/NewProject.tsx

@@ -193,9 +193,66 @@ export default class NewProject extends Component<PropsType, StateType> {
     return false;
     return false;
   }
   }
 
 
+  provisionECR = (proj: ProjectType, callback: (proj: ProjectType, ecr: any) => void) => {
+    let { awsAccessId, awsSecretKey, awsRegion } = this.state;
+
+    api.createAWSIntegration('<token>', {
+      aws_region: awsRegion,
+      aws_access_key_id: awsAccessId,
+      aws_secret_access_key: awsSecretKey,
+    }, { id: proj.id }, (err: any, res: any) => {
+      if (err) {
+        console.log(err);
+        return;
+      }
+
+      api.provisionECR('<token>', {
+        aws_integration_id: res.data.id,
+        ecr_name: `${proj.name}-registry`
+      }, {id: proj.id}, (err: any, ecr:any) => {
+        if (err) {
+          console.log(err)
+          return;
+        }
+
+        callback(proj, ecr);
+      })
+      
+    });
+  }
+
+  provisionEKS = (proj: ProjectType, ecr: any) => {
+    let { awsAccessId, awsSecretKey, awsRegion } = this.state;
+
+    api.createAWSIntegration('<token>', {
+      aws_region: awsRegion,
+      aws_access_key_id: awsAccessId,
+      aws_secret_access_key: awsSecretKey,
+    }, { id: proj.id }, (err: any, res: any) => {
+      if (err) {
+        console.log(err)
+        return;
+      }
+
+      api.provisionEKS('<token>', {
+        aws_integration_id: res.data.id,
+        eks_name: `${proj.name}-cluster`,
+      }, { id: proj.id}, (err: any, eks: any) => {
+        if (err) {
+          console.log(err)
+          return;
+        }
+
+        this.props.setCurrentView('provisioner', [
+          {infra_id: ecr?.data?.id, kind: ecr?.data?.kind},
+          {infra_id: eks?.data?.id, kind: eks?.data?.kind},
+        ]);
+      })
+    })
+  }
+
   createProject = () => {
   createProject = () => {
     this.setState({ status: 'loading' });
     this.setState({ status: 'loading' });
-    let { awsAccessId, awsSecretKey, awsRegion } = this.state;
 
 
     api.createProject('<token>', {
     api.createProject('<token>', {
       name: this.state.projectName
       name: this.state.projectName
@@ -212,33 +269,9 @@ export default class NewProject extends Component<PropsType, StateType> {
             if (res.data.length > 0) {
             if (res.data.length > 0) {
               let proj = res.data.find((el: ProjectType) => el.name === this.state.projectName);
               let proj = res.data.find((el: ProjectType) => el.name === this.state.projectName);
               this.context.setCurrentProject(proj);
               this.context.setCurrentProject(proj);
-
-              // Handle provisioning logic
+              
               if (this.state.selectedProvider === 'aws') {
               if (this.state.selectedProvider === 'aws') {
-                let clusterName = `${proj.name}-cluster`
-
-                api.createAWSIntegration('<token>', {
-                  aws_region: awsRegion,
-                  aws_cluster_id: clusterName,
-                  aws_access_key_id: awsAccessId,
-                  aws_secret_access_key: awsSecretKey,
-                }, { id: proj.id }, (err2: any, res2: any) => {
-                  if (err2) {
-                    console.log(err2);
-                  } else {
-                    api.provisionEKS('<token>', {
-                      aws_integration_id: res2.data.id,
-                      eks_name: clusterName,
-                    }, {id: proj.id}, (err3: any, res3:any) => {
-                      if (err3) {
-                        console.log(err3)
-                      } else {
-                        this.props.setCurrentView('provisioner', {infra_id: res3.data.id, kind: res3.data.kind});
-                      }
-                    })
-                  }
-                });
-
+                this.provisionECR(proj, this.provisionEKS)
               } else {
               } else {
                 this.props.setCurrentView('dashboard', null);
                 this.props.setCurrentView('dashboard', null);
               }
               }

+ 66 - 22
dashboard/src/main/home/new-project/Provisioner.tsx

@@ -7,6 +7,7 @@ import { integrationList } from '../../../shared/common';
 import loading from '../../../assets/loading.gif';
 import loading from '../../../assets/loading.gif';
 
 
 import Helper from '../../../components/values-form/Helper';
 import Helper from '../../../components/values-form/Helper';
+import { eventNames } from 'process';
 
 
 type PropsType = {
 type PropsType = {
   viewData: any,
   viewData: any,
@@ -14,15 +15,17 @@ type PropsType = {
 
 
 type StateType = {
 type StateType = {
   logs: string[],
   logs: string[],
-  ws: any
+  websockets: any[],
+  maxStep : Record<string, number>,
+  currentStep: Record<string, number>,
 };
 };
 
 
-const loadMax = 40;
-
 export default class Provisioner extends Component<PropsType, StateType> {
 export default class Provisioner extends Component<PropsType, StateType> {
   state = {
   state = {
     logs: [] as string[],
     logs: [] as string[],
-    ws : null as any
+    websockets : [] as any[],
+    maxStep: {} as Record<string, any>,
+    currentStep: {} as Record<string, number>,
   }
   }
 
 
   scrollToBottom = () => {
   scrollToBottom = () => {
@@ -32,36 +35,66 @@ export default class Provisioner extends Component<PropsType, StateType> {
   componentDidMount() {
   componentDidMount() {
     let { currentProject } = this.context;
     let { currentProject } = this.context;
     let protocol = process.env.NODE_ENV == 'production' ? 'wss' : 'ws'
     let protocol = process.env.NODE_ENV == 'production' ? 'wss' : 'ws'
-    let ws = new WebSocket(`${protocol}://${process.env.API_SERVER}/api/projects/${currentProject.id}/provision/${this.props.viewData.kind}/${this.props.viewData.infra_id}/logs`)
 
 
-    this.setState({ ws }, () => {
-      if (!this.state.ws) return;
-  
-      this.state.ws.onopen = () => {
+    let websockets = this.props.viewData.forEach((infra: any) => {
+      let ws = new WebSocket(`${protocol}://${process.env.API_SERVER}/api/projects/${currentProject.id}/provision/${infra.kind}/${infra.infra_id}/logs`)
+      
+      ws.onopen = () => {
         console.log('connected to websocket')
         console.log('connected to websocket')
       }
       }
-  
-      this.state.ws.onmessage = (evt: MessageEvent) => {
+
+      ws.onmessage = (evt: MessageEvent) => {
         let event = JSON.parse(evt.data)
         let event = JSON.parse(evt.data)
-        let data = event.map((msg: any) => { return msg["Values"]["data"]})
-        this.setState({ logs: [...this.state.logs, ...data] }, () => {
+        let data = event.map((msg: any) => { return `${infra.kind}: ${msg["Values"]["data"]}` })
+        let err = null
+
+        // check for error
+        event.forEach((e: any) => {
+          err = e["Values"]["kind"] == "error" ? e["Values"]["data"] : null
+        })
+
+        if (err) {
+          this.setState({ logs: [err] })
+        }
+        
+        if (!this.state.maxStep[infra.kind]) {
+          this.setState({
+            maxStep: {
+              ...this.state.maxStep,
+              [infra.kind] : event[event.length]["Values"]["created_resources"]
+            }
+          })
+        }
+
+        this.setState({ 
+          logs: [...this.state.logs, ...data], 
+          currentStep: {
+            ...this.state.currentStep,
+            [infra.kind] : event[event.length]["Values"]["created_resources"]
+          },
+        }, () => {
           this.scrollToBottom()
           this.scrollToBottom()
         })
         })
       }
       }
-  
-      this.state.ws.onerror = (err: ErrorEvent) => {
+
+      ws.onerror = (err: ErrorEvent) => {
         console.log(err)
         console.log(err)
       }
       }
-    })
 
 
-    this.setState({ logs: [] });
+      ws.onclose = () => {
+        console.log('closing provisioner websocket')
+      }
+
+      return ws
+    });
+
+    this.setState({ websockets, logs: [] });
   }
   }
 
 
   componentWillUnmount() {
   componentWillUnmount() {
-    if (this.state.ws) {
-      console.log('closing websocket')
-      this.state.ws.close()
-    }
+    this.state.websockets?.forEach((ws) => {
+      ws.close()
+    })
   }
   }
 
 
   scrollRef = React.createRef<HTMLDivElement>();
   scrollRef = React.createRef<HTMLDivElement>();
@@ -73,6 +106,17 @@ export default class Provisioner extends Component<PropsType, StateType> {
   }
   }
   
   
   render() {
   render() {
+    let maxStep = 0;
+    let currentStep = 0;
+
+    for (let key in this.state.maxStep) {
+      maxStep += this.state.maxStep[key]
+    }
+
+    for (let key in this.state.currentStep) {
+      currentStep += this.state.currentStep[key]
+    }
+
     return (
     return (
       <StyledProvisioner>
       <StyledProvisioner>
         <TitleSection>
         <TitleSection>
@@ -84,7 +128,7 @@ export default class Provisioner extends Component<PropsType, StateType> {
         </Helper>
         </Helper>
 
 
         <LoadingBar>
         <LoadingBar>
-          <Loaded progress={((7 / loadMax) * 100).toString() + '%'} />
+          <Loaded progress={((currentStep / maxStep) * 100).toString() + '%'} />
         </LoadingBar>
         </LoadingBar>
 
 
         <LogStream ref={this.scrollRef}>
         <LogStream ref={this.scrollRef}>

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

@@ -269,8 +269,16 @@ const getGitRepos = baseApi<{
   return `/api/projects/${pathParams.project_id}/gitrepos`;
   return `/api/projects/${pathParams.project_id}/gitrepos`;
 });
 });
 
 
+const getInfra = baseApi<{
+}, {
+  project_id: number,
+}>('GET', pathParams => {
+  return `/api/projects/${pathParams.project_id}/infra`;
+});
+
 // Bundle export to allow default api import (api.<method> is more readable)
 // Bundle export to allow default api import (api.<method> is more readable)
 export default {
 export default {
+  getInfra,
   linkGithubProject,
   linkGithubProject,
   getGitRepos,
   getGitRepos,
   checkAuth,
   checkAuth,

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

@@ -140,4 +140,11 @@ export interface ImageType {
   source: string,
   source: string,
   registryId: number,
   registryId: number,
   name: string,
   name: string,
+}
+
+export interface InfraType {
+  id: number,
+  project_d: number,
+  kind: string,
+  status: string,
 }
 }