Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

misc status footer refactors #3624

Merged
merged 6 commits into from
Sep 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions api/server/handlers/porter_app/latest_app_revisions.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
"github.com/porter-dev/porter/internal/telemetry"
)

// LatestAppRevisionsHandler handles requests to the /apps/{porter_app_name}/revisions endpoint
// LatestAppRevisionsHandler handles requests to the /apps/revisions endpoint
type LatestAppRevisionsHandler struct {
handlers.PorterHandlerReadWriter
}
Expand All @@ -31,7 +31,7 @@ func NewLatestAppRevisionsHandler(
}
}

// LatestAppRevisionsRequest represents the response from the /apps/{porter_app_name}/revisions endpoint
// LatestAppRevisionsRequest represents the response from the /apps/revisions endpoint
type LatestAppRevisionsRequest struct{}

// LatestRevisionWithSource is an app revision and its source porter app
Expand All @@ -40,7 +40,7 @@ type LatestRevisionWithSource struct {
Source types.PorterApp `json:"source"`
}

// LatestAppRevisionsResponse represents the response from the /apps/{porter_app_name}/revisions endpoint
// LatestAppRevisionsResponse represents the response from the /apps/revisions endpoint
type LatestAppRevisionsResponse struct {
AppRevisions []LatestRevisionWithSource `json:"app_revisions"`
}
Expand Down
16 changes: 13 additions & 3 deletions api/server/handlers/porter_app/pod_status.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package porter_app

import (
"fmt"
"net/http"

"connectrpc.com/connect"
Expand All @@ -10,6 +11,7 @@ import (
"github.com/porter-dev/porter/api/server/shared"
"github.com/porter-dev/porter/api/server/shared/apierrors"
"github.com/porter-dev/porter/api/server/shared/config"
"github.com/porter-dev/porter/api/server/shared/requestutils"
"github.com/porter-dev/porter/api/types"
"github.com/porter-dev/porter/internal/models"
"github.com/porter-dev/porter/internal/telemetry"
Expand Down Expand Up @@ -37,7 +39,7 @@ func NewPodStatusHandler(
// PodStatusRequest is the expected format for a request body on GET /apps/pods
type PodStatusRequest struct {
DeploymentTargetID string `schema:"deployment_target_id"`
Selectors string `schema:"selectors"`
ServiceName string `schema:"service"`
}

func (c *PodStatusHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
Expand All @@ -51,10 +53,17 @@ func (c *PodStatusHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}

appName, reqErr := requestutils.GetURLParamString(r, types.URLParamPorterAppName)
if reqErr != nil {
err := telemetry.Error(ctx, span, reqErr, "porter app name not found in request")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
return
}

cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
project, _ := r.Context().Value(types.ProjectScope).(*models.Project)

telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "selectors", Value: request.Selectors})
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "service-name", Value: request.ServiceName}, telemetry.AttributeKV{Key: "app-name", Value: appName})

if request.DeploymentTargetID == "" {
err := telemetry.Error(ctx, span, nil, "must provide deployment target id")
Expand Down Expand Up @@ -99,7 +108,8 @@ func (c *PodStatusHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

pods := []v1.Pod{}

podsList, err := agent.GetPodsByLabel(request.Selectors, namespace)
selectors := fmt.Sprintf("porter.run/service-name=%s,porter.run/deployment-target-id=%s,porter.run/app-name=%s", request.ServiceName, request.DeploymentTargetID, appName)
podsList, err := agent.GetPodsByLabel(selectors, namespace)
if err != nil {
err = telemetry.Error(ctx, span, err, "unable to get pods by label")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
Expand Down
4 changes: 2 additions & 2 deletions api/server/router/porter_app.go
Original file line number Diff line number Diff line change
Expand Up @@ -980,14 +980,14 @@ func getPorterAppRoutes(
Router: r,
})

// GET /api/projects/{project_id}/clusters/{cluster_id}/apps/pods -> cluster.NewPodStatusHandler
// GET /api/projects/{project_id}/clusters/{cluster_id}/apps/{porter_app_name}/pods -> cluster.NewPodStatusHandler
appPodStatusEndpoint := factory.NewAPIEndpoint(
&types.APIRequestMetadata{
Verb: types.APIVerbGet,
Method: types.HTTPVerbGet,
Path: &types.Path{
Parent: basePath,
RelativePath: fmt.Sprintf("%s/pods", relPathV2),
RelativePath: fmt.Sprintf("%s/{%s}/pods", relPathV2, types.URLParamPorterAppName),
},
Scopes: []types.PermissionScope{
types.UserScope,
Expand Down
18 changes: 9 additions & 9 deletions dashboard/src/lib/hooks/useAppStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import _ from "lodash";
import { useEffect, useMemo, useState } from "react";
import api from "shared/api";
import { NewWebsocketOptions, useWebsockets } from "shared/hooks/useWebsockets";
import { useRevisionIdToNumber } from "./useRevisionList";
import { useRevisionList } from "./useRevisionList";
import { valueExists } from "shared/util";

export type PorterAppVersionStatus = {
Expand Down Expand Up @@ -38,7 +38,7 @@ export const useAppStatus = (
) => {
const [servicePodMap, setServicePodMap] = useState<Record<string, ClientPod[]>>({});

const revisionIdToNumber = useRevisionIdToNumber(appName, deploymentTargetId);
const { revisionIdToNumber } = useRevisionList({ appName, deploymentTargetId, projectId, clusterId });

const {
newWebsocket,
Expand Down Expand Up @@ -76,18 +76,17 @@ export const useAppStatus = (
};

const updatePods = async (serviceName: string) => {
const selectors = `porter.run/service-name=${serviceName},porter.run/deployment-target-id=${deploymentTargetId}`;

try {
const res = await api.appPodStatus(
"<token>",
{
deployment_target_id: deploymentTargetId,
selectors,
service: serviceName,
},
{
id: projectId,
project_id: projectId,
cluster_id: clusterId,
app_name: appName,
}
);
// TODO: type the response
Expand Down Expand Up @@ -143,7 +142,7 @@ export const useAppStatus = (
setupWebsocket(serviceName);
}
return () => closeAllWebsockets();
}, [projectId, clusterId, deploymentTargetId, appName, JSON.stringify(revisionIdToNumber)]);
}, [projectId, clusterId, deploymentTargetId, appName]);

const processReplicaSetArray = (replicaSetArray: ClientPod[][]): PorterAppVersionStatus[] => {
return replicaSetArray.map((replicaSet, i) => {
Expand All @@ -161,7 +160,8 @@ export const useAppStatus = (
message = `${replicaSet.length} replica${replicaSet.length === 1 ? "" : "s"} ${replicaSet.length === 1 ? "is" : "are"
} failing to run Version ${version}`;
} else if (
i > 0 && replicaSetArray[i - 1].every(p => !p.isFailing)
// last check ensures that we don't say 'spinning down' unless there exists a version status above it
i > 0 && replicaSetArray[i - 1].every(p => !p.isFailing) && revisionIdToNumber[replicaSetArray[i - 1][0].revisionId] != null
) {
status = "spinningDown";
message = `${replicaSet.length} replica${replicaSet.length === 1 ? "" : "s"} ${replicaSet.length === 1 ? "is" : "are"
Expand Down Expand Up @@ -207,7 +207,7 @@ export const useAppStatus = (
}));

return serviceReplicaSetMap;
}, [JSON.stringify(servicePodMap)]);
}, [JSON.stringify(servicePodMap), JSON.stringify(revisionIdToNumber)]);

return {
serviceVersionStatus,
Expand Down
55 changes: 24 additions & 31 deletions dashboard/src/lib/hooks/useRevisionList.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,39 @@
import { useQuery } from "@tanstack/react-query";
import { useContext, useEffect, useState } from "react";
import { Context } from "shared/Context";
import { useEffect, useState } from "react";
import api from "shared/api";
import { z } from "zod";
import { AppRevision, appRevisionValidator } from "../revisions/types";
import { useLatestRevision } from "../../main/home/app-dashboard/app-view/LatestRevisionContext";

export function useRevisionList(appName: string, deploymentTargetId: string) {
const { currentProject, currentCluster } = useContext(Context);
const { latestRevision } = useLatestRevision();

import { useLatestRevision } from "main/home/app-dashboard/app-view/LatestRevisionContext";

export function useRevisionList({
appName,
deploymentTargetId,
projectId,
clusterId,
}: {
appName: string,
deploymentTargetId: string,
projectId: number,
clusterId: number
}): { revisionList: AppRevision[], revisionIdToNumber: Record<string, number> } {
const [
revisionList,
setRevisionList,
] = useState<AppRevision[]>([]);

if (currentProject == null || currentCluster == null) {
return [];
}
const [revisionIdToNumber, setRevisionIdToNumber] = useState<Record<string, number>>({});
const { latestRevision } = useLatestRevision();

const { data } = useQuery(
["listAppRevisions", currentProject.id, currentCluster.id, appName, deploymentTargetId, latestRevision],
["listAppRevisions", projectId, clusterId, appName, deploymentTargetId, latestRevision],
async () => {
const res = await api.listAppRevisions(
"<token>",
{
deployment_target_id: deploymentTargetId,
},
{
project_id: currentProject.id,
cluster_id: currentCluster.id,
project_id: projectId,
cluster_id: clusterId,
porter_app_name: appName,
}
);
Expand All @@ -39,32 +43,21 @@ export function useRevisionList(appName: string, deploymentTargetId: string) {
app_revisions: z.array(appRevisionValidator),
})
.parseAsync(res.data);

return revisions;
},
{
enabled: !!currentProject && !!currentCluster,
refetchInterval: 5000,
refetchOnWindowFocus: false,
}
);

useEffect(() => {
if (data) {
setRevisionList(data.app_revisions);
const revisionList = data.app_revisions
setRevisionList(revisionList);
setRevisionIdToNumber(Object.fromEntries(revisionList.map(r => ([r.id, r.revision_number]))))
}
}, [data]);

return revisionList;
}

export function useRevisionIdToNumber(appName: string, deploymentTargetId: string) {
const revisionList = useRevisionList(appName, deploymentTargetId);
const revisionIdToNumber: Record<string, number> = Object.fromEntries(revisionList.map(r => ([r.id, r.revision_number])))

return revisionIdToNumber;
}

export function useLatestRevisionNumber(appName: string, deploymentTargetId: string) {
const revisionList = useRevisionList(appName, deploymentTargetId);
return revisionList.map((revision) => revision.revision_number).reduce((a, b) => Math.max(a, b), 0)
return { revisionList, revisionIdToNumber };
}
4 changes: 2 additions & 2 deletions dashboard/src/main/home/app-dashboard/app-view/AppHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ const AppHeader: React.FC = () => {
[]
);

return domains.length === 1 ? prefixSubdomain(domains[0]) : "";
return domains.length === 1 ? domains[0] : "";
}, [latestProto]);

return (
Expand Down Expand Up @@ -125,7 +125,7 @@ const AppHeader: React.FC = () => {
<>
<Container>
<Text>
<a href={displayDomain} target="_blank">
<a href={prefixSubdomain(displayDomain)} target="_blank">
{displayDomain}
</a>
</Text>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,12 @@ export const LatestRevisionProvider = ({
app_revision: appRevisionValidator,
})
.parseAsync(res.data);

return revisionData.app_revision;
},
{
enabled: appParamsExist,
refetchInterval: 5000,
refetchOnWindowFocus: false,
}
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,24 @@ import { PorterAppDeployEvent } from "../types";
import AnimateHeight from "react-animate-height";
import ServiceStatusDetail from "./ServiceStatusDetail";
import { useLatestRevision } from "main/home/app-dashboard/app-view/LatestRevisionContext";
import { useRevisionIdToNumber } from "lib/hooks/useRevisionList";
import { useRevisionList } from "lib/hooks/useRevisionList";

type Props = {
event: PorterAppDeployEvent;
appName: string;
showServiceStatusDetail?: boolean;
deploymentTargetId: string;
projectId: number;
clusterId: number;
};

const DeployEventCard: React.FC<Props> = ({ event, appName, deploymentTargetId, showServiceStatusDetail = false }) => {
const DeployEventCard: React.FC<Props> = ({ event, appName, deploymentTargetId, projectId, clusterId, showServiceStatusDetail = false }) => {
const { latestRevision } = useLatestRevision();
const [diffModalVisible, setDiffModalVisible] = useState(false);
const [revertModalVisible, setRevertModalVisible] = useState(false);
const [serviceStatusVisible, setServiceStatusVisible] = useState(showServiceStatusDetail);

const revisionIdToNumber = useRevisionIdToNumber(appName, deploymentTargetId);
const { revisionIdToNumber } = useRevisionList({ appName, deploymentTargetId, projectId, clusterId });

const renderStatusText = () => {
switch (event.status) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const EventCard: React.FC<Props> = ({ event, deploymentTargetId, isLatestDeployE
return match(event)
.with({ type: "APP_EVENT" }, (ev) => <AppEventCard event={ev} deploymentTargetId={deploymentTargetId} projectId={projectId} clusterId={clusterId} appName={appName} />)
.with({ type: "BUILD" }, (ev) => <BuildEventCard event={ev} projectId={projectId} clusterId={clusterId} appName={appName} />)
.with({ type: "DEPLOY" }, (ev) => <DeployEventCard event={ev} appName={appName} showServiceStatusDetail={isLatestDeployEvent} deploymentTargetId={deploymentTargetId} appName={appName} />)
.with({ type: "DEPLOY" }, (ev) => <DeployEventCard event={ev} appName={appName} showServiceStatusDetail={isLatestDeployEvent} deploymentTargetId={deploymentTargetId} projectId={projectId} clusterId={clusterId} />)
.with({ type: "PRE_DEPLOY" }, (ev) => <PreDeployEventCard event={ev} appName={appName} projectId={projectId} clusterId={clusterId} />)
.exhaustive();
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ import Container from "components/porter/Container";
import Button from "components/porter/Button";
import LogFilterContainer from "../../expanded-app/logs/LogFilterContainer";
import StyledLogs from "../../expanded-app/logs/StyledLogs";
import { useLatestRevisionNumber, useRevisionIdToNumber } from "lib/hooks/useRevisionList";
import { useRevisionList } from "lib/hooks/useRevisionList";
import { useLocation } from "react-router";
import { useLatestRevision } from "../../app-view/LatestRevisionContext";

type Props = {
projectId: number;
Expand Down Expand Up @@ -81,8 +82,8 @@ const Logs: React.FC<Props> = ({
output_stream: logQueryParamOpts.output_stream ?? GenericLogFilter.getDefaultOption("output_stream").value,
});

const revisionIdToNumber = useRevisionIdToNumber(appName, deploymentTargetId)
const latestRevisionNumber = useLatestRevisionNumber(appName, deploymentTargetId)
const { revisionIdToNumber } = useRevisionList({ appName, deploymentTargetId, projectId, clusterId });
const { latestRevision: { revision_number: latestRevisionNumber } } = useLatestRevision();

const isAgentVersionUpdated = (agentImage: string | undefined) => {
if (agentImage == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ export const useLogs = ({
setDate,
JSON.stringify(selectedFilterValues),
JSON.stringify(timeRange?.endTime),
filterPredeploy
filterPredeploy,
]);

useEffect(() => {
Expand Down
8 changes: 4 additions & 4 deletions dashboard/src/shared/api.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -320,11 +320,11 @@ const appJobs = baseApi<
const appPodStatus = baseApi<
{
deployment_target_id: string;
selectors: string;
service: string;
},
{ id: number; cluster_id: number }
>("GET", (pathParams) => {
return `/api/projects/${pathParams.id}/clusters/${pathParams.cluster_id}/apps/pods`;
{ project_id: number; cluster_id: number, app_name: string }
>("GET", ({ project_id, cluster_id, app_name }) => {
return `/api/projects/${project_id}/clusters/${cluster_id}/apps/${app_name}/pods`;
});

const getFeedEvents = baseApi<
Expand Down
Loading