فهرست منبع

[towards POR-1668] Fix pre-deploy logs in activity feed (#3564)

Feroze Mohideen 2 سال پیش
والد
کامیت
0f2df24f99

+ 9 - 3
api/server/handlers/porter_app/logs_apply_v2.go

@@ -48,12 +48,14 @@ type AppLogsRequest struct {
 	EndRange           time.Time `schema:"end_range,omitempty"`
 	SearchParam        string    `schema:"search_param"`
 	Direction          string    `schema:"direction"`
+	AppRevisionID      string    `schema:"app_revision_id"`
 }
 
 const (
-	lokiLabel_PorterAppName     = "porter_run_app_name"
-	lokiLabel_PorterServiceName = "porter_run_service_name"
-	lokiLabel_Namespace         = "namespace"
+	lokiLabel_PorterAppName       = "porter_run_app_name"
+	lokiLabel_PorterServiceName   = "porter_run_service_name"
+	lokiLabel_PorterAppRevisionID = "porter_run_app_revision_id"
+	lokiLabel_Namespace           = "namespace"
 )
 
 // ServeHTTP gets logs for a given app, service, and deployment target
@@ -152,6 +154,10 @@ func (c *AppLogsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		matchLabels[lokiLabel_PorterServiceName] = request.ServiceName
 	}
 
+	if request.AppRevisionID != "" {
+		matchLabels[lokiLabel_PorterAppRevisionID] = request.AppRevisionID
+	}
+
 	logRequest := &types.LogRequest{
 		Limit:       request.Limit,
 		StartRange:  &request.StartRange,

+ 4 - 0
api/server/handlers/porter_app/stream_logs.go

@@ -133,6 +133,10 @@ func (c *StreamLogsLokiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request
 		labels = append(labels, fmt.Sprintf("%s=%s", lokiLabel_PorterServiceName, request.ServiceName))
 	}
 
+	if request.AppRevisionID != "" {
+		labels = append(labels, fmt.Sprintf("%s=%s", lokiLabel_PorterAppRevisionID, request.AppRevisionID))
+	}
+
 	telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "labels", Value: strings.Join(labels, ",")})
 
 	err = agent.StreamPorterAgentLokiLog(labels, string(startTime), request.SearchParam, 0, safeRW)

+ 2 - 1
dashboard/src/lib/porter-apps/services.ts

@@ -285,7 +285,8 @@ export function deserializeService({
             health: config.healthCheck,
             override: overrideWebConfig?.healthCheck,
           }),
-          domains: [...config.domains, ...(overrideWebConfig?.domains ?? [])].map((domain) => ({
+
+          domains: Array.from(new Set([...config.domains, ...(overrideWebConfig?.domains ?? [])])).map((domain) => ({
             name: ServiceField.string(
               domain.name,
               overrideWebConfig?.domains.find(

+ 1 - 2
dashboard/src/main/home/app-dashboard/app-view/tabs/LogsTab.tsx

@@ -3,7 +3,7 @@ import Logs from "../../validate-apply/logs/Logs"
 import { useLatestRevision } from "../LatestRevisionContext";
 
 const LogsTab: React.FC = () => {
-    const { projectId, clusterId, latestProto, deploymentTargetId, latestRevision } = useLatestRevision();
+    const { projectId, clusterId, latestProto, deploymentTargetId } = useLatestRevision();
 
     const appName = latestProto.name
     const serviceNames = Object.keys(latestProto.services)
@@ -15,7 +15,6 @@ const LogsTab: React.FC = () => {
             appName={appName}
             serviceNames={serviceNames}
             deploymentTargetId={deploymentTargetId}
-            latestRevision={latestRevision}
         />
     );
 };

+ 1 - 1
dashboard/src/main/home/app-dashboard/app-view/tabs/activity-feed/events/cards/PreDeployEventCard.tsx

@@ -63,7 +63,7 @@ const PreDeployEventCard: React.FC<Props> = ({ event, appName, projectId, cluste
             <>
               <Spacer inline x={1} />
               <Wrapper>
-                <Link to={`/apps/${appName}/events?event_id=${event.id}`} hasunderline>
+                <Link to={`/apps/${appName}/events?event_id=${event.id}&service=${appName}-predeploy`} hasunderline>
                   <Container row>
                     <Icon src={document} height="10px" />
                     <Spacer inline width="5px" />

+ 6 - 5
dashboard/src/main/home/app-dashboard/app-view/tabs/activity-feed/events/focus-views/PredeployEventFocusView.tsx

@@ -3,7 +3,7 @@ import React from "react";
 import dayjs from "dayjs";
 import Text from "components/porter/Text";
 import { readableDate } from "shared/string_utils";
-import { getDuration } from "../utils";
+import { getDuration, getStatusColor } from "../utils";
 import { AppearingView } from "./EventFocusView";
 import Icon from "components/porter/Icon";
 import loading from "assets/loading.gif";
@@ -27,15 +27,15 @@ const PreDeployEventFocusView: React.FC<Props> = ({
   const renderHeaderText = () => {
     switch (event.status) {
       case "SUCCESS":
-        return <Text color="#68BF8B" size={16}>Pre-deploy succeeded</Text>;
+        return <Text color={getStatusColor(event.status)} size={16}>Pre-deploy succeeded</Text>;
       case "FAILED":
-        return <Text color="#FF6060" size={16}>Pre-deploy failed</Text>;
+        return <Text color={getStatusColor(event.status)} size={16}>Pre-deploy failed</Text>;
       default:
         return (
           <Container row>
             <Icon height="16px" src={loading} />
             <Spacer inline width="10px" />
-            <Text size={16}>Pre-deploy in progress...</Text>
+            <Text size={16} color={getStatusColor(event.status)}>Pre-deploy in progress...</Text>
           </Container>
         );
     }
@@ -64,7 +64,8 @@ const PreDeployEventFocusView: React.FC<Props> = ({
         appName={appName}
         serviceNames={serviceNames}
         deploymentTargetId={deploymentTargetId}
-        latestRevision={latestRevision}
+        appRevisionId={event.metadata.app_revision_id}
+        logFilterNames={["service_name"]}
       />
     </>
   );

+ 1 - 0
dashboard/src/main/home/app-dashboard/app-view/tabs/activity-feed/events/types.ts

@@ -30,6 +30,7 @@ const porterAppBuildEventMetadataValidator = z.object({
 const porterAppPreDeployEventMetadataValidator = z.object({
     start_time: z.string(),
     end_time: z.string().optional(),
+    app_revision_id: z.string(),
 });
 export const porterAppEventValidator = z.discriminatedUnion("type", [
     z.object({

+ 15 - 10
dashboard/src/main/home/app-dashboard/validate-apply/logs/Logs.tsx

@@ -28,6 +28,7 @@ import StyledLogs from "../../expanded-app/logs/StyledLogs";
 import { AppRevision } from "lib/revisions/types";
 import { useLatestRevisionNumber, useRevisionIdToNumber } from "lib/hooks/useRevisionList";
 import { useLocation } from "react-router";
+import { valueExists } from "shared/util";
 
 type Props = {
     projectId: number;
@@ -35,7 +36,8 @@ type Props = {
     appName: string;
     serviceNames: string[];
     deploymentTargetId: string;
-    latestRevision: AppRevision;
+    appRevisionId?: string;
+    logFilterNames?: LogFilterName[];
 };
 
 const Logs: React.FC<Props> = ({
@@ -44,7 +46,8 @@ const Logs: React.FC<Props> = ({
     appName,
     serviceNames,
     deploymentTargetId,
-    latestRevision,
+    appRevisionId,
+    logFilterNames = ["service_name", "revision", "output_stream"],
 }) => {
     const { search } = useLocation();
     const queryParams = new URLSearchParams(search);
@@ -131,7 +134,7 @@ const Logs: React.FC<Props> = ({
                     service_name: value,
                 }));
             }
-        },
+        } as GenericLogFilter,
         {
             name: "revision",
             displayName: "Version",
@@ -143,7 +146,7 @@ const Logs: React.FC<Props> = ({
                     revision: value,
                 }));
             }
-        },
+        } as GenericLogFilter,
         {
             name: "output_stream",
             displayName: "Output Stream",
@@ -158,8 +161,9 @@ const Logs: React.FC<Props> = ({
                     output_stream: value,
                 }));
             }
-        },
-    ]);
+        } as GenericLogFilter,
+    ].filter((f: GenericLogFilter) => logFilterNames.includes(f.name)));
+
 
     const notify = (message: string) => {
         setNotification(message);
@@ -181,6 +185,7 @@ const Logs: React.FC<Props> = ({
         setIsLoading,
         revisionIdToNumber,
         selectedDate,
+        appRevisionId,
     );
 
     useEffect(() => {
@@ -198,7 +203,7 @@ const Logs: React.FC<Props> = ({
                         service_name: value,
                     }));
                 }
-            },
+            } as GenericLogFilter,
             {
                 name: "revision",
                 displayName: "Version",
@@ -210,7 +215,7 @@ const Logs: React.FC<Props> = ({
                         revision: value,
                     }));
                 }
-            },
+            } as GenericLogFilter,
             {
                 name: "output_stream",
                 displayName: "Output Stream",
@@ -225,8 +230,8 @@ const Logs: React.FC<Props> = ({
                         output_stream: value,
                     }));
                 }
-            },
-        ])
+            } as GenericLogFilter,
+        ].filter((f: GenericLogFilter) => logFilterNames.includes(f.name)))
     }, [latestRevisionNumber]);
 
     useEffect(() => {

+ 22 - 18
dashboard/src/main/home/app-dashboard/validate-apply/logs/utils.ts

@@ -42,8 +42,8 @@ export const parseLogs = (logs: any[] = []): PorterLog[] => {
 };
 
 export const useLogs = (
-    projectID: number,
-    clusterID: number,
+  projectID: number,
+  clusterID: number,
   selectedFilterValues: Record<LogFilterName, string>,
   appName: string,
   serviceName: string,
@@ -52,8 +52,9 @@ export const useLogs = (
   notify: (message: string) => void,
   setLoading: (isLoading: boolean) => void,
   revisionIdToNumber: Record<string, number>,
-    // if setDate is set, results are not live
+  // if setDate is set, results are not live
   setDate?: Date,
+  appRevisionId: string = "",
   timeRange?: {
     startTime?: Dayjs,
     endTime?: Dayjs,
@@ -166,6 +167,7 @@ export const useLogs = (
       service_name: serviceName,
       deployment_target_id: deploymentTargetId,
       search_param: searchParam,
+      app_revision_id: appRevisionId,
     }
 
     const q = new URLSearchParams(searchParams).toString();
@@ -212,12 +214,12 @@ export const useLogs = (
       }
 
       if (selectedFilterValues.output_stream !== GenericLogFilter.getDefaultOption("output_stream").value &&
-          log.metadata.output_stream !== selectedFilterValues.output_stream) {
+        log.metadata.output_stream !== selectedFilterValues.output_stream) {
         return false;
       }
 
       if (selectedFilterValues.revision !== GenericLogFilter.getDefaultOption("revision").value &&
-          log.metadata.revision !== selectedFilterValues.revision) {
+        log.metadata.revision !== selectedFilterValues.revision) {
         return false;
       }
 
@@ -245,15 +247,16 @@ export const useLogs = (
         end_range: endDate,
         limit,
         direction,
+        app_revision_id: appRevisionId,
       };
 
       const logsResp = await api.appLogs(
-          "<token>",
-          getLogsReq,
-          {
-            cluster_id: clusterID,
-            project_id: projectID,
-          }
+        "<token>",
+        getLogsReq,
+        {
+          cluster_id: clusterID,
+          project_id: projectID,
+        }
       )
 
       if (logsResp.data == null) {
@@ -271,15 +274,16 @@ export const useLogs = (
 
       newLogs.filter((log) => {
         return log.metadata?.raw_labels?.porter_run_app_revision_id != null
-            && revisionIdToNumber[log.metadata.raw_labels.porter_run_app_revision_id] != null
-            && revisionIdToNumber[log.metadata.raw_labels.porter_run_app_revision_id] != 0
+          && revisionIdToNumber[log.metadata.raw_labels.porter_run_app_revision_id] != null
+          && revisionIdToNumber[log.metadata.raw_labels.porter_run_app_revision_id] != 0
       }).forEach((log) => {
         if (log.metadata?.raw_labels?.porter_run_app_revision_id != null) {
-            const revisionNumber = revisionIdToNumber[log.metadata.raw_labels.porter_run_app_revision_id];
-            if (revisionNumber != null && revisionNumber != 0) {
-              log.metadata.revision = revisionNumber.toString();
-            }
-      }})
+          const revisionNumber = revisionIdToNumber[log.metadata.raw_labels.porter_run_app_revision_id];
+          if (revisionNumber != null && revisionNumber != 0) {
+            log.metadata.revision = revisionNumber.toString();
+          }
+        }
+      })
 
       return {
         logs: newLogs,

+ 70 - 82
dashboard/src/shared/api.tsx

@@ -279,24 +279,25 @@ const getLogsWithinTimeRange = baseApi<
 );
 
 const appLogs = baseApi<
-    {
-        app_name: string;
-        service_name: string;
-        deployment_target_id: string;
-        limit: number;
-        start_range: string;
-        end_range: string;
-        search_param?: string;
-        direction?: string;
-    },
-    {
-        project_id: number;
-        cluster_id: number;
-    }
+  {
+    app_name: string;
+    service_name: string;
+    deployment_target_id: string;
+    limit: number;
+    start_range: string;
+    end_range: string;
+    search_param?: string;
+    direction?: string;
+    app_revision_id?: string;
+  },
+  {
+    project_id: number;
+    cluster_id: number;
+  }
 >(
-    "GET",
-    ({ project_id, cluster_id }) =>
-        `/api/projects/${project_id}/clusters/${cluster_id}/apps/logs`
+  "GET",
+  ({ project_id, cluster_id }) =>
+    `/api/projects/${project_id}/clusters/${cluster_id}/apps/logs`
 );
 
 const getFeedEvents = baseApi<
@@ -309,9 +310,8 @@ const getFeedEvents = baseApi<
   }
 >("GET", (pathParams) => {
   let { project_id, cluster_id, stack_name, page } = pathParams;
-  return `/api/projects/${project_id}/clusters/${cluster_id}/applications/${stack_name}/events?page=${
-    page || 1
-  }`;
+  return `/api/projects/${project_id}/clusters/${cluster_id}/applications/${stack_name}/events?page=${page || 1
+    }`;
 });
 
 const createEnvironment = baseApi<
@@ -736,11 +736,9 @@ const detectBuildpack = baseApi<
     branch: string;
   }
 >("GET", (pathParams) => {
-  return `/api/projects/${pathParams.project_id}/gitrepos/${
-    pathParams.git_repo_id
-  }/repos/${pathParams.kind}/${pathParams.owner}/${
-    pathParams.name
-  }/${encodeURIComponent(pathParams.branch)}/buildpack/detect`;
+  return `/api/projects/${pathParams.project_id}/gitrepos/${pathParams.git_repo_id
+    }/repos/${pathParams.kind}/${pathParams.owner}/${pathParams.name
+    }/${encodeURIComponent(pathParams.branch)}/buildpack/detect`;
 });
 
 const detectGitlabBuildpack = baseApi<
@@ -771,11 +769,9 @@ const getBranchContents = baseApi<
     branch: string;
   }
 >("GET", (pathParams) => {
-  return `/api/projects/${pathParams.project_id}/gitrepos/${
-    pathParams.git_repo_id
-  }/repos/${pathParams.kind}/${pathParams.owner}/${
-    pathParams.name
-  }/${encodeURIComponent(pathParams.branch)}/contents`;
+  return `/api/projects/${pathParams.project_id}/gitrepos/${pathParams.git_repo_id
+    }/repos/${pathParams.kind}/${pathParams.owner}/${pathParams.name
+    }/${encodeURIComponent(pathParams.branch)}/contents`;
 });
 
 const getProcfileContents = baseApi<
@@ -791,11 +787,9 @@ const getProcfileContents = baseApi<
     branch: string;
   }
 >("GET", (pathParams) => {
-  return `/api/projects/${pathParams.project_id}/gitrepos/${
-    pathParams.git_repo_id
-  }/repos/${pathParams.kind}/${pathParams.owner}/${
-    pathParams.name
-  }/${encodeURIComponent(pathParams.branch)}/procfile`;
+  return `/api/projects/${pathParams.project_id}/gitrepos/${pathParams.git_repo_id
+    }/repos/${pathParams.kind}/${pathParams.owner}/${pathParams.name
+    }/${encodeURIComponent(pathParams.branch)}/procfile`;
 });
 
 const getPorterYamlContents = baseApi<
@@ -811,11 +805,9 @@ const getPorterYamlContents = baseApi<
     branch: string;
   }
 >("GET", (pathParams) => {
-  return `/api/projects/${pathParams.project_id}/gitrepos/${
-    pathParams.git_repo_id
-  }/repos/${pathParams.kind}/${pathParams.owner}/${
-    pathParams.name
-  }/${encodeURIComponent(pathParams.branch)}/porteryaml`;
+  return `/api/projects/${pathParams.project_id}/gitrepos/${pathParams.git_repo_id
+    }/repos/${pathParams.kind}/${pathParams.owner}/${pathParams.name
+    }/${encodeURIComponent(pathParams.branch)}/porteryaml`;
 });
 
 const parsePorterYaml = baseApi<
@@ -851,11 +843,9 @@ const getBranchHead = baseApi<
     branch: string;
   }
 >("GET", (pathParams) => {
-  return `/api/projects/${pathParams.project_id}/gitrepos/${
-    pathParams.git_repo_id
-  }/repos/${pathParams.kind}/${pathParams.owner}/${
-    pathParams.name
-  }/${encodeURIComponent(pathParams.branch)}/head`;
+  return `/api/projects/${pathParams.project_id}/gitrepos/${pathParams.git_repo_id
+    }/repos/${pathParams.kind}/${pathParams.owner}/${pathParams.name
+    }/${encodeURIComponent(pathParams.branch)}/head`;
 });
 
 const validatePorterApp = baseApi<
@@ -878,21 +868,21 @@ const validatePorterApp = baseApi<
 
 const createApp = baseApi<
   | {
-      name: string;
-      type: "github";
-      git_repo_id: number;
-      git_branch: string;
-      git_repo_name: string;
-      porter_yaml_path: string;
-    }
+    name: string;
+    type: "github";
+    git_repo_id: number;
+    git_branch: string;
+    git_repo_name: string;
+    porter_yaml_path: string;
+  }
   | {
-      name: string;
-      type: "docker-registry";
-      image: {
-        repository: string;
-        tag: string;
-      };
-    },
+    name: string;
+    type: "docker-registry";
+    image: {
+      repository: string;
+      tag: string;
+    };
+  },
   {
     project_id: number;
     cluster_id: number;
@@ -1485,24 +1475,24 @@ const getMetrics = baseApi<
 });
 
 const appMetrics = baseApi<
-    {
-        metric: string;
-        shouldsum: boolean;
-        pods?: string[];
-        kind?: string; // the controller kind
-        name?: string;
-        percentile?: number;
-        deployment_target_id: string;
-        startrange: number;
-        endrange: number;
-        resolution: string;
-    },
-    {
-        id: number;
-        cluster_id: number;
-    }
+  {
+    metric: string;
+    shouldsum: boolean;
+    pods?: string[];
+    kind?: string; // the controller kind
+    name?: string;
+    percentile?: number;
+    deployment_target_id: string;
+    startrange: number;
+    endrange: number;
+    resolution: string;
+  },
+  {
+    id: number;
+    cluster_id: number;
+  }
 >("GET", (pathParams) => {
-    return `/api/projects/${pathParams.id}/clusters/${pathParams.cluster_id}/apps/metrics`;
+  return `/api/projects/${pathParams.id}/clusters/${pathParams.cluster_id}/apps/metrics`;
 });
 
 const getNamespaces = baseApi<
@@ -1836,11 +1826,9 @@ const getEnvGroup = baseApi<
     version?: number;
   }
 >("GET", (pathParams) => {
-  return `/api/projects/${pathParams.id}/clusters/${
-    pathParams.cluster_id
-  }/namespaces/${pathParams.namespace}/envgroup?name=${pathParams.name}${
-    pathParams.version ? "&version=" + pathParams.version : ""
-  }`;
+  return `/api/projects/${pathParams.id}/clusters/${pathParams.cluster_id
+    }/namespaces/${pathParams.namespace}/envgroup?name=${pathParams.name}${pathParams.version ? "&version=" + pathParams.version : ""
+    }`;
 });
 
 const getConfigMap = baseApi<
@@ -2897,7 +2885,7 @@ const removeStackEnvGroup = baseApi<
     `/api/v1/projects/${project_id}/clusters/${cluster_id}/namespaces/${namespace}/stacks/${stack_id}/remove_env_group/${env_group_name}`
 );
 
-const getGithubStatus = baseApi<{}, {}>("GET", ({}) => `/api/status/github`);
+const getGithubStatus = baseApi<{}, {}>("GET", ({ }) => `/api/status/github`);
 
 const createSecretAndOpenGitHubPullRequest = baseApi<
   {