diff --git a/agent/go.mod b/agent/go.mod index c2c3ba36e..a4ccbb693 100644 --- a/agent/go.mod +++ b/agent/go.mod @@ -7,7 +7,7 @@ require ( github.com/djherbis/atime v1.0.0 github.com/getkin/kin-openapi v0.76.0 github.com/gin-contrib/static v0.0.1 - github.com/gin-gonic/gin v1.7.2 + github.com/gin-gonic/gin v1.7.7 github.com/go-playground/locales v0.13.0 github.com/go-playground/universal-translator v0.17.0 github.com/go-playground/validator/v10 v10.5.0 diff --git a/agent/go.sum b/agent/go.sum index 2c113c74a..ed288cfc1 100644 --- a/agent/go.sum +++ b/agent/go.sum @@ -125,6 +125,8 @@ github.com/gin-contrib/static v0.0.1/go.mod h1:CSxeF+wep05e0kCOsqWdAWbSszmc31zTI github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gin-gonic/gin v1.7.2 h1:Tg03T9yM2xa8j6I3Z3oqLaQRSmKvxPd6g/2HJ6zICFA= github.com/gin-gonic/gin v1.7.2/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= +github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs= +github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= diff --git a/agent/main.go b/agent/main.go index ab8612022..564588a02 100644 --- a/agent/main.go +++ b/agent/main.go @@ -23,6 +23,7 @@ import ( "path/filepath" "plugin" "sort" + "strings" "syscall" "time" @@ -465,14 +466,19 @@ func startMizuTapperSyncer(ctx context.Context, provider *kubernetes.Provider) ( logger.Log.Debug("mizuTapperSyncer pod changes channel closed, ending listener loop") return } - tapStatus := shared.TapStatus{Pods: kubernetes.GetPodInfosForPods(tapperSyncer.CurrentlyTappedPods)} + providers.TapStatus = shared.TapStatus{Pods: kubernetes.GetPodInfosForPods(tapperSyncer.CurrentlyTappedPods)} - serializedTapStatus, err := json.Marshal(shared.CreateWebSocketStatusMessage(tapStatus)) + tappedPodsStatus := make([]shared.TappedPodStatus, 0) + for _, pod := range providers.TapStatus.Pods { + isTapped := strings.ToLower(providers.TappersStatus[pod.NodeName].Status) == "started" + tappedPodsStatus = append(tappedPodsStatus, shared.TappedPodStatus{Name: pod.Name, Namespace: pod.Namespace, IsTapped: isTapped}) + } + + serializedTapStatus, err := json.Marshal(shared.CreateWebSocketStatusMessage(tappedPodsStatus)) if err != nil { logger.Log.Fatalf("error serializing tap status: %v", err) } api.BroadcastToBrowserClients(serializedTapStatus) - providers.TapStatus.Pods = tapStatus.Pods providers.ExpectedTapperAmount = tapPodChangeEvent.ExpectedTapperAmount case tapperStatus, ok := <-tapperSyncer.TapperStatusChangedOut: if !ok { diff --git a/agent/pkg/api/socket_server_handlers.go b/agent/pkg/api/socket_server_handlers.go index d596becb2..a02aaa82b 100644 --- a/agent/pkg/api/socket_server_handlers.go +++ b/agent/pkg/api/socket_server_handlers.go @@ -83,7 +83,6 @@ func (h *RoutesEventHandlers) WebSocketMessage(_ int, message []byte) { if err != nil { logger.Log.Infof("Could not unmarshal message of message type %s %v", socketMessageBase.MessageType, err) } else { - providers.TapStatus.Pods = statusMessage.TappingStatus.Pods BroadcastToBrowserClients(message) } case shared.WebsocketMessageTypeOutboundLink: diff --git a/agent/pkg/controllers/status_controller.go b/agent/pkg/controllers/status_controller.go index 8e95e82e2..6d049433d 100644 --- a/agent/pkg/controllers/status_controller.go +++ b/agent/pkg/controllers/status_controller.go @@ -10,6 +10,7 @@ import ( "mizuserver/pkg/up9" "mizuserver/pkg/validation" "net/http" + "strings" "github.com/gin-gonic/gin" "github.com/up9inc/mizu/shared" @@ -49,7 +50,17 @@ func PostTappedPods(c *gin.Context) { } logger.Log.Infof("[Status] POST request: %d tapped pods", len(tapStatus.Pods)) providers.TapStatus.Pods = tapStatus.Pods - message := shared.CreateWebSocketStatusMessage(*tapStatus) + broadcastTappedPodsStatus() +} + +func broadcastTappedPodsStatus() { + tappedPodsStatus := make([]shared.TappedPodStatus, 0) + for _, pod := range providers.TapStatus.Pods { + isTapped := strings.ToLower(providers.TappersStatus[pod.NodeName].Status) == "started" + tappedPodsStatus = append(tappedPodsStatus, shared.TappedPodStatus{Name: pod.Name, Namespace: pod.Namespace, IsTapped: isTapped}) + } + + message := shared.CreateWebSocketStatusMessage(tappedPodsStatus) if jsonBytes, err := json.Marshal(message); err != nil { logger.Log.Errorf("Could not Marshal message %v", err) } else { @@ -72,6 +83,7 @@ func PostTapperStatus(c *gin.Context) { providers.TappersStatus = make(map[string]shared.TapperStatus) } providers.TappersStatus[tapperStatus.NodeName] = *tapperStatus + broadcastTappedPodsStatus() } func GetTappersCount(c *gin.Context) { @@ -89,7 +101,12 @@ func GetAuthStatus(c *gin.Context) { } func GetTappingStatus(c *gin.Context) { - c.JSON(http.StatusOK, providers.TapStatus) + tappedPodsStatus := make([]shared.TappedPodStatus, 0) + for _, pod := range providers.TapStatus.Pods { + isTapped := strings.ToLower(providers.TappersStatus[pod.NodeName].Status) == "started" + tappedPodsStatus = append(tappedPodsStatus, shared.TappedPodStatus{Name: pod.Name, Namespace: pod.Namespace, IsTapped: isTapped}) + } + c.JSON(http.StatusOK, tappedPodsStatus) } func AnalyzeInformation(c *gin.Context) { diff --git a/shared/models.go b/shared/models.go index c12edcb72..ef0cb2949 100644 --- a/shared/models.go +++ b/shared/models.go @@ -64,7 +64,7 @@ type AnalyzeStatus struct { type WebSocketStatusMessage struct { *WebSocketMessageMetadata - TappingStatus TapStatus `json:"tappingStatus"` + TappingStatus []TappedPodStatus `json:"tappingStatus"` } type TapperStatus struct { @@ -73,9 +73,14 @@ type TapperStatus struct { Status string `json:"status"` } +type TappedPodStatus struct { + Name string `json:"name"` + Namespace string `json:"namespace"` + IsTapped bool `json:"isTapped"` +} + type TapStatus struct { - Pods []PodInfo `json:"pods"` - TLSLinks []TLSLinkInfo `json:"tlsLinks"` + Pods []PodInfo `json:"pods"` } type PodInfo struct { @@ -98,12 +103,12 @@ type SyncEntriesConfig struct { UploadIntervalSec int `json:"interval"` } -func CreateWebSocketStatusMessage(tappingStatus TapStatus) WebSocketStatusMessage { +func CreateWebSocketStatusMessage(tappedPodsStatus []TappedPodStatus) WebSocketStatusMessage { return WebSocketStatusMessage{ WebSocketMessageMetadata: &WebSocketMessageMetadata{ MessageType: WebSocketMessageTypeUpdateStatus, }, - TappingStatus: tappingStatus, + TappingStatus: tappedPodsStatus, } } diff --git a/ui/src/components/TrafficPage.tsx b/ui/src/components/TrafficPage.tsx index cdc21e1de..0faf7a816 100644 --- a/ui/src/components/TrafficPage.tsx +++ b/ui/src/components/TrafficPage.tsx @@ -323,7 +323,7 @@ export const TrafficPage: React.FC = ({setAnalyzeStatus, onTLS {selectedEntryData && } } - {tappingStatus?.pods != null && } + {tappingStatus && } { @@ -22,23 +26,29 @@ export const StatusBar: React.FC = ({tappingStatus}) => { const [expandedBar, setExpandedBar] = useState(false); - const uniqueNamespaces = Array.from(new Set(tappingStatus.pods.map(pod => pod.namespace))); - const amountOfPods = tappingStatus.pods.length; + const uniqueNamespaces = Array.from(new Set(tappingStatus.map(pod => pod.namespace))); + const amountOfPods = tappingStatus.length; + const amountOfTappedPods = tappingStatus.filter(pod => pod.isTapped).length; + const amountOfUntappedPods = amountOfPods - amountOfTappedPods; return
setExpandedBar(true)} onMouseLeave={() => setExpandedBar(false)}> -
{`Tapping ${amountOfPods} ${pluralize('pod', amountOfPods)} in ${pluralize('namespace', uniqueNamespaces.length)} ${uniqueNamespaces.join(", ")}`}
+
+ {tappingStatus.some(pod => !pod.isTapped) && warning} + {`Tapping ${amountOfUntappedPods > 0 ? amountOfTappedPods + " / " + amountOfPods : amountOfPods} ${pluralize('pod', amountOfPods)} in ${pluralize('namespace', uniqueNamespaces.length)} ${uniqueNamespaces.join(", ")}`}
{expandedBar &&
+ - {tappingStatus.pods.map(pod => + {tappingStatus.map(pod => + )}
Pod name NamespaceTapping
{pod.name} {pod.namespace}status
diff --git a/ui/src/components/UI/style/StatusBar.sass b/ui/src/components/UI/style/StatusBar.sass index 6a55dbc36..f6e66624c 100644 --- a/ui/src/components/UI/style/StatusBar.sass +++ b/ui/src/components/UI/style/StatusBar.sass @@ -24,8 +24,13 @@ padding: 8px font-weight: 600 + img + margin-right: 10px + height: 22px + th text-align: left + padding-right: 15px td padding-right: 15px padding-top: 5px diff --git a/ui/src/components/assets/failed.svg b/ui/src/components/assets/failed.svg new file mode 100644 index 000000000..bab53af12 --- /dev/null +++ b/ui/src/components/assets/failed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/components/assets/success.svg b/ui/src/components/assets/success.svg new file mode 100644 index 000000000..f8fe3aa64 --- /dev/null +++ b/ui/src/components/assets/success.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/components/assets/warning_icon.svg b/ui/src/components/assets/warning_icon.svg new file mode 100644 index 000000000..7ef6ba5a2 --- /dev/null +++ b/ui/src/components/assets/warning_icon.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +