diff --git a/.github/workflows/acceptance_tests.yml b/.github/workflows/acceptance_tests.yml index fe3a66279..41ab1136f 100644 --- a/.github/workflows/acceptance_tests.yml +++ b/.github/workflows/acceptance_tests.yml @@ -43,7 +43,7 @@ jobs: with: status: ${{ job.status }} notification_title: 'Mizu {workflow} has {status_message}' - message_format: '{emoji} *{workflow}* {status_message} during <{run_url}|run>, after commit <{commit_url}|{commit_sha} ${{ github.event.head_commit.message }}> ${{ github.event.head_commit.committer.name }} <${{ github.event.head_commit.committer.email }}>' + message_format: '{emoji} *{workflow}* {status_message} during <{run_url}|run>, after commit <{commit_url}|{commit_sha}> by ${{ github.event.head_commit.committer.name }} <${{ github.event.head_commit.committer.email }}> ```${{ github.event.head_commit.message }}```' footer: 'Linked Repo <{repo_url}|{repo}>' notify_when: 'failure' env: diff --git a/agent/pkg/controllers/service_map_controller_test.go b/agent/pkg/controllers/service_map_controller_test.go index c87bdbb96..b990530e9 100644 --- a/agent/pkg/controllers/service_map_controller_test.go +++ b/agent/pkg/controllers/service_map_controller_test.go @@ -101,16 +101,18 @@ func (s *ServiceMapControllerSuite) TestGet() { // response nodes aNode := servicemap.ServiceMapNode{ - Id: 1, - Name: TCPEntryA.Name, - Entry: TCPEntryA, - Count: 1, + Id: 1, + Name: TCPEntryA.Name, + Entry: TCPEntryA, + Resolved: true, + Count: 1, } bNode := servicemap.ServiceMapNode{ - Id: 2, - Name: TCPEntryB.Name, - Entry: TCPEntryB, - Count: 1, + Id: 2, + Name: TCPEntryB.Name, + Entry: TCPEntryB, + Resolved: true, + Count: 1, } assert.Contains(response.Nodes, aNode) assert.Contains(response.Nodes, bNode) diff --git a/agent/pkg/servicemap/models.go b/agent/pkg/servicemap/models.go index 9550c70d9..ba0639b3b 100644 --- a/agent/pkg/servicemap/models.go +++ b/agent/pkg/servicemap/models.go @@ -18,10 +18,11 @@ type ServiceMapResponse struct { } type ServiceMapNode struct { - Id int `json:"id"` - Name string `json:"name"` - Entry *tapApi.TCP `json:"entry"` - Count int `json:"count"` + Id int `json:"id"` + Name string `json:"name"` + Entry *tapApi.TCP `json:"entry"` + Count int `json:"count"` + Resolved bool `json:"resolved"` } type ServiceMapEdge struct { diff --git a/agent/pkg/servicemap/servicemap.go b/agent/pkg/servicemap/servicemap.go index 954772d45..c4afa9420 100644 --- a/agent/pkg/servicemap/servicemap.go +++ b/agent/pkg/servicemap/servicemap.go @@ -227,10 +227,11 @@ func (s *defaultServiceMap) GetNodes() []ServiceMapNode { var nodes []ServiceMapNode for i, n := range s.graph.Nodes { nodes = append(nodes, ServiceMapNode{ - Id: n.id, - Name: string(i), - Entry: n.entry, - Count: n.count, + Id: n.id, + Name: string(i), + Resolved: n.entry.Name != UnresolvedNodeName, + Entry: n.entry, + Count: n.count, }) } return nodes @@ -243,16 +244,18 @@ func (s *defaultServiceMap) GetEdges() []ServiceMapEdge { for _, p := range s.graph.Edges[u][v].data { edges = append(edges, ServiceMapEdge{ Source: ServiceMapNode{ - Id: s.graph.Nodes[u].id, - Name: string(u), - Entry: s.graph.Nodes[u].entry, - Count: s.graph.Nodes[u].count, + Id: s.graph.Nodes[u].id, + Name: string(u), + Entry: s.graph.Nodes[u].entry, + Resolved: s.graph.Nodes[u].entry.Name != UnresolvedNodeName, + Count: s.graph.Nodes[u].count, }, Destination: ServiceMapNode{ - Id: s.graph.Nodes[v].id, - Name: string(v), - Entry: s.graph.Nodes[v].entry, - Count: s.graph.Nodes[v].count, + Id: s.graph.Nodes[v].id, + Name: string(v), + Entry: s.graph.Nodes[v].entry, + Resolved: s.graph.Nodes[v].entry.Name != UnresolvedNodeName, + Count: s.graph.Nodes[v].count, }, Count: p.count, Protocol: p.protocol, diff --git a/ui-common/src/components/TrafficViewer/EntriesList.tsx b/ui-common/src/components/TrafficViewer/EntriesList.tsx index c7c8e9e86..e34f8e195 100644 --- a/ui-common/src/components/TrafficViewer/EntriesList.tsx +++ b/ui-common/src/components/TrafficViewer/EntriesList.tsx @@ -10,6 +10,7 @@ import entriesAtom from "../../recoil/entries"; import queryAtom from "../../recoil/query"; import TrafficViewerApiAtom from "../../recoil/TrafficViewerApi"; import TrafficViewerApi from "./TrafficViewerApi"; +import focusedEntryIdAtom from "../../recoil/focusedEntryId"; interface EntriesListProps { listEntryREF: any; @@ -18,8 +19,6 @@ interface EntriesListProps { setIsSnappedToBottom: any; queriedCurrent: number; setQueriedCurrent: any; - queriedTotal: number; - setQueriedTotal: any; startTime: number; noMoreDataTop: boolean; setNoMoreDataTop: (flag: boolean) => void; @@ -33,16 +32,18 @@ interface EntriesListProps { ws: any; } -export const EntriesList: React.FC = ({listEntryREF, onSnapBrokenEvent, isSnappedToBottom, setIsSnappedToBottom, queriedCurrent, setQueriedCurrent, queriedTotal, setQueriedTotal, startTime, noMoreDataTop, setNoMoreDataTop, leftOffTop, setLeftOffTop, openWebSocket, leftOffBottom, truncatedTimestamp, setTruncatedTimestamp, scrollableRef, ws}) => { +export const EntriesList: React.FC = ({listEntryREF, onSnapBrokenEvent, isSnappedToBottom, setIsSnappedToBottom, queriedCurrent, setQueriedCurrent, startTime, noMoreDataTop, setNoMoreDataTop, leftOffTop, setLeftOffTop, openWebSocket, leftOffBottom, truncatedTimestamp, setTruncatedTimestamp, scrollableRef, ws}) => { const [entries, setEntries] = useRecoilState(entriesAtom); const query = useRecoilValue(queryAtom); const isWsConnectionClosed = ws?.current?.readyState !== WebSocket.OPEN; + const [focusedEntryId, setFocusedEntryId] = useRecoilState(focusedEntryIdAtom); const trafficViewerApi = useRecoilValue(TrafficViewerApiAtom as RecoilState) const [loadMoreTop, setLoadMoreTop] = useState(false); const [isLoadingTop, setIsLoadingTop] = useState(false); + const [queriedTotal, setQueriedTotal] = useState(0); useEffect(() => { const list = document.getElementById('list').firstElementChild; @@ -103,6 +104,29 @@ export const EntriesList: React.FC = ({listEntryREF, onSnapBro const scrollbarVisible = scrollableRef.current?.childWrapperRef.current.clientHeight > scrollableRef.current?.wrapperRef.current.clientHeight; + if (ws.current) { + ws.current.addEventListener("message", (e) => { + if (!e?.data) return; + const message = JSON.parse(e.data); + switch (message.messageType) { + case "entry": + const entry = message.data; + if (!focusedEntryId) setFocusedEntryId(entry.id.toString()); + const newEntries = [...entries, entry]; + if (newEntries.length === 10001) { + setLeftOffTop(newEntries[0].entry.id); + newEntries.shift(); + setNoMoreDataTop(false); + } + setEntries(newEntries); + break; + case "queryMetadata": + setQueriedTotal(message.data.total); + break; + }; + }); + } + return
diff --git a/ui-common/src/components/TrafficViewer/TrafficViewer.tsx b/ui-common/src/components/TrafficViewer/TrafficViewer.tsx index 8cd01934b..deb4c1d89 100644 --- a/ui-common/src/components/TrafficViewer/TrafficViewer.tsx +++ b/ui-common/src/components/TrafficViewer/TrafficViewer.tsx @@ -58,19 +58,18 @@ export const TrafficViewer: React.FC = ({ setAnalyzeStatus, const classes = useLayoutStyles(); - const [entries, setEntries] = useRecoilState(entriesAtom); + const setEntries = useSetRecoilState(entriesAtom); const [focusedEntryId, setFocusedEntryId] = useRecoilState(focusedEntryIdAtom); const query = useRecoilValue(queryAtom); const setTrafficViewerApiState = useSetRecoilState(trafficViewerApiAtom as RecoilState) const [tappingStatus, setTappingStatus] = useRecoilState(tappingStatusAtom); const [noMoreDataTop, setNoMoreDataTop] = useState(false); const [isSnappedToBottom, setIsSnappedToBottom] = useState(true); - const [forceRender, setForceRender] = useState(0); + const [wsReadyState, setWsReadyState] = useState(0); const [queryBackgroundColor, setQueryBackgroundColor] = useState("#f5f5f5"); const [queriedCurrent, setQueriedCurrent] = useState(0); - const [queriedTotal, setQueriedTotal] = useState(0); const [leftOffBottom, setLeftOffBottom] = useState(0); const [leftOffTop, setLeftOffTop] = useState(null); const [truncatedTimestamp, setTruncatedTimestamp] = useState(0); @@ -144,9 +143,12 @@ export const TrafficViewer: React.FC = ({ setAnalyzeStatus, ws.current = new WebSocket(webSocketUrl); sendQueryWhenWsOpen(query); + ws.current.onopen = () => { + setWsReadyState(ws?.current?.readyState); + } + ws.current.onclose = () => { - if (window.location.pathname === "/") - setForceRender(forceRender + 1); + setWsReadyState(ws?.current?.readyState); } ws.current.onerror = (event) => { console.error("WebSocket error:", event); @@ -173,21 +175,10 @@ export const TrafficViewer: React.FC = ({ setAnalyzeStatus, } if (ws.current) { - ws.current.onmessage = (e) => { + ws.current.addEventListener("message", (e) => { if (!e?.data) return; const message = JSON.parse(e.data); switch (message.messageType) { - case "entry": - const entry = message.data; - if (!focusedEntryId) setFocusedEntryId(entry.id.toString()); - const newEntries = [...entries, entry]; - if (newEntries.length === 10001) { - setLeftOffTop(newEntries[0].entry.id); - newEntries.shift(); - setNoMoreDataTop(false); - } - setEntries(newEntries); - break; case "status": setTappingStatus(message.tappingStatus); break; @@ -208,7 +199,6 @@ export const TrafficViewer: React.FC = ({ setAnalyzeStatus, break; case "queryMetadata": setQueriedCurrent(queriedCurrent + message.data.current); - setQueriedTotal(message.data.total); setLeftOffBottom(message.data.leftOff); setTruncatedTimestamp(message.data.truncatedTimestamp); if (leftOffTop === null) { @@ -218,12 +208,8 @@ export const TrafficViewer: React.FC = ({ setAnalyzeStatus, case "startTime": setStartTime(message.data); break; - default: - console.error( - `unsupported websocket message type, Got: ${message.messageType}` - ); } - }; + }) } useEffect(() => { @@ -272,7 +258,7 @@ export const TrafficViewer: React.FC = ({ setAnalyzeStatus, }; const getConnectionIndicator = () => { - switch (ws?.current?.readyState) { + switch (wsReadyState) { case WebSocket.OPEN: return
@@ -285,7 +271,7 @@ export const TrafficViewer: React.FC = ({ setAnalyzeStatus, } const getConnectionTitle = () => { - switch (ws?.current?.readyState) { + switch (wsReadyState) { case WebSocket.OPEN: return "streaming live traffic" default: @@ -305,9 +291,9 @@ export const TrafficViewer: React.FC = ({ setAnalyzeStatus, {tappingStatus && isShowStatusBar && }
- pause - play
{getConnectionTitle()} @@ -331,8 +317,6 @@ export const TrafficViewer: React.FC = ({ setAnalyzeStatus, setIsSnappedToBottom={setIsSnappedToBottom} queriedCurrent={queriedCurrent} setQueriedCurrent={setQueriedCurrent} - queriedTotal={queriedTotal} - setQueriedTotal={setQueriedTotal} startTime={startTime} noMoreDataTop={noMoreDataTop} setNoMoreDataTop={setNoMoreDataTop} diff --git a/ui/package.json b/ui/package.json index 63846ff89..4d6e96dc2 100644 --- a/ui/package.json +++ b/ui/package.json @@ -48,7 +48,8 @@ "npm-link-shared": "^0.5.6", "react-app-rewire-alias": "^1.1.7", "react-dev-utils": "^12.0.0", - "recoil": "^0.5.2" + "recoil": "^0.5.2", + "react-error-overlay": "6.0.9" }, "scripts": { "start": "craco start",