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

Merge branch '0.2.0-gke-service-account-file-upload' of https://github.com/porter-dev/porter into 0.2.0-gke-service-account-file-upload

sunguroku 5 лет назад
Родитель
Сommit
110b503ec2

+ 1 - 0
.github/workflows/dev.yaml

@@ -31,6 +31,7 @@ jobs:
           FEEDBACK_ENDPOINT=${{secrets.FEEDBACK_ENDPOINT}}
           POSTHOG_API_KEY=${{secrets.POSTHOG_API_KEY}}
           POSTHOG_HOST=${{secrets.POSTHOG_HOST}}
+          SEGMENT_PUBLIC_KEY=${{secrets.SEGMENT_PUBLIC_KEY}}
           APPLICATION_CHART_REPO_URL=${{secrets.APPLICATION_CHART_REPO_URL}}
           EOL
       - name: Build

+ 11 - 6
dashboard/package-lock.json

@@ -28,8 +28,8 @@
         "@visx/shape": "^1.4.0",
         "@visx/tooltip": "^1.3.0",
         "ace-builds": "^1.4.12",
+        "anser": "^2.0.1",
         "axios": "^0.20.0",
-        "brace": "^0.11.1",
         "d3-array": "^2.11.0",
         "d3-time-format": "^3.0.0",
         "dotenv": "^8.2.0",
@@ -1509,6 +1509,11 @@
       "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
       "dev": true
     },
+    "node_modules/anser": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/anser/-/anser-2.0.1.tgz",
+      "integrity": "sha512-4g5Np4CVD3c5c/36Mj0jllEA5bQcuXF0dqakZcuHGeubBzw93EAhwRuQCzgFm4/ZwvyBMzFdtn9BcihOjnxIdQ=="
+    },
     "node_modules/ansi-colors": {
       "version": "3.2.3",
       "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz",
@@ -1969,11 +1974,6 @@
       "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
       "dev": true
     },
-    "node_modules/brace": {
-      "version": "0.11.1",
-      "resolved": "https://registry.npmjs.org/brace/-/brace-0.11.1.tgz",
-      "integrity": "sha1-SJb8ydVE7vRfS7dmDbMg07N5/lg="
-    },
     "node_modules/brace-expansion": {
       "version": "1.1.11",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -11722,6 +11722,11 @@
       "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
       "dev": true
     },
+    "anser": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/anser/-/anser-2.0.1.tgz",
+      "integrity": "sha512-4g5Np4CVD3c5c/36Mj0jllEA5bQcuXF0dqakZcuHGeubBzw93EAhwRuQCzgFm4/ZwvyBMzFdtn9BcihOjnxIdQ=="
+    },
     "ansi-colors": {
       "version": "3.2.3",
       "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz",

+ 1 - 0
dashboard/package.json

@@ -23,6 +23,7 @@
     "@visx/shape": "^1.4.0",
     "@visx/tooltip": "^1.3.0",
     "ace-builds": "^1.4.12",
+    "anser": "^2.0.1",
     "axios": "^0.20.0",
     "brace": "^0.11.1",
     "d3-array": "^2.11.0",

+ 18 - 15
dashboard/src/components/repo-selector/ActionDetails.tsx

@@ -169,21 +169,24 @@ export default class ActionDetails extends Component<PropsType, StateType> {
             <i className="material-icons">keyboard_backspace</i>
             Select Folder
           </BackButton>
-          {!this.props.procfilePath && !this.props.dockerfilePath ? (
-            <StatusWrapper>
-              <i className="material-icons">error_outline</i>
-              Procfile not detected.
-            </StatusWrapper>
-          ) : this.props.selectedRegistry ? (
-            <StatusWrapper successful={true}>
-              <i className="material-icons">done</i> Source selected
-            </StatusWrapper>
-          ) : (
-            <StatusWrapper>
-              <i className="material-icons">error_outline</i>A connected
-              container registry is required
-            </StatusWrapper>
-          )}
+          {
+            // !this.props.procfilePath && !this.props.dockerfilePath ? (
+            //   <StatusWrapper>
+            //     <i className="material-icons">error_outline</i>
+            //     Procfile not detected.
+            //   </StatusWrapper>
+            // ) :
+            this.props.selectedRegistry ? (
+              <StatusWrapper successful={true}>
+                <i className="material-icons">done</i> Source selected
+              </StatusWrapper>
+            ) : (
+              <StatusWrapper>
+                <i className="material-icons">error_outline</i>A connected
+                container registry is required
+              </StatusWrapper>
+            )
+          }
         </Flex>
       </>
     );

+ 11 - 4
dashboard/src/components/repo-selector/ContentsList.tsx

@@ -197,8 +197,8 @@ export default class ContentsList extends Component<PropsType, StateType> {
       if (fileName.includes("Dockerfile")) {
         dockerfiles.push(fileName);
       }
-      if (fileName.includes("Procfile")) {
-        this.props.setProcfilePath(item.Path);
+      if (this.state.currentDir === "" && fileName == "Procfile") {
+        this.props.setProcfilePath("./Procfile");
       }
     });
     if (dockerfiles.length > 0) {
@@ -213,8 +213,9 @@ export default class ContentsList extends Component<PropsType, StateType> {
   };
 
   renderOverlay = () => {
+    console.log(this.props.procfilePath)
     if (this.props.procfilePath) {
-      let processes = Object.keys(this.state.processes);
+      let processes = this.state.processes ? Object.keys(this.state.processes) : [];
       return (
         <Overlay>
           <BgOverlay
@@ -289,7 +290,13 @@ export default class ContentsList extends Component<PropsType, StateType> {
           <ConfirmButton
             onClick={() => {
               this.props.setFolderPath(this.state.currentDir || "./");
-              this.props.setProcfilePath("./Procfile");
+              if (
+                this.state.processes &&
+                Object.keys(this.state.processes).length > 0
+              ) {
+                console.log('setting procfile')
+                this.props.setProcfilePath("./Procfile");
+              }
             }}
           >
             No, I don't want to use a Dockerfile

+ 4 - 0
dashboard/src/index.html

@@ -104,6 +104,10 @@
       href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined"
       rel="stylesheet"
     />
+    <link
+      href="https://fonts.googleapis.com/icon?family=Roboto+Mono"
+      rel="stylesheet"
+    />
   </head>
   <body>
     <div id="output"></div>

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

@@ -55,8 +55,9 @@ export default class ChartList extends Component<PropsType, StateType> {
             "deployed",
             "uninstalled",
             "pending",
-            "pending_upgrade",
-            "pending_rollback",
+            "pending-install",
+            "pending-upgrade",
+            "pending-rollback",
             "superseded",
             "failed",
           ],

+ 24 - 11
dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedChart.tsx

@@ -98,9 +98,15 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
         }
       )
       .then((res) => {
-        this.setState({ currentChart: res.data, loading: false }, () => {
-          this.updateTabs();
-        });
+        this.updateComponents({ currentChart: res.data, loading: false }, res.data);
+        // // if the current tab is manifests or chart overview, update components as well
+        // if (this.state.currentTab == "graph" || this.state.currentTab == "list") {
+        //   this.updateComponents({ currentChart: res.data, loading: false }, currentChart);
+        // } else {
+        //   this.setState({ currentChart: res.data, loading: false }, () => {
+        //     this.updateTabs()
+        //   })
+        // }
       })
       .catch(console.log);
   };
@@ -197,9 +203,8 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
     this.setState({ websockets });
   };
 
-  updateResources = () => {
+  updateComponents = (state : any, currentChart : ChartType) => {
     let { currentCluster, currentProject } = this.context;
-    let { currentChart } = this.state;
 
     api
       .getChartComponents(
@@ -216,10 +221,13 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
         }
       )
       .then((res) => {
-        this.setState({
-          components: res.data.Objects,
-          podSelectors: res.data.PodSelectors,
-        });
+        let newState = state || {}
+
+        newState.components = res.data.Objects
+        newState.podSelectors = res.data.PodSelectors
+
+        this.setState(newState);
+        this.updateTabs();
       })
       .catch(console.log);
   };
@@ -232,18 +240,22 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
     // Convert dotted keys to nested objects
     let values = {};
 
+    // Weave in preexisting values and convert to yaml
+    if (this.props.currentChart.config) {
+      values = this.props.currentChart.config;
+    }
+
     for (let key in rawValues) {
       _.set(values, key, rawValues[key]);
     }
 
-    // Weave in preexisting values and convert to yaml
     let valuesYaml = yaml.dump({
-      ...(this.state.currentChart.config as Object),
       ...values,
     });
 
     this.setState({ saveValuesStatus: "loading" });
     this.refreshChart();
+    
     api
       .upgradeChartValues(
         "<token>",
@@ -270,6 +282,7 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
         });
       })
       .catch((err) => {
+        console.log(err)
         this.setState({ saveValuesStatus: "error" });
         window.analytics.track("Failed to Upgrade Chart", {
           chart: this.state.currentChart.name,

+ 8 - 3
dashboard/src/main/home/cluster-dashboard/expanded-chart/graph/GraphDisplay.tsx

@@ -8,6 +8,7 @@ import Edge from "./Edge";
 import InfoPanel from "./InfoPanel";
 import ZoomPanel from "./ZoomPanel";
 import SelectRegion from "./SelectRegion";
+import _ from "lodash";
 
 const zoomConstant = 0.01;
 const panConstant = 0.8;
@@ -100,8 +101,10 @@ export default class GraphDisplay extends Component<PropsType, StateType> {
     let graph = localStorage.getItem(
       `charts.${currentChart.name}-${currentChart.version}`
     );
+    
     let nodes = [] as NodeType[];
     let edges = [] as EdgeType[];
+
     if (!graph) {
       nodes = this.createNodes(components);
       edges = this.createEdges(components);
@@ -143,7 +146,7 @@ export default class GraphDisplay extends Component<PropsType, StateType> {
 
   // Live update on rollback/upgrade
   componentDidUpdate(prevProps: PropsType) {
-    if (prevProps.components !== this.props.components) {
+    if (!(_.isEqual(prevProps.currentChart, this.props.currentChart))) {
       this.storeChartGraph(prevProps);
       this.getChartGraph();
     }
@@ -243,7 +246,9 @@ export default class GraphDisplay extends Component<PropsType, StateType> {
   };
 
   storeChartGraph = (props?: PropsType) => {
+
     let useProps = props || this.props;
+
     let { currentChart } = useProps;
     let graph = JSON.parse(JSON.stringify(this.state));
 
@@ -559,9 +564,9 @@ export default class GraphDisplay extends Component<PropsType, StateType> {
           showKindLabels={this.state.showKindLabels}
           isOpen={node === this.state.openedNode}
           // Parameterized to allow setting to null
-          setCurrentNode={(node: NodeType) =>
+          setCurrentNode={(node: NodeType) => {
             this.setState({ currentNode: node })
-          }
+          }}
         />
       );
     });

+ 32 - 13
dashboard/src/main/home/cluster-dashboard/expanded-chart/status/Logs.tsx

@@ -1,6 +1,7 @@
 import React, { Component } from "react";
 import styled from "styled-components";
 import { Context } from "shared/Context";
+import * as Anser from 'anser';
 
 type PropsType = {
   selectedPod: any;
@@ -9,14 +10,14 @@ type PropsType = {
 };
 
 type StateType = {
-  logs: string[];
+  logs: Anser.AnserJsonEntry[][];
   ws: any;
   scroll: boolean;
 };
 
 export default class Logs extends Component<PropsType, StateType> {
   state = {
-    logs: [] as string[],
+    logs: [] as Anser.AnserJsonEntry[][],
     ws: null as any,
     scroll: true,
   };
@@ -58,8 +59,19 @@ export default class Logs extends Component<PropsType, StateType> {
     if (this.state.logs.length == 0) {
       return <Message>No logs to display from this pod.</Message>;
     }
+
     return this.state.logs.map((log, i) => {
-      return <Log key={i}>{log}</Log>;
+      return <Log key={i}>
+        {this.state.logs[i].map((ansi, j) => {
+          if (ansi.clearLine) {
+            return null
+          }
+
+          return <LogSpan key={i + "." + j} ansi={ansi}>
+            {ansi.content.replace(/ /g, '\u00a0')}
+          </LogSpan>
+        })}
+      </Log>;
     });
   };
 
@@ -72,25 +84,24 @@ export default class Logs extends Component<PropsType, StateType> {
       `${protocol}://${process.env.API_SERVER}/api/projects/${currentProject.id}/k8s/${selectedPod?.metadata?.namespace}/pod/${selectedPod?.metadata?.name}/logs?cluster_id=${currentCluster.id}&service_account_id=${currentCluster.service_account_id}`
     );
 
-    this.ws.onopen = () => {
-      console.log("connected to websocket");
-    };
+    this.ws.onopen = () => {};
 
     this.ws.onmessage = (evt: MessageEvent) => {
-      this.setState({ logs: [...this.state.logs, evt.data] }, () => {
+      let ansiLog = Anser.ansiToJson(evt.data)
+
+      let logs = this.state.logs
+      logs.push(ansiLog)
+
+      this.setState({ logs: logs }, () => {
         if (this.state.scroll) {
           this.scrollToBottom(false);
         }
       });
     };
 
-    this.ws.onerror = (err: ErrorEvent) => {
-      console.log("websocket error:", err);
-    };
+    this.ws.onerror = (err: ErrorEvent) => {};
 
-    this.ws.onclose = () => {
-      console.log("closing pod logs");
-    };
+    this.ws.onclose = () => {};
   };
 
   refreshLogs = () => {
@@ -247,3 +258,11 @@ const Message = styled.div`
 const Log = styled.div`
   font-family: monospace;
 `;
+
+const LogSpan = styled.span`
+  font-family: 'Roboto Mono';
+  font-size: 12px;
+  font-weight: ${(props: { ansi: Anser.AnserJsonEntry }) => props.ansi?.decoration && props.ansi?.decoration == "bold" ? "700" : "400"};
+  color: ${(props: { ansi: Anser.AnserJsonEntry }) => props.ansi?.fg ? `rgb(${props.ansi?.fg})` : "white"};
+  background-color: ${(props: { ansi: Anser.AnserJsonEntry }) => props.ansi?.bg ? `rgb(${props.ansi?.bg})` : "transparent"};
+`

+ 13 - 14
dashboard/src/main/home/launch/expanded-template/LaunchTemplate.tsx

@@ -244,7 +244,7 @@ class LaunchTemplate extends Component<PropsType, StateType> {
 
     // check if template is docker and create external domain if necessary
     if (this.props.currentTemplate.name == "web") {
-      if (values?.ingress?.enabled && values?.ingress?.hosts?.length == 0) {
+      if (!values?.ingress?.custom_domain) {
         url = await new Promise((resolve, reject) => {
           api
             .createSubdomain(
@@ -265,8 +265,7 @@ class LaunchTemplate extends Component<PropsType, StateType> {
             });
         });
 
-        values.ingress.hosts = [url];
-        values.ingress.custom_domain = true;
+        values.ingress.porter_hosts = [url];
       }
     }
 
@@ -383,14 +382,14 @@ class LaunchTemplate extends Component<PropsType, StateType> {
       procfilePath,
     } = this.state;
 
-    if (
-      sourceType === "repo" &&
-      !dockerfilePath &&
-      folderPath &&
-      !procfilePath
-    ) {
-      return "Procfile not detected.";
-    }
+    // if (
+    //   sourceType === "repo" &&
+    //   !dockerfilePath &&
+    //   folderPath &&
+    //   !procfilePath
+    // ) {
+    //   return "Procfile not detected.";
+    // }
 
     if (!this.submitIsDisabled()) {
       return this.state.saveValuesStatus;
@@ -434,9 +433,9 @@ class LaunchTemplate extends Component<PropsType, StateType> {
           }
 
           // handle when procfileProcess is already specified
-          metaState["container.command"] = this.state.procfileProcess
-            ? this.state.procfileProcess
-            : "";
+          if (this.state.procfileProcess) {
+            metaState["container.command"] = this.state.procfileProcess
+          }
 
           return this.props.form?.tabs.map((tab: any, i: number) => {
             // If tab is current, render

+ 2 - 2
internal/helm/filter.go

@@ -16,8 +16,8 @@ type ListFilter struct {
 
 // listStatesFromNames accepts the following list of names:
 //
-// "deployed", "uninstalled", "uninstalling", "pending", "pending_upgrade",
-// "pending_rollback", "superseded", "failed"
+// "deployed", "uninstalled", "uninstalling", "pending-install", "pending-upgrade",
+// "pending-rollback", "superseded", "failed"
 //
 // It returns an action.ListStates to be used in an action.List as filters for
 // releases in a certain state.

+ 10 - 0
server/router/router.go

@@ -3,6 +3,8 @@ package router
 import (
 	"net/http"
 	"os"
+	"path"
+	"strings"
 
 	"github.com/go-chi/chi"
 	"github.com/porter-dev/porter/internal/auth/token"
@@ -1358,8 +1360,16 @@ func New(a *api.App) *chi.Mux {
 
 	r.Get("/*", func(w http.ResponseWriter, r *http.Request) {
 		if _, err := os.Stat(staticFilePath + r.RequestURI); os.IsNotExist(err) {
+			w.Header().Set("Cache-Control", "no-cache")
+
 			http.StripPrefix(r.URL.Path, fs).ServeHTTP(w, r)
 		} else {
+			// Set static files involving html, js, or empty cache to "no-cache", which means they must be validated
+			// for changes before the browser uses the cache
+			if base := path.Base(r.URL.Path); strings.Contains(base, "html") || strings.Contains(base, "js") || base == "." || base == "/" {
+				w.Header().Set("Cache-Control", "no-cache")
+			}
+
 			fs.ServeHTTP(w, r)
 		}
 	})