mirror of
https://github.com/kubeshark/kubeshark.git
synced 2025-09-02 11:05:22 +00:00
Report pods "isTapped" to FE (#535)
This commit is contained in:
@@ -7,7 +7,7 @@ require (
|
|||||||
github.com/djherbis/atime v1.0.0
|
github.com/djherbis/atime v1.0.0
|
||||||
github.com/getkin/kin-openapi v0.76.0
|
github.com/getkin/kin-openapi v0.76.0
|
||||||
github.com/gin-contrib/static v0.0.1
|
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/locales v0.13.0
|
||||||
github.com/go-playground/universal-translator v0.17.0
|
github.com/go-playground/universal-translator v0.17.0
|
||||||
github.com/go-playground/validator/v10 v10.5.0
|
github.com/go-playground/validator/v10 v10.5.0
|
||||||
|
@@ -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.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 h1:Tg03T9yM2xa8j6I3Z3oqLaQRSmKvxPd6g/2HJ6zICFA=
|
||||||
github.com/gin-gonic/gin v1.7.2/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
|
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-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/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=
|
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||||
|
@@ -23,6 +23,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"plugin"
|
"plugin"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -465,14 +466,19 @@ func startMizuTapperSyncer(ctx context.Context, provider *kubernetes.Provider) (
|
|||||||
logger.Log.Debug("mizuTapperSyncer pod changes channel closed, ending listener loop")
|
logger.Log.Debug("mizuTapperSyncer pod changes channel closed, ending listener loop")
|
||||||
return
|
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 {
|
if err != nil {
|
||||||
logger.Log.Fatalf("error serializing tap status: %v", err)
|
logger.Log.Fatalf("error serializing tap status: %v", err)
|
||||||
}
|
}
|
||||||
api.BroadcastToBrowserClients(serializedTapStatus)
|
api.BroadcastToBrowserClients(serializedTapStatus)
|
||||||
providers.TapStatus.Pods = tapStatus.Pods
|
|
||||||
providers.ExpectedTapperAmount = tapPodChangeEvent.ExpectedTapperAmount
|
providers.ExpectedTapperAmount = tapPodChangeEvent.ExpectedTapperAmount
|
||||||
case tapperStatus, ok := <-tapperSyncer.TapperStatusChangedOut:
|
case tapperStatus, ok := <-tapperSyncer.TapperStatusChangedOut:
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@@ -83,7 +83,6 @@ func (h *RoutesEventHandlers) WebSocketMessage(_ int, message []byte) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log.Infof("Could not unmarshal message of message type %s %v", socketMessageBase.MessageType, err)
|
logger.Log.Infof("Could not unmarshal message of message type %s %v", socketMessageBase.MessageType, err)
|
||||||
} else {
|
} else {
|
||||||
providers.TapStatus.Pods = statusMessage.TappingStatus.Pods
|
|
||||||
BroadcastToBrowserClients(message)
|
BroadcastToBrowserClients(message)
|
||||||
}
|
}
|
||||||
case shared.WebsocketMessageTypeOutboundLink:
|
case shared.WebsocketMessageTypeOutboundLink:
|
||||||
|
@@ -10,6 +10,7 @@ import (
|
|||||||
"mizuserver/pkg/up9"
|
"mizuserver/pkg/up9"
|
||||||
"mizuserver/pkg/validation"
|
"mizuserver/pkg/validation"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/up9inc/mizu/shared"
|
"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))
|
logger.Log.Infof("[Status] POST request: %d tapped pods", len(tapStatus.Pods))
|
||||||
providers.TapStatus.Pods = 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 {
|
if jsonBytes, err := json.Marshal(message); err != nil {
|
||||||
logger.Log.Errorf("Could not Marshal message %v", err)
|
logger.Log.Errorf("Could not Marshal message %v", err)
|
||||||
} else {
|
} else {
|
||||||
@@ -72,6 +83,7 @@ func PostTapperStatus(c *gin.Context) {
|
|||||||
providers.TappersStatus = make(map[string]shared.TapperStatus)
|
providers.TappersStatus = make(map[string]shared.TapperStatus)
|
||||||
}
|
}
|
||||||
providers.TappersStatus[tapperStatus.NodeName] = *tapperStatus
|
providers.TappersStatus[tapperStatus.NodeName] = *tapperStatus
|
||||||
|
broadcastTappedPodsStatus()
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetTappersCount(c *gin.Context) {
|
func GetTappersCount(c *gin.Context) {
|
||||||
@@ -89,7 +101,12 @@ func GetAuthStatus(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetTappingStatus(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) {
|
func AnalyzeInformation(c *gin.Context) {
|
||||||
|
@@ -64,7 +64,7 @@ type AnalyzeStatus struct {
|
|||||||
|
|
||||||
type WebSocketStatusMessage struct {
|
type WebSocketStatusMessage struct {
|
||||||
*WebSocketMessageMetadata
|
*WebSocketMessageMetadata
|
||||||
TappingStatus TapStatus `json:"tappingStatus"`
|
TappingStatus []TappedPodStatus `json:"tappingStatus"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TapperStatus struct {
|
type TapperStatus struct {
|
||||||
@@ -73,9 +73,14 @@ type TapperStatus struct {
|
|||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TappedPodStatus struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Namespace string `json:"namespace"`
|
||||||
|
IsTapped bool `json:"isTapped"`
|
||||||
|
}
|
||||||
|
|
||||||
type TapStatus struct {
|
type TapStatus struct {
|
||||||
Pods []PodInfo `json:"pods"`
|
Pods []PodInfo `json:"pods"`
|
||||||
TLSLinks []TLSLinkInfo `json:"tlsLinks"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type PodInfo struct {
|
type PodInfo struct {
|
||||||
@@ -98,12 +103,12 @@ type SyncEntriesConfig struct {
|
|||||||
UploadIntervalSec int `json:"interval"`
|
UploadIntervalSec int `json:"interval"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateWebSocketStatusMessage(tappingStatus TapStatus) WebSocketStatusMessage {
|
func CreateWebSocketStatusMessage(tappedPodsStatus []TappedPodStatus) WebSocketStatusMessage {
|
||||||
return WebSocketStatusMessage{
|
return WebSocketStatusMessage{
|
||||||
WebSocketMessageMetadata: &WebSocketMessageMetadata{
|
WebSocketMessageMetadata: &WebSocketMessageMetadata{
|
||||||
MessageType: WebSocketMessageTypeUpdateStatus,
|
MessageType: WebSocketMessageTypeUpdateStatus,
|
||||||
},
|
},
|
||||||
TappingStatus: tappingStatus,
|
TappingStatus: tappedPodsStatus,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -323,7 +323,7 @@ export const TrafficPage: React.FC<TrafficPageProps> = ({setAnalyzeStatus, onTLS
|
|||||||
{selectedEntryData && <EntryDetailed entryData={selectedEntryData} updateQuery={updateQuery}/>}
|
{selectedEntryData && <EntryDetailed entryData={selectedEntryData} updateQuery={updateQuery}/>}
|
||||||
</div>
|
</div>
|
||||||
</div>}
|
</div>}
|
||||||
{tappingStatus?.pods != null && <StatusBar tappingStatus={tappingStatus}/>}
|
{tappingStatus && <StatusBar tappingStatus={tappingStatus}/>}
|
||||||
<ToastContainer
|
<ToastContainer
|
||||||
position="bottom-right"
|
position="bottom-right"
|
||||||
autoClose={5000}
|
autoClose={5000}
|
||||||
|
@@ -1,9 +1,13 @@
|
|||||||
import './style/StatusBar.sass';
|
import './style/StatusBar.sass';
|
||||||
import React, {useState} from "react";
|
import React, {useState} from "react";
|
||||||
|
import warningIcon from '../assets/warning_icon.svg';
|
||||||
|
import failIcon from '../assets/failed.svg';
|
||||||
|
import successIcon from '../assets/success.svg';
|
||||||
|
|
||||||
export interface TappingStatusPod {
|
export interface TappingStatusPod {
|
||||||
name: string;
|
name: string;
|
||||||
namespace: string;
|
namespace: string;
|
||||||
|
isTapped: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TappingStatus {
|
export interface TappingStatus {
|
||||||
@@ -11,7 +15,7 @@ export interface TappingStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
tappingStatus: TappingStatus
|
tappingStatus: TappingStatusPod[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const pluralize = (noun: string, amount: number) => {
|
const pluralize = (noun: string, amount: number) => {
|
||||||
@@ -22,23 +26,29 @@ export const StatusBar: React.FC<Props> = ({tappingStatus}) => {
|
|||||||
|
|
||||||
const [expandedBar, setExpandedBar] = useState(false);
|
const [expandedBar, setExpandedBar] = useState(false);
|
||||||
|
|
||||||
const uniqueNamespaces = Array.from(new Set(tappingStatus.pods.map(pod => pod.namespace)));
|
const uniqueNamespaces = Array.from(new Set(tappingStatus.map(pod => pod.namespace)));
|
||||||
const amountOfPods = tappingStatus.pods.length;
|
const amountOfPods = tappingStatus.length;
|
||||||
|
const amountOfTappedPods = tappingStatus.filter(pod => pod.isTapped).length;
|
||||||
|
const amountOfUntappedPods = amountOfPods - amountOfTappedPods;
|
||||||
|
|
||||||
return <div className={'statusBar' + (expandedBar ? ' expandedStatusBar' : "")} onMouseOver={() => setExpandedBar(true)} onMouseLeave={() => setExpandedBar(false)}>
|
return <div className={'statusBar' + (expandedBar ? ' expandedStatusBar' : "")} onMouseOver={() => setExpandedBar(true)} onMouseLeave={() => setExpandedBar(false)}>
|
||||||
<div className="podsCount">{`Tapping ${amountOfPods} ${pluralize('pod', amountOfPods)} in ${pluralize('namespace', uniqueNamespaces.length)} ${uniqueNamespaces.join(", ")}`}</div>
|
<div className="podsCount">
|
||||||
|
{tappingStatus.some(pod => !pod.isTapped) && <img src={warningIcon} alt="warning"/>}
|
||||||
|
{`Tapping ${amountOfUntappedPods > 0 ? amountOfTappedPods + " / " + amountOfPods : amountOfPods} ${pluralize('pod', amountOfPods)} in ${pluralize('namespace', uniqueNamespaces.length)} ${uniqueNamespaces.join(", ")}`}</div>
|
||||||
{expandedBar && <div style={{marginTop: 20}}>
|
{expandedBar && <div style={{marginTop: 20}}>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Pod name</th>
|
<th>Pod name</th>
|
||||||
<th>Namespace</th>
|
<th>Namespace</th>
|
||||||
|
<th style={{marginLeft: 10}}>Tapping</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{tappingStatus.pods.map(pod => <tr key={pod.name}>
|
{tappingStatus.map(pod => <tr key={pod.name}>
|
||||||
<td>{pod.name}</td>
|
<td>{pod.name}</td>
|
||||||
<td>{pod.namespace}</td>
|
<td>{pod.namespace}</td>
|
||||||
|
<td style={{textAlign: "center"}}><img style={{height: 20}} alt="status" src={pod.isTapped ? successIcon : failIcon}/></td>
|
||||||
</tr>)}
|
</tr>)}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
@@ -24,8 +24,13 @@
|
|||||||
padding: 8px
|
padding: 8px
|
||||||
font-weight: 600
|
font-weight: 600
|
||||||
|
|
||||||
|
img
|
||||||
|
margin-right: 10px
|
||||||
|
height: 22px
|
||||||
|
|
||||||
th
|
th
|
||||||
text-align: left
|
text-align: left
|
||||||
|
padding-right: 15px
|
||||||
td
|
td
|
||||||
padding-right: 15px
|
padding-right: 15px
|
||||||
padding-top: 5px
|
padding-top: 5px
|
||||||
|
1
ui/src/components/assets/failed.svg
Normal file
1
ui/src/components/assets/failed.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48px" height="48px"><linearGradient id="wRKXFJsqHCxLE9yyOYHkza" x1="9.858" x2="38.142" y1="9.858" y2="38.142" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#f44f5a"/><stop offset=".443" stop-color="#ee3d4a"/><stop offset="1" stop-color="#e52030"/></linearGradient><path fill="url(#wRKXFJsqHCxLE9yyOYHkza)" d="M44,24c0,11.045-8.955,20-20,20S4,35.045,4,24S12.955,4,24,4S44,12.955,44,24z"/><path d="M33.192,28.95L28.243,24l4.95-4.95c0.781-0.781,0.781-2.047,0-2.828l-1.414-1.414 c-0.781-0.781-2.047-0.781-2.828,0L24,19.757l-4.95-4.95c-0.781-0.781-2.047-0.781-2.828,0l-1.414,1.414 c-0.781,0.781-0.781,2.047,0,2.828l4.95,4.95l-4.95,4.95c-0.781,0.781-0.781,2.047,0,2.828l1.414,1.414 c0.781,0.781,2.047,0.781,2.828,0l4.95-4.95l4.95,4.95c0.781,0.781,2.047,0.781,2.828,0l1.414-1.414 C33.973,30.997,33.973,29.731,33.192,28.95z" opacity=".05"/><path d="M32.839,29.303L27.536,24l5.303-5.303c0.586-0.586,0.586-1.536,0-2.121l-1.414-1.414 c-0.586-0.586-1.536-0.586-2.121,0L24,20.464l-5.303-5.303c-0.586-0.586-1.536-0.586-2.121,0l-1.414,1.414 c-0.586,0.586-0.586,1.536,0,2.121L20.464,24l-5.303,5.303c-0.586,0.586-0.586,1.536,0,2.121l1.414,1.414 c0.586,0.586,1.536,0.586,2.121,0L24,27.536l5.303,5.303c0.586,0.586,1.536,0.586,2.121,0l1.414-1.414 C33.425,30.839,33.425,29.889,32.839,29.303z" opacity=".07"/><path fill="#fff" d="M31.071,15.515l1.414,1.414c0.391,0.391,0.391,1.024,0,1.414L18.343,32.485 c-0.391,0.391-1.024,0.391-1.414,0l-1.414-1.414c-0.391-0.391-0.391-1.024,0-1.414l14.142-14.142 C30.047,15.124,30.681,15.124,31.071,15.515z"/><path fill="#fff" d="M32.485,31.071l-1.414,1.414c-0.391,0.391-1.024,0.391-1.414,0L15.515,18.343 c-0.391-0.391-0.391-1.024,0-1.414l1.414-1.414c0.391-0.391,1.024-0.391,1.414,0l14.142,14.142 C32.876,30.047,32.876,30.681,32.485,31.071z"/></svg>
|
After Width: | Height: | Size: 1.8 KiB |
1
ui/src/components/assets/success.svg
Normal file
1
ui/src/components/assets/success.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48px" height="48px"><linearGradient id="I9GV0SozQFknxHSR6DCx5a" x1="9.858" x2="38.142" y1="9.858" y2="38.142" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#21ad64"/><stop offset="1" stop-color="#088242"/></linearGradient><path fill="url(#I9GV0SozQFknxHSR6DCx5a)" d="M44,24c0,11.045-8.955,20-20,20S4,35.045,4,24S12.955,4,24,4S44,12.955,44,24z"/><path d="M32.172,16.172L22,26.344l-5.172-5.172c-0.781-0.781-2.047-0.781-2.828,0l-1.414,1.414 c-0.781,0.781-0.781,2.047,0,2.828l8,8c0.781,0.781,2.047,0.781,2.828,0l13-13c0.781-0.781,0.781-2.047,0-2.828L35,16.172 C34.219,15.391,32.953,15.391,32.172,16.172z" opacity=".05"/><path d="M20.939,33.061l-8-8c-0.586-0.586-0.586-1.536,0-2.121l1.414-1.414c0.586-0.586,1.536-0.586,2.121,0 L22,27.051l10.525-10.525c0.586-0.586,1.536-0.586,2.121,0l1.414,1.414c0.586,0.586,0.586,1.536,0,2.121l-13,13 C22.475,33.646,21.525,33.646,20.939,33.061z" opacity=".07"/><path fill="#fff" d="M21.293,32.707l-8-8c-0.391-0.391-0.391-1.024,0-1.414l1.414-1.414c0.391-0.391,1.024-0.391,1.414,0 L22,27.758l10.879-10.879c0.391-0.391,1.024-0.391,1.414,0l1.414,1.414c0.391,0.391,0.391,1.024,0,1.414l-13,13 C22.317,33.098,21.683,33.098,21.293,32.707z"/></svg>
|
After Width: | Height: | Size: 1.2 KiB |
34
ui/src/components/assets/warning_icon.svg
Normal file
34
ui/src/components/assets/warning_icon.svg
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="29.999" viewBox="0 0 22 29.999">
|
||||||
|
<defs>
|
||||||
|
<filter id="Rectangle_2909" width="21" height="25.999" x=".43" y="0" filterUnits="userSpaceOnUse">
|
||||||
|
<feOffset dy="3"/>
|
||||||
|
<feGaussianBlur result="blur" stdDeviation="3"/>
|
||||||
|
<feFlood flood-opacity=".161"/>
|
||||||
|
<feComposite in2="blur" operator="in"/>
|
||||||
|
<feComposite in="SourceGraphic"/>
|
||||||
|
</filter>
|
||||||
|
<filter id="Rectangle_2911" width="21" height="20.999" x=".43" y="9" filterUnits="userSpaceOnUse">
|
||||||
|
<feOffset dy="3"/>
|
||||||
|
<feGaussianBlur result="blur-2" stdDeviation="3"/>
|
||||||
|
<feFlood flood-opacity=".161"/>
|
||||||
|
<feComposite in2="blur-2" operator="in"/>
|
||||||
|
<feComposite in="SourceGraphic"/>
|
||||||
|
</filter>
|
||||||
|
<style>
|
||||||
|
.cls-2{fill:#fff}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<g id="warning_icon" transform="translate(-883 -4234.5)">
|
||||||
|
<circle id="Ellipse_1021" cx="11" cy="11" r="11" fill="#fdab2b" data-name="Ellipse 1021" transform="translate(883 4235)"/>
|
||||||
|
<g id="Group_5975" data-name="Group 5975" transform="translate(892.43 4240.5)">
|
||||||
|
<g id="Group_5974" data-name="Group 5974">
|
||||||
|
<g filter="url(#Rectangle_2909)" transform="translate(-9.43 -6)">
|
||||||
|
<rect id="Rectangle_2909-2" width="3" height="7.999" class="cls-2" data-name="Rectangle 2909" rx="1.5" transform="translate(9.43 6)"/>
|
||||||
|
</g>
|
||||||
|
<g filter="url(#Rectangle_2911)" transform="translate(-9.43 -6)">
|
||||||
|
<rect id="Rectangle_2911-2" width="3" height="2.999" class="cls-2" data-name="Rectangle 2911" rx="1.499" transform="translate(9.43 15)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
Reference in New Issue
Block a user