diff --git a/plugins/kiali/dev/__fixtures__/index.ts b/plugins/kiali/dev/__fixtures__/index.ts index 1d64a37ba2..7a58b15853 100644 --- a/plugins/kiali/dev/__fixtures__/index.ts +++ b/plugins/kiali/dev/__fixtures__/index.ts @@ -19,6 +19,8 @@ import bookinfoHealthWorkload from './namespaces/bookinfo/health/workload.json'; import bookInfoMetrics from './namespaces/bookinfo/metrics'; /* bookinfo */ import bookinfoTls from './namespaces/bookinfo/tls.json'; +/** Workloads **/ +import bookinfoWorkloads from './namespaces/bookinfo/workloads.json'; /** health **/ import istioSystemHealthApp from './namespaces/istio-system/health/app.json'; import istioSystemHealthService from './namespaces/istio-system/health/service.json'; @@ -26,6 +28,7 @@ import istioSystemHealthWorkload from './namespaces/istio-system/health/workload import istioSystemMetrics from './namespaces/istio-system/metrics'; /* istio-system */ import istioSystemTls from './namespaces/istio-system/tls.json'; +import istioSystemWorkloads from './namespaces/istio-system/workloads.json'; /** health **/ import travelAgencyHealthApp from './namespaces/travel-agency/health/app.json'; @@ -34,6 +37,7 @@ import travelAgencyHealthWorkload from './namespaces/travel-agency/health/worklo import travelAgencyMetrics from './namespaces/travel-agency/metrics'; /* Travel agency */ import travelAgencyTls from './namespaces/travel-agency/tls.json'; +import travelAgencyWorkloads from './namespaces/travel-agency/workloads.json'; /** health **/ import travelControlHealthApp from './namespaces/travel-control/health/app.json'; @@ -42,6 +46,7 @@ import travelControlHealthWorkload from './namespaces/travel-control/health/work import travelControlMetrics from './namespaces/travel-control/metrics'; /* Travel control */ import travelControlTls from './namespaces/travel-control/tls.json'; +import travelControlWorkloads from './namespaces/travel-control/workloads.json'; /** health **/ import travelPortalHealthApp from './namespaces/travel-portal/health/app.json'; @@ -50,6 +55,7 @@ import travelPortalHealthWorkload from './namespaces/travel-portal/health/worklo import travelPortalMetrics from './namespaces/travel-portal/metrics'; /* Travel portal */ import travelPortalTls from './namespaces/travel-portal/tls.json'; +import travelPortalWorkloads from './namespaces/travel-portal/workloads.json'; export const kialiData: { [index: string]: any } = { auth: anonymousAuth, @@ -110,5 +116,12 @@ export const kialiData: { [index: string]: any } = { }, }, }, + workloads: { + 'istio-system': istioSystemWorkloads, + bookinfo: bookinfoWorkloads, + 'travel-portal': travelPortalWorkloads, + 'travel-agency': travelAgencyWorkloads, + 'travel-control': travelControlWorkloads, + }, status: status, }; diff --git a/plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads.json b/plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads.json new file mode 100644 index 0000000000..167840794c --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads.json @@ -0,0 +1,370 @@ +{ + "namespace": { + "name": "bookinfo", + "cluster": "", + "isAmbient": false, + "labels": null, + "annotations": null + }, + "workloads": [ + { + "name": "details-v1", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-01-29T20:10:15Z", + "resourceVersion": "26835", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "details", + "version": "v1" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 1, + "annotations": { + "deployment.kubernetes.io/revision": "1", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"details\",\"version\":\"v1\"},\"name\":\"details-v1\",\"namespace\":\"bookinfo\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"details\",\"version\":\"v1\"}},\"template\":{\"metadata\":{\"labels\":{\"app\":\"details\",\"version\":\"v1\"}},\"spec\":{\"containers\":[{\"image\":\"docker.io/istio/examples-bookinfo-details-v1:1.18.0\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"details\",\"ports\":[{\"containerPort\":9080}]}],\"serviceAccountName\":\"bookinfo-details\"}}}}\n" + }, + "healthAnnotations": {}, + "istioReferences": [], + "dashboardAnnotations": null, + "serviceAccountNames": ["bookinfo-details"], + "health": { + "workloadStatus": { + "name": "details-v1", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": 1 + }, + "requests": { + "inbound": { + "http": { + "200": 1.0000017094046314 + } + }, + "outbound": {}, + "healthAnnotations": {} + } + } + }, + { + "name": "kiali-traffic-generator", + "cluster": "Kubernetes", + "type": "ReplicaSet", + "createdAt": "2024-01-29T20:11:02Z", + "resourceVersion": "26851", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "kiali-traffic-generator", + "kiali-test": "traffic-generator" + }, + "appLabel": true, + "versionLabel": false, + "podCount": 1, + "annotations": {}, + "healthAnnotations": {}, + "istioReferences": [], + "dashboardAnnotations": null, + "serviceAccountNames": ["default"], + "health": { + "workloadStatus": { + "name": "kiali-traffic-generator", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": 1 + }, + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + }, + { + "name": "productpage-v1", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-01-29T20:10:15Z", + "resourceVersion": "26856", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "productpage", + "version": "v1" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 1, + "annotations": { + "deployment.kubernetes.io/revision": "1", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"productpage\",\"version\":\"v1\"},\"name\":\"productpage-v1\",\"namespace\":\"bookinfo\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"productpage\",\"version\":\"v1\"}},\"template\":{\"metadata\":{\"annotations\":{\"prometheus.io/path\":\"/metrics\",\"prometheus.io/port\":\"9080\",\"prometheus.io/scrape\":\"true\"},\"labels\":{\"app\":\"productpage\",\"version\":\"v1\"}},\"spec\":{\"containers\":[{\"image\":\"docker.io/istio/examples-bookinfo-productpage-v1:1.18.0\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"productpage\",\"ports\":[{\"containerPort\":9080}],\"volumeMounts\":[{\"mountPath\":\"/tmp\",\"name\":\"tmp\"}]}],\"serviceAccountName\":\"bookinfo-productpage\",\"volumes\":[{\"emptyDir\":{},\"name\":\"tmp\"}]}}}}\n" + }, + "healthAnnotations": {}, + "istioReferences": [], + "dashboardAnnotations": null, + "serviceAccountNames": ["bookinfo-productpage"], + "health": { + "workloadStatus": { + "name": "productpage-v1", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": 1 + }, + "requests": { + "inbound": { + "http": { + "200": 1.0000017094046316 + } + }, + "outbound": { + "http": { + "200": 1.9999999999999996 + } + }, + "healthAnnotations": {} + } + } + }, + { + "name": "ratings-v1", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-01-29T20:10:15Z", + "resourceVersion": "26849", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "ratings", + "version": "v1" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 1, + "annotations": { + "deployment.kubernetes.io/revision": "1", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"ratings\",\"version\":\"v1\"},\"name\":\"ratings-v1\",\"namespace\":\"bookinfo\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"ratings\",\"version\":\"v1\"}},\"template\":{\"metadata\":{\"labels\":{\"app\":\"ratings\",\"version\":\"v1\"}},\"spec\":{\"containers\":[{\"image\":\"docker.io/istio/examples-bookinfo-ratings-v1:1.18.0\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"ratings\",\"ports\":[{\"containerPort\":9080}]}],\"serviceAccountName\":\"bookinfo-ratings\"}}}}\n" + }, + "healthAnnotations": {}, + "istioReferences": [], + "dashboardAnnotations": null, + "serviceAccountNames": ["bookinfo-ratings"], + "health": { + "workloadStatus": { + "name": "ratings-v1", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": 1 + }, + "requests": { + "inbound": { + "http": { + "200": 0.6905982905982905 + } + }, + "outbound": {}, + "healthAnnotations": {} + } + } + }, + { + "name": "reviews-v1", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-01-29T20:10:15Z", + "resourceVersion": "26845", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "reviews", + "version": "v1" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 1, + "annotations": { + "deployment.kubernetes.io/revision": "1", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"reviews\",\"version\":\"v1\"},\"name\":\"reviews-v1\",\"namespace\":\"bookinfo\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"reviews\",\"version\":\"v1\"}},\"template\":{\"metadata\":{\"labels\":{\"app\":\"reviews\",\"version\":\"v1\"}},\"spec\":{\"containers\":[{\"env\":[{\"name\":\"LOG_DIR\",\"value\":\"/tmp/logs\"}],\"image\":\"docker.io/istio/examples-bookinfo-reviews-v1:1.18.0\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"reviews\",\"ports\":[{\"containerPort\":9080}],\"volumeMounts\":[{\"mountPath\":\"/tmp\",\"name\":\"tmp\"},{\"mountPath\":\"/opt/ibm/wlp/output\",\"name\":\"wlp-output\"}]}],\"serviceAccountName\":\"bookinfo-reviews\",\"volumes\":[{\"emptyDir\":{},\"name\":\"wlp-output\"},{\"emptyDir\":{},\"name\":\"tmp\"}]}}}}\n" + }, + "healthAnnotations": {}, + "istioReferences": [], + "dashboardAnnotations": null, + "serviceAccountNames": ["bookinfo-reviews"], + "health": { + "workloadStatus": { + "name": "reviews-v1", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": 1 + }, + "requests": { + "inbound": { + "http": { + "200": 0.31111111111111106 + } + }, + "outbound": {}, + "healthAnnotations": {} + } + } + }, + { + "name": "reviews-v2", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-01-29T20:10:15Z", + "resourceVersion": "26828", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "reviews", + "version": "v2" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 1, + "annotations": { + "deployment.kubernetes.io/revision": "1", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"reviews\",\"version\":\"v2\"},\"name\":\"reviews-v2\",\"namespace\":\"bookinfo\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"reviews\",\"version\":\"v2\"}},\"template\":{\"metadata\":{\"labels\":{\"app\":\"reviews\",\"version\":\"v2\"}},\"spec\":{\"containers\":[{\"env\":[{\"name\":\"LOG_DIR\",\"value\":\"/tmp/logs\"}],\"image\":\"docker.io/istio/examples-bookinfo-reviews-v2:1.18.0\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"reviews\",\"ports\":[{\"containerPort\":9080}],\"volumeMounts\":[{\"mountPath\":\"/tmp\",\"name\":\"tmp\"},{\"mountPath\":\"/opt/ibm/wlp/output\",\"name\":\"wlp-output\"}]}],\"serviceAccountName\":\"bookinfo-reviews\",\"volumes\":[{\"emptyDir\":{},\"name\":\"wlp-output\"},{\"emptyDir\":{},\"name\":\"tmp\"}]}}}}\n" + }, + "healthAnnotations": {}, + "istioReferences": [], + "dashboardAnnotations": null, + "serviceAccountNames": ["bookinfo-reviews"], + "health": { + "workloadStatus": { + "name": "reviews-v2", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": 1 + }, + "requests": { + "inbound": { + "http": { + "200": 0.34358974358974353 + } + }, + "outbound": { + "http": { + "200": 0.34358974358974353 + } + }, + "healthAnnotations": {} + } + } + }, + { + "name": "reviews-v3", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-01-29T20:10:15Z", + "resourceVersion": "26840", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "reviews", + "version": "v3" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 1, + "annotations": { + "deployment.kubernetes.io/revision": "1", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"reviews\",\"version\":\"v3\"},\"name\":\"reviews-v3\",\"namespace\":\"bookinfo\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"reviews\",\"version\":\"v3\"}},\"template\":{\"metadata\":{\"labels\":{\"app\":\"reviews\",\"version\":\"v3\"}},\"spec\":{\"containers\":[{\"env\":[{\"name\":\"LOG_DIR\",\"value\":\"/tmp/logs\"}],\"image\":\"docker.io/istio/examples-bookinfo-reviews-v3:1.18.0\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"reviews\",\"ports\":[{\"containerPort\":9080}],\"volumeMounts\":[{\"mountPath\":\"/tmp\",\"name\":\"tmp\"},{\"mountPath\":\"/opt/ibm/wlp/output\",\"name\":\"wlp-output\"}]}],\"serviceAccountName\":\"bookinfo-reviews\",\"volumes\":[{\"emptyDir\":{},\"name\":\"wlp-output\"},{\"emptyDir\":{},\"name\":\"tmp\"}]}}}}\n" + }, + "healthAnnotations": {}, + "istioReferences": [], + "dashboardAnnotations": null, + "serviceAccountNames": ["bookinfo-reviews"], + "health": { + "workloadStatus": { + "name": "reviews-v3", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": 1 + }, + "requests": { + "inbound": { + "http": { + "200": 0.34700854700854694 + } + }, + "outbound": { + "http": { + "200": 0.34700854700854694 + } + }, + "healthAnnotations": {} + } + } + } + ], + "validations": { + "workload": { + "details-v1.bookinfo": { + "name": "details-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "kiali-traffic-generator.bookinfo": { + "name": "kiali-traffic-generator", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "productpage-v1.bookinfo": { + "name": "productpage-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "ratings-v1.bookinfo": { + "name": "ratings-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "reviews-v1.bookinfo": { + "name": "reviews-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "reviews-v2.bookinfo": { + "name": "reviews-v2", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "reviews-v3.bookinfo": { + "name": "reviews-v3", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + } + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads.json b/plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads.json new file mode 100644 index 0000000000..5035889528 --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads.json @@ -0,0 +1,374 @@ +{ + "namespace": { + "name": "istio-system", + "cluster": "", + "isAmbient": false, + "labels": null, + "annotations": null + }, + "workloads": [ + { + "name": "grafana", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-01-29T20:09:28Z", + "resourceVersion": "756", + "istioSidecar": false, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app.kubernetes.io/instance": "grafana", + "app.kubernetes.io/name": "grafana", + "sidecar.istio.io/inject": "false" + }, + "appLabel": false, + "versionLabel": false, + "podCount": 1, + "annotations": { + "deployment.kubernetes.io/revision": "1", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"app.kubernetes.io/instance\":\"grafana\",\"app.kubernetes.io/managed-by\":\"Helm\",\"app.kubernetes.io/name\":\"grafana\",\"app.kubernetes.io/version\":\"10.1.5\",\"helm.sh/chart\":\"grafana-6.61.1\"},\"name\":\"grafana\",\"namespace\":\"istio-system\"},\"spec\":{\"replicas\":1,\"revisionHistoryLimit\":10,\"selector\":{\"matchLabels\":{\"app.kubernetes.io/instance\":\"grafana\",\"app.kubernetes.io/name\":\"grafana\"}},\"strategy\":{\"type\":\"RollingUpdate\"},\"template\":{\"metadata\":{\"annotations\":{\"checksum/config\":\"e6dc57066020dcc7ec73db1b5e39370203985e5071b47f1f1414ee5c42679d46\",\"checksum/dashboards-json-config\":\"01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b\",\"checksum/sc-dashboard-provider-config\":\"01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b\",\"kubectl.kubernetes.io/default-container\":\"grafana\"},\"labels\":{\"app.kubernetes.io/instance\":\"grafana\",\"app.kubernetes.io/name\":\"grafana\",\"sidecar.istio.io/inject\":\"false\"}},\"spec\":{\"automountServiceAccountToken\":true,\"containers\":[{\"env\":[{\"name\":\"POD_IP\",\"valueFrom\":{\"fieldRef\":{\"fieldPath\":\"status.podIP\"}}},{\"name\":\"GF_PATHS_DATA\",\"value\":\"/var/lib/grafana/\"},{\"name\":\"GF_PATHS_LOGS\",\"value\":\"/var/log/grafana\"},{\"name\":\"GF_PATHS_PLUGINS\",\"value\":\"/var/lib/grafana/plugins\"},{\"name\":\"GF_PATHS_PROVISIONING\",\"value\":\"/etc/grafana/provisioning\"},{\"name\":\"GF_AUTH_ANONYMOUS_ENABLED\",\"value\":\"true\"},{\"name\":\"GF_AUTH_ANONYMOUS_ORG_ROLE\",\"value\":\"Admin\"},{\"name\":\"GF_AUTH_BASIC_ENABLED\",\"value\":\"false\"},{\"name\":\"GF_SECURITY_ADMIN_PASSWORD\",\"value\":\"admin\"},{\"name\":\"GF_SECURITY_ADMIN_USER\",\"value\":\"admin\"}],\"image\":\"docker.io/grafana/grafana:10.1.5\",\"imagePullPolicy\":\"IfNotPresent\",\"livenessProbe\":{\"failureThreshold\":10,\"httpGet\":{\"path\":\"/api/health\",\"port\":3000},\"initialDelaySeconds\":60,\"timeoutSeconds\":30},\"name\":\"grafana\",\"ports\":[{\"containerPort\":3000,\"name\":\"grafana\",\"protocol\":\"TCP\"},{\"containerPort\":9094,\"name\":\"gossip-tcp\",\"protocol\":\"TCP\"},{\"containerPort\":9094,\"name\":\"gossip-udp\",\"protocol\":\"UDP\"}],\"readinessProbe\":{\"httpGet\":{\"path\":\"/api/health\",\"port\":3000}},\"securityContext\":{\"allowPrivilegeEscalation\":false,\"capabilities\":{\"drop\":[\"ALL\"]},\"seccompProfile\":{\"type\":\"RuntimeDefault\"}},\"volumeMounts\":[{\"mountPath\":\"/etc/grafana/grafana.ini\",\"name\":\"config\",\"subPath\":\"grafana.ini\"},{\"mountPath\":\"/var/lib/grafana\",\"name\":\"storage\"},{\"mountPath\":\"/var/lib/grafana/dashboards/istio\",\"name\":\"dashboards-istio\"},{\"mountPath\":\"/var/lib/grafana/dashboards/istio-services\",\"name\":\"dashboards-istio-services\"},{\"mountPath\":\"/etc/grafana/provisioning/datasources/datasources.yaml\",\"name\":\"config\",\"subPath\":\"datasources.yaml\"},{\"mountPath\":\"/etc/grafana/provisioning/dashboards/dashboardproviders.yaml\",\"name\":\"config\",\"subPath\":\"dashboardproviders.yaml\"}]}],\"enableServiceLinks\":true,\"serviceAccountName\":\"grafana\",\"volumes\":[{\"configMap\":{\"name\":\"grafana\"},\"name\":\"config\"},{\"configMap\":{\"name\":\"istio-grafana-dashboards\"},\"name\":\"dashboards-istio\"},{\"configMap\":{\"name\":\"istio-services-grafana-dashboards\"},\"name\":\"dashboards-istio-services\"},{\"emptyDir\":{},\"name\":\"storage\"}]}}}}\n" + }, + "healthAnnotations": {}, + "istioReferences": [], + "dashboardAnnotations": null, + "serviceAccountNames": ["grafana"], + "health": { + "workloadStatus": { + "name": "grafana", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": -1 + }, + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + }, + { + "name": "istio-egressgateway", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-01-29T20:09:17Z", + "resourceVersion": "26870", + "istioSidecar": false, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "istio-egressgateway", + "chart": "gateways", + "heritage": "Tiller", + "install.operator.istio.io/owning-resource": "unknown", + "istio": "egressgateway", + "istio.io/rev": "default", + "operator.istio.io/component": "EgressGateways", + "release": "istio", + "service.istio.io/canonical-name": "istio-egressgateway", + "service.istio.io/canonical-revision": "latest", + "sidecar.istio.io/inject": "false" + }, + "appLabel": true, + "versionLabel": false, + "podCount": 1, + "annotations": { + "deployment.kubernetes.io/revision": "1" + }, + "healthAnnotations": {}, + "istioReferences": [], + "dashboardAnnotations": null, + "serviceAccountNames": ["istio-egressgateway-service-account"], + "health": { + "workloadStatus": { + "name": "istio-egressgateway", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": -1 + }, + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + }, + { + "name": "istio-ingressgateway", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-01-29T20:09:17Z", + "resourceVersion": "26868", + "istioSidecar": false, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "istio-ingressgateway", + "chart": "gateways", + "heritage": "Tiller", + "install.operator.istio.io/owning-resource": "unknown", + "istio": "ingressgateway", + "istio.io/rev": "default", + "operator.istio.io/component": "IngressGateways", + "release": "istio", + "service.istio.io/canonical-name": "istio-ingressgateway", + "service.istio.io/canonical-revision": "latest", + "sidecar.istio.io/inject": "false" + }, + "appLabel": true, + "versionLabel": false, + "podCount": 1, + "annotations": { + "deployment.kubernetes.io/revision": "1" + }, + "healthAnnotations": {}, + "istioReferences": [], + "dashboardAnnotations": null, + "serviceAccountNames": ["istio-ingressgateway-service-account"], + "health": { + "workloadStatus": { + "name": "istio-ingressgateway", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": -1 + }, + "requests": { + "inbound": {}, + "outbound": { + "http": { + "200": 1.0000017094046316 + } + }, + "healthAnnotations": {} + } + } + }, + { + "name": "istiod", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-01-29T20:09:07Z", + "resourceVersion": "541", + "istioSidecar": false, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "istiod", + "install.operator.istio.io/owning-resource": "unknown", + "istio": "pilot", + "istio.io/rev": "default", + "operator.istio.io/component": "Pilot", + "sidecar.istio.io/inject": "false" + }, + "appLabel": true, + "versionLabel": false, + "podCount": 1, + "annotations": { + "deployment.kubernetes.io/revision": "1" + }, + "healthAnnotations": {}, + "istioReferences": [], + "dashboardAnnotations": null, + "serviceAccountNames": ["istiod"], + "health": { + "workloadStatus": { + "name": "istiod", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": -1 + }, + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + }, + { + "name": "jaeger", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-01-29T20:09:28Z", + "resourceVersion": "772", + "istioSidecar": false, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "jaeger", + "sidecar.istio.io/inject": "false" + }, + "appLabel": true, + "versionLabel": false, + "podCount": 1, + "annotations": { + "deployment.kubernetes.io/revision": "1", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"jaeger\"},\"name\":\"jaeger\",\"namespace\":\"istio-system\"},\"spec\":{\"selector\":{\"matchLabels\":{\"app\":\"jaeger\"}},\"template\":{\"metadata\":{\"annotations\":{\"prometheus.io/port\":\"14269\",\"prometheus.io/scrape\":\"true\"},\"labels\":{\"app\":\"jaeger\",\"sidecar.istio.io/inject\":\"false\"}},\"spec\":{\"containers\":[{\"env\":[{\"name\":\"BADGER_EPHEMERAL\",\"value\":\"false\"},{\"name\":\"SPAN_STORAGE_TYPE\",\"value\":\"badger\"},{\"name\":\"BADGER_DIRECTORY_VALUE\",\"value\":\"/badger/data\"},{\"name\":\"BADGER_DIRECTORY_KEY\",\"value\":\"/badger/key\"},{\"name\":\"COLLECTOR_ZIPKIN_HOST_PORT\",\"value\":\":9411\"},{\"name\":\"MEMORY_MAX_TRACES\",\"value\":\"50000\"},{\"name\":\"QUERY_BASE_PATH\",\"value\":\"/jaeger\"}],\"image\":\"docker.io/jaegertracing/all-in-one:1.46\",\"livenessProbe\":{\"httpGet\":{\"path\":\"/\",\"port\":14269}},\"name\":\"jaeger\",\"readinessProbe\":{\"httpGet\":{\"path\":\"/\",\"port\":14269}},\"resources\":{\"requests\":{\"cpu\":\"10m\"}},\"volumeMounts\":[{\"mountPath\":\"/badger\",\"name\":\"data\"}]}],\"volumes\":[{\"emptyDir\":{},\"name\":\"data\"}]}}}}\n" + }, + "healthAnnotations": {}, + "istioReferences": [], + "dashboardAnnotations": null, + "serviceAccountNames": ["default"], + "health": { + "workloadStatus": { + "name": "jaeger", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": -1 + }, + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + }, + { + "name": "kiali", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-01-29T20:10:06Z", + "resourceVersion": "1056", + "istioSidecar": false, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "kiali", + "app.kubernetes.io/instance": "kiali", + "app.kubernetes.io/managed-by": "Helm", + "app.kubernetes.io/name": "kiali", + "app.kubernetes.io/part-of": "kiali", + "app.kubernetes.io/version": "v1.79.0", + "helm.sh/chart": "kiali-server-1.79.0", + "sidecar.istio.io/inject": "false", + "version": "v1.79.0" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 1, + "annotations": { + "deployment.kubernetes.io/revision": "1", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"kiali\",\"app.kubernetes.io/instance\":\"kiali\",\"app.kubernetes.io/managed-by\":\"Helm\",\"app.kubernetes.io/name\":\"kiali\",\"app.kubernetes.io/part-of\":\"kiali\",\"app.kubernetes.io/version\":\"v1.79.0\",\"helm.sh/chart\":\"kiali-server-1.79.0\",\"version\":\"v1.79.0\"},\"name\":\"kiali\",\"namespace\":\"istio-system\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app.kubernetes.io/instance\":\"kiali\",\"app.kubernetes.io/name\":\"kiali\"}},\"strategy\":{\"rollingUpdate\":{\"maxSurge\":1,\"maxUnavailable\":1},\"type\":\"RollingUpdate\"},\"template\":{\"metadata\":{\"annotations\":{\"checksum/config\":\"ce97ed2896a797e7aace42e18922786c78956f2ee862cfae70cc24ff1a9a600f\",\"kiali.io/dashboards\":\"go,kiali\",\"prometheus.io/port\":\"\",\"prometheus.io/scrape\":\"false\"},\"labels\":{\"app\":\"kiali\",\"app.kubernetes.io/instance\":\"kiali\",\"app.kubernetes.io/managed-by\":\"Helm\",\"app.kubernetes.io/name\":\"kiali\",\"app.kubernetes.io/part-of\":\"kiali\",\"app.kubernetes.io/version\":\"v1.79.0\",\"helm.sh/chart\":\"kiali-server-1.79.0\",\"sidecar.istio.io/inject\":\"false\",\"version\":\"v1.79.0\"},\"name\":\"kiali\"},\"spec\":{\"containers\":[{\"command\":[\"/opt/kiali/kiali\",\"-config\",\"/kiali-configuration/config.yaml\"],\"env\":[{\"name\":\"ACTIVE_NAMESPACE\",\"valueFrom\":{\"fieldRef\":{\"fieldPath\":\"metadata.namespace\"}}},{\"name\":\"LOG_LEVEL\",\"value\":\"info\"},{\"name\":\"LOG_FORMAT\",\"value\":\"text\"},{\"name\":\"LOG_TIME_FIELD_FORMAT\",\"value\":\"2006-01-02T15:04:05Z07:00\"},{\"name\":\"LOG_SAMPLER_RATE\",\"value\":\"1\"}],\"image\":\"quay.io/kiali/kiali:v1.79\",\"imagePullPolicy\":\"Always\",\"livenessProbe\":{\"httpGet\":{\"path\":\"/kiali/healthz\",\"port\":\"api-port\",\"scheme\":\"HTTP\"},\"initialDelaySeconds\":5,\"periodSeconds\":30},\"name\":\"kiali\",\"ports\":[{\"containerPort\":20001,\"name\":\"api-port\"}],\"readinessProbe\":{\"httpGet\":{\"path\":\"/kiali/healthz\",\"port\":\"api-port\",\"scheme\":\"HTTP\"},\"initialDelaySeconds\":5,\"periodSeconds\":30},\"resources\":{\"limits\":{\"memory\":\"1Gi\"},\"requests\":{\"cpu\":\"10m\",\"memory\":\"64Mi\"}},\"securityContext\":{\"allowPrivilegeEscalation\":false,\"capabilities\":{\"drop\":[\"ALL\"]},\"privileged\":false,\"readOnlyRootFilesystem\":true,\"runAsNonRoot\":true},\"volumeMounts\":[{\"mountPath\":\"/kiali-configuration\",\"name\":\"kiali-configuration\"},{\"mountPath\":\"/kiali-cert\",\"name\":\"kiali-cert\"},{\"mountPath\":\"/kiali-secret\",\"name\":\"kiali-secret\"},{\"mountPath\":\"/kiali-cabundle\",\"name\":\"kiali-cabundle\"}]}],\"serviceAccountName\":\"kiali\",\"volumes\":[{\"configMap\":{\"name\":\"kiali\"},\"name\":\"kiali-configuration\"},{\"name\":\"kiali-cert\",\"secret\":{\"optional\":true,\"secretName\":\"istio.kiali-service-account\"}},{\"name\":\"kiali-secret\",\"secret\":{\"optional\":true,\"secretName\":\"kiali\"}},{\"configMap\":{\"name\":\"kiali-cabundle\",\"optional\":true},\"name\":\"kiali-cabundle\"}]}}}}\n" + }, + "healthAnnotations": {}, + "istioReferences": [], + "dashboardAnnotations": null, + "serviceAccountNames": ["kiali"], + "health": { + "workloadStatus": { + "name": "kiali", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": -1 + }, + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + }, + { + "name": "prometheus", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-01-29T20:09:28Z", + "resourceVersion": "26771", + "istioSidecar": false, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app.kubernetes.io/component": "server", + "app.kubernetes.io/instance": "prometheus", + "app.kubernetes.io/managed-by": "Helm", + "app.kubernetes.io/name": "prometheus", + "app.kubernetes.io/part-of": "prometheus", + "app.kubernetes.io/version": "v2.48.0", + "helm.sh/chart": "prometheus-25.8.0", + "sidecar.istio.io/inject": "false" + }, + "appLabel": false, + "versionLabel": false, + "podCount": 1, + "annotations": { + "deployment.kubernetes.io/revision": "1", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"app.kubernetes.io/component\":\"server\",\"app.kubernetes.io/instance\":\"prometheus\",\"app.kubernetes.io/managed-by\":\"Helm\",\"app.kubernetes.io/name\":\"prometheus\",\"app.kubernetes.io/part-of\":\"prometheus\",\"app.kubernetes.io/version\":\"v2.48.0\",\"helm.sh/chart\":\"prometheus-25.8.0\"},\"name\":\"prometheus\",\"namespace\":\"istio-system\"},\"spec\":{\"replicas\":1,\"revisionHistoryLimit\":10,\"selector\":{\"matchLabels\":{\"app.kubernetes.io/component\":\"server\",\"app.kubernetes.io/instance\":\"prometheus\",\"app.kubernetes.io/name\":\"prometheus\"}},\"strategy\":{\"rollingUpdate\":null,\"type\":\"Recreate\"},\"template\":{\"metadata\":{\"labels\":{\"app.kubernetes.io/component\":\"server\",\"app.kubernetes.io/instance\":\"prometheus\",\"app.kubernetes.io/managed-by\":\"Helm\",\"app.kubernetes.io/name\":\"prometheus\",\"app.kubernetes.io/part-of\":\"prometheus\",\"app.kubernetes.io/version\":\"v2.48.0\",\"helm.sh/chart\":\"prometheus-25.8.0\",\"sidecar.istio.io/inject\":\"false\"}},\"spec\":{\"containers\":[{\"args\":[\"--watched-dir=/etc/config\",\"--reload-url=http://127.0.0.1:9090/-/reload\"],\"image\":\"ghcr.io/prometheus-operator/prometheus-config-reloader:v0.67.0\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"prometheus-server-configmap-reload\",\"volumeMounts\":[{\"mountPath\":\"/etc/config\",\"name\":\"config-volume\",\"readOnly\":true}]},{\"args\":[\"--storage.tsdb.retention.time=15d\",\"--config.file=/etc/config/prometheus.yml\",\"--storage.tsdb.path=/data\",\"--web.console.libraries=/etc/prometheus/console_libraries\",\"--web.console.templates=/etc/prometheus/consoles\",\"--web.enable-lifecycle\"],\"image\":\"prom/prometheus:v2.48.0\",\"imagePullPolicy\":\"IfNotPresent\",\"livenessProbe\":{\"failureThreshold\":3,\"httpGet\":{\"path\":\"/-/healthy\",\"port\":9090,\"scheme\":\"HTTP\"},\"initialDelaySeconds\":30,\"periodSeconds\":15,\"successThreshold\":1,\"timeoutSeconds\":10},\"name\":\"prometheus-server\",\"ports\":[{\"containerPort\":9090}],\"readinessProbe\":{\"failureThreshold\":3,\"httpGet\":{\"path\":\"/-/ready\",\"port\":9090,\"scheme\":\"HTTP\"},\"initialDelaySeconds\":0,\"periodSeconds\":5,\"successThreshold\":1,\"timeoutSeconds\":4},\"volumeMounts\":[{\"mountPath\":\"/etc/config\",\"name\":\"config-volume\"},{\"mountPath\":\"/data\",\"name\":\"storage-volume\",\"subPath\":\"\"}]}],\"dnsPolicy\":\"ClusterFirst\",\"enableServiceLinks\":true,\"serviceAccountName\":\"prometheus\",\"terminationGracePeriodSeconds\":300,\"volumes\":[{\"configMap\":{\"name\":\"prometheus\"},\"name\":\"config-volume\"},{\"emptyDir\":{},\"name\":\"storage-volume\"}]}}}}\n" + }, + "healthAnnotations": {}, + "istioReferences": [], + "dashboardAnnotations": null, + "serviceAccountNames": ["prometheus"], + "health": { + "workloadStatus": { + "name": "prometheus", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": -1 + }, + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + } + ], + "validations": { + "workload": { + "grafana.istio-system": { + "name": "grafana", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "istio-egressgateway.istio-system": { + "name": "istio-egressgateway", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "istio-ingressgateway.istio-system": { + "name": "istio-ingressgateway", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "istiod.istio-system": { + "name": "istiod", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "jaeger.istio-system": { + "name": "jaeger", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "kiali.istio-system": { + "name": "kiali", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "prometheus.istio-system": { + "name": "prometheus", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + } + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads.json b/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads.json new file mode 100644 index 0000000000..63d7aade5d --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads.json @@ -0,0 +1,337 @@ +{ + "namespace": { + "name": "travel-agency", + "cluster": "", + "isAmbient": false, + "labels": null, + "annotations": null + }, + "workloads": [ + { + "name": "cars-v1", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-01-31T14:07:05Z", + "resourceVersion": "81047", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "cars", + "version": "v1" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 1, + "annotations": { + "deployment.kubernetes.io/revision": "1", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"name\":\"cars-v1\",\"namespace\":\"travel-agency\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"cars\",\"version\":\"v1\"}},\"template\":{\"metadata\":{\"annotations\":{\"proxy.istio.io/config\":\"tracing:\\n zipkin:\\n address: zipkin.istio-system:9411\\n sampling: 10\\n custom_tags:\\n http.header.portal:\\n header:\\n name: portal\\n http.header.device:\\n header:\\n name: device\\n http.header.user:\\n header:\\n name: user\\n http.header.travel:\\n header:\\n name: travel\\n\",\"readiness.status.sidecar.istio.io/applicationPorts\":\"\"},\"labels\":{\"app\":\"cars\",\"version\":\"v1\"}},\"spec\":{\"containers\":[{\"env\":[{\"name\":\"CURRENT_SERVICE\",\"value\":\"cars\"},{\"name\":\"CURRENT_VERSION\",\"value\":\"v1\"},{\"name\":\"LISTEN_ADDRESS\",\"value\":\":8000\"},{\"name\":\"DISCOUNTS_SERVICE\",\"value\":\"http://discounts.travel-agency:8000\"},{\"name\":\"MYSQL_SERVICE\",\"value\":\"mysqldb.travel-agency:3306\"},{\"name\":\"MYSQL_USER\",\"value\":\"root\"},{\"name\":\"MYSQL_PASSWORD\",\"valueFrom\":{\"secretKeyRef\":{\"key\":\"rootpasswd\",\"name\":\"mysql-credentials\"}}},{\"name\":\"MYSQL_DATABASE\",\"value\":\"test\"}],\"image\":\"quay.io/kiali/demo_travels_cars:v1\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"cars\",\"ports\":[{\"containerPort\":8000}],\"securityContext\":{\"allowPrivilegeEscalation\":false,\"capabilities\":{\"drop\":[\"ALL\"]},\"privileged\":false,\"readOnlyRootFilesystem\":true}}]}}}}\n" + }, + "healthAnnotations": {}, + "istioReferences": [], + "dashboardAnnotations": null, + "serviceAccountNames": ["default"], + "health": { + "workloadStatus": { + "name": "cars-v1", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 0, + "syncedProxies": -1 + }, + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + }, + { + "name": "discounts-v1", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-01-31T14:07:05Z", + "resourceVersion": "81336", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "discounts", + "version": "v1" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 1, + "annotations": { + "deployment.kubernetes.io/revision": "1", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"name\":\"discounts-v1\",\"namespace\":\"travel-agency\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"discounts\",\"version\":\"v1\"}},\"template\":{\"metadata\":{\"annotations\":{\"proxy.istio.io/config\":\"tracing:\\n zipkin:\\n address: zipkin.istio-system:9411\\n sampling: 10\\n custom_tags:\\n http.header.portal:\\n header:\\n name: portal\\n http.header.device:\\n header:\\n name: device\\n http.header.user:\\n header:\\n name: user\\n http.header.travel:\\n header:\\n name: travel\\n\",\"readiness.status.sidecar.istio.io/applicationPorts\":\"\"},\"labels\":{\"app\":\"discounts\",\"version\":\"v1\"}},\"spec\":{\"containers\":[{\"env\":[{\"name\":\"CURRENT_SERVICE\",\"value\":\"discounts\"},{\"name\":\"CURRENT_VERSION\",\"value\":\"v1\"},{\"name\":\"LISTEN_ADDRESS\",\"value\":\":8000\"}],\"image\":\"quay.io/kiali/demo_travels_discounts:v1\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"discounts\",\"ports\":[{\"containerPort\":8000}],\"securityContext\":{\"allowPrivilegeEscalation\":false,\"capabilities\":{\"drop\":[\"ALL\"]},\"privileged\":false,\"readOnlyRootFilesystem\":true}}]}}}}\n" + }, + "healthAnnotations": {}, + "istioReferences": [], + "dashboardAnnotations": null, + "serviceAccountNames": ["default"], + "health": { + "workloadStatus": { + "name": "discounts-v1", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": -1 + }, + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + }, + { + "name": "flights-v1", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-01-31T14:07:05Z", + "resourceVersion": "81285", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "flights", + "version": "v1" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 1, + "annotations": { + "deployment.kubernetes.io/revision": "1", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"name\":\"flights-v1\",\"namespace\":\"travel-agency\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"flights\",\"version\":\"v1\"}},\"template\":{\"metadata\":{\"annotations\":{\"proxy.istio.io/config\":\"tracing:\\n zipkin:\\n address: zipkin.istio-system:9411\\n sampling: 10\\n custom_tags:\\n http.header.portal:\\n header:\\n name: portal\\n http.header.device:\\n header:\\n name: device\\n http.header.user:\\n header:\\n name: user\\n http.header.travel:\\n header:\\n name: travel\\n\",\"readiness.status.sidecar.istio.io/applicationPorts\":\"\"},\"labels\":{\"app\":\"flights\",\"version\":\"v1\"}},\"spec\":{\"containers\":[{\"env\":[{\"name\":\"CURRENT_SERVICE\",\"value\":\"flights\"},{\"name\":\"CURRENT_VERSION\",\"value\":\"v1\"},{\"name\":\"LISTEN_ADDRESS\",\"value\":\":8000\"},{\"name\":\"DISCOUNTS_SERVICE\",\"value\":\"http://discounts.travel-agency:8000\"},{\"name\":\"MYSQL_SERVICE\",\"value\":\"mysqldb.travel-agency:3306\"},{\"name\":\"MYSQL_USER\",\"value\":\"root\"},{\"name\":\"MYSQL_PASSWORD\",\"valueFrom\":{\"secretKeyRef\":{\"key\":\"rootpasswd\",\"name\":\"mysql-credentials\"}}},{\"name\":\"MYSQL_DATABASE\",\"value\":\"test\"}],\"image\":\"quay.io/kiali/demo_travels_flights:v1\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"flights\",\"ports\":[{\"containerPort\":8000}],\"securityContext\":{\"allowPrivilegeEscalation\":false,\"capabilities\":{\"drop\":[\"ALL\"]},\"privileged\":false,\"readOnlyRootFilesystem\":true}}]}}}}\n" + }, + "healthAnnotations": {}, + "istioReferences": [], + "dashboardAnnotations": null, + "serviceAccountNames": ["default"], + "health": { + "workloadStatus": { + "name": "flights-v1", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": 1 + }, + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + }, + { + "name": "hotels-v1", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-01-31T14:07:05Z", + "resourceVersion": "81106", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "hotels", + "version": "v1" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 1, + "annotations": { + "deployment.kubernetes.io/revision": "1", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"name\":\"hotels-v1\",\"namespace\":\"travel-agency\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"hotels\",\"version\":\"v1\"}},\"template\":{\"metadata\":{\"annotations\":{\"proxy.istio.io/config\":\"tracing:\\n zipkin:\\n address: zipkin.istio-system:9411\\n sampling: 10\\n custom_tags:\\n http.header.portal:\\n header:\\n name: portal\\n http.header.device:\\n header:\\n name: device\\n http.header.user:\\n header:\\n name: user\\n http.header.travel:\\n header:\\n name: travel\\n\",\"readiness.status.sidecar.istio.io/applicationPorts\":\"\"},\"labels\":{\"app\":\"hotels\",\"version\":\"v1\"}},\"spec\":{\"containers\":[{\"env\":[{\"name\":\"CURRENT_SERVICE\",\"value\":\"hotels\"},{\"name\":\"CURRENT_VERSION\",\"value\":\"v1\"},{\"name\":\"LISTEN_ADDRESS\",\"value\":\":8000\"},{\"name\":\"DISCOUNTS_SERVICE\",\"value\":\"http://discounts.travel-agency:8000\"},{\"name\":\"MYSQL_SERVICE\",\"value\":\"mysqldb.travel-agency:3306\"},{\"name\":\"MYSQL_USER\",\"value\":\"root\"},{\"name\":\"MYSQL_PASSWORD\",\"valueFrom\":{\"secretKeyRef\":{\"key\":\"rootpasswd\",\"name\":\"mysql-credentials\"}}},{\"name\":\"MYSQL_DATABASE\",\"value\":\"test\"}],\"image\":\"quay.io/kiali/demo_travels_hotels:v1\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"hotels\",\"ports\":[{\"containerPort\":8000}],\"securityContext\":{\"allowPrivilegeEscalation\":false,\"capabilities\":{\"drop\":[\"ALL\"]},\"privileged\":false,\"readOnlyRootFilesystem\":true}}]}}}}\n" + }, + "healthAnnotations": {}, + "istioReferences": [], + "dashboardAnnotations": null, + "serviceAccountNames": ["default"], + "health": { + "workloadStatus": { + "name": "hotels-v1", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 0, + "syncedProxies": -1 + }, + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + }, + { + "name": "insurances-v1", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-01-31T14:07:05Z", + "resourceVersion": "81115", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "insurances", + "version": "v1" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 1, + "annotations": { + "deployment.kubernetes.io/revision": "1", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"name\":\"insurances-v1\",\"namespace\":\"travel-agency\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"insurances\",\"version\":\"v1\"}},\"template\":{\"metadata\":{\"annotations\":{\"readiness.status.sidecar.istio.io/applicationPorts\":\"\"},\"labels\":{\"app\":\"insurances\",\"version\":\"v1\"}},\"spec\":{\"containers\":[{\"env\":[{\"name\":\"CURRENT_SERVICE\",\"value\":\"insurances\"},{\"name\":\"CURRENT_VERSION\",\"value\":\"v1\"},{\"name\":\"LISTEN_ADDRESS\",\"value\":\":8000\"},{\"name\":\"DISCOUNTS_SERVICE\",\"value\":\"http://discounts.travel-agency:8000\"},{\"name\":\"MYSQL_SERVICE\",\"value\":\"mysqldb.travel-agency:3306\"},{\"name\":\"MYSQL_USER\",\"value\":\"root\"},{\"name\":\"MYSQL_PASSWORD\",\"valueFrom\":{\"secretKeyRef\":{\"key\":\"rootpasswd\",\"name\":\"mysql-credentials\"}}},{\"name\":\"MYSQL_DATABASE\",\"value\":\"test\"}],\"image\":\"quay.io/kiali/demo_travels_insurances:v1\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"insurances\",\"ports\":[{\"containerPort\":8000}],\"securityContext\":{\"allowPrivilegeEscalation\":false,\"capabilities\":{\"drop\":[\"ALL\"]},\"privileged\":false,\"readOnlyRootFilesystem\":true}}]}}}}\n" + }, + "healthAnnotations": {}, + "istioReferences": [], + "dashboardAnnotations": null, + "serviceAccountNames": ["default"], + "health": { + "workloadStatus": { + "name": "insurances-v1", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 0, + "syncedProxies": -1 + }, + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + }, + { + "name": "mysqldb-v1", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-01-31T14:07:05Z", + "resourceVersion": "81316", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "mysqldb", + "version": "v1" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 1, + "annotations": { + "deployment.kubernetes.io/revision": "1", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"mysqldb\",\"version\":\"v1\"},\"name\":\"mysqldb-v1\",\"namespace\":\"travel-agency\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"mysqldb\",\"version\":\"v1\"}},\"template\":{\"metadata\":{\"labels\":{\"app\":\"mysqldb\",\"version\":\"v1\"}},\"spec\":{\"containers\":[{\"args\":[\"--default-authentication-plugin\",\"mysql_native_password\"],\"env\":[{\"name\":\"MYSQL_ROOT_PASSWORD\",\"valueFrom\":{\"secretKeyRef\":{\"key\":\"rootpasswd\",\"name\":\"mysql-credentials\"}}}],\"image\":\"quay.io/kiali/demo_travels_mysqldb:v1\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"mysqldb\",\"ports\":[{\"containerPort\":3306}],\"volumeMounts\":[{\"mountPath\":\"/var/lib/mysql\",\"name\":\"var-lib-mysql\"}]}],\"volumes\":[{\"emptyDir\":null,\"name\":\"var-lib-mysql\"}]}}}}\n" + }, + "healthAnnotations": {}, + "istioReferences": [], + "dashboardAnnotations": null, + "serviceAccountNames": ["default"], + "health": { + "workloadStatus": { + "name": "mysqldb-v1", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": -1 + }, + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + }, + { + "name": "travels-v1", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-01-31T14:07:05Z", + "resourceVersion": "81119", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "travels", + "version": "v1" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 1, + "annotations": { + "deployment.kubernetes.io/revision": "1", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"name\":\"travels-v1\",\"namespace\":\"travel-agency\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"travels\",\"version\":\"v1\"}},\"template\":{\"metadata\":{\"annotations\":{\"proxy.istio.io/config\":\"tracing:\\n zipkin:\\n address: zipkin.istio-system:9411\\n sampling: 10\\n custom_tags:\\n http.header.portal:\\n header:\\n name: portal\\n http.header.device:\\n header:\\n name: device\\n http.header.user:\\n header:\\n name: user\\n http.header.travel:\\n header:\\n name: travel\\n\",\"readiness.status.sidecar.istio.io/applicationPorts\":\"\"},\"labels\":{\"app\":\"travels\",\"version\":\"v1\"}},\"spec\":{\"containers\":[{\"env\":[{\"name\":\"CURRENT_SERVICE\",\"value\":\"travels\"},{\"name\":\"CURRENT_VERSION\",\"value\":\"v1\"},{\"name\":\"LISTEN_ADDRESS\",\"value\":\":8000\"},{\"name\":\"FLIGHTS_SERVICE\",\"value\":\"http://flights.travel-agency:8000\"},{\"name\":\"HOTELS_SERVICE\",\"value\":\"http://hotels.travel-agency:8000\"},{\"name\":\"CARS_SERVICE\",\"value\":\"http://cars.travel-agency:8000\"},{\"name\":\"INSURANCES_SERVICE\",\"value\":\"http://insurances.travel-agency:8000\"}],\"image\":\"quay.io/kiali/demo_travels_travels:v1\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"travels\",\"ports\":[{\"containerPort\":8000}],\"securityContext\":{\"allowPrivilegeEscalation\":false,\"capabilities\":{\"drop\":[\"ALL\"]},\"privileged\":false,\"readOnlyRootFilesystem\":true}}]}}}}\n" + }, + "healthAnnotations": {}, + "istioReferences": [], + "dashboardAnnotations": null, + "serviceAccountNames": ["default"], + "health": { + "workloadStatus": { + "name": "travels-v1", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 0, + "syncedProxies": -1 + }, + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + } + ], + "validations": { + "workload": { + "cars-v1.travel-agency": { + "name": "cars-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "discounts-v1.travel-agency": { + "name": "discounts-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "flights-v1.travel-agency": { + "name": "flights-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "hotels-v1.travel-agency": { + "name": "hotels-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "insurances-v1.travel-agency": { + "name": "insurances-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "mysqldb-v1.travel-agency": { + "name": "mysqldb-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "travels-v1.travel-agency": { + "name": "travels-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + } + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/travel-control/workloads.json b/plugins/kiali/dev/__fixtures__/namespaces/travel-control/workloads.json new file mode 100644 index 0000000000..8f788699cd --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/travel-control/workloads.json @@ -0,0 +1,61 @@ +{ + "namespace": { + "name": "travel-control", + "cluster": "", + "isAmbient": false, + "labels": null, + "annotations": null + }, + "workloads": [ + { + "name": "control", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-01-31T14:07:06Z", + "resourceVersion": "81463", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "control", + "version": "v1" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 1, + "annotations": { + "deployment.kubernetes.io/revision": "1", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"name\":\"control\",\"namespace\":\"travel-control\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"control\",\"version\":\"v1\"}},\"template\":{\"metadata\":{\"annotations\":{\"readiness.status.sidecar.istio.io/applicationPorts\":\"\"},\"labels\":{\"app\":\"control\",\"version\":\"v1\"}},\"spec\":{\"containers\":[{\"env\":[{\"name\":\"PORTAL_SERVICES\",\"value\":\"voyages.fr;http://voyages.travel-portal:8000,viaggi.it;http://viaggi.travel-portal:8000,travels.uk;http://travels.travel-portal:8000\"}],\"image\":\"quay.io/kiali/demo_travels_control:v1\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"control\",\"ports\":[{\"containerPort\":8080}],\"securityContext\":{\"allowPrivilegeEscalation\":false,\"capabilities\":{\"drop\":[\"ALL\"]},\"privileged\":false,\"readOnlyRootFilesystem\":true}}]}}}}\n" + }, + "healthAnnotations": {}, + "istioReferences": [], + "dashboardAnnotations": null, + "serviceAccountNames": ["default"], + "health": { + "workloadStatus": { + "name": "control", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": 1 + }, + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + } + ], + "validations": { + "workload": { + "control.travel-control": { + "name": "control", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + } + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/travel-portal/workloads.json b/plugins/kiali/dev/__fixtures__/namespaces/travel-portal/workloads.json new file mode 100644 index 0000000000..ad60ded885 --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/travel-portal/workloads.json @@ -0,0 +1,165 @@ +{ + "namespace": { + "name": "travel-portal", + "cluster": "", + "isAmbient": false, + "labels": null, + "annotations": null + }, + "workloads": [ + { + "name": "travels", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-01-31T14:07:06Z", + "resourceVersion": "81433", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "travels", + "version": "v1" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 1, + "annotations": { + "deployment.kubernetes.io/revision": "1", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"name\":\"travels\",\"namespace\":\"travel-portal\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"travels\",\"version\":\"v1\"}},\"template\":{\"metadata\":{\"annotations\":{\"proxy.istio.io/config\":\"tracing:\\n zipkin:\\n address: zipkin.istio-system:9411\\n sampling: 10\\n custom_tags:\\n http.header.portal:\\n header:\\n name: portal\\n http.header.device:\\n header:\\n name: device\\n http.header.user:\\n header:\\n name: user\\n http.header.travel:\\n header:\\n name: travel\\n\",\"readiness.status.sidecar.istio.io/applicationPorts\":\"\"},\"labels\":{\"app\":\"travels\",\"version\":\"v1\"}},\"spec\":{\"containers\":[{\"env\":[{\"name\":\"LISTEN_ADDRESS\",\"value\":\":8000\"},{\"name\":\"PORTAL_COORDINATES\",\"value\":\"55.956245,-3.187915\"},{\"name\":\"PORTAL_COUNTRY\",\"value\":\"United Kingdom\"},{\"name\":\"PORTAL_NAME\",\"value\":\"travels.uk\"},{\"name\":\"TRAVELS_AGENCY_SERVICE\",\"value\":\"http://travels.travel-agency:8000\"}],\"image\":\"quay.io/kiali/demo_travels_portal:v1\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"control\",\"ports\":[{\"containerPort\":8000}],\"securityContext\":{\"allowPrivilegeEscalation\":false,\"capabilities\":{\"drop\":[\"ALL\"]},\"privileged\":false,\"readOnlyRootFilesystem\":true}}]}}}}\n" + }, + "healthAnnotations": {}, + "istioReferences": [], + "dashboardAnnotations": null, + "serviceAccountNames": ["default"], + "health": { + "workloadStatus": { + "name": "travels", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": 1 + }, + "requests": { + "inbound": {}, + "outbound": { + "http": { + "200": 0.2666666666666666 + } + }, + "healthAnnotations": {} + } + } + }, + { + "name": "viaggi", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-01-31T14:07:06Z", + "resourceVersion": "81479", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "viaggi", + "version": "v1" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 1, + "annotations": { + "deployment.kubernetes.io/revision": "1", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"name\":\"viaggi\",\"namespace\":\"travel-portal\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"viaggi\",\"version\":\"v1\"}},\"template\":{\"metadata\":{\"annotations\":{\"proxy.istio.io/config\":\"tracing:\\n zipkin:\\n address: zipkin.istio-system:9411\\n sampling: 10\\n custom_tags:\\n http.header.portal:\\n header:\\n name: portal\\n http.header.device:\\n header:\\n name: device\\n http.header.user:\\n header:\\n name: user\\n http.header.travel:\\n header:\\n name: travel\\n\",\"readiness.status.sidecar.istio.io/applicationPorts\":\"\"},\"labels\":{\"app\":\"viaggi\",\"version\":\"v1\"}},\"spec\":{\"containers\":[{\"env\":[{\"name\":\"LISTEN_ADDRESS\",\"value\":\":8000\"},{\"name\":\"PORTAL_COORDINATES\",\"value\":\"41.890668,12.492194\"},{\"name\":\"PORTAL_COUNTRY\",\"value\":\"Italy\"},{\"name\":\"PORTAL_NAME\",\"value\":\"viaggi.it\"},{\"name\":\"TRAVELS_AGENCY_SERVICE\",\"value\":\"http://travels.travel-agency:8000\"}],\"image\":\"quay.io/kiali/demo_travels_portal:v1\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"control\",\"ports\":[{\"containerPort\":8000}],\"securityContext\":{\"allowPrivilegeEscalation\":false,\"capabilities\":{\"drop\":[\"ALL\"]},\"privileged\":false,\"readOnlyRootFilesystem\":true}}]}}}}\n" + }, + "healthAnnotations": {}, + "istioReferences": [], + "dashboardAnnotations": null, + "serviceAccountNames": ["default"], + "health": { + "workloadStatus": { + "name": "viaggi", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": 1 + }, + "requests": { + "inbound": {}, + "outbound": { + "http": { + "200": 0.2644533333333333 + } + }, + "healthAnnotations": {} + } + } + }, + { + "name": "voyages", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-01-31T14:07:06Z", + "resourceVersion": "81495", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "voyages", + "version": "v1" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 1, + "annotations": { + "deployment.kubernetes.io/revision": "1", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"name\":\"voyages\",\"namespace\":\"travel-portal\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"voyages\",\"version\":\"v1\"}},\"template\":{\"metadata\":{\"annotations\":{\"proxy.istio.io/config\":\"tracing:\\n zipkin:\\n address: zipkin.istio-system:9411\\n sampling: 10\\n custom_tags:\\n http.header.portal:\\n header:\\n name: portal\\n http.header.device:\\n header:\\n name: device\\n http.header.user:\\n header:\\n name: user\\n http.header.travel:\\n header:\\n name: travel\\n\",\"readiness.status.sidecar.istio.io/applicationPorts\":\"\"},\"labels\":{\"app\":\"voyages\",\"version\":\"v1\"}},\"spec\":{\"containers\":[{\"env\":[{\"name\":\"LISTEN_ADDRESS\",\"value\":\":8000\"},{\"name\":\"PORTAL_COORDINATES\",\"value\":\"48.861310,2.337418\"},{\"name\":\"PORTAL_COUNTRY\",\"value\":\"France\"},{\"name\":\"PORTAL_NAME\",\"value\":\"voyages.fr\"},{\"name\":\"TRAVELS_AGENCY_SERVICE\",\"value\":\"http://travels.travel-agency:8000\"}],\"image\":\"quay.io/kiali/demo_travels_portal:v1\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"voyages\",\"ports\":[{\"containerPort\":8000}],\"securityContext\":{\"allowPrivilegeEscalation\":false,\"capabilities\":{\"drop\":[\"ALL\"]},\"privileged\":false,\"readOnlyRootFilesystem\":true}}]}}}}\n" + }, + "healthAnnotations": {}, + "istioReferences": [], + "dashboardAnnotations": null, + "serviceAccountNames": ["default"], + "health": { + "workloadStatus": { + "name": "voyages", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": 1 + }, + "requests": { + "inbound": {}, + "outbound": { + "http": { + "200": 0.2605377777777778 + } + }, + "healthAnnotations": {} + } + } + } + ], + "validations": { + "workload": { + "travels.travel-portal": { + "name": "travels", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "viaggi.travel-portal": { + "name": "viaggi", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "voyages.travel-portal": { + "name": "voyages", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + } + } + } +} diff --git a/plugins/kiali/dev/index.tsx b/plugins/kiali/dev/index.tsx index 06df658f98..11b0b4d012 100644 --- a/plugins/kiali/dev/index.tsx +++ b/plugins/kiali/dev/index.tsx @@ -1,16 +1,19 @@ import React from 'react'; +import { BrowserRouter, Route, Routes, useNavigate } from 'react-router-dom'; import { Entity } from '@backstage/catalog-model'; -import { Content, Page } from '@backstage/core-components'; +import { Content, HeaderTabs, Page } from '@backstage/core-components'; import { createDevApp } from '@backstage/dev-utils'; import { EntityProvider } from '@backstage/plugin-catalog-react'; import { TestApiProvider } from '@backstage/test-utils'; +import { kialiPlugin } from '../src'; +import { KialiNoPath } from '../src/pages/Kiali'; import { KialiHeader } from '../src/pages/Kiali/Header/KialiHeader'; import { KialiHeaderEntity } from '../src/pages/Kiali/Header/KialiHeaderEntity'; import { KialiEntity } from '../src/pages/Kiali/KialiEntity'; import { OverviewPage } from '../src/pages/Overview/OverviewPage'; -import { kialiPlugin } from '../src/plugin'; +import { WorkloadListPage } from '../src/pages/WorkloadList/WorkloadListPage'; import { KialiApi, kialiApiRef } from '../src/services/Api'; import { KialiProvider } from '../src/store/KialiProvider'; import { AuthInfo } from '../src/types/Auth'; @@ -40,6 +43,11 @@ import { Namespace } from '../src/types/Namespace'; import { ServerConfig } from '../src/types/ServerConfig'; import { StatusState } from '../src/types/StatusState'; import { TLSStatus } from '../src/types/TLSStatus'; +import { + WorkloadListItem, + WorkloadNamespaceResponse, + WorkloadOverview, +} from '../src/types/Workload'; import { filterNsByAnnotation } from '../src/utils/entityFilter'; import { kialiData } from './__fixtures__'; import { mockEntity } from './mockEntity'; @@ -73,6 +81,37 @@ class MockKialiClient implements KialiApi { ); } + async getWorkloads( + namespace: string, + duration: number, + ): Promise { + const nsl = kialiData.workloads as WorkloadNamespaceResponse[]; + // @ts-ignore + return nsl[namespace].workloads.map( + (w: WorkloadOverview): WorkloadListItem => { + return { + name: w.name, + namespace: namespace, + cluster: w.cluster, + type: w.type, + istioSidecar: w.istioSidecar, + istioAmbient: w.istioAmbient, + additionalDetailSample: undefined, + appLabel: w.appLabel, + versionLabel: w.versionLabel, + labels: w.labels, + istioReferences: w.istioReferences, + notCoveredAuthPolicy: w.notCoveredAuthPolicy, + health: WorkloadHealth.fromJson(namespace, w.name, w.health, { + rateInterval: duration, + hasSidecar: w.istioSidecar, + hasAmbient: w.istioAmbient, + }), + }; + }, + ); + } + async getServerConfig(): Promise { return kialiData.config; } @@ -244,20 +283,62 @@ class MockKialiClient implements KialiApi { } interface Props { - children: React.ReactNode; + children?: React.ReactNode; isEntity?: boolean; } +export const TabsMock = () => { + const [selectedTab, setSelectedTab] = React.useState(0); + const tabs = [ + { label: 'Overview', route: 'overview' }, + { label: 'Workloads', route: 'workloads' }, + ]; + const navigate = useNavigate(); + return ( + { + navigate(tabs[index].route); + setSelectedTab(index); + }} + tabs={tabs.map(({ label }, index) => ({ + id: index.toString(), + label, + }))} + /> + ); +}; + +const RoutesList = () => ( + + } /> + } /> + } /> + } /> + } /> + +); + const MockProvider = (props: Props) => { const content = ( - - {!props.isEntity && } - - {props.isEntity && } - {props.children} - - + + + {!props.isEntity && ( + <> + + + + + )} + {props.isEntity && ( + + + + + )} + + ); @@ -275,20 +356,12 @@ const MockProvider = (props: Props) => { createDevApp() .registerPlugin(kialiPlugin) .addPage({ - element: ( - - - - ), + element: , title: 'Kiali Overview', path: '/overview', }) .addPage({ - element: ( - - - - ), + element: , title: 'Kiali Entity', path: '/kiali', }) diff --git a/plugins/kiali/src/Router.tsx b/plugins/kiali/src/Router.tsx index dfbd71a36a..f1398cf93e 100644 --- a/plugins/kiali/src/Router.tsx +++ b/plugins/kiali/src/Router.tsx @@ -14,8 +14,10 @@ import { Button } from '@material-ui/core'; import { KialiNoPath } from './pages/Kiali'; import { KialiHeader } from './pages/Kiali/Header/KialiHeader'; import { KialiHeaderEntity } from './pages/Kiali/Header/KialiHeaderEntity'; +import { KialiTabs } from './pages/Kiali/Header/KialiTabs'; import { KialiEntity } from './pages/Kiali/KialiEntity'; import { OverviewPage } from './pages/Overview/OverviewPage'; +import { WorkloadListPage } from './pages/WorkloadList/WorkloadListPage'; import { KialiProvider } from './store/KialiProvider'; export const KUBERNETES_ANNOTATION = 'backstage.io/kubernetes-id'; @@ -70,10 +72,12 @@ export const Router = () => { + } /> } /> + } /> } /> diff --git a/plugins/kiali/src/components/DefaultSecondaryMasthead/DefaultSecondaryMasthead.tsx b/plugins/kiali/src/components/DefaultSecondaryMasthead/DefaultSecondaryMasthead.tsx new file mode 100644 index 0000000000..ecf9cdbc77 --- /dev/null +++ b/plugins/kiali/src/components/DefaultSecondaryMasthead/DefaultSecondaryMasthead.tsx @@ -0,0 +1,37 @@ +import React from 'react'; + +import { Grid, IconButton, Tooltip } from '@material-ui/core'; +import Refresh from '@material-ui/icons/Refresh'; + +type Props = { + hideNamespaceSelector?: boolean; + elements?: JSX.Element[]; + showClusterSelector?: boolean; + onRefresh: () => void; +}; + +export const DefaultSecondaryMasthead: React.FC = (props: Props) => { + const refresh = ( + + + + + + ); + + return ( + + {props.elements?.map(element => { + return element; + })} + + {refresh} + + + ); +}; diff --git a/plugins/kiali/src/components/FilterList/FitlerHelper.ts b/plugins/kiali/src/components/FilterList/FilterHelper.ts similarity index 100% rename from plugins/kiali/src/components/FilterList/FitlerHelper.ts rename to plugins/kiali/src/components/FilterList/FilterHelper.ts diff --git a/plugins/kiali/src/components/Filters/StatefulFilters.tsx b/plugins/kiali/src/components/Filters/StatefulFilters.tsx index ee8f4b0edb..3b79587b51 100644 --- a/plugins/kiali/src/components/Filters/StatefulFilters.tsx +++ b/plugins/kiali/src/components/Filters/StatefulFilters.tsx @@ -1,3 +1,5 @@ +import * as React from 'react'; + import { history, HistoryManager } from '../../app/History'; import { ActiveFilter, @@ -7,7 +9,7 @@ import { LabelOperation, ToggleType, } from '../../types/Filters'; -import * as FilterHelper from '../FilterList/FitlerHelper'; +import * as FilterHelper from '../FilterList/FilterHelper'; export class FilterSelected { static selectedFilters: ActiveFilter[] | undefined = undefined; @@ -82,3 +84,17 @@ export class Toggles { return new Map(Toggles.checked); }; } + +export interface StatefulFiltersProps { + childrenFirst?: boolean; + initialFilters: FilterType[]; + initialToggles?: ToggleType[]; + onFilterChange: (active: ActiveFiltersInfo) => void; + onToggleChange?: (active: ActiveTogglesInfo) => void; + ref?: React.RefObject; +} + +export interface StatefulFilters { + filterAdded(labelFilt: FilterType, label: string): unknown; + removeFilter(category: string, label: string): unknown; +} diff --git a/plugins/kiali/src/components/Health/HealthDetails.tsx b/plugins/kiali/src/components/Health/HealthDetails.tsx new file mode 100644 index 0000000000..a821bf5b3a --- /dev/null +++ b/plugins/kiali/src/components/Health/HealthDetails.tsx @@ -0,0 +1,119 @@ +import * as React from 'react'; + +import { KialiIcon } from '../../config'; +import * as H from '../../types/Health'; +import { PFColors } from '../Pf/PfColors'; +import { createIcon } from './Helper'; + +interface HealthDetailsProps { + health: H.Health; +} + +// @ts-ignore +export const HealthDetails: React.FC = ( + props: HealthDetailsProps, +) => { + const renderErrorRate = ( + item: H.HealthItem, + idx: number, + ): React.ReactNode => { + const config = props.health.getStatusConfig(); + + const isValueInConfig = + config && props.health.health.statusConfig + ? props.health.health.statusConfig.value > 0 + : false; + + const showTraffic = item.children + ? item.children.filter(sub => { + const showItem = sub.value && sub.value > 0; + + return showItem; + }).length > 0 + : false; + + return showTraffic ? ( +
+ <> + {`${item.title}${item.text && item.text.length > 0 ? ': ' : ''} `} + + {config && } + + + {item.text} + + {item.children && ( +
    + {item.children.map((sub, subIdx) => { + const showItem = sub.value && sub.value > 0; + + return showItem ? ( +
  • + + {createIcon(sub.status)} + + {sub.text} +
  • + ) : ( + + ); + })} + + {config && isValueInConfig && ( +
  • + + {createIcon(H.DEGRADED)} + + : {config.degraded === 0 ? '>' : '>='} + {config.degraded}% {createIcon(H.FAILURE)}:{' '} + {config.degraded === 0 ? '>' : '>='} + {config.failure}% +
  • + )} +
+ )} +
+ ) : ( + + ); + }; + + const renderChildren = (item: H.HealthItem, idx: number): React.ReactNode => { + return item.title.startsWith(H.TRAFFICSTATUS) ? ( + renderErrorRate(item, idx) + ) : ( +
+ <>{`${item.title}${item.text && item.text.length > 0 ? ': ' : ''}`} + + {item.text} + + {item.children && ( +
    + {item.children.map((sub, subIdx) => { + return ( +
  • + + {createIcon(sub.status)} + + + {sub.text} +
  • + ); + })} +
+ )} +
+ ); + }; + + const health = props.health; + + return ( + <> + {health.health.items.map((item, idx) => { + return renderChildren(item, idx); + })} + + ); + // @ts-ignore +}; diff --git a/plugins/kiali/src/components/Health/HealthIndicator.tsx b/plugins/kiali/src/components/Health/HealthIndicator.tsx new file mode 100644 index 0000000000..08b31e96dc --- /dev/null +++ b/plugins/kiali/src/components/Health/HealthIndicator.tsx @@ -0,0 +1,43 @@ +import * as React from 'react'; + +import { PopoverPosition, Tooltip } from '@patternfly/react-core'; + +import { createTooltipIcon } from '../../config/KialiIcon'; +import * as H from '../../types/Health'; +import { HealthDetails } from './HealthDetails'; +import { healthIndicatorStyle } from './HealthStyle'; +import { createIcon } from './Helper'; + +interface HealthIndicatorProps { + health?: H.Health; + id: string; + tooltipPlacement?: PopoverPosition; +} + +export const HealthIndicator: React.FC = ( + props: HealthIndicatorProps, +) => { + const globalStatus = props.health ? props.health.getGlobalStatus() : H.NA; + + if (props.health) { + const icon = createIcon(globalStatus); + + return ( + + {globalStatus.name} + + + } + position={PopoverPosition.auto} + className={healthIndicatorStyle} + > + {createTooltipIcon(icon)} + + ); + } + + return ; +}; diff --git a/plugins/kiali/src/components/Label/Label.tsx b/plugins/kiali/src/components/Label/Label.tsx new file mode 100644 index 0000000000..ef9bce4d48 --- /dev/null +++ b/plugins/kiali/src/components/Label/Label.tsx @@ -0,0 +1,42 @@ +import * as React from 'react'; + +import { Label as PfLabel } from '@patternfly/react-core'; + +import { kialiStyle } from '../../styles/StyleUtils'; +import { canRender } from '../../utils/SafeRender'; + +interface Props { + name: string; + onClick?: () => void; + style?: React.CSSProperties; + value: string; +} + +const labelStyle = kialiStyle({ + display: 'block', + float: 'left', + fontSize: 'var(--kiali-global--font-size)', + margin: '0 0.25rem 0.25rem 0', + maxWidth: '100%', +}); + +export const Label = (props: Props) => { + const { name, value } = props; + let label = 'This label has an unexpected format'; + + if (canRender(name) && canRender(value)) { + label = value && value.length > 0 ? `${name}=${value}` : name; + } + + return ( + + {label} + + ); +}; diff --git a/plugins/kiali/src/components/Link/IstioObjectLink.ts b/plugins/kiali/src/components/Link/IstioObjectLink.ts new file mode 100644 index 0000000000..078b356de1 --- /dev/null +++ b/plugins/kiali/src/components/Link/IstioObjectLink.ts @@ -0,0 +1,29 @@ +import { isMultiCluster, Paths } from '../../config'; +import { IstioTypes } from '../VirtualList/Config'; + +export const getIstioObjectUrl = ( + name: string, + namespace: string, + type: string, + cluster?: string, + query?: string, +): string => { + const istioType = IstioTypes[type]; + let to = `/namespaces/${namespace}/${Paths.ISTIO}`; + + to = `${to}/${istioType.url}/${name}`; + + if (cluster && isMultiCluster) { + to = `${to}?clusterName=${cluster}`; + } + + if (!!query) { + if (to.includes('?')) { + to = `${to}&${query}`; + } else { + to = `${to}?${query}`; + } + } + + return to; +}; diff --git a/plugins/kiali/src/components/Time/TimeDurationComponent.tsx b/plugins/kiali/src/components/Time/TimeDurationComponent.tsx new file mode 100644 index 0000000000..a0d0b28fae --- /dev/null +++ b/plugins/kiali/src/components/Time/TimeDurationComponent.tsx @@ -0,0 +1,33 @@ +import * as React from 'react'; + +import { Select } from '@backstage/core-components'; + +import { HistoryManager, URLParam } from '../../app/History'; +import { getDurationType } from '../../pages/Overview/OverviewToolbar'; + +type TimeControlsProps = { + disabled: boolean; + id: string; + duration: string; + label: string; + setDuration: React.Dispatch>; + supportsReplay?: boolean; +}; + +export const TimeDurationComponent: React.FC = ( + props: TimeControlsProps, +) => { + const updateDurationType = (duration: number) => { + HistoryManager.setParam(URLParam.DURATION, duration.toString()); + props.setDuration(duration); + }; + + return ( + updateOverviewType(e as String)} + label="Health for" + items={healthTypeItems} + selected={props.overviewType} + />, + updateOverviewType(e as String)} - label="Health for" - items={healthTypeItems} - selected={props.overviewType} - /> - - - updateDurationType(e as number)} - label="Metrics from" - items={getDurationType()} - selected={props.duration.toString()} - /> - - - - - - - - - - + props.onRefresh} + /> ); }; diff --git a/plugins/kiali/src/pages/WorkloadList/FiltersAndSorts.tsx b/plugins/kiali/src/pages/WorkloadList/FiltersAndSorts.tsx new file mode 100644 index 0000000000..f4ba017508 --- /dev/null +++ b/plugins/kiali/src/pages/WorkloadList/FiltersAndSorts.tsx @@ -0,0 +1,51 @@ +import { presenceValues } from '../../components/Filters/CommonFilters'; +import { + ActiveFiltersInfo, + AllFilterTypes, + FILTER_ACTION_APPEND, + FILTER_ACTION_UPDATE, + FilterType, + RunnableFilter, +} from '../../types/Filters'; +import { NamespaceInfo } from '../../types/NamespaceInfo'; + +export const appLabelFilter: FilterType = { + category: 'App Label', + placeholder: 'Filter by App Label Validation', + filterType: AllFilterTypes.select, + action: FILTER_ACTION_UPDATE, + filterValues: presenceValues, +}; + +export const labelFilter: RunnableFilter = { + category: 'Namespace Label', + placeholder: 'Filter by Namespace Label', + filterType: AllFilterTypes.nsLabel, + action: FILTER_ACTION_APPEND, + filterValues: [], + run: (ns: NamespaceInfo, filters: ActiveFiltersInfo) => { + return filters.filters.some(f => { + if (f.value.includes('=')) { + const [k, v] = f.value.split('='); + return v + .split(',') + .some( + val => + !!ns.labels && k in ns.labels && ns.labels[k].startsWith(val), + ); + } + return ( + !!ns.labels && + Object.keys(ns.labels).some(label => label.startsWith(f.value)) + ); + }); + }, +}; + +export const versionLabelFilter: FilterType = { + category: 'Version Label', + placeholder: 'Filter by Version Label Validation', + filterType: AllFilterTypes.select, + action: FILTER_ACTION_UPDATE, + filterValues: presenceValues, +}; diff --git a/plugins/kiali/src/pages/WorkloadList/WorkloadListPage.tsx b/plugins/kiali/src/pages/WorkloadList/WorkloadListPage.tsx new file mode 100644 index 0000000000..74709517ad --- /dev/null +++ b/plugins/kiali/src/pages/WorkloadList/WorkloadListPage.tsx @@ -0,0 +1,135 @@ +import * as React from 'react'; +import { useRef } from 'react'; +import { useAsyncFn, useDebounce } from 'react-use'; + +import { Content, Page } from '@backstage/core-components'; +import { useApi } from '@backstage/core-plugin-api'; + +import { CircularProgress } from '@material-ui/core'; + +import { DefaultSecondaryMasthead } from '../../components/DefaultSecondaryMasthead/DefaultSecondaryMasthead'; +import * as FilterHelper from '../../components/FilterList/FilterHelper'; +import { TimeDurationComponent } from '../../components/Time/TimeDurationComponent'; +import { VirtualList } from '../../components/VirtualList/VirtualList'; +import { isMultiCluster } from '../../config'; +import { getErrorString, kialiApiRef } from '../../services/Api'; +import { KialiAppState, KialiContext } from '../../store'; +import { WorkloadListItem } from '../../types/Workload'; +import { NamespaceInfo } from '../Overview/NamespaceInfo'; +import { getNamespaces } from '../Overview/OverviewPage'; + +export const WorkloadListPage = () => { + const kialiClient = useApi(kialiApiRef); + const [namespaces, setNamespaces] = React.useState([]); + const [allWorkloads, setWorkloads] = React.useState([]); + const [duration, setDuration] = React.useState( + FilterHelper.currentDuration(), + ); + const kialiState = React.useContext(KialiContext) as KialiAppState; + const activeNs = kialiState.namespaces.activeNamespaces.map(ns => ns.name); + const prevActiveNs = useRef(activeNs); + const prevDuration = useRef(duration); + + const fetchWorkloads = ( + nss: NamespaceInfo[], + timeDuration: number, + ): Promise => { + return Promise.all( + nss.map(nsInfo => { + return kialiClient + .getWorkloads(nsInfo.name, timeDuration) + .then(workloadsResponse => { + return workloadsResponse; + }); + }), + ) + .then(results => { + let wkList: WorkloadListItem[] = []; + results.forEach(result => { + wkList = Array.from(wkList).concat(result); + }); + setWorkloads(wkList); + }) + .catch(err => + kialiState.alertUtils!.add( + `Could not fetch workloads: ${getErrorString(err)}`, + ), + ); + }; + + const nsEqual = (ns: string[], ns2: string[]): boolean => { + return ( + ns.length === ns2.length && + ns.every((value: any, index: number) => value === ns2[index]) + ); + }; + + const load = async () => { + kialiClient.getNamespaces().then(namespacesResponse => { + const allNamespaces: NamespaceInfo[] = getNamespaces( + namespacesResponse, + namespaces, + ); + const nsl = allNamespaces.filter(ns => activeNs.includes(ns.name)); + setNamespaces(nsl); + fetchWorkloads(nsl, duration); + }); + }; + + const [{ loading }, refresh] = useAsyncFn( + async () => { + // Check if the config is loaded + await load(); + }, + [], + { loading: true }, + ); + useDebounce(refresh, 10); + + React.useEffect(() => { + if ( + duration !== prevDuration.current || + !nsEqual(activeNs, prevActiveNs.current) + ) { + load(); + prevDuration.current = duration; + prevActiveNs.current = activeNs; + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [activeNs, duration]); + + if (loading) { + return ; + } + + const hiddenColumns = isMultiCluster ? [] : ['cluster']; + + const grids = () => { + const elements = []; + elements.push( + , + ); + return elements; + }; + + return ( + + + load()} /> + + + + ); +}; diff --git a/plugins/kiali/src/plugin.ts b/plugins/kiali/src/plugin.ts index 0faaf576bd..c33ce56e0c 100644 --- a/plugins/kiali/src/plugin.ts +++ b/plugins/kiali/src/plugin.ts @@ -6,7 +6,7 @@ import { identityApiRef, } from '@backstage/core-plugin-api'; -import { overviewRouteRef, rootRouteRef } from './routes'; +import { overviewRouteRef, rootRouteRef, workloadsRouteRef } from './routes'; import { KialiApiClient, kialiApiRef } from './services/Api'; import '@patternfly/patternfly/patternfly.css'; @@ -16,6 +16,7 @@ export const kialiPlugin = createPlugin({ routes: { root: rootRouteRef, overview: overviewRouteRef, + workloads: workloadsRouteRef, }, apis: [ createApiFactory({ diff --git a/plugins/kiali/src/routes.ts b/plugins/kiali/src/routes.ts index 2200361666..0faa5afcb5 100644 --- a/plugins/kiali/src/routes.ts +++ b/plugins/kiali/src/routes.ts @@ -1,4 +1,8 @@ -import { createRouteRef, createSubRouteRef } from '@backstage/core-plugin-api'; +import { + createRouteRef, + createSubRouteRef, + SubRouteRef, +} from '@backstage/core-plugin-api'; export const rootRouteRef = createRouteRef({ id: 'kiali', @@ -9,3 +13,9 @@ export const overviewRouteRef = createSubRouteRef({ path: '/overview', parent: rootRouteRef, }); + +export const workloadsRouteRef: SubRouteRef = createSubRouteRef({ + id: 'kiali-workloads', + path: '/workloads', + parent: rootRouteRef, +}); diff --git a/plugins/kiali/src/services/Api.ts b/plugins/kiali/src/services/Api.ts index 6386f34d5d..939b53f4ee 100644 --- a/plugins/kiali/src/services/Api.ts +++ b/plugins/kiali/src/services/Api.ts @@ -31,6 +31,7 @@ import { Namespace } from '../types/Namespace'; import { ServerConfig } from '../types/ServerConfig'; import { StatusState } from '../types/StatusState'; import { TLSStatus } from '../types/TLSStatus'; +import { WorkloadListItem, WorkloadNamespaceResponse } from '../types/Workload'; import { filterNsByAnnotation } from '../utils/entityFilter'; export const ANONYMOUS_USER = 'anonymous'; @@ -116,6 +117,11 @@ export interface KialiApi { getIstioCertsInfo(): Promise; setEntity(entity?: Entity): void; status(): Promise; + + getWorkloads( + namespace: string, + duration: number, + ): Promise; } export const kialiApiRef = createApiRef({ @@ -478,6 +484,45 @@ export class KialiApiClient implements KialiApi { setEntity = (entity?: Entity) => { this.entity = entity; }; + + getWorkloads = async ( + namespace: string, + duration: number, + ): Promise => { + return this.newRequest( + HTTP_VERBS.GET, + urls.workloads(namespace), + { health: true, istioResources: true, rateInterval: `${duration}s` }, + {}, + ).then(resp => { + return resp.workloads.map(w => { + return { + name: w.name, + namespace: resp.namespace.name, + cluster: w.cluster, + type: w.type, + istioSidecar: w.istioSidecar, + istioAmbient: w.istioAmbient, + additionalDetailSample: undefined, + appLabel: w.appLabel, + versionLabel: w.versionLabel, + labels: w.labels, + istioReferences: w.istioReferences, + notCoveredAuthPolicy: w.notCoveredAuthPolicy, + health: WorkloadHealth.fromJson( + resp.namespace.name, + w.name, + w.health, + { + rateInterval: duration, + hasSidecar: w.istioSidecar, + hasAmbient: w.istioAmbient, + }, + ), + }; + }); + }); + }; } export const getErrorString = (error: AxiosError): string => { diff --git a/plugins/kiali/src/types/AppList.ts b/plugins/kiali/src/types/AppList.ts new file mode 100644 index 0000000000..31ba366ce0 --- /dev/null +++ b/plugins/kiali/src/types/AppList.ts @@ -0,0 +1,16 @@ +import { AppHealth } from './Health'; +import { ObjectReference } from './IstioObjects'; + +export interface AppOverview { + cluster?: string; + health: AppHealth; + istioAmbient: boolean; + istioReferences: ObjectReference[]; + istioSidecar: boolean; + labels: { [key: string]: string }; + name: string; +} + +export interface AppListItem extends AppOverview { + namespace: string; +} diff --git a/plugins/kiali/src/types/NamespaceInfo.tsx b/plugins/kiali/src/types/NamespaceInfo.tsx new file mode 100644 index 0000000000..1b74cf299c --- /dev/null +++ b/plugins/kiali/src/types/NamespaceInfo.tsx @@ -0,0 +1,28 @@ +import { IstioConfigList } from './IstioConfigList'; +import { ValidationStatus } from './IstioObjects'; +import { ControlPlaneMetricsMap, Metric } from './Metrics'; +import { TLSStatus } from './TLSStatus'; + +export type NamespaceInfo = { + annotations?: { [key: string]: string }; + controlPlaneMetrics?: ControlPlaneMetricsMap; + cluster?: string; + errorMetrics?: Metric[]; + isAmbient?: boolean; + istioConfig?: IstioConfigList; + labels?: { [key: string]: string }; + metrics?: Metric[]; + name: string; + outboundPolicyMode?: string; + status?: NamespaceStatus; + tlsStatus?: TLSStatus; + validations?: ValidationStatus; +}; + +export type NamespaceStatus = { + inError: string[]; + inNotReady: string[]; + inSuccess: string[]; + inWarning: string[]; + notAvailable: string[]; +}; diff --git a/plugins/kiali/src/types/ServiceList.ts b/plugins/kiali/src/types/ServiceList.ts index 44da7089b0..1a6ad71402 100644 --- a/plugins/kiali/src/types/ServiceList.ts +++ b/plugins/kiali/src/types/ServiceList.ts @@ -24,6 +24,7 @@ export interface ServiceOverview { } export interface ServiceListItem extends ServiceOverview { + type: string; namespace: string; validation?: ObjectValidation; } diff --git a/plugins/kiali/src/utils/SafeRender.tsx b/plugins/kiali/src/utils/SafeRender.tsx new file mode 100644 index 0000000000..71bdbb556b --- /dev/null +++ b/plugins/kiali/src/utils/SafeRender.tsx @@ -0,0 +1,19 @@ +import * as React from 'react'; + +import { ErrorCircleOIcon } from '@patternfly/react-icons'; + +export const canRender = (value: any): boolean => { + return typeof value !== 'object'; +}; + +export const renderErrorMessage = (message: string): any => { + return ( + <> + {`${message} `} + + ); +}; + +export const safeRender = (value: any, message = 'Invalid value'): any => { + return canRender(value) ? value : renderErrorMessage(message); +};