Sfoglia il codice sorgente

Merge branch 'pr/meehawk/2336' into dev

jnfrati 3 anni fa
parent
commit
f01093fcf0

+ 1 - 1
.github/workflows/prerelease.yaml

@@ -134,7 +134,7 @@ jobs:
         run: |
           mkdir -p ./release/static
           cd dashboard
-          npm i --production=false
+          npm i --production=false --legacy-peer-deps
           npm run build
           cd ..
           zip --junk-paths ./release/static/static_${{steps.tag_name.outputs.tag}}.zip ./dashboard/build/*

+ 35 - 3
cli/cmd/run.go

@@ -35,6 +35,7 @@ var namespace string
 var verbose bool
 var existingPod bool
 var nonInteractive bool
+var containerName string
 
 // runCmd represents the "porter run" base command when called
 // without any subcommands
@@ -98,6 +99,14 @@ func init() {
 		"whether to run in non-interactive mode",
 	)
 
+	runCmd.PersistentFlags().StringVarP(
+		&containerName,
+		"container",
+		"c",
+		"",
+		"name of the container inside pod to run the command in",
+	)
+
 	runCmd.AddCommand(cleanupCmd)
 }
 
@@ -144,12 +153,35 @@ func run(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []strin
 
 	var selectedContainerName string
 
-	// if the selected pod has multiple container, spawn selector
 	if len(selectedPod.ContainerNames) == 0 {
-		return fmt.Errorf("At least one pod must exist in this deployment.")
+		return fmt.Errorf("At least one container must exist in the selected pod.")
 	} else if len(selectedPod.ContainerNames) == 1 {
+		if containerName != "" && containerName != selectedPod.ContainerNames[0] {
+			return fmt.Errorf("provided container %s does not exist in pod %s", containerName, selectedPod.Name)
+		}
+
 		selectedContainerName = selectedPod.ContainerNames[0]
-	} else {
+	}
+
+	if containerName != "" && selectedContainerName == "" {
+		// check if provided container name exists in the pod
+		for _, name := range selectedPod.ContainerNames {
+			if name == containerName {
+				selectedContainerName = name
+				break
+			}
+		}
+
+		if selectedContainerName == "" {
+			return fmt.Errorf("provided container %s does not exist in pod %s", containerName, selectedPod.Name)
+		}
+	}
+
+	if selectedContainerName == "" {
+		if nonInteractive {
+			return fmt.Errorf("container name must be specified using the --container flag when using non-interactive mode")
+		}
+
 		selectedContainer, err := utils.PromptSelect("Select the container:", selectedPod.ContainerNames)
 
 		if err != nil {

+ 78 - 21
dashboard/src/main/home/cluster-dashboard/expanded-chart/status/Logs.tsx

@@ -14,7 +14,8 @@ import { NewWebsocketOptions, useWebsockets } from "shared/hooks/useWebsockets";
 import CommandLineIcon from "assets/command-line-icon";
 import ConnectToLogsInstructionModal from "./ConnectToLogsInstructionModal";
 
-const MAX_LOGS = 250;
+const MAX_LOGS = 5000;
+const LOGS_BUFFER_SIZE = 1000;
 
 type SelectedPodType = {
   spec: {
@@ -257,6 +258,7 @@ const useLogs = (
   currentPod: SelectedPodType,
   scroll?: (smooth: boolean) => void
 ) => {
+  let logsBuffer: Anser.AnserJsonEntry[][] = [];
   const currentPodName = useRef<string>();
 
   const { currentCluster, currentProject } = useContext(Context);
@@ -340,6 +342,51 @@ const useLogs = (
     } catch (error) {}
   };
 
+  /**
+   * Updates the `logs` for `containerName` with `newLogs`
+   * @param containerName Name of the container
+   * @param newLogs New logs to update for
+   */
+  const updateContainerLogs = (
+    containerName: string,
+    newLogs: Anser.AnserJsonEntry[][]
+  ) => {
+    setLogs((logs) => {
+      const tmpLogs = { ...logs };
+      let containerLogs = tmpLogs[containerName] || [];
+
+      containerLogs.push(...newLogs);
+      // this is technically not as efficient as things could be
+      // if there are performance issues, a deque can be used in place of a list
+      // for storing logs
+      if (containerLogs.length > MAX_LOGS) {
+        containerLogs.shift();
+      }
+
+      if (typeof scroll === "function") {
+        scroll(true);
+      }
+      return {
+        ...logs,
+        [containerName]: containerLogs,
+      };
+    });
+  };
+
+  /**
+   * Flushes the logs buffer. If `containerName` is provided,
+   * it will update logs for the `containerName` before executing
+   * the flush operation
+   * @param containerName Name of the container
+   */
+  const flushLogsBuffer = (containerName?: string) => {
+    if (containerName) {
+      updateContainerLogs(containerName, logsBuffer);
+    }
+
+    logsBuffer = [];
+  };
+
   const setupWebsocket = (containerName: string, websocketKey: string) => {
     if (!currentPod?.metadata?.name) return;
 
@@ -351,25 +398,12 @@ const useLogs = (
       },
       onmessage: (evt: MessageEvent) => {
         let ansiLog = Anser.ansiToJson(evt.data);
-        setLogs((logs) => {
-          const tmpLogs = { ...logs };
-          let containerLogs = tmpLogs[containerName] || [];
-
-          containerLogs.push(ansiLog);
-          // this is technically not as efficient as things could be
-          // if there are performance issues, a deque can be used in place of a list
-          // for storing logs
-          if (containerLogs.length > MAX_LOGS) {
-            containerLogs.shift();
-          }
-          if (typeof scroll === "function") {
-            scroll(true);
-          }
-          return {
-            ...logs,
-            [containerName]: containerLogs,
-          };
-        });
+        logsBuffer.push(ansiLog);
+
+        // If size of the logs buffer is exceeded, immediately flush the buffer
+        if (logsBuffer.length > LOGS_BUFFER_SIZE) {
+          flushLogsBuffer(containerName);
+        }
       },
       onclose: () => {
         console.log("Closed websocket:", websocketKey);
@@ -384,6 +418,8 @@ const useLogs = (
     const websocketKey = `${currentPodName.current}-${currentContainer}-websocket`;
     closeWebsocket(websocketKey);
 
+    // Flush and re-initialize empty buffer
+    flushLogsBuffer();
     setPrevLogs((prev) => ({ ...prev, [currentContainer]: [] }));
     setLogs((prev) => ({ ...prev, [currentContainer]: [] }));
 
@@ -420,6 +456,7 @@ const useLogs = (
 
     closeAllWebsockets();
 
+    flushLogsBuffer();
     setPrevLogs({});
     setLogs({});
 
@@ -445,6 +482,22 @@ const useLogs = (
     };
   }, []);
 
+  /**
+   * In some situations, we might never hit the limit for the max buffer size.
+   * An example is if the total logs for the pod < LOGS_BUFFER_SIZE.
+   *
+   * For handling situations like this, we would want to force a flush operation
+   * on the buffer so that we dont have any stale logs
+   */
+  useEffect(() => {
+    const flushLogsBufferInterval = setInterval(
+      () => flushLogsBuffer(currentContainer),
+      3000
+    );
+
+    return () => clearInterval(flushLogsBufferInterval);
+  }, [currentContainer]);
+
   const currentLogs = useMemo(() => {
     return logs[currentContainer] || [];
   }, [currentContainer, logs]);
@@ -456,7 +509,11 @@ const useLogs = (
   return {
     containers,
     currentContainer,
-    setCurrentContainer,
+    setCurrentContainer: (newContainer: string) => {
+      // First flush the logs of the older container
+      flushLogsBuffer(currentContainer);
+      setCurrentContainer(newContainer);
+    },
     logs: currentLogs,
     previousLogs: currentPreviousLogs,
     refresh,