فهرست منبع

add support for setting init log data and revision label queries

Alexander Belanger 3 سال پیش
والد
کامیت
85b3b4bea7

+ 64 - 0
api/server/handlers/cluster/get_logs_revision_values.go

@@ -0,0 +1,64 @@
+package cluster
+
+import (
+	"net/http"
+
+	"github.com/porter-dev/porter/api/server/authz"
+	"github.com/porter-dev/porter/api/server/handlers"
+	"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/types"
+	porter_agent "github.com/porter-dev/porter/internal/kubernetes/porter_agent/v2"
+	"github.com/porter-dev/porter/internal/models"
+)
+
+type GetLogRevisionValuesHandler struct {
+	handlers.PorterHandlerReadWriter
+	authz.KubernetesAgentGetter
+}
+
+func NewGetLogRevisionValuesHandler(
+	config *config.Config,
+	decoderValidator shared.RequestDecoderValidator,
+	writer shared.ResultWriter,
+) *GetLogRevisionValuesHandler {
+	return &GetLogRevisionValuesHandler{
+		PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
+		KubernetesAgentGetter:   authz.NewOutOfClusterAgentGetter(config),
+	}
+}
+
+func (c *GetLogRevisionValuesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
+
+	request := &types.GetRevisionValuesRequest{}
+
+	if ok := c.DecodeAndValidate(w, r, request); !ok {
+		return
+	}
+
+	agent, err := c.GetAgent(r, cluster, "")
+
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	// get agent service
+	agentSvc, err := porter_agent.GetAgentService(agent.Clientset)
+
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	revisions, err := porter_agent.GetRevisionValues(agent.Clientset, agentSvc, request)
+
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	c.WriteResult(w, r, revisions)
+}

+ 29 - 0
api/server/router/cluster.go

@@ -1149,6 +1149,35 @@ func getClusterRoutes(
 		Router:   r,
 	})
 
+	// GET /api/projects/{project_id}/clusters/{cluster_id}/logs/revision_values -> cluster.NewGetLogPodValuesHandler
+	getLogRevisionValuesEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbGet,
+			Method: types.HTTPVerbGet,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: fmt.Sprintf("%s/logs/revision_values", relPath),
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.ClusterScope,
+			},
+		},
+	)
+
+	getLogRevisionValuesHandler := cluster.NewGetLogRevisionValuesHandler(
+		config,
+		factory.GetDecoderValidator(),
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &router.Route{
+		Endpoint: getLogRevisionValuesEndpoint,
+		Handler:  getLogRevisionValuesHandler,
+		Router:   r,
+	})
+
 	// GET /api/projects/{project_id}/clusters/{cluster_id}/events -> cluster.NewGetEventsHandler
 	getEventsEndpoint := factory.NewAPIEndpoint(
 		&types.APIRequestMetadata{

+ 6 - 0
api/types/incident.go

@@ -111,6 +111,12 @@ type GetPodValuesRequest struct {
 	MatchPrefix string     `schema:"match_prefix"`
 }
 
+type GetRevisionValuesRequest struct {
+	StartRange  *time.Time `schema:"start_range"`
+	EndRange    *time.Time `schema:"end_range"`
+	MatchPrefix string     `schema:"match_prefix"`
+}
+
 type LogLine struct {
 	Timestamp *time.Time `json:"timestamp"`
 	Line      string     `json:"line"`

+ 4 - 2
dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedChart.tsx

@@ -14,7 +14,7 @@ import RevisionSection from "./RevisionSection";
 import ValuesYaml from "./ValuesYaml";
 import GraphSection from "./GraphSection";
 import MetricsSection from "./metrics/MetricsSection";
-import LogsSection from "./logs-section/LogsSection";
+import LogsSection, { InitLogData } from "./logs-section/LogsSection";
 import ListSection from "./ListSection";
 import StatusSection from "./status/StatusSection";
 import SettingsSection from "./SettingsSection";
@@ -76,6 +76,7 @@ const ExpandedChart: React.FC<Props> = (props) => {
   const [isAuthorized] = useAuth();
   const [fullScreenLogs, setFullScreenLogs] = useState<boolean>(false);
   const [isFullscreen, setIsFullscreen] = useState<boolean>(false);
+  const [logData, setLogData] = useState<InitLogData>();
 
   const {
     isStack,
@@ -423,6 +424,7 @@ const ExpandedChart: React.FC<Props> = (props) => {
             currentChart={chart}
             isFullscreen={isFullscreen}
             setIsFullscreen={setIsFullscreen}
+            initData={logData}
           />
         );
       case "metrics":
@@ -431,7 +433,7 @@ const ExpandedChart: React.FC<Props> = (props) => {
         if (DisabledNamespacesForIncidents.includes(currentChart.namespace)) {
           return null;
         }
-        return <EventsTab currentChart={chart} />;
+        return <EventsTab currentChart={chart} setLogData={setLogData} />;
       case "status":
         if (isLoadingChartData) {
           return (

+ 10 - 2
dashboard/src/main/home/cluster-dashboard/expanded-chart/events/EventList.tsx

@@ -14,14 +14,16 @@ import api from "shared/api";
 import Modal from "main/home/modals/Modal";
 import time from "assets/time.svg";
 import { Context } from "shared/Context";
+import { InitLogData } from "../logs-section/LogsSection";
 
 const iconDict: any = {};
 
 type Props = {
   filters: any;
+  setLogData?: (logData: InitLogData) => void;
 };
 
-const EventList: React.FC<Props> = ({ filters }) => {
+const EventList: React.FC<Props> = ({ filters, setLogData }) => {
   const { currentProject, currentCluster } = useContext(Context);
   const [events, setEvents] = useState([]);
   const [expandedEvent, setExpandedEvent] = useState(null);
@@ -133,7 +135,13 @@ const EventList: React.FC<Props> = ({ filters }) => {
             width: 30,
             Cell: ({ row }: CellProps<any>) => {
               return (
-                <TableButton width="102px">
+                <TableButton
+                  width="102px"
+                  onClick={() => {
+                    // setLogData()
+                    // TODO: POD NAME AND TIMESTAMP
+                  }}
+                >
                   <Icon src={document} />
                   View logs
                 </TableButton>

+ 4 - 1
dashboard/src/main/home/cluster-dashboard/expanded-chart/events/EventsTab.tsx

@@ -6,12 +6,14 @@ import Loading from "components/Loading";
 import InfiniteScroll from "react-infinite-scroll-component";
 import { Context } from "shared/Context";
 import Dropdown from "components/Dropdown";
+import { InitLogData } from "../logs-section/LogsSection";
 
 type Props = {
   currentChart: any;
+  setLogData?: (logData: InitLogData) => void;
 };
 
-const EventsTab: React.FC<Props> = ({ currentChart }) => {
+const EventsTab: React.FC<Props> = ({ currentChart, setLogData }) => {
   const [hasPorterAgent, setHasPorterAgent] = useState(true);
   const { currentProject, currentCluster } = useContext(Context);
   const [isLoading, setIsLoading] = useState(true);
@@ -78,6 +80,7 @@ const EventsTab: React.FC<Props> = ({ currentChart }) => {
   return (
     <EventsPageWrapper>
       <EventList
+        setLogData={setLogData}
         filters={{
           release_name: currentChart.name,
           release_namespace: currentChart.namespace,

+ 14 - 3
dashboard/src/main/home/cluster-dashboard/expanded-chart/logs-section/LogsSection.tsx

@@ -13,10 +13,16 @@ import { flatMap } from "lodash";
 import time from "assets/time.svg";
 import DateTimePicker from "components/date-time-picker/DateTimePicker";
 
+export type InitLogData = {
+  podName: string;
+  timestamp: string;
+};
+
 type Props = {
   currentChart?: any;
   isFullscreen: boolean;
   setIsFullscreen: (x: boolean) => void;
+  initData?: InitLogData;
 };
 
 const escapeRegExp = (str: string) => {
@@ -27,14 +33,19 @@ const LogsSection: React.FC<Props> = ({
   currentChart,
   isFullscreen,
   setIsFullscreen,
+  initData,
 }) => {
   const { currentProject, currentCluster } = useContext(Context);
-  const [podFilter, setPodFilter] = useState();
-  const [podFilterOpts, setPodFilterOpts] = useState<string[]>();
+  const [podFilter, setPodFilter] = useState(initData?.podName);
+  const [podFilterOpts, setPodFilterOpts] = useState<string[]>(
+    initData?.podName ? [initData?.podName] : []
+  );
   const [scrollToBottom, setScrollToBottom] = useState(true);
   const [searchText, setSearchText] = useState("");
   const [enteredSearchText, setEnteredSearchText] = useState("");
-  const [selectedDate, setSelectedDate] = useState<Date>(null);
+  const [selectedDate, setSelectedDate] = useState<Date>(
+    initData ? new Date(initData?.timestamp) : null
+  );
 
   const { logs, refresh, moveCursor } = useLogs(
     podFilter,

+ 52 - 0
internal/kubernetes/porter_agent/v2/agent_server.go

@@ -256,6 +256,58 @@ func GetPodValues(
 	return valsResp, nil
 }
 
+func GetRevisionValues(
+	clientset kubernetes.Interface,
+	service *v1.Service,
+	req *types.GetRevisionValuesRequest,
+) ([]string, error) {
+	vals := make(map[string]string)
+
+	if req.StartRange != nil {
+		startVal, err := req.StartRange.MarshalText()
+
+		if err != nil {
+			return nil, err
+		}
+
+		vals["start_range"] = string(startVal)
+	}
+
+	if req.EndRange != nil {
+		endVal, err := req.EndRange.MarshalText()
+
+		if err != nil {
+			return nil, err
+		}
+
+		vals["end_range"] = string(endVal)
+	}
+
+	vals["match_prefix"] = req.MatchPrefix
+
+	resp := clientset.CoreV1().Services(service.Namespace).ProxyGet(
+		"http",
+		service.Name,
+		fmt.Sprintf("%d", service.Spec.Ports[0].Port),
+		"/logs/revision_values",
+		vals,
+	)
+
+	rawQuery, err := resp.DoRaw(context.Background())
+	if err != nil {
+		return nil, err
+	}
+
+	valsResp := make([]string, 0)
+
+	err = json.Unmarshal(rawQuery, &valsResp)
+	if err != nil {
+		return nil, err
+	}
+
+	return valsResp, nil
+}
+
 func GetHistoricalEvents(
 	clientset kubernetes.Interface,
 	service *v1.Service,