From 34adc57837406e80b93a1a1657e96ff902bf24bd Mon Sep 17 00:00:00 2001 From: Josune Cordoba <49480155+josunect@users.noreply.github.com> Date: Wed, 21 Feb 2024 10:19:33 +0000 Subject: [PATCH] feat(kiali): workloads details page overview tab (#1198) * Add details components * Update detalis * Update to Material UI components * Workloads description * Update Overview * Istio Config List * Update icons * Remove tabs * Update missing label * Update regex * dev * lint * lint * Sonar * sonar * sonar * Update Istioconfig * sonar * fix istio config and styles, add istio config to fixtures * Remove links to Istio Objects for now * Update links * sonar * Remove link for config * sonar * Added breadcrumb * fix after rebase * link * lint * sonnar * fix breadcrumb * Update prop * Breadcrumbs view in dev --- plugins/kiali/dev/__fixtures__/index.ts | 70 ++++ .../namespaces/bookinfo/istio_config.json | 86 +++++ .../bookinfo/workloads/details_v1.json | 194 ++++++++++ .../workloads/kiali_traffic_generator.json | 156 ++++++++ .../bookinfo/workloads/productpage_v1.json | 198 ++++++++++ .../bookinfo/workloads/ratings_v1.json | 194 ++++++++++ .../bookinfo/workloads/reviews_v1.json | 194 ++++++++++ .../bookinfo/workloads/reviews_v2.json | 198 ++++++++++ .../bookinfo/workloads/reviews_v3.json | 198 ++++++++++ .../namespaces/istio-system/istio_config.json | 35 ++ .../istio-system/workloads/grafana.json | 155 ++++++++ .../workloads/istio_egressgateway.json | 183 +++++++++ .../workloads/istio_ingressgateway.json | 187 ++++++++++ .../istio-system/workloads/istiod.json | 172 +++++++++ .../istio-system/workloads/jaeger.json | 210 +++++++++++ .../istio-system/workloads/kiali.json | 187 ++++++++++ .../istio-system/workloads/prometheus.json | 162 ++++++++ .../travel-agency/istio_config.json | 35 ++ .../travel-agency/workloads/cars_v1.json | 199 ++++++++++ .../travel-agency/workloads/discounts_v1.json | 195 ++++++++++ .../travel-agency/workloads/flights_v1.json | 199 ++++++++++ .../travel-agency/workloads/hotels_v1.json | 199 ++++++++++ .../workloads/insurances_v1.json | 198 ++++++++++ .../travel-agency/workloads/mysqldb_v1.json | 189 ++++++++++ .../travel-agency/workloads/travels_v1.json | 350 ++++++++++++++++++ .../travel-control/istio_config.json | 35 ++ .../travel-control/workloads/control.json | 190 ++++++++++ .../travel-portal/istio_config.json | 27 ++ .../travel-portal/workloads/travels.json | 346 +++++++++++++++++ .../travel-portal/workloads/viaggi.json | 346 +++++++++++++++++ .../travel-portal/workloads/voyages.json | 346 +++++++++++++++++ plugins/kiali/dev/index.tsx | 47 ++- plugins/kiali/src/Router.tsx | 17 +- plugins/kiali/src/assets/img/api/graphql.svg | 1 + plugins/kiali/src/assets/img/api/grpc.svg | 1 + plugins/kiali/src/assets/img/api/rest.svg | 1 + plugins/kiali/src/assets/img/runtime/go.svg | 1 + plugins/kiali/src/assets/img/runtime/java.svg | 1 + .../src/assets/img/runtime/microprofile.svg | 1 + .../kiali/src/assets/img/runtime/nodejs.svg | 1 + .../kiali/src/assets/img/runtime/quarkus.svg | 1 + .../src/assets/img/runtime/spring-boot.svg | 1 + .../src/assets/img/runtime/thorntail.svg | 1 + .../kiali/src/assets/img/runtime/tomcat.svg | 1 + .../kiali/src/assets/img/runtime/vertx.svg | 1 + .../BreadcrumbView/BreadcrumbView.tsx | 126 +++++++ .../DetailsDescription/DetailDescription.tsx | 350 ++++++++++++++++++ .../src/components/Health/HealthDetails.tsx | 64 ++++ .../IstioConfigCard/IstioConfigCard.tsx | 103 ++++++ plugins/kiali/src/components/Label/Labels.tsx | 95 +++++ .../src/components/Link/IstioObjectLink.ts | 29 -- .../src/components/Link/IstioObjectLink.tsx | 57 +++ plugins/kiali/src/components/Logos/Logos.tsx | 75 ++++ .../MissingAuthPolicy/MissingAuthPolicy.tsx | 68 ++++ .../components/MissingLabel/MissingLabel.tsx | 94 +++++ plugins/kiali/src/components/SimpleTable.tsx | 94 +++++ plugins/kiali/src/components/TextOrLink.tsx | 29 ++ .../kiali/src/components/Time/LocalTime.tsx | 21 ++ plugins/kiali/src/components/Time/Utils.tsx | 17 + .../Validations/ValidationObjectSummary.tsx | 39 ++ .../Validations/ValidationStack.tsx | 45 +++ .../Validations/ValidationSummary.tsx | 1 + .../src/components/VirtualList/Renderers.tsx | 12 +- plugins/kiali/src/config/KialiIcon.tsx | 29 +- plugins/kiali/src/config/ServerConfig.ts | 4 + .../kiali/src/helpers/LabelFilterHelper.ts | 8 + .../src/pages/Kiali/Header/KialiTabs.tsx | 13 +- .../src/pages/WorkloadDetails/PodStatus.tsx | 62 ++++ .../pages/WorkloadDetails/ProxyStatusList.tsx | 49 +++ .../WorkloadDetails/WorkloadDetailsPage.tsx | 117 ++++++ .../pages/WorkloadDetails/WorkloadInfo.tsx | 332 +++++++++++++++++ .../pages/WorkloadDetails/WorkloadPods.tsx | 173 +++++++++ .../WorkloadDetails/WorkloadsDescription.tsx | 279 ++++++++++++++ plugins/kiali/src/routes.ts | 6 + plugins/kiali/src/services/Api.ts | 95 ++++- plugins/kiali/src/styles/HealthStyle.ts | 17 + plugins/kiali/src/styles/StyleUtils.ts | 5 + plugins/kiali/src/types/App.ts | 32 ++ plugins/kiali/src/types/IstioConfigDetails.ts | 34 +- plugins/kiali/src/types/IstioConfigList.ts | 39 +- plugins/kiali/src/types/IstioObjects.ts | 132 +++++++ plugins/kiali/src/types/ServerConfig.ts | 4 + plugins/kiali/src/types/Workload.ts | 6 + plugins/kiali/src/types/types.ts | 14 + plugins/kiali/src/utils/IstioConfigUtils.ts | 169 +++++++++ 85 files changed, 8570 insertions(+), 77 deletions(-) create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/bookinfo/istio_config.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/details_v1.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/kiali_traffic_generator.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/productpage_v1.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/ratings_v1.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/reviews_v1.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/reviews_v2.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/reviews_v3.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/istio-system/istio_config.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/grafana.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/istio_egressgateway.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/istio_ingressgateway.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/istiod.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/jaeger.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/kiali.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/prometheus.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/travel-agency/istio_config.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/cars_v1.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/discounts_v1.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/flights_v1.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/hotels_v1.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/insurances_v1.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/mysqldb_v1.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/travels_v1.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/travel-control/istio_config.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/travel-control/workloads/control.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/travel-portal/istio_config.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/travel-portal/workloads/travels.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/travel-portal/workloads/viaggi.json create mode 100644 plugins/kiali/dev/__fixtures__/namespaces/travel-portal/workloads/voyages.json create mode 100644 plugins/kiali/src/assets/img/api/graphql.svg create mode 100644 plugins/kiali/src/assets/img/api/grpc.svg create mode 100644 plugins/kiali/src/assets/img/api/rest.svg create mode 100644 plugins/kiali/src/assets/img/runtime/go.svg create mode 100644 plugins/kiali/src/assets/img/runtime/java.svg create mode 100644 plugins/kiali/src/assets/img/runtime/microprofile.svg create mode 100644 plugins/kiali/src/assets/img/runtime/nodejs.svg create mode 100644 plugins/kiali/src/assets/img/runtime/quarkus.svg create mode 100644 plugins/kiali/src/assets/img/runtime/spring-boot.svg create mode 100644 plugins/kiali/src/assets/img/runtime/thorntail.svg create mode 100644 plugins/kiali/src/assets/img/runtime/tomcat.svg create mode 100644 plugins/kiali/src/assets/img/runtime/vertx.svg create mode 100644 plugins/kiali/src/components/BreadcrumbView/BreadcrumbView.tsx create mode 100644 plugins/kiali/src/components/DetailsDescription/DetailDescription.tsx create mode 100644 plugins/kiali/src/components/IstioConfigCard/IstioConfigCard.tsx create mode 100644 plugins/kiali/src/components/Label/Labels.tsx delete mode 100644 plugins/kiali/src/components/Link/IstioObjectLink.ts create mode 100644 plugins/kiali/src/components/Link/IstioObjectLink.tsx create mode 100644 plugins/kiali/src/components/Logos/Logos.tsx create mode 100644 plugins/kiali/src/components/MissingAuthPolicy/MissingAuthPolicy.tsx create mode 100644 plugins/kiali/src/components/MissingLabel/MissingLabel.tsx create mode 100644 plugins/kiali/src/components/SimpleTable.tsx create mode 100644 plugins/kiali/src/components/TextOrLink.tsx create mode 100644 plugins/kiali/src/components/Time/LocalTime.tsx create mode 100644 plugins/kiali/src/components/Time/Utils.tsx create mode 100644 plugins/kiali/src/components/Validations/ValidationObjectSummary.tsx create mode 100644 plugins/kiali/src/components/Validations/ValidationStack.tsx create mode 100644 plugins/kiali/src/pages/WorkloadDetails/PodStatus.tsx create mode 100644 plugins/kiali/src/pages/WorkloadDetails/ProxyStatusList.tsx create mode 100644 plugins/kiali/src/pages/WorkloadDetails/WorkloadDetailsPage.tsx create mode 100644 plugins/kiali/src/pages/WorkloadDetails/WorkloadInfo.tsx create mode 100644 plugins/kiali/src/pages/WorkloadDetails/WorkloadPods.tsx create mode 100644 plugins/kiali/src/pages/WorkloadDetails/WorkloadsDescription.tsx create mode 100644 plugins/kiali/src/styles/HealthStyle.ts create mode 100644 plugins/kiali/src/types/App.ts create mode 100644 plugins/kiali/src/types/types.ts create mode 100644 plugins/kiali/src/utils/IstioConfigUtils.ts diff --git a/plugins/kiali/dev/__fixtures__/index.ts b/plugins/kiali/dev/__fixtures__/index.ts index 7a58b15853..5d77dedf56 100644 --- a/plugins/kiali/dev/__fixtures__/index.ts +++ b/plugins/kiali/dev/__fixtures__/index.ts @@ -16,46 +16,76 @@ import status from './general/status.json'; import bookinfoHealthApp from './namespaces/bookinfo/health/app.json'; import bookinfoHealthService from './namespaces/bookinfo/health/service.json'; import bookinfoHealthWorkload from './namespaces/bookinfo/health/workload.json'; +import bookinfoIstioConfig from './namespaces/bookinfo/istio_config.json'; import bookInfoMetrics from './namespaces/bookinfo/metrics'; /* bookinfo */ import bookinfoTls from './namespaces/bookinfo/tls.json'; /** Workloads **/ import bookinfoWorkloads from './namespaces/bookinfo/workloads.json'; +import detailsWorkload from './namespaces/bookinfo/workloads/details_v1.json'; +import kialitrafficWorkload from './namespaces/bookinfo/workloads/kiali_traffic_generator.json'; +import productpagev1Workload from './namespaces/bookinfo/workloads/productpage_v1.json'; +import ratingsv1Workload from './namespaces/bookinfo/workloads/ratings_v1.json'; +import reviewsv1Workload from './namespaces/bookinfo/workloads/reviews_v1.json'; +import reviewsv2Workload from './namespaces/bookinfo/workloads/reviews_v2.json'; +import reviewsv3Workload from './namespaces/bookinfo/workloads/reviews_v3.json'; /** health **/ import istioSystemHealthApp from './namespaces/istio-system/health/app.json'; import istioSystemHealthService from './namespaces/istio-system/health/service.json'; import istioSystemHealthWorkload from './namespaces/istio-system/health/workload.json'; +import istioSystemIstioConfig from './namespaces/istio-system/istio_config.json'; 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'; +import grafanaWorkload from './namespaces/istio-system/workloads/grafana.json'; +import istioegressgatewayWorkload from './namespaces/istio-system/workloads/istio_egressgateway.json'; +import istioingressgatewayWorkload from './namespaces/istio-system/workloads/istio_ingressgateway.json'; +import istiodWorkload from './namespaces/istio-system/workloads/istiod.json'; +import jaegerWorkload from './namespaces/istio-system/workloads/jaeger.json'; +import kialiWorkload from './namespaces/istio-system/workloads/kiali.json'; +import prometheusWorkload from './namespaces/istio-system/workloads/prometheus.json'; /** health **/ import travelAgencyHealthApp from './namespaces/travel-agency/health/app.json'; import travelAgencyHealthService from './namespaces/travel-agency/health/service.json'; import travelAgencyHealthWorkload from './namespaces/travel-agency/health/workload.json'; +import travelAgencyIstioConfig from './namespaces/travel-agency/istio_config.json'; 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'; +import carsv1Workload from './namespaces/travel-agency/workloads/cars_v1.json'; +import discountsv1Workload from './namespaces/travel-agency/workloads/discounts_v1.json'; +import flightsv1Workload from './namespaces/travel-agency/workloads/flights_v1.json'; +import hotelsv1Workload from './namespaces/travel-agency/workloads/hotels_v1.json'; +import insurancesv1Workload from './namespaces/travel-agency/workloads/insurances_v1.json'; +import mysqldbv1Workload from './namespaces/travel-agency/workloads/mysqldb_v1.json'; +import travelsv1Workload from './namespaces/travel-agency/workloads/travels_v1.json'; /** health **/ import travelControlHealthApp from './namespaces/travel-control/health/app.json'; import travelControlHealthService from './namespaces/travel-control/health/service.json'; import travelControlHealthWorkload from './namespaces/travel-control/health/workload.json'; +import travelControlIstioConfig from './namespaces/travel-control/istio_config.json'; 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'; +import travelControlWorkload from './namespaces/travel-control/workloads/control.json'; /** health **/ import travelPortalHealthApp from './namespaces/travel-portal/health/app.json'; import travelPortalHealthService from './namespaces/travel-portal/health/service.json'; import travelPortalHealthWorkload from './namespaces/travel-portal/health/workload.json'; +import travelPortalIstioConfig from './namespaces/travel-portal/istio_config.json'; 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'; +import travelPortalTravels from './namespaces/travel-portal/workloads/travels.json'; +import travelPortalViaggi from './namespaces/travel-portal/workloads/viaggi.json'; +import travelPortalVoyages from './namespaces/travel-portal/workloads/voyages.json'; export const kialiData: { [index: string]: any } = { auth: anonymousAuth, @@ -78,6 +108,16 @@ export const kialiData: { [index: string]: any } = { service: istioSystemHealthService, workload: istioSystemHealthWorkload, }, + workloads: { + grafana: grafanaWorkload, + istioegressgateway: istioegressgatewayWorkload, + istioingressgateway: istioingressgatewayWorkload, + istiod: istiodWorkload, + jaeger: jaegerWorkload, + kiali: kialiWorkload, + prometheus: prometheusWorkload, + }, + istioConfigList: istioSystemIstioConfig, }, bookinfo: { tls: bookinfoTls, @@ -87,6 +127,16 @@ export const kialiData: { [index: string]: any } = { service: bookinfoHealthService, workload: bookinfoHealthWorkload, }, + workloads: { + detailsv1: detailsWorkload, + kialitrafficgenerator: kialitrafficWorkload, + productpagev1: productpagev1Workload, + ratingsv1: ratingsv1Workload, + reviewsv1: reviewsv1Workload, + reviewsv2: reviewsv2Workload, + reviewsv3: reviewsv3Workload, + }, + istioConfigList: bookinfoIstioConfig, }, 'travel-control': { tls: travelControlTls, @@ -96,6 +146,10 @@ export const kialiData: { [index: string]: any } = { service: travelControlHealthService, workload: travelControlHealthWorkload, }, + workloads: { + control: travelControlWorkload, + }, + istioConfigList: travelControlIstioConfig, }, 'travel-portal': { tls: travelPortalTls, @@ -105,6 +159,12 @@ export const kialiData: { [index: string]: any } = { service: travelPortalHealthService, workload: travelPortalHealthWorkload, }, + workloads: { + travels: travelPortalTravels, + viaggi: travelPortalViaggi, + voyages: travelPortalVoyages, + }, + istioConfigList: travelPortalIstioConfig, }, 'travel-agency': { tls: travelAgencyTls, @@ -114,6 +174,16 @@ export const kialiData: { [index: string]: any } = { service: travelAgencyHealthService, workload: travelAgencyHealthWorkload, }, + workloads: { + carsv1: carsv1Workload, + discountsv1: discountsv1Workload, + flightsv1: flightsv1Workload, + hotelsv1: hotelsv1Workload, + insurancesv1: insurancesv1Workload, + mysqldbv1: mysqldbv1Workload, + travels: travelsv1Workload, + }, + istioConfigList: travelAgencyIstioConfig, }, }, workloads: { diff --git a/plugins/kiali/dev/__fixtures__/namespaces/bookinfo/istio_config.json b/plugins/kiali/dev/__fixtures__/namespaces/bookinfo/istio_config.json new file mode 100644 index 0000000000..742c918145 --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/bookinfo/istio_config.json @@ -0,0 +1,86 @@ +{ + "namespace": { + "name": "bookinfo", + "cluster": "", + "isAmbient": false, + "labels": null, + "annotations": null + }, + "destinationRules": [], + "envoyFilters": [ + { + "kind": "EnvoyFilter", + "apiVersion": "networking.istio.io/v1alpha3", + "metadata": { + "name": "reviews-lua", + "namespace": "bookinfo", + "uid": "6188f1b2-d252-441b-912f-abd011ff8556", + "resourceVersion": "90356", + "generation": 1, + "creationTimestamp": "2024-02-19T09:39:06Z", + "annotations": { + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"networking.istio.io/v1alpha3\",\"kind\":\"EnvoyFilter\",\"metadata\":{\"annotations\":{},\"name\":\"reviews-lua\",\"namespace\":\"bookinfo\"},\"spec\":{\"workloadSelector\":{\"labels\":{\"app\":\"reviews\"}}}}\n" + }, + "managedFields": [ + { + "manager": "kubectl-client-side-apply", + "operation": "Update", + "apiVersion": "networking.istio.io/v1alpha3", + "time": "2024-02-19T09:39:06Z", + "fieldsType": "FieldsV1", + "fieldsV1": { + "f:metadata": { + "f:annotations": { + ".": {}, + "f:kubectl.kubernetes.io/last-applied-configuration": {} + } + }, + "f:spec": { + ".": {}, + "f:workloadSelector": { + ".": {}, + "f:labels": { + ".": {}, + "f:app": {} + } + } + } + } + } + ] + }, + "spec": { + "workloadSelector": { + "labels": { + "app": "reviews" + } + } + }, + "status": {} + } + ], + "gateways": [], + "serviceEntries": [], + "sidecars": [], + "virtualServices": [], + "workloadEntries": [], + "workloadGroups": [], + "wasmPlugins": [], + "telemetries": [], + "k8sGateways": [], + "k8sHTTPRoutes": [], + "authorizationPolicies": [], + "peerAuthentications": [], + "requestAuthentications": [], + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": true, + "checks": [], + "references": null + } + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/details_v1.json b/plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/details_v1.json new file mode 100644 index 0000000000..ac1ec7d292 --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/details_v1.json @@ -0,0 +1,194 @@ +{ + "name": "details-v1", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-02-15T09:25:39Z", + "resourceVersion": "16316", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "details", + "version": "v1" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 0, + "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": null, + "dashboardAnnotations": {}, + "serviceAccountNames": null, + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "pods": [ + { + "name": "details-v1-698d88b-dzrhr", + "labels": { + "app": "details", + "pod-template-hash": "698d88b", + "security.istio.io/tlsMode": "istio", + "service.istio.io/canonical-name": "details", + "service.istio.io/canonical-revision": "v1", + "version": "v1" + }, + "createdAt": "2024-02-15T09:25:39Z", + "createdBy": [ + { + "name": "details-v1-698d88b", + "kind": "ReplicaSet" + } + ], + "containers": [ + { + "name": "details", + "image": "docker.io/istio/examples-bookinfo-details-v1:1.18.0", + "isProxy": false, + "isReady": true, + "isAmbient": false + } + ], + "istioContainers": [ + { + "name": "istio-proxy", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "istioInitContainers": [ + { + "name": "istio-init", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "status": "Running", + "statusMessage": "", + "statusReason": "", + "appLabel": true, + "versionLabel": true, + "annotations": { + "istio.io/rev": "default", + "kubectl.kubernetes.io/default-container": "details", + "kubectl.kubernetes.io/default-logs-container": "details", + "prometheus.io/path": "/stats/prometheus", + "prometheus.io/port": "15020", + "prometheus.io/scrape": "true", + "sidecar.istio.io/status": "{\"initContainers\":[\"istio-init\"],\"containers\":[\"istio-proxy\"],\"volumes\":[\"workload-socket\",\"credential-socket\",\"workload-certs\",\"istio-envoy\",\"istio-data\",\"istio-podinfo\",\"istio-token\",\"istiod-ca-cert\"],\"imagePullSecrets\":null,\"revision\":\"default\"}" + }, + "proxyStatus": { + "CDS": "Synced", + "EDS": "Synced", + "LDS": "Synced", + "RDS": "Synced" + }, + "serviceAccountName": "bookinfo-details" + } + ], + "services": [ + { + "name": "details", + "namespace": "bookinfo", + "istioSidecar": true, + "cluster": "Kubernetes", + "istioAmbient": false, + "appLabel": true, + "additionalDetailSample": null, + "annotations": null, + "healthAnnotations": {}, + "ports": null, + "labels": { + "app": "details", + "service": "details" + }, + "selector": { + "app": "details" + }, + "istioReferences": [], + "kialiWizard": "", + "serviceRegistry": "Kubernetes", + "health": { + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + } + ], + "runtimes": [ + { + "name": "", + "dashboardRefs": [ + { + "template": "envoy", + "title": "Envoy Metrics" + } + ] + } + ], + "additionalDetails": [], + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": false, + "checks": [ + { + "code": "KIA0302", + "message": "No matching workload found for gateway selector in this namespace", + "severity": "warning", + "path": "spec/selector" + } + ], + "references": null + } + }, + "virtualservice": { + "bookinfo.bookinfo": { + "name": "bookinfo", + "objectType": "virtualservice", + "valid": true, + "checks": [], + "references": null + } + }, + "workload": { + "details-v1.bookinfo": { + "name": "details-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + } + } + }, + "waypointWorkloads": null, + "health": { + "workloadStatus": { + "name": "details-v1", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": 1 + }, + "requests": { + "inbound": { + "http": { + "200": 0.9999999999999999 + } + }, + "outbound": {}, + "healthAnnotations": {} + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/kiali_traffic_generator.json b/plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/kiali_traffic_generator.json new file mode 100644 index 0000000000..8d9003cd72 --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/kiali_traffic_generator.json @@ -0,0 +1,156 @@ +{ + "name": "kiali-traffic-generator", + "cluster": "Kubernetes", + "type": "ReplicaSet", + "createdAt": "2024-02-15T09:26:32Z", + "resourceVersion": "16299", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "kiali-traffic-generator", + "kiali-test": "traffic-generator" + }, + "appLabel": true, + "versionLabel": false, + "podCount": 0, + "annotations": null, + "healthAnnotations": {}, + "istioReferences": null, + "dashboardAnnotations": {}, + "serviceAccountNames": null, + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "pods": [ + { + "name": "kiali-traffic-generator-nsgk4", + "labels": { + "app": "kiali-traffic-generator", + "kiali-test": "traffic-generator", + "security.istio.io/tlsMode": "istio", + "service.istio.io/canonical-name": "kiali-traffic-generator", + "service.istio.io/canonical-revision": "latest" + }, + "createdAt": "2024-02-15T09:26:32Z", + "createdBy": [ + { + "name": "kiali-traffic-generator", + "kind": "ReplicaSet" + } + ], + "containers": [ + { + "name": "kiali-traffic-generator", + "image": "quay.io/kiali/kiali-test-mesh-traffic-generator:latest", + "isProxy": false, + "isReady": true, + "isAmbient": false + } + ], + "istioContainers": [ + { + "name": "istio-proxy", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "istioInitContainers": [ + { + "name": "istio-init", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "status": "Running", + "statusMessage": "", + "statusReason": "", + "appLabel": true, + "versionLabel": false, + "annotations": { + "istio.io/rev": "default", + "kubectl.kubernetes.io/default-container": "kiali-traffic-generator", + "kubectl.kubernetes.io/default-logs-container": "kiali-traffic-generator", + "prometheus.io/path": "/stats/prometheus", + "prometheus.io/port": "15020", + "prometheus.io/scrape": "true", + "sidecar.istio.io/status": "{\"initContainers\":[\"istio-init\"],\"containers\":[\"istio-proxy\"],\"volumes\":[\"workload-socket\",\"credential-socket\",\"workload-certs\",\"istio-envoy\",\"istio-data\",\"istio-podinfo\",\"istio-token\",\"istiod-ca-cert\"],\"imagePullSecrets\":null,\"revision\":\"default\"}" + }, + "proxyStatus": { + "CDS": "Synced", + "EDS": "Synced", + "LDS": "Synced", + "RDS": "Synced" + }, + "serviceAccountName": "default" + } + ], + "services": [], + "runtimes": [ + { + "name": "", + "dashboardRefs": [ + { + "template": "envoy", + "title": "Envoy Metrics" + } + ] + } + ], + "additionalDetails": [], + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": false, + "checks": [ + { + "code": "KIA0302", + "message": "No matching workload found for gateway selector in this namespace", + "severity": "warning", + "path": "spec/selector" + } + ], + "references": null + } + }, + "virtualservice": { + "bookinfo.bookinfo": { + "name": "bookinfo", + "objectType": "virtualservice", + "valid": true, + "checks": [], + "references": null + } + }, + "workload": { + "kiali-traffic-generator.bookinfo": { + "name": "kiali-traffic-generator", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + } + } + }, + "waypointWorkloads": null, + "health": { + "workloadStatus": { + "name": "kiali-traffic-generator", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": 1 + }, + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/productpage_v1.json b/plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/productpage_v1.json new file mode 100644 index 0000000000..17a2c753c1 --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/productpage_v1.json @@ -0,0 +1,198 @@ +{ + "name": "productpage-v1", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-02-15T09:25:39Z", + "resourceVersion": "16263", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "productpage", + "version": "v1" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 0, + "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": null, + "dashboardAnnotations": {}, + "serviceAccountNames": null, + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "pods": [ + { + "name": "productpage-v1-675fc69cf-jbb29", + "labels": { + "app": "productpage", + "pod-template-hash": "675fc69cf", + "security.istio.io/tlsMode": "istio", + "service.istio.io/canonical-name": "productpage", + "service.istio.io/canonical-revision": "v1", + "version": "v1" + }, + "createdAt": "2024-02-15T09:25:40Z", + "createdBy": [ + { + "name": "productpage-v1-675fc69cf", + "kind": "ReplicaSet" + } + ], + "containers": [ + { + "name": "productpage", + "image": "docker.io/istio/examples-bookinfo-productpage-v1:1.18.0", + "isProxy": false, + "isReady": true, + "isAmbient": false + } + ], + "istioContainers": [ + { + "name": "istio-proxy", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "istioInitContainers": [ + { + "name": "istio-init", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "status": "Running", + "statusMessage": "", + "statusReason": "", + "appLabel": true, + "versionLabel": true, + "annotations": { + "istio.io/rev": "default", + "kubectl.kubernetes.io/default-container": "productpage", + "kubectl.kubernetes.io/default-logs-container": "productpage", + "prometheus.io/path": "/stats/prometheus", + "prometheus.io/port": "15020", + "prometheus.io/scrape": "true", + "sidecar.istio.io/status": "{\"initContainers\":[\"istio-init\"],\"containers\":[\"istio-proxy\"],\"volumes\":[\"workload-socket\",\"credential-socket\",\"workload-certs\",\"istio-envoy\",\"istio-data\",\"istio-podinfo\",\"istio-token\",\"istiod-ca-cert\"],\"imagePullSecrets\":null,\"revision\":\"default\"}" + }, + "proxyStatus": { + "CDS": "Synced", + "EDS": "Synced", + "LDS": "Synced", + "RDS": "Synced" + }, + "serviceAccountName": "bookinfo-productpage" + } + ], + "services": [ + { + "name": "productpage", + "namespace": "bookinfo", + "istioSidecar": true, + "cluster": "Kubernetes", + "istioAmbient": false, + "appLabel": true, + "additionalDetailSample": null, + "annotations": null, + "healthAnnotations": {}, + "ports": null, + "labels": { + "app": "productpage", + "service": "productpage" + }, + "selector": { + "app": "productpage" + }, + "istioReferences": [], + "kialiWizard": "", + "serviceRegistry": "Kubernetes", + "health": { + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + } + ], + "runtimes": [ + { + "name": "", + "dashboardRefs": [ + { + "template": "envoy", + "title": "Envoy Metrics" + } + ] + } + ], + "additionalDetails": [], + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": false, + "checks": [ + { + "code": "KIA0302", + "message": "No matching workload found for gateway selector in this namespace", + "severity": "warning", + "path": "spec/selector" + } + ], + "references": null + } + }, + "virtualservice": { + "bookinfo.bookinfo": { + "name": "bookinfo", + "objectType": "virtualservice", + "valid": true, + "checks": [], + "references": null + } + }, + "workload": { + "productpage-v1.bookinfo": { + "name": "productpage-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + } + } + }, + "waypointWorkloads": null, + "health": { + "workloadStatus": { + "name": "productpage-v1", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": 1 + }, + "requests": { + "inbound": { + "http": { + "200": 0.9999999999999999 + } + }, + "outbound": { + "http": { + "200": 2 + } + }, + "healthAnnotations": {} + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/ratings_v1.json b/plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/ratings_v1.json new file mode 100644 index 0000000000..9fbfc87bfd --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/ratings_v1.json @@ -0,0 +1,194 @@ +{ + "name": "ratings-v1", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-02-15T09:25:39Z", + "resourceVersion": "16304", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "ratings", + "version": "v1" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 0, + "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": null, + "dashboardAnnotations": {}, + "serviceAccountNames": null, + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "pods": [ + { + "name": "ratings-v1-6484c4d9bb-84w5q", + "labels": { + "app": "ratings", + "pod-template-hash": "6484c4d9bb", + "security.istio.io/tlsMode": "istio", + "service.istio.io/canonical-name": "ratings", + "service.istio.io/canonical-revision": "v1", + "version": "v1" + }, + "createdAt": "2024-02-15T09:25:39Z", + "createdBy": [ + { + "name": "ratings-v1-6484c4d9bb", + "kind": "ReplicaSet" + } + ], + "containers": [ + { + "name": "ratings", + "image": "docker.io/istio/examples-bookinfo-ratings-v1:1.18.0", + "isProxy": false, + "isReady": true, + "isAmbient": false + } + ], + "istioContainers": [ + { + "name": "istio-proxy", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "istioInitContainers": [ + { + "name": "istio-init", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "status": "Running", + "statusMessage": "", + "statusReason": "", + "appLabel": true, + "versionLabel": true, + "annotations": { + "istio.io/rev": "default", + "kubectl.kubernetes.io/default-container": "ratings", + "kubectl.kubernetes.io/default-logs-container": "ratings", + "prometheus.io/path": "/stats/prometheus", + "prometheus.io/port": "15020", + "prometheus.io/scrape": "true", + "sidecar.istio.io/status": "{\"initContainers\":[\"istio-init\"],\"containers\":[\"istio-proxy\"],\"volumes\":[\"workload-socket\",\"credential-socket\",\"workload-certs\",\"istio-envoy\",\"istio-data\",\"istio-podinfo\",\"istio-token\",\"istiod-ca-cert\"],\"imagePullSecrets\":null,\"revision\":\"default\"}" + }, + "proxyStatus": { + "CDS": "Synced", + "EDS": "Synced", + "LDS": "Synced", + "RDS": "Synced" + }, + "serviceAccountName": "bookinfo-ratings" + } + ], + "services": [ + { + "name": "ratings", + "namespace": "bookinfo", + "istioSidecar": true, + "cluster": "Kubernetes", + "istioAmbient": false, + "appLabel": true, + "additionalDetailSample": null, + "annotations": null, + "healthAnnotations": {}, + "ports": null, + "labels": { + "app": "ratings", + "service": "ratings" + }, + "selector": { + "app": "ratings" + }, + "istioReferences": [], + "kialiWizard": "", + "serviceRegistry": "Kubernetes", + "health": { + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + } + ], + "runtimes": [ + { + "name": "", + "dashboardRefs": [ + { + "template": "envoy", + "title": "Envoy Metrics" + } + ] + } + ], + "additionalDetails": [], + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": false, + "checks": [ + { + "code": "KIA0302", + "message": "No matching workload found for gateway selector in this namespace", + "severity": "warning", + "path": "spec/selector" + } + ], + "references": null + } + }, + "virtualservice": { + "bookinfo.bookinfo": { + "name": "bookinfo", + "objectType": "virtualservice", + "valid": true, + "checks": [], + "references": null + } + }, + "workload": { + "ratings-v1.bookinfo": { + "name": "ratings-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + } + } + }, + "waypointWorkloads": null, + "health": { + "workloadStatus": { + "name": "ratings-v1", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": 1 + }, + "requests": { + "inbound": { + "http": { + "200": 0.7555555555555555 + } + }, + "outbound": {}, + "healthAnnotations": {} + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/reviews_v1.json b/plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/reviews_v1.json new file mode 100644 index 0000000000..5787dc820a --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/reviews_v1.json @@ -0,0 +1,194 @@ +{ + "name": "reviews-v1", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-02-15T09:25:39Z", + "resourceVersion": "16297", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "reviews", + "version": "v1" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 0, + "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": null, + "dashboardAnnotations": {}, + "serviceAccountNames": null, + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "pods": [ + { + "name": "reviews-v1-5b5d6494f4-2vw99", + "labels": { + "app": "reviews", + "pod-template-hash": "5b5d6494f4", + "security.istio.io/tlsMode": "istio", + "service.istio.io/canonical-name": "reviews", + "service.istio.io/canonical-revision": "v1", + "version": "v1" + }, + "createdAt": "2024-02-15T09:25:39Z", + "createdBy": [ + { + "name": "reviews-v1-5b5d6494f4", + "kind": "ReplicaSet" + } + ], + "containers": [ + { + "name": "reviews", + "image": "docker.io/istio/examples-bookinfo-reviews-v1:1.18.0", + "isProxy": false, + "isReady": true, + "isAmbient": false + } + ], + "istioContainers": [ + { + "name": "istio-proxy", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "istioInitContainers": [ + { + "name": "istio-init", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "status": "Running", + "statusMessage": "", + "statusReason": "", + "appLabel": true, + "versionLabel": true, + "annotations": { + "istio.io/rev": "default", + "kubectl.kubernetes.io/default-container": "reviews", + "kubectl.kubernetes.io/default-logs-container": "reviews", + "prometheus.io/path": "/stats/prometheus", + "prometheus.io/port": "15020", + "prometheus.io/scrape": "true", + "sidecar.istio.io/status": "{\"initContainers\":[\"istio-init\"],\"containers\":[\"istio-proxy\"],\"volumes\":[\"workload-socket\",\"credential-socket\",\"workload-certs\",\"istio-envoy\",\"istio-data\",\"istio-podinfo\",\"istio-token\",\"istiod-ca-cert\"],\"imagePullSecrets\":null,\"revision\":\"default\"}" + }, + "proxyStatus": { + "CDS": "Synced", + "EDS": "Synced", + "LDS": "Synced", + "RDS": "Synced" + }, + "serviceAccountName": "bookinfo-reviews" + } + ], + "services": [ + { + "name": "reviews", + "namespace": "bookinfo", + "istioSidecar": true, + "cluster": "Kubernetes", + "istioAmbient": false, + "appLabel": true, + "additionalDetailSample": null, + "annotations": null, + "healthAnnotations": {}, + "ports": null, + "labels": { + "app": "reviews", + "service": "reviews" + }, + "selector": { + "app": "reviews" + }, + "istioReferences": [], + "kialiWizard": "", + "serviceRegistry": "Kubernetes", + "health": { + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + } + ], + "runtimes": [ + { + "name": "", + "dashboardRefs": [ + { + "template": "envoy", + "title": "Envoy Metrics" + } + ] + } + ], + "additionalDetails": [], + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": false, + "checks": [ + { + "code": "KIA0302", + "message": "No matching workload found for gateway selector in this namespace", + "severity": "warning", + "path": "spec/selector" + } + ], + "references": null + } + }, + "virtualservice": { + "bookinfo.bookinfo": { + "name": "bookinfo", + "objectType": "virtualservice", + "valid": true, + "checks": [], + "references": null + } + }, + "workload": { + "reviews-v1.bookinfo": { + "name": "reviews-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + } + } + }, + "waypointWorkloads": null, + "health": { + "workloadStatus": { + "name": "reviews-v1", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": 1 + }, + "requests": { + "inbound": { + "http": { + "200": 0.3333333333333333 + } + }, + "outbound": {}, + "healthAnnotations": {} + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/reviews_v2.json b/plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/reviews_v2.json new file mode 100644 index 0000000000..3b78c259f1 --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/reviews_v2.json @@ -0,0 +1,198 @@ +{ + "name": "reviews-v2", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-02-15T09:25:39Z", + "resourceVersion": "16292", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "reviews", + "version": "v2" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 0, + "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": null, + "dashboardAnnotations": {}, + "serviceAccountNames": null, + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "pods": [ + { + "name": "reviews-v2-5b667bcbf8-d54v4", + "labels": { + "app": "reviews", + "pod-template-hash": "5b667bcbf8", + "security.istio.io/tlsMode": "istio", + "service.istio.io/canonical-name": "reviews", + "service.istio.io/canonical-revision": "v2", + "version": "v2" + }, + "createdAt": "2024-02-15T09:25:39Z", + "createdBy": [ + { + "name": "reviews-v2-5b667bcbf8", + "kind": "ReplicaSet" + } + ], + "containers": [ + { + "name": "reviews", + "image": "docker.io/istio/examples-bookinfo-reviews-v2:1.18.0", + "isProxy": false, + "isReady": true, + "isAmbient": false + } + ], + "istioContainers": [ + { + "name": "istio-proxy", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "istioInitContainers": [ + { + "name": "istio-init", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "status": "Running", + "statusMessage": "", + "statusReason": "", + "appLabel": true, + "versionLabel": true, + "annotations": { + "istio.io/rev": "default", + "kubectl.kubernetes.io/default-container": "reviews", + "kubectl.kubernetes.io/default-logs-container": "reviews", + "prometheus.io/path": "/stats/prometheus", + "prometheus.io/port": "15020", + "prometheus.io/scrape": "true", + "sidecar.istio.io/status": "{\"initContainers\":[\"istio-init\"],\"containers\":[\"istio-proxy\"],\"volumes\":[\"workload-socket\",\"credential-socket\",\"workload-certs\",\"istio-envoy\",\"istio-data\",\"istio-podinfo\",\"istio-token\",\"istiod-ca-cert\"],\"imagePullSecrets\":null,\"revision\":\"default\"}" + }, + "proxyStatus": { + "CDS": "Synced", + "EDS": "Synced", + "LDS": "Synced", + "RDS": "Synced" + }, + "serviceAccountName": "bookinfo-reviews" + } + ], + "services": [ + { + "name": "reviews", + "namespace": "bookinfo", + "istioSidecar": true, + "cluster": "Kubernetes", + "istioAmbient": false, + "appLabel": true, + "additionalDetailSample": null, + "annotations": null, + "healthAnnotations": {}, + "ports": null, + "labels": { + "app": "reviews", + "service": "reviews" + }, + "selector": { + "app": "reviews" + }, + "istioReferences": [], + "kialiWizard": "", + "serviceRegistry": "Kubernetes", + "health": { + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + } + ], + "runtimes": [ + { + "name": "", + "dashboardRefs": [ + { + "template": "envoy", + "title": "Envoy Metrics" + } + ] + } + ], + "additionalDetails": [], + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": false, + "checks": [ + { + "code": "KIA0302", + "message": "No matching workload found for gateway selector in this namespace", + "severity": "warning", + "path": "spec/selector" + } + ], + "references": null + } + }, + "virtualservice": { + "bookinfo.bookinfo": { + "name": "bookinfo", + "objectType": "virtualservice", + "valid": true, + "checks": [], + "references": null + } + }, + "workload": { + "reviews-v2.bookinfo": { + "name": "reviews-v2", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + } + } + }, + "waypointWorkloads": null, + "health": { + "workloadStatus": { + "name": "reviews-v2", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": 1 + }, + "requests": { + "inbound": { + "http": { + "200": 0.3111111111111111 + } + }, + "outbound": { + "http": { + "200": 0.31111111111111106 + } + }, + "healthAnnotations": {} + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/reviews_v3.json b/plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/reviews_v3.json new file mode 100644 index 0000000000..ac269d2345 --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/bookinfo/workloads/reviews_v3.json @@ -0,0 +1,198 @@ +{ + "name": "reviews-v3", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-02-15T09:25:39Z", + "resourceVersion": "16288", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "reviews", + "version": "v3" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 0, + "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": null, + "dashboardAnnotations": {}, + "serviceAccountNames": null, + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "pods": [ + { + "name": "reviews-v3-5b9bd44f4-457bd", + "labels": { + "app": "reviews", + "pod-template-hash": "5b9bd44f4", + "security.istio.io/tlsMode": "istio", + "service.istio.io/canonical-name": "reviews", + "service.istio.io/canonical-revision": "v3", + "version": "v3" + }, + "createdAt": "2024-02-15T09:25:39Z", + "createdBy": [ + { + "name": "reviews-v3-5b9bd44f4", + "kind": "ReplicaSet" + } + ], + "containers": [ + { + "name": "reviews", + "image": "docker.io/istio/examples-bookinfo-reviews-v3:1.18.0", + "isProxy": false, + "isReady": true, + "isAmbient": false + } + ], + "istioContainers": [ + { + "name": "istio-proxy", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "istioInitContainers": [ + { + "name": "istio-init", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "status": "Running", + "statusMessage": "", + "statusReason": "", + "appLabel": true, + "versionLabel": true, + "annotations": { + "istio.io/rev": "default", + "kubectl.kubernetes.io/default-container": "reviews", + "kubectl.kubernetes.io/default-logs-container": "reviews", + "prometheus.io/path": "/stats/prometheus", + "prometheus.io/port": "15020", + "prometheus.io/scrape": "true", + "sidecar.istio.io/status": "{\"initContainers\":[\"istio-init\"],\"containers\":[\"istio-proxy\"],\"volumes\":[\"workload-socket\",\"credential-socket\",\"workload-certs\",\"istio-envoy\",\"istio-data\",\"istio-podinfo\",\"istio-token\",\"istiod-ca-cert\"],\"imagePullSecrets\":null,\"revision\":\"default\"}" + }, + "proxyStatus": { + "CDS": "Synced", + "EDS": "Synced", + "LDS": "Synced", + "RDS": "Synced" + }, + "serviceAccountName": "bookinfo-reviews" + } + ], + "services": [ + { + "name": "reviews", + "namespace": "bookinfo", + "istioSidecar": true, + "cluster": "Kubernetes", + "istioAmbient": false, + "appLabel": true, + "additionalDetailSample": null, + "annotations": null, + "healthAnnotations": {}, + "ports": null, + "labels": { + "app": "reviews", + "service": "reviews" + }, + "selector": { + "app": "reviews" + }, + "istioReferences": [], + "kialiWizard": "", + "serviceRegistry": "Kubernetes", + "health": { + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + } + ], + "runtimes": [ + { + "name": "", + "dashboardRefs": [ + { + "template": "envoy", + "title": "Envoy Metrics" + } + ] + } + ], + "additionalDetails": [], + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": false, + "checks": [ + { + "code": "KIA0302", + "message": "No matching workload found for gateway selector in this namespace", + "severity": "warning", + "path": "spec/selector" + } + ], + "references": null + } + }, + "virtualservice": { + "bookinfo.bookinfo": { + "name": "bookinfo", + "objectType": "virtualservice", + "valid": true, + "checks": [], + "references": null + } + }, + "workload": { + "reviews-v3.bookinfo": { + "name": "reviews-v3", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + } + } + }, + "waypointWorkloads": null, + "health": { + "workloadStatus": { + "name": "reviews-v3", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": 1 + }, + "requests": { + "inbound": { + "http": { + "200": 0.31111111111111106 + } + }, + "outbound": { + "http": { + "200": 0.31111111111111106 + } + }, + "healthAnnotations": {} + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/istio-system/istio_config.json b/plugins/kiali/dev/__fixtures__/namespaces/istio-system/istio_config.json new file mode 100644 index 0000000000..12755e2b56 --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/istio-system/istio_config.json @@ -0,0 +1,35 @@ +{ + "namespace": { + "name": "istio-system", + "cluster": "", + "isAmbient": false, + "labels": null, + "annotations": null + }, + "destinationRules": [], + "envoyFilters": [], + "gateways": [], + "serviceEntries": [], + "sidecars": [], + "virtualServices": [], + "workloadEntries": [], + "workloadGroups": [], + "wasmPlugins": [], + "telemetries": [], + "k8sGateways": [], + "k8sHTTPRoutes": [], + "authorizationPolicies": [], + "peerAuthentications": [], + "requestAuthentications": [], + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": true, + "checks": [], + "references": null + } + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/grafana.json b/plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/grafana.json new file mode 100644 index 0000000000..0176417d10 --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/grafana.json @@ -0,0 +1,155 @@ +{ + "name": "grafana", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-02-15T09:24:58Z", + "resourceVersion": "1095", + "istioInjectionAnnotation": false, + "istioSidecar": false, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "grafana", + "app.kubernetes.io/instance": "grafana", + "app.kubernetes.io/name": "grafana", + "sidecar.istio.io/inject": "false" + }, + "appLabel": true, + "versionLabel": false, + "podCount": 0, + "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\":\"9.5.5\",\"helm.sh/chart\":\"grafana-6.57.4\"},\"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\":\"aec3d18ca2ea82d1d48f8965db1440aba0680ed2f32c5a29e6cdb5e7afc8b395\",\"checksum/dashboards-json-config\":\"01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b\",\"checksum/sc-dashboard-provider-config\":\"01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b\",\"kubectl.kubernetes.io/default-container\":\"grafana\"},\"labels\":{\"app\":\"grafana\",\"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:9.5.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,\"securityContext\":{\"fsGroup\":472,\"runAsGroup\":472,\"runAsNonRoot\":true,\"runAsUser\":472},\"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": null, + "dashboardAnnotations": {}, + "serviceAccountNames": null, + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "pods": [ + { + "name": "grafana-b8bbdc84d-pgmx4", + "labels": { + "app": "grafana", + "app.kubernetes.io/instance": "grafana", + "app.kubernetes.io/name": "grafana", + "pod-template-hash": "b8bbdc84d", + "sidecar.istio.io/inject": "false" + }, + "createdAt": "2024-02-15T09:24:58Z", + "createdBy": [ + { + "name": "grafana-b8bbdc84d", + "kind": "ReplicaSet" + } + ], + "containers": [ + { + "name": "grafana", + "image": "docker.io/grafana/grafana:9.5.5", + "isProxy": false, + "isReady": true, + "isAmbient": false + } + ], + "istioContainers": null, + "istioInitContainers": null, + "status": "Running", + "statusMessage": "", + "statusReason": "", + "appLabel": true, + "versionLabel": false, + "annotations": { + "checksum/config": "aec3d18ca2ea82d1d48f8965db1440aba0680ed2f32c5a29e6cdb5e7afc8b395", + "checksum/dashboards-json-config": "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b", + "checksum/sc-dashboard-provider-config": "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b", + "kubectl.kubernetes.io/default-container": "grafana" + }, + "proxyStatus": null, + "serviceAccountName": "grafana" + } + ], + "services": [ + { + "name": "grafana", + "namespace": "istio-system", + "istioSidecar": true, + "cluster": "Kubernetes", + "istioAmbient": false, + "appLabel": false, + "additionalDetailSample": null, + "annotations": null, + "healthAnnotations": {}, + "ports": null, + "labels": { + "app.kubernetes.io/instance": "grafana", + "app.kubernetes.io/managed-by": "Helm", + "app.kubernetes.io/name": "grafana", + "app.kubernetes.io/version": "9.5.5", + "helm.sh/chart": "grafana-6.57.4" + }, + "selector": { + "app.kubernetes.io/instance": "grafana", + "app.kubernetes.io/name": "grafana" + }, + "istioReferences": [], + "kialiWizard": "", + "serviceRegistry": "Kubernetes", + "health": { + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + } + ], + "runtimes": [], + "additionalDetails": [], + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": true, + "checks": [], + "references": null + } + }, + "virtualservice": { + "bookinfo.bookinfo": { + "name": "bookinfo", + "objectType": "virtualservice", + "valid": true, + "checks": [], + "references": null + } + }, + "workload": { + "grafana.istio-system": { + "name": "grafana", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + } + } + }, + "waypointWorkloads": null, + "health": { + "workloadStatus": { + "name": "grafana", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": -1 + }, + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/istio_egressgateway.json b/plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/istio_egressgateway.json new file mode 100644 index 0000000000..354968cfec --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/istio_egressgateway.json @@ -0,0 +1,183 @@ +{ + "name": "istio-egressgateway", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-02-15T09:24:44Z", + "resourceVersion": "16281", + "istioInjectionAnnotation": false, + "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": 0, + "annotations": { + "deployment.kubernetes.io/revision": "1" + }, + "healthAnnotations": {}, + "istioReferences": null, + "dashboardAnnotations": {}, + "serviceAccountNames": null, + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "pods": [ + { + "name": "istio-egressgateway-55bc68746f-l87v2", + "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", + "pod-template-hash": "55bc68746f", + "release": "istio", + "service.istio.io/canonical-name": "istio-egressgateway", + "service.istio.io/canonical-revision": "latest", + "sidecar.istio.io/inject": "false" + }, + "createdAt": "2024-02-15T09:24:44Z", + "createdBy": [ + { + "name": "istio-egressgateway-55bc68746f", + "kind": "ReplicaSet" + } + ], + "containers": [ + { + "name": "istio-proxy", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "istioContainers": null, + "istioInitContainers": null, + "status": "Running", + "statusMessage": "", + "statusReason": "", + "appLabel": true, + "versionLabel": false, + "annotations": { + "istio.io/rev": "default", + "prometheus.io/path": "/stats/prometheus", + "prometheus.io/port": "15020", + "prometheus.io/scrape": "true", + "sidecar.istio.io/inject": "false" + }, + "proxyStatus": null, + "serviceAccountName": "istio-egressgateway-service-account" + } + ], + "services": [ + { + "name": "istio-egressgateway", + "namespace": "istio-system", + "istioSidecar": true, + "cluster": "Kubernetes", + "istioAmbient": false, + "appLabel": true, + "additionalDetailSample": null, + "annotations": null, + "healthAnnotations": {}, + "ports": null, + "labels": { + "app": "istio-egressgateway", + "install.operator.istio.io/owning-resource": "installed-state", + "install.operator.istio.io/owning-resource-namespace": "istio-system", + "istio": "egressgateway", + "istio.io/rev": "default", + "operator.istio.io/component": "EgressGateways", + "operator.istio.io/managed": "Reconcile", + "operator.istio.io/version": "1.20.1", + "release": "istio" + }, + "selector": { + "app": "istio-egressgateway", + "istio": "egressgateway" + }, + "istioReferences": [], + "kialiWizard": "", + "serviceRegistry": "Kubernetes", + "health": { + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + } + ], + "runtimes": [ + { + "name": "", + "dashboardRefs": [ + { + "template": "envoy", + "title": "Envoy Metrics" + } + ] + } + ], + "additionalDetails": [], + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": true, + "checks": [], + "references": null + } + }, + "virtualservice": { + "bookinfo.bookinfo": { + "name": "bookinfo", + "objectType": "virtualservice", + "valid": true, + "checks": [], + "references": null + } + }, + "workload": { + "istio-egressgateway.istio-system": { + "name": "istio-egressgateway", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + } + } + }, + "waypointWorkloads": null, + "health": { + "workloadStatus": { + "name": "istio-egressgateway", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": -1 + }, + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/istio_ingressgateway.json b/plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/istio_ingressgateway.json new file mode 100644 index 0000000000..250aa688cd --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/istio_ingressgateway.json @@ -0,0 +1,187 @@ +{ + "name": "istio-ingressgateway", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-02-15T09:24:44Z", + "resourceVersion": "16279", + "istioInjectionAnnotation": false, + "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": 0, + "annotations": { + "deployment.kubernetes.io/revision": "1" + }, + "healthAnnotations": {}, + "istioReferences": null, + "dashboardAnnotations": {}, + "serviceAccountNames": null, + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "pods": [ + { + "name": "istio-ingressgateway-7998f4487c-llhrj", + "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", + "pod-template-hash": "7998f4487c", + "release": "istio", + "service.istio.io/canonical-name": "istio-ingressgateway", + "service.istio.io/canonical-revision": "latest", + "sidecar.istio.io/inject": "false" + }, + "createdAt": "2024-02-15T09:24:44Z", + "createdBy": [ + { + "name": "istio-ingressgateway-7998f4487c", + "kind": "ReplicaSet" + } + ], + "containers": [ + { + "name": "istio-proxy", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "istioContainers": null, + "istioInitContainers": null, + "status": "Running", + "statusMessage": "", + "statusReason": "", + "appLabel": true, + "versionLabel": false, + "annotations": { + "istio.io/rev": "default", + "prometheus.io/path": "/stats/prometheus", + "prometheus.io/port": "15020", + "prometheus.io/scrape": "true", + "sidecar.istio.io/inject": "false" + }, + "proxyStatus": null, + "serviceAccountName": "istio-ingressgateway-service-account" + } + ], + "services": [ + { + "name": "istio-ingressgateway", + "namespace": "istio-system", + "istioSidecar": true, + "cluster": "Kubernetes", + "istioAmbient": false, + "appLabel": true, + "additionalDetailSample": null, + "annotations": null, + "healthAnnotations": {}, + "ports": null, + "labels": { + "app": "istio-ingressgateway", + "install.operator.istio.io/owning-resource": "installed-state", + "install.operator.istio.io/owning-resource-namespace": "istio-system", + "istio": "ingressgateway", + "istio.io/rev": "default", + "operator.istio.io/component": "IngressGateways", + "operator.istio.io/managed": "Reconcile", + "operator.istio.io/version": "1.20.1", + "release": "istio" + }, + "selector": { + "app": "istio-ingressgateway", + "istio": "ingressgateway" + }, + "istioReferences": [], + "kialiWizard": "", + "serviceRegistry": "Kubernetes", + "health": { + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + } + ], + "runtimes": [ + { + "name": "", + "dashboardRefs": [ + { + "template": "envoy", + "title": "Envoy Metrics" + } + ] + } + ], + "additionalDetails": [], + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": true, + "checks": [], + "references": null + } + }, + "virtualservice": { + "bookinfo.bookinfo": { + "name": "bookinfo", + "objectType": "virtualservice", + "valid": true, + "checks": [], + "references": null + } + }, + "workload": { + "istio-ingressgateway.istio-system": { + "name": "istio-ingressgateway", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + } + } + }, + "waypointWorkloads": null, + "health": { + "workloadStatus": { + "name": "istio-ingressgateway", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": -1 + }, + "requests": { + "inbound": {}, + "outbound": { + "http": { + "200": 0.9999999999999999 + } + }, + "healthAnnotations": {} + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/istiod.json b/plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/istiod.json new file mode 100644 index 0000000000..5ddf582380 --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/istiod.json @@ -0,0 +1,172 @@ +{ + "name": "istiod", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-02-15T09:24:36Z", + "resourceVersion": "852", + "istioInjectionAnnotation": false, + "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": 0, + "annotations": { + "deployment.kubernetes.io/revision": "1" + }, + "healthAnnotations": {}, + "istioReferences": null, + "dashboardAnnotations": {}, + "serviceAccountNames": null, + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "pods": [ + { + "name": "istiod-74586f98bb-khmv4", + "labels": { + "app": "istiod", + "install.operator.istio.io/owning-resource": "unknown", + "istio": "pilot", + "istio.io/rev": "default", + "operator.istio.io/component": "Pilot", + "pod-template-hash": "74586f98bb", + "sidecar.istio.io/inject": "false" + }, + "createdAt": "2024-02-15T09:24:36Z", + "createdBy": [ + { + "name": "istiod-74586f98bb", + "kind": "ReplicaSet" + } + ], + "containers": [ + { + "name": "discovery", + "image": "gcr.io/istio-release/pilot:1.20.1", + "isProxy": false, + "isReady": true, + "isAmbient": false + } + ], + "istioContainers": null, + "istioInitContainers": null, + "status": "Running", + "statusMessage": "", + "statusReason": "", + "appLabel": true, + "versionLabel": false, + "annotations": { + "ambient.istio.io/redirection": "disabled", + "prometheus.io/port": "15014", + "prometheus.io/scrape": "true", + "sidecar.istio.io/inject": "false" + }, + "proxyStatus": null, + "serviceAccountName": "istiod" + } + ], + "services": [ + { + "name": "istiod", + "namespace": "istio-system", + "istioSidecar": true, + "cluster": "Kubernetes", + "istioAmbient": false, + "appLabel": true, + "additionalDetailSample": null, + "annotations": null, + "healthAnnotations": {}, + "ports": null, + "labels": { + "app": "istiod", + "install.operator.istio.io/owning-resource": "installed-state", + "install.operator.istio.io/owning-resource-namespace": "istio-system", + "istio": "pilot", + "istio.io/rev": "default", + "operator.istio.io/component": "Pilot", + "operator.istio.io/managed": "Reconcile", + "operator.istio.io/version": "1.20.1", + "release": "istio" + }, + "selector": { + "app": "istiod", + "istio": "pilot" + }, + "istioReferences": [], + "kialiWizard": "", + "serviceRegistry": "Kubernetes", + "health": { + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + } + ], + "runtimes": [ + { + "name": "Go", + "dashboardRefs": [ + { + "template": "go", + "title": "Go Metrics" + } + ] + } + ], + "additionalDetails": [], + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": true, + "checks": [], + "references": null + } + }, + "virtualservice": { + "bookinfo.bookinfo": { + "name": "bookinfo", + "objectType": "virtualservice", + "valid": true, + "checks": [], + "references": null + } + }, + "workload": { + "istiod.istio-system": { + "name": "istiod", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + } + } + }, + "waypointWorkloads": null, + "health": { + "workloadStatus": { + "name": "istiod", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": -1 + }, + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/jaeger.json b/plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/jaeger.json new file mode 100644 index 0000000000..7dd725d45e --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/jaeger.json @@ -0,0 +1,210 @@ +{ + "name": "jaeger", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-02-15T09:24:59Z", + "resourceVersion": "1068", + "istioInjectionAnnotation": false, + "istioSidecar": false, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "jaeger", + "sidecar.istio.io/inject": "false" + }, + "appLabel": true, + "versionLabel": false, + "podCount": 0, + "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": null, + "dashboardAnnotations": {}, + "serviceAccountNames": null, + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "pods": [ + { + "name": "jaeger-7d7d59b9d-q8wzb", + "labels": { + "app": "jaeger", + "pod-template-hash": "7d7d59b9d", + "sidecar.istio.io/inject": "false" + }, + "createdAt": "2024-02-15T09:24:59Z", + "createdBy": [ + { + "name": "jaeger-7d7d59b9d", + "kind": "ReplicaSet" + } + ], + "containers": [ + { + "name": "jaeger", + "image": "docker.io/jaegertracing/all-in-one:1.46", + "isProxy": false, + "isReady": true, + "isAmbient": false + } + ], + "istioContainers": null, + "istioInitContainers": null, + "status": "Running", + "statusMessage": "", + "statusReason": "", + "appLabel": true, + "versionLabel": false, + "annotations": { + "prometheus.io/port": "14269", + "prometheus.io/scrape": "true" + }, + "proxyStatus": null, + "serviceAccountName": "default" + } + ], + "services": [ + { + "name": "jaeger-collector", + "namespace": "istio-system", + "istioSidecar": true, + "cluster": "Kubernetes", + "istioAmbient": false, + "appLabel": true, + "additionalDetailSample": null, + "annotations": null, + "healthAnnotations": {}, + "ports": null, + "labels": { + "app": "jaeger" + }, + "selector": { + "app": "jaeger" + }, + "istioReferences": [], + "kialiWizard": "", + "serviceRegistry": "Kubernetes", + "health": { + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + }, + { + "name": "zipkin", + "namespace": "istio-system", + "istioSidecar": true, + "cluster": "Kubernetes", + "istioAmbient": false, + "appLabel": true, + "additionalDetailSample": null, + "annotations": null, + "healthAnnotations": {}, + "ports": null, + "labels": { + "name": "zipkin" + }, + "selector": { + "app": "jaeger" + }, + "istioReferences": [], + "kialiWizard": "", + "serviceRegistry": "Kubernetes", + "health": { + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + }, + { + "name": "tracing", + "namespace": "istio-system", + "istioSidecar": true, + "cluster": "Kubernetes", + "istioAmbient": false, + "appLabel": true, + "additionalDetailSample": null, + "annotations": null, + "healthAnnotations": {}, + "ports": null, + "labels": { + "app": "jaeger" + }, + "selector": { + "app": "jaeger" + }, + "istioReferences": [], + "kialiWizard": "", + "serviceRegistry": "Kubernetes", + "health": { + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + } + ], + "runtimes": [ + { + "name": "Go", + "dashboardRefs": [ + { + "template": "go", + "title": "Go Metrics" + } + ] + } + ], + "additionalDetails": [], + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": true, + "checks": [], + "references": null + } + }, + "virtualservice": { + "bookinfo.bookinfo": { + "name": "bookinfo", + "objectType": "virtualservice", + "valid": true, + "checks": [], + "references": null + } + }, + "workload": { + "jaeger.istio-system": { + "name": "jaeger", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + } + } + }, + "waypointWorkloads": null, + "health": { + "workloadStatus": { + "name": "jaeger", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": -1 + }, + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/kiali.json b/plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/kiali.json new file mode 100644 index 0000000000..bb30fc0cc4 --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/kiali.json @@ -0,0 +1,187 @@ +{ + "name": "kiali", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-02-15T09:25:30Z", + "resourceVersion": "16310", + "istioInjectionAnnotation": false, + "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.76.0", + "helm.sh/chart": "kiali-server-1.76.0", + "sidecar.istio.io/inject": "false", + "version": "v1.76.0" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 0, + "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.76.0\",\"helm.sh/chart\":\"kiali-server-1.76.0\",\"version\":\"v1.76.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\":\"aebd819b94172ef9b148702b7bb438ac35bd1eb284bbb9b13769d8576374fbda\",\"kiali.io/dashboards\":\"go,kiali\",\"prometheus.io/port\":\"9090\",\"prometheus.io/scrape\":\"true\"},\"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.76.0\",\"helm.sh/chart\":\"kiali-server-1.76.0\",\"sidecar.istio.io/inject\":\"false\",\"version\":\"v1.76.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.76\",\"imagePullPolicy\":\"Always\",\"livenessProbe\":{\"httpGet\":{\"path\":\"/kiali/healthz\",\"port\":\"api-port\",\"scheme\":\"HTTP\"},\"initialDelaySeconds\":5,\"periodSeconds\":30},\"name\":\"kiali\",\"ports\":[{\"containerPort\":20001,\"name\":\"api-port\"},{\"containerPort\":9090,\"name\":\"http-metrics\"}],\"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": null, + "dashboardAnnotations": {}, + "serviceAccountNames": null, + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "pods": [ + { + "name": "kiali-545878ddbb-swl5j", + "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.76.0", + "helm.sh/chart": "kiali-server-1.76.0", + "pod-template-hash": "545878ddbb", + "sidecar.istio.io/inject": "false", + "version": "v1.76.0" + }, + "createdAt": "2024-02-15T09:25:30Z", + "createdBy": [ + { + "name": "kiali-545878ddbb", + "kind": "ReplicaSet" + } + ], + "containers": [ + { + "name": "kiali", + "image": "quay.io/kiali/kiali:v1.76", + "isProxy": false, + "isReady": true, + "isAmbient": false + } + ], + "istioContainers": null, + "istioInitContainers": null, + "status": "Running", + "statusMessage": "", + "statusReason": "", + "appLabel": true, + "versionLabel": true, + "annotations": { + "checksum/config": "aebd819b94172ef9b148702b7bb438ac35bd1eb284bbb9b13769d8576374fbda", + "kiali.io/dashboards": "go,kiali", + "prometheus.io/port": "9090", + "prometheus.io/scrape": "true" + }, + "proxyStatus": null, + "serviceAccountName": "kiali" + } + ], + "services": [ + { + "name": "kiali", + "namespace": "istio-system", + "istioSidecar": true, + "cluster": "Kubernetes", + "istioAmbient": false, + "appLabel": false, + "additionalDetailSample": null, + "annotations": null, + "healthAnnotations": {}, + "ports": 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.76.0", + "helm.sh/chart": "kiali-server-1.76.0", + "version": "v1.76.0" + }, + "selector": { + "app.kubernetes.io/instance": "kiali", + "app.kubernetes.io/name": "kiali" + }, + "istioReferences": [], + "kialiWizard": "", + "serviceRegistry": "Kubernetes", + "health": { + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + } + ], + "runtimes": [ + { + "name": "Go", + "dashboardRefs": [ + { + "template": "go", + "title": "Go Metrics" + } + ] + }, + { + "name": "", + "dashboardRefs": [ + { + "template": "kiali", + "title": "Kiali Internal Metrics" + } + ] + } + ], + "additionalDetails": [], + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": true, + "checks": [], + "references": null + } + }, + "virtualservice": { + "bookinfo.bookinfo": { + "name": "bookinfo", + "objectType": "virtualservice", + "valid": true, + "checks": [], + "references": null + } + }, + "workload": { + "kiali.istio-system": { + "name": "kiali", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + } + } + }, + "waypointWorkloads": null, + "health": { + "workloadStatus": { + "name": "kiali", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": -1 + }, + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/prometheus.json b/plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/prometheus.json new file mode 100644 index 0000000000..cc5cbd93c3 --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/istio-system/workloads/prometheus.json @@ -0,0 +1,162 @@ +{ + "name": "prometheus", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-02-15T09:24:58Z", + "resourceVersion": "1113", + "istioInjectionAnnotation": false, + "istioSidecar": false, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "prometheus", + "chart": "prometheus-19.6.1", + "component": "server", + "heritage": "Helm", + "release": "prometheus", + "sidecar.istio.io/inject": "false" + }, + "appLabel": true, + "versionLabel": false, + "podCount": 0, + "annotations": { + "deployment.kubernetes.io/revision": "1", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"prometheus\",\"chart\":\"prometheus-19.6.1\",\"component\":\"server\",\"heritage\":\"Helm\",\"release\":\"prometheus\"},\"name\":\"prometheus\",\"namespace\":\"istio-system\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"prometheus\",\"component\":\"server\",\"release\":\"prometheus\"}},\"strategy\":{\"rollingUpdate\":null,\"type\":\"Recreate\"},\"template\":{\"metadata\":{\"labels\":{\"app\":\"prometheus\",\"chart\":\"prometheus-19.6.1\",\"component\":\"server\",\"heritage\":\"Helm\",\"release\":\"prometheus\",\"sidecar.istio.io/inject\":\"false\"}},\"spec\":{\"containers\":[{\"args\":[\"--volume-dir=/etc/config\",\"--webhook-url=http://127.0.0.1:9090/-/reload\"],\"image\":\"jimmidyson/configmap-reload:v0.8.0\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"prometheus-server-configmap-reload\",\"resources\":{},\"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.41.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},\"resources\":{},\"volumeMounts\":[{\"mountPath\":\"/etc/config\",\"name\":\"config-volume\"},{\"mountPath\":\"/data\",\"name\":\"storage-volume\",\"subPath\":\"\"}]}],\"dnsPolicy\":\"ClusterFirst\",\"enableServiceLinks\":true,\"securityContext\":{\"fsGroup\":65534,\"runAsGroup\":65534,\"runAsNonRoot\":true,\"runAsUser\":65534},\"serviceAccountName\":\"prometheus\",\"terminationGracePeriodSeconds\":300,\"volumes\":[{\"configMap\":{\"name\":\"prometheus\"},\"name\":\"config-volume\"},{\"emptyDir\":{},\"name\":\"storage-volume\"}]}}}}\n" + }, + "healthAnnotations": {}, + "istioReferences": null, + "dashboardAnnotations": {}, + "serviceAccountNames": null, + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "pods": [ + { + "name": "prometheus-db8b4588f-hhrn9", + "labels": { + "app": "prometheus", + "chart": "prometheus-19.6.1", + "component": "server", + "heritage": "Helm", + "pod-template-hash": "db8b4588f", + "release": "prometheus", + "sidecar.istio.io/inject": "false" + }, + "createdAt": "2024-02-15T09:24:58Z", + "createdBy": [ + { + "name": "prometheus-db8b4588f", + "kind": "ReplicaSet" + } + ], + "containers": [ + { + "name": "prometheus-server-configmap-reload", + "image": "jimmidyson/configmap-reload:v0.8.0", + "isProxy": false, + "isReady": true, + "isAmbient": false + }, + { + "name": "prometheus-server", + "image": "prom/prometheus:v2.41.0", + "isProxy": false, + "isReady": true, + "isAmbient": false + } + ], + "istioContainers": null, + "istioInitContainers": null, + "status": "Running", + "statusMessage": "", + "statusReason": "", + "appLabel": true, + "versionLabel": false, + "annotations": null, + "proxyStatus": null, + "serviceAccountName": "prometheus" + } + ], + "services": [ + { + "name": "prometheus", + "namespace": "istio-system", + "istioSidecar": true, + "cluster": "Kubernetes", + "istioAmbient": false, + "appLabel": true, + "additionalDetailSample": null, + "annotations": null, + "healthAnnotations": {}, + "ports": null, + "labels": { + "app": "prometheus", + "chart": "prometheus-19.6.1", + "component": "server", + "heritage": "Helm", + "release": "prometheus" + }, + "selector": { + "app": "prometheus", + "component": "server", + "release": "prometheus" + }, + "istioReferences": [], + "kialiWizard": "", + "serviceRegistry": "Kubernetes", + "health": { + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + } + ], + "runtimes": [], + "additionalDetails": [], + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": true, + "checks": [], + "references": null + } + }, + "virtualservice": { + "bookinfo.bookinfo": { + "name": "bookinfo", + "objectType": "virtualservice", + "valid": true, + "checks": [], + "references": null + } + }, + "workload": { + "prometheus.istio-system": { + "name": "prometheus", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + } + } + }, + "waypointWorkloads": null, + "health": { + "workloadStatus": { + "name": "prometheus", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": -1 + }, + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/istio_config.json b/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/istio_config.json new file mode 100644 index 0000000000..41de250779 --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/istio_config.json @@ -0,0 +1,35 @@ +{ + "namespace": { + "name": "travel-agency", + "cluster": "", + "isAmbient": false, + "labels": null, + "annotations": null + }, + "destinationRules": [], + "envoyFilters": [], + "gateways": [], + "serviceEntries": [], + "sidecars": [], + "virtualServices": [], + "workloadEntries": [], + "workloadGroups": [], + "wasmPlugins": [], + "telemetries": [], + "k8sGateways": [], + "k8sHTTPRoutes": [], + "authorizationPolicies": [], + "peerAuthentications": [], + "requestAuthentications": [], + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": true, + "checks": [], + "references": null + } + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/cars_v1.json b/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/cars_v1.json new file mode 100644 index 0000000000..b3c875098f --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/cars_v1.json @@ -0,0 +1,199 @@ +{ + "name": "cars-v1", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-02-16T11:27:38Z", + "resourceVersion": "56786", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "cars", + "version": "v1" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 0, + "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": null, + "dashboardAnnotations": {}, + "serviceAccountNames": null, + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "pods": [ + { + "name": "cars-v1-6c869ff769-8vnf9", + "labels": { + "app": "cars", + "pod-template-hash": "6c869ff769", + "security.istio.io/tlsMode": "istio", + "service.istio.io/canonical-name": "cars", + "service.istio.io/canonical-revision": "v1", + "version": "v1" + }, + "createdAt": "2024-02-16T11:27:38Z", + "createdBy": [ + { + "name": "cars-v1-6c869ff769", + "kind": "ReplicaSet" + } + ], + "containers": [ + { + "name": "cars", + "image": "quay.io/kiali/demo_travels_cars:v1", + "isProxy": false, + "isReady": true, + "isAmbient": false + } + ], + "istioContainers": [ + { + "name": "istio-proxy", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "istioInitContainers": [ + { + "name": "istio-init", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "status": "Running", + "statusMessage": "", + "statusReason": "", + "appLabel": true, + "versionLabel": true, + "annotations": { + "istio.io/rev": "default", + "kubectl.kubernetes.io/default-container": "cars", + "kubectl.kubernetes.io/default-logs-container": "cars", + "prometheus.io/path": "/stats/prometheus", + "prometheus.io/port": "15020", + "prometheus.io/scrape": "true", + "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": "", + "sidecar.istio.io/status": "{\"initContainers\":[\"istio-init\"],\"containers\":[\"istio-proxy\"],\"volumes\":[\"workload-socket\",\"credential-socket\",\"workload-certs\",\"istio-envoy\",\"istio-data\",\"istio-podinfo\",\"istio-token\",\"istiod-ca-cert\"],\"imagePullSecrets\":null,\"revision\":\"default\"}" + }, + "proxyStatus": { + "CDS": "Synced", + "EDS": "Synced", + "LDS": "Synced", + "RDS": "Synced" + }, + "serviceAccountName": "default" + } + ], + "services": [ + { + "name": "cars", + "namespace": "travel-agency", + "istioSidecar": true, + "cluster": "Kubernetes", + "istioAmbient": false, + "appLabel": true, + "additionalDetailSample": null, + "annotations": null, + "healthAnnotations": {}, + "ports": null, + "labels": { + "app": "cars" + }, + "selector": { + "app": "cars" + }, + "istioReferences": [], + "kialiWizard": "", + "serviceRegistry": "Kubernetes", + "health": { + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + } + ], + "runtimes": [ + { + "name": "", + "dashboardRefs": [ + { + "template": "envoy", + "title": "Envoy Metrics" + } + ] + } + ], + "additionalDetails": [], + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": false, + "checks": [ + { + "code": "KIA0302", + "message": "No matching workload found for gateway selector in this namespace", + "severity": "warning", + "path": "spec/selector" + } + ], + "references": null + } + }, + "virtualservice": { + "bookinfo.bookinfo": { + "name": "bookinfo", + "objectType": "virtualservice", + "valid": true, + "checks": [], + "references": null + } + }, + "workload": { + "cars-v1.travel-agency": { + "name": "cars-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + } + } + }, + "waypointWorkloads": null, + "health": { + "workloadStatus": { + "name": "cars-v1", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": 1 + }, + "requests": { + "inbound": { + "http": { + "200": 0.2666666666666666 + } + }, + "outbound": { + "http": { + "200": 0.2666666666666666 + } + }, + "healthAnnotations": {} + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/discounts_v1.json b/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/discounts_v1.json new file mode 100644 index 0000000000..b808060658 --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/discounts_v1.json @@ -0,0 +1,195 @@ +{ + "name": "discounts-v1", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-02-16T11:27:38Z", + "resourceVersion": "56804", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "discounts", + "version": "v1" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 0, + "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": null, + "dashboardAnnotations": {}, + "serviceAccountNames": null, + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "pods": [ + { + "name": "discounts-v1-76f667c5db-d4q9c", + "labels": { + "app": "discounts", + "pod-template-hash": "76f667c5db", + "security.istio.io/tlsMode": "istio", + "service.istio.io/canonical-name": "discounts", + "service.istio.io/canonical-revision": "v1", + "version": "v1" + }, + "createdAt": "2024-02-16T11:27:38Z", + "createdBy": [ + { + "name": "discounts-v1-76f667c5db", + "kind": "ReplicaSet" + } + ], + "containers": [ + { + "name": "discounts", + "image": "quay.io/kiali/demo_travels_discounts:v1", + "isProxy": false, + "isReady": true, + "isAmbient": false + } + ], + "istioContainers": [ + { + "name": "istio-proxy", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "istioInitContainers": [ + { + "name": "istio-init", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "status": "Running", + "statusMessage": "", + "statusReason": "", + "appLabel": true, + "versionLabel": true, + "annotations": { + "istio.io/rev": "default", + "kubectl.kubernetes.io/default-container": "discounts", + "kubectl.kubernetes.io/default-logs-container": "discounts", + "prometheus.io/path": "/stats/prometheus", + "prometheus.io/port": "15020", + "prometheus.io/scrape": "true", + "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": "", + "sidecar.istio.io/status": "{\"initContainers\":[\"istio-init\"],\"containers\":[\"istio-proxy\"],\"volumes\":[\"workload-socket\",\"credential-socket\",\"workload-certs\",\"istio-envoy\",\"istio-data\",\"istio-podinfo\",\"istio-token\",\"istiod-ca-cert\"],\"imagePullSecrets\":null,\"revision\":\"default\"}" + }, + "proxyStatus": { + "CDS": "Synced", + "EDS": "Synced", + "LDS": "Synced", + "RDS": "Synced" + }, + "serviceAccountName": "default" + } + ], + "services": [ + { + "name": "discounts", + "namespace": "travel-agency", + "istioSidecar": true, + "cluster": "Kubernetes", + "istioAmbient": false, + "appLabel": true, + "additionalDetailSample": null, + "annotations": null, + "healthAnnotations": {}, + "ports": null, + "labels": { + "app": "discounts" + }, + "selector": { + "app": "discounts" + }, + "istioReferences": [], + "kialiWizard": "", + "serviceRegistry": "Kubernetes", + "health": { + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + } + ], + "runtimes": [ + { + "name": "", + "dashboardRefs": [ + { + "template": "envoy", + "title": "Envoy Metrics" + } + ] + } + ], + "additionalDetails": [], + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": false, + "checks": [ + { + "code": "KIA0302", + "message": "No matching workload found for gateway selector in this namespace", + "severity": "warning", + "path": "spec/selector" + } + ], + "references": null + } + }, + "virtualservice": { + "bookinfo.bookinfo": { + "name": "bookinfo", + "objectType": "virtualservice", + "valid": true, + "checks": [], + "references": null + } + }, + "workload": { + "discounts-v1.travel-agency": { + "name": "discounts-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + } + } + }, + "waypointWorkloads": null, + "health": { + "workloadStatus": { + "name": "discounts-v1", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": 1 + }, + "requests": { + "inbound": { + "http": { + "200": 1.333333333333333 + } + }, + "outbound": {}, + "healthAnnotations": {} + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/flights_v1.json b/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/flights_v1.json new file mode 100644 index 0000000000..5e186087b0 --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/flights_v1.json @@ -0,0 +1,199 @@ +{ + "name": "flights-v1", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-02-16T11:27:38Z", + "resourceVersion": "56729", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "flights", + "version": "v1" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 0, + "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": null, + "dashboardAnnotations": {}, + "serviceAccountNames": null, + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "pods": [ + { + "name": "flights-v1-5c654b5b7f-gzqh4", + "labels": { + "app": "flights", + "pod-template-hash": "5c654b5b7f", + "security.istio.io/tlsMode": "istio", + "service.istio.io/canonical-name": "flights", + "service.istio.io/canonical-revision": "v1", + "version": "v1" + }, + "createdAt": "2024-02-16T11:27:38Z", + "createdBy": [ + { + "name": "flights-v1-5c654b5b7f", + "kind": "ReplicaSet" + } + ], + "containers": [ + { + "name": "flights", + "image": "quay.io/kiali/demo_travels_flights:v1", + "isProxy": false, + "isReady": true, + "isAmbient": false + } + ], + "istioContainers": [ + { + "name": "istio-proxy", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "istioInitContainers": [ + { + "name": "istio-init", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "status": "Running", + "statusMessage": "", + "statusReason": "", + "appLabel": true, + "versionLabel": true, + "annotations": { + "istio.io/rev": "default", + "kubectl.kubernetes.io/default-container": "flights", + "kubectl.kubernetes.io/default-logs-container": "flights", + "prometheus.io/path": "/stats/prometheus", + "prometheus.io/port": "15020", + "prometheus.io/scrape": "true", + "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": "", + "sidecar.istio.io/status": "{\"initContainers\":[\"istio-init\"],\"containers\":[\"istio-proxy\"],\"volumes\":[\"workload-socket\",\"credential-socket\",\"workload-certs\",\"istio-envoy\",\"istio-data\",\"istio-podinfo\",\"istio-token\",\"istiod-ca-cert\"],\"imagePullSecrets\":null,\"revision\":\"default\"}" + }, + "proxyStatus": { + "CDS": "Synced", + "EDS": "Synced", + "LDS": "Synced", + "RDS": "Synced" + }, + "serviceAccountName": "default" + } + ], + "services": [ + { + "name": "flights", + "namespace": "travel-agency", + "istioSidecar": true, + "cluster": "Kubernetes", + "istioAmbient": false, + "appLabel": true, + "additionalDetailSample": null, + "annotations": null, + "healthAnnotations": {}, + "ports": null, + "labels": { + "app": "flights" + }, + "selector": { + "app": "flights" + }, + "istioReferences": [], + "kialiWizard": "", + "serviceRegistry": "Kubernetes", + "health": { + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + } + ], + "runtimes": [ + { + "name": "", + "dashboardRefs": [ + { + "template": "envoy", + "title": "Envoy Metrics" + } + ] + } + ], + "additionalDetails": [], + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": false, + "checks": [ + { + "code": "KIA0302", + "message": "No matching workload found for gateway selector in this namespace", + "severity": "warning", + "path": "spec/selector" + } + ], + "references": null + } + }, + "virtualservice": { + "bookinfo.bookinfo": { + "name": "bookinfo", + "objectType": "virtualservice", + "valid": true, + "checks": [], + "references": null + } + }, + "workload": { + "flights-v1.travel-agency": { + "name": "flights-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + } + } + }, + "waypointWorkloads": null, + "health": { + "workloadStatus": { + "name": "flights-v1", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": 1 + }, + "requests": { + "inbound": { + "http": { + "200": 0.2666666666666666 + } + }, + "outbound": { + "http": { + "200": 0.2666666666666666 + } + }, + "healthAnnotations": {} + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/hotels_v1.json b/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/hotels_v1.json new file mode 100644 index 0000000000..0b5bd82a2d --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/hotels_v1.json @@ -0,0 +1,199 @@ +{ + "name": "hotels-v1", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-02-16T11:27:38Z", + "resourceVersion": "56712", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "hotels", + "version": "v1" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 0, + "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": null, + "dashboardAnnotations": {}, + "serviceAccountNames": null, + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "pods": [ + { + "name": "hotels-v1-57d9574745-kjqqj", + "labels": { + "app": "hotels", + "pod-template-hash": "57d9574745", + "security.istio.io/tlsMode": "istio", + "service.istio.io/canonical-name": "hotels", + "service.istio.io/canonical-revision": "v1", + "version": "v1" + }, + "createdAt": "2024-02-16T11:27:38Z", + "createdBy": [ + { + "name": "hotels-v1-57d9574745", + "kind": "ReplicaSet" + } + ], + "containers": [ + { + "name": "hotels", + "image": "quay.io/kiali/demo_travels_hotels:v1", + "isProxy": false, + "isReady": true, + "isAmbient": false + } + ], + "istioContainers": [ + { + "name": "istio-proxy", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "istioInitContainers": [ + { + "name": "istio-init", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "status": "Running", + "statusMessage": "", + "statusReason": "", + "appLabel": true, + "versionLabel": true, + "annotations": { + "istio.io/rev": "default", + "kubectl.kubernetes.io/default-container": "hotels", + "kubectl.kubernetes.io/default-logs-container": "hotels", + "prometheus.io/path": "/stats/prometheus", + "prometheus.io/port": "15020", + "prometheus.io/scrape": "true", + "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": "", + "sidecar.istio.io/status": "{\"initContainers\":[\"istio-init\"],\"containers\":[\"istio-proxy\"],\"volumes\":[\"workload-socket\",\"credential-socket\",\"workload-certs\",\"istio-envoy\",\"istio-data\",\"istio-podinfo\",\"istio-token\",\"istiod-ca-cert\"],\"imagePullSecrets\":null,\"revision\":\"default\"}" + }, + "proxyStatus": { + "CDS": "Synced", + "EDS": "Synced", + "LDS": "Synced", + "RDS": "Synced" + }, + "serviceAccountName": "default" + } + ], + "services": [ + { + "name": "hotels", + "namespace": "travel-agency", + "istioSidecar": true, + "cluster": "Kubernetes", + "istioAmbient": false, + "appLabel": true, + "additionalDetailSample": null, + "annotations": null, + "healthAnnotations": {}, + "ports": null, + "labels": { + "app": "hotels" + }, + "selector": { + "app": "hotels" + }, + "istioReferences": [], + "kialiWizard": "", + "serviceRegistry": "Kubernetes", + "health": { + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + } + ], + "runtimes": [ + { + "name": "", + "dashboardRefs": [ + { + "template": "envoy", + "title": "Envoy Metrics" + } + ] + } + ], + "additionalDetails": [], + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": false, + "checks": [ + { + "code": "KIA0302", + "message": "No matching workload found for gateway selector in this namespace", + "severity": "warning", + "path": "spec/selector" + } + ], + "references": null + } + }, + "virtualservice": { + "bookinfo.bookinfo": { + "name": "bookinfo", + "objectType": "virtualservice", + "valid": true, + "checks": [], + "references": null + } + }, + "workload": { + "hotels-v1.travel-agency": { + "name": "hotels-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + } + } + }, + "waypointWorkloads": null, + "health": { + "workloadStatus": { + "name": "hotels-v1", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": 1 + }, + "requests": { + "inbound": { + "http": { + "200": 0.7999999999999999 + } + }, + "outbound": { + "http": { + "200": 0.39999999999999997 + } + }, + "healthAnnotations": {} + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/insurances_v1.json b/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/insurances_v1.json new file mode 100644 index 0000000000..a0287306c3 --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/insurances_v1.json @@ -0,0 +1,198 @@ +{ + "name": "insurances-v1", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-02-16T11:27:38Z", + "resourceVersion": "56833", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "insurances", + "version": "v1" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 0, + "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": null, + "dashboardAnnotations": {}, + "serviceAccountNames": null, + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "pods": [ + { + "name": "insurances-v1-b66754b7b-9wkm6", + "labels": { + "app": "insurances", + "pod-template-hash": "b66754b7b", + "security.istio.io/tlsMode": "istio", + "service.istio.io/canonical-name": "insurances", + "service.istio.io/canonical-revision": "v1", + "version": "v1" + }, + "createdAt": "2024-02-16T11:27:38Z", + "createdBy": [ + { + "name": "insurances-v1-b66754b7b", + "kind": "ReplicaSet" + } + ], + "containers": [ + { + "name": "insurances", + "image": "quay.io/kiali/demo_travels_insurances:v1", + "isProxy": false, + "isReady": true, + "isAmbient": false + } + ], + "istioContainers": [ + { + "name": "istio-proxy", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "istioInitContainers": [ + { + "name": "istio-init", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "status": "Running", + "statusMessage": "", + "statusReason": "", + "appLabel": true, + "versionLabel": true, + "annotations": { + "istio.io/rev": "default", + "kubectl.kubernetes.io/default-container": "insurances", + "kubectl.kubernetes.io/default-logs-container": "insurances", + "prometheus.io/path": "/stats/prometheus", + "prometheus.io/port": "15020", + "prometheus.io/scrape": "true", + "readiness.status.sidecar.istio.io/applicationPorts": "", + "sidecar.istio.io/status": "{\"initContainers\":[\"istio-init\"],\"containers\":[\"istio-proxy\"],\"volumes\":[\"workload-socket\",\"credential-socket\",\"workload-certs\",\"istio-envoy\",\"istio-data\",\"istio-podinfo\",\"istio-token\",\"istiod-ca-cert\"],\"imagePullSecrets\":null,\"revision\":\"default\"}" + }, + "proxyStatus": { + "CDS": "Synced", + "EDS": "Synced", + "LDS": "Synced", + "RDS": "Synced" + }, + "serviceAccountName": "default" + } + ], + "services": [ + { + "name": "insurances", + "namespace": "travel-agency", + "istioSidecar": true, + "cluster": "Kubernetes", + "istioAmbient": false, + "appLabel": true, + "additionalDetailSample": null, + "annotations": null, + "healthAnnotations": {}, + "ports": null, + "labels": { + "app": "insurances" + }, + "selector": { + "app": "insurances" + }, + "istioReferences": [], + "kialiWizard": "", + "serviceRegistry": "Kubernetes", + "health": { + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + } + ], + "runtimes": [ + { + "name": "", + "dashboardRefs": [ + { + "template": "envoy", + "title": "Envoy Metrics" + } + ] + } + ], + "additionalDetails": [], + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": false, + "checks": [ + { + "code": "KIA0302", + "message": "No matching workload found for gateway selector in this namespace", + "severity": "warning", + "path": "spec/selector" + } + ], + "references": null + } + }, + "virtualservice": { + "bookinfo.bookinfo": { + "name": "bookinfo", + "objectType": "virtualservice", + "valid": true, + "checks": [], + "references": null + } + }, + "workload": { + "insurances-v1.travel-agency": { + "name": "insurances-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + } + } + }, + "waypointWorkloads": null, + "health": { + "workloadStatus": { + "name": "insurances-v1", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": 1 + }, + "requests": { + "inbound": { + "http": { + "200": 0.39999999999999997 + } + }, + "outbound": { + "http": { + "200": 0.39999999999999997 + } + }, + "healthAnnotations": {} + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/mysqldb_v1.json b/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/mysqldb_v1.json new file mode 100644 index 0000000000..00f6c570d6 --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/mysqldb_v1.json @@ -0,0 +1,189 @@ +{ + "name": "mysqldb-v1", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-02-16T11:27:38Z", + "resourceVersion": "56885", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "mysqldb", + "version": "v1" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 0, + "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": null, + "dashboardAnnotations": {}, + "serviceAccountNames": null, + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "pods": [ + { + "name": "mysqldb-v1-64bc584fdc-zh5tl", + "labels": { + "app": "mysqldb", + "pod-template-hash": "64bc584fdc", + "security.istio.io/tlsMode": "istio", + "service.istio.io/canonical-name": "mysqldb", + "service.istio.io/canonical-revision": "v1", + "version": "v1" + }, + "createdAt": "2024-02-16T11:27:38Z", + "createdBy": [ + { + "name": "mysqldb-v1-64bc584fdc", + "kind": "ReplicaSet" + } + ], + "containers": [ + { + "name": "mysqldb", + "image": "quay.io/kiali/demo_travels_mysqldb:v1", + "isProxy": false, + "isReady": true, + "isAmbient": false + } + ], + "istioContainers": [ + { + "name": "istio-proxy", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "istioInitContainers": [ + { + "name": "istio-init", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "status": "Running", + "statusMessage": "", + "statusReason": "", + "appLabel": true, + "versionLabel": true, + "annotations": { + "istio.io/rev": "default", + "kubectl.kubernetes.io/default-container": "mysqldb", + "kubectl.kubernetes.io/default-logs-container": "mysqldb", + "prometheus.io/path": "/stats/prometheus", + "prometheus.io/port": "15020", + "prometheus.io/scrape": "true", + "sidecar.istio.io/status": "{\"initContainers\":[\"istio-init\"],\"containers\":[\"istio-proxy\"],\"volumes\":[\"workload-socket\",\"credential-socket\",\"workload-certs\",\"istio-envoy\",\"istio-data\",\"istio-podinfo\",\"istio-token\",\"istiod-ca-cert\"],\"imagePullSecrets\":null,\"revision\":\"default\"}" + }, + "proxyStatus": { + "CDS": "Synced", + "EDS": "Synced", + "LDS": "Synced", + "RDS": "Synced" + }, + "serviceAccountName": "default" + } + ], + "services": [ + { + "name": "mysqldb", + "namespace": "travel-agency", + "istioSidecar": true, + "cluster": "Kubernetes", + "istioAmbient": false, + "appLabel": true, + "additionalDetailSample": null, + "annotations": null, + "healthAnnotations": {}, + "ports": null, + "labels": { + "app": "mysqldb" + }, + "selector": { + "app": "mysqldb" + }, + "istioReferences": [], + "kialiWizard": "", + "serviceRegistry": "Kubernetes", + "health": { + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + } + ], + "runtimes": [ + { + "name": "", + "dashboardRefs": [ + { + "template": "envoy", + "title": "Envoy Metrics" + } + ] + } + ], + "additionalDetails": [], + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": false, + "checks": [ + { + "code": "KIA0302", + "message": "No matching workload found for gateway selector in this namespace", + "severity": "warning", + "path": "spec/selector" + } + ], + "references": null + } + }, + "virtualservice": { + "bookinfo.bookinfo": { + "name": "bookinfo", + "objectType": "virtualservice", + "valid": true, + "checks": [], + "references": null + } + }, + "workload": { + "mysqldb-v1.travel-agency": { + "name": "mysqldb-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + } + } + }, + "waypointWorkloads": null, + "health": { + "workloadStatus": { + "name": "mysqldb-v1", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": 1 + }, + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/travels_v1.json b/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/travels_v1.json new file mode 100644 index 0000000000..ef5315e0d3 --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/workloads/travels_v1.json @@ -0,0 +1,350 @@ +{ + "service": { + "name": "travels", + "createdAt": "2024-02-16T11:27:38Z", + "resourceVersion": "56507", + "namespace": { + "name": "travel-agency", + "cluster": "", + "isAmbient": false, + "labels": null, + "annotations": null + }, + "labels": { + "app": "travels" + }, + "selectors": { + "app": "travels" + }, + "type": "ClusterIP", + "ip": "10.100.8.39", + "ports": [ + { + "name": "http", + "protocol": "TCP", + "port": 8000 + } + ], + "externalName": "", + "annotations": { + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"travels\"},\"name\":\"travels\",\"namespace\":\"travel-agency\"},\"spec\":{\"ports\":[{\"name\":\"http\",\"port\":8000}],\"selector\":{\"app\":\"travels\"}}}\n" + }, + "healthAnnotations": {}, + "additionalDetails": [] + }, + "cluster": "Kubernetes", + "istioSidecar": true, + "endpoints": [ + { + "addresses": [ + { + "kind": "Pod", + "name": "travels-v1-78fc68bb6b-xstzx", + "ip": "10.244.0.39", + "port": 0 + } + ], + "ports": [ + { + "name": "http", + "protocol": "TCP", + "port": 8000 + } + ] + } + ], + "virtualServices": [], + "destinationRules": [], + "k8sHTTPRoutes": [], + "serviceEntries": null, + "istioPermissions": { + "create": true, + "update": true, + "delete": true + }, + "workloads": [ + { + "name": "travels-v1", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-02-16T11:27:38Z", + "resourceVersion": "56691", + "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": null, + "requests": { + "inbound": null, + "outbound": null, + "healthAnnotations": null + } + } + } + ], + "subServices": [ + { + "name": "travels", + "namespace": "", + "istioSidecar": false, + "cluster": "", + "istioAmbient": false, + "appLabel": false, + "additionalDetailSample": null, + "annotations": null, + "healthAnnotations": null, + "ports": { + "http": 8000 + }, + "labels": null, + "selector": null, + "istioReferences": null, + "kialiWizard": "", + "serviceRegistry": "", + "health": { + "requests": { + "inbound": null, + "outbound": null, + "healthAnnotations": null + } + } + } + ], + "health": { + "requests": { + "inbound": { + "http": { + "200": 0.7999999999999998 + } + }, + "outbound": {}, + "healthAnnotations": {} + } + }, + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": true, + "checks": [], + "references": null + } + }, + "service": { + "travels.travel-agency": { + "name": "travels", + "objectType": "service", + "valid": true, + "checks": [], + "references": null + } + }, + "virtualservice": { + "bookinfo.bookinfo": { + "name": "bookinfo", + "objectType": "virtualservice", + "valid": true, + "checks": [], + "references": null + } + }, + "workload": { + "cars-v1.travel-agency": { + "name": "cars-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "control.travel-control": { + "name": "control", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "details-v1.bookinfo": { + "name": "details-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 + }, + "grafana.istio-system": { + "name": "grafana", + "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 + }, + "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-traffic-generator.bookinfo": { + "name": "kiali-traffic-generator", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "kiali.istio-system": { + "name": "kiali", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "mysqldb-v1.travel-agency": { + "name": "mysqldb-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "productpage-v1.bookinfo": { + "name": "productpage-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "prometheus.istio-system": { + "name": "prometheus", + "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 + }, + "travels-v1.travel-agency": { + "name": "travels-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "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 + } + } + }, + "namespaceMTLS": { + "status": "MTLS_NOT_ENABLED", + "autoMTLSEnabled": true, + "minTLS": "" + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/travel-control/istio_config.json b/plugins/kiali/dev/__fixtures__/namespaces/travel-control/istio_config.json new file mode 100644 index 0000000000..b447e08f8a --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/travel-control/istio_config.json @@ -0,0 +1,35 @@ +{ + "namespace": { + "name": "travel-control", + "cluster": "", + "isAmbient": false, + "labels": null, + "annotations": null + }, + "destinationRules": [], + "envoyFilters": [], + "gateways": [], + "serviceEntries": [], + "sidecars": [], + "virtualServices": [], + "workloadEntries": [], + "workloadGroups": [], + "wasmPlugins": [], + "telemetries": [], + "k8sGateways": [], + "k8sHTTPRoutes": [], + "authorizationPolicies": [], + "peerAuthentications": [], + "requestAuthentications": [], + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": true, + "checks": [], + "references": null + } + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/travel-control/workloads/control.json b/plugins/kiali/dev/__fixtures__/namespaces/travel-control/workloads/control.json new file mode 100644 index 0000000000..c8f50fb974 --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/travel-control/workloads/control.json @@ -0,0 +1,190 @@ +{ + "name": "control", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-02-16T11:27:39Z", + "resourceVersion": "56909", + "istioSidecar": true, + "istioAmbient": false, + "additionalDetailSample": null, + "labels": { + "app": "control", + "version": "v1" + }, + "appLabel": true, + "versionLabel": true, + "podCount": 0, + "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": null, + "dashboardAnnotations": {}, + "serviceAccountNames": null, + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "pods": [ + { + "name": "control-746644dd7d-sqpw4", + "labels": { + "app": "control", + "pod-template-hash": "746644dd7d", + "security.istio.io/tlsMode": "istio", + "service.istio.io/canonical-name": "control", + "service.istio.io/canonical-revision": "v1", + "version": "v1" + }, + "createdAt": "2024-02-16T11:27:39Z", + "createdBy": [ + { + "name": "control-746644dd7d", + "kind": "ReplicaSet" + } + ], + "containers": [ + { + "name": "control", + "image": "quay.io/kiali/demo_travels_control:v1", + "isProxy": false, + "isReady": true, + "isAmbient": false + } + ], + "istioContainers": [ + { + "name": "istio-proxy", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "istioInitContainers": [ + { + "name": "istio-init", + "image": "gcr.io/istio-release/proxyv2:1.20.1", + "isProxy": true, + "isReady": true, + "isAmbient": false + } + ], + "status": "Running", + "statusMessage": "", + "statusReason": "", + "appLabel": true, + "versionLabel": true, + "annotations": { + "istio.io/rev": "default", + "kubectl.kubernetes.io/default-container": "control", + "kubectl.kubernetes.io/default-logs-container": "control", + "prometheus.io/path": "/stats/prometheus", + "prometheus.io/port": "15020", + "prometheus.io/scrape": "true", + "readiness.status.sidecar.istio.io/applicationPorts": "", + "sidecar.istio.io/status": "{\"initContainers\":[\"istio-init\"],\"containers\":[\"istio-proxy\"],\"volumes\":[\"workload-socket\",\"credential-socket\",\"workload-certs\",\"istio-envoy\",\"istio-data\",\"istio-podinfo\",\"istio-token\",\"istiod-ca-cert\"],\"imagePullSecrets\":null,\"revision\":\"default\"}" + }, + "proxyStatus": { + "CDS": "Synced", + "EDS": "Synced", + "LDS": "Synced", + "RDS": "Synced" + }, + "serviceAccountName": "default" + } + ], + "services": [ + { + "name": "control", + "namespace": "travel-control", + "istioSidecar": true, + "cluster": "Kubernetes", + "istioAmbient": false, + "appLabel": true, + "additionalDetailSample": null, + "annotations": null, + "healthAnnotations": {}, + "ports": null, + "labels": { + "app": "control" + }, + "selector": { + "app": "control" + }, + "istioReferences": [], + "kialiWizard": "", + "serviceRegistry": "Kubernetes", + "health": { + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } + } + ], + "runtimes": [ + { + "name": "", + "dashboardRefs": [ + { + "template": "envoy", + "title": "Envoy Metrics" + } + ] + } + ], + "additionalDetails": [], + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": false, + "checks": [ + { + "code": "KIA0302", + "message": "No matching workload found for gateway selector in this namespace", + "severity": "warning", + "path": "spec/selector" + } + ], + "references": null + } + }, + "virtualservice": { + "bookinfo.bookinfo": { + "name": "bookinfo", + "objectType": "virtualservice", + "valid": true, + "checks": [], + "references": null + } + }, + "workload": { + "control.travel-control": { + "name": "control", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + } + } + }, + "waypointWorkloads": null, + "health": { + "workloadStatus": { + "name": "control", + "desiredReplicas": 1, + "currentReplicas": 1, + "availableReplicas": 1, + "syncedProxies": 1 + }, + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/travel-portal/istio_config.json b/plugins/kiali/dev/__fixtures__/namespaces/travel-portal/istio_config.json new file mode 100644 index 0000000000..693fb9a932 --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/travel-portal/istio_config.json @@ -0,0 +1,27 @@ +{ + "travel-portal": { + "namespace": { + "name": "travel-portal", + "cluster": "", + "isAmbient": false, + "labels": null, + "annotations": null + }, + "destinationRules": [], + "envoyFilters": [], + "gateways": [], + "serviceEntries": [], + "sidecars": [], + "virtualServices": [], + "workloadEntries": [], + "workloadGroups": [], + "wasmPlugins": [], + "telemetries": [], + "k8sGateways": [], + "k8sHTTPRoutes": [], + "authorizationPolicies": [], + "peerAuthentications": [], + "requestAuthentications": [], + "validations": {} + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/travel-portal/workloads/travels.json b/plugins/kiali/dev/__fixtures__/namespaces/travel-portal/workloads/travels.json new file mode 100644 index 0000000000..01a8e86a2b --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/travel-portal/workloads/travels.json @@ -0,0 +1,346 @@ +{ + "service": { + "name": "travels", + "createdAt": "2024-02-16T11:27:39Z", + "resourceVersion": "56566", + "namespace": { + "name": "travel-portal", + "cluster": "", + "isAmbient": false, + "labels": null, + "annotations": null + }, + "labels": { + "app": "travels" + }, + "selectors": { + "app": "travels" + }, + "type": "ClusterIP", + "ip": "10.108.25.150", + "ports": [ + { + "name": "http", + "protocol": "TCP", + "port": 8000 + } + ], + "externalName": "", + "annotations": { + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"travels\"},\"name\":\"travels\",\"namespace\":\"travel-portal\"},\"spec\":{\"ports\":[{\"name\":\"http\",\"port\":8000}],\"selector\":{\"app\":\"travels\"}}}\n" + }, + "healthAnnotations": {}, + "additionalDetails": [] + }, + "cluster": "Kubernetes", + "istioSidecar": true, + "endpoints": [ + { + "addresses": [ + { + "kind": "Pod", + "name": "travels-969557fd4-h5cgh", + "ip": "10.244.0.42", + "port": 0 + } + ], + "ports": [ + { + "name": "http", + "protocol": "TCP", + "port": 8000 + } + ] + } + ], + "virtualServices": [], + "destinationRules": [], + "k8sHTTPRoutes": [], + "serviceEntries": null, + "istioPermissions": { + "create": true, + "update": true, + "delete": true + }, + "workloads": [ + { + "name": "travels", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-02-16T11:27:39Z", + "resourceVersion": "56923", + "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": null, + "requests": { + "inbound": null, + "outbound": null, + "healthAnnotations": null + } + } + } + ], + "subServices": [ + { + "name": "travels", + "namespace": "", + "istioSidecar": false, + "cluster": "", + "istioAmbient": false, + "appLabel": false, + "additionalDetailSample": null, + "annotations": null, + "healthAnnotations": null, + "ports": { + "http": 8000 + }, + "labels": null, + "selector": null, + "istioReferences": null, + "kialiWizard": "", + "serviceRegistry": "", + "health": { + "requests": { + "inbound": null, + "outbound": null, + "healthAnnotations": null + } + } + } + ], + "health": { + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + }, + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": true, + "checks": [], + "references": null + } + }, + "service": { + "travels.travel-portal": { + "name": "travels", + "objectType": "service", + "valid": true, + "checks": [], + "references": null + } + }, + "virtualservice": { + "bookinfo.bookinfo": { + "name": "bookinfo", + "objectType": "virtualservice", + "valid": true, + "checks": [], + "references": null + } + }, + "workload": { + "cars-v1.travel-agency": { + "name": "cars-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "control.travel-control": { + "name": "control", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "details-v1.bookinfo": { + "name": "details-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 + }, + "grafana.istio-system": { + "name": "grafana", + "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 + }, + "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-traffic-generator.bookinfo": { + "name": "kiali-traffic-generator", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "kiali.istio-system": { + "name": "kiali", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "mysqldb-v1.travel-agency": { + "name": "mysqldb-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "productpage-v1.bookinfo": { + "name": "productpage-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "prometheus.istio-system": { + "name": "prometheus", + "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 + }, + "travels-v1.travel-agency": { + "name": "travels-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "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 + } + } + }, + "namespaceMTLS": { + "status": "MTLS_NOT_ENABLED", + "autoMTLSEnabled": true, + "minTLS": "" + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/travel-portal/workloads/viaggi.json b/plugins/kiali/dev/__fixtures__/namespaces/travel-portal/workloads/viaggi.json new file mode 100644 index 0000000000..68daefa26f --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/travel-portal/workloads/viaggi.json @@ -0,0 +1,346 @@ +{ + "service": { + "name": "viaggi", + "createdAt": "2024-02-16T11:27:39Z", + "resourceVersion": "56553", + "namespace": { + "name": "travel-portal", + "cluster": "", + "isAmbient": false, + "labels": null, + "annotations": null + }, + "labels": { + "app": "viaggi" + }, + "selectors": { + "app": "viaggi" + }, + "type": "ClusterIP", + "ip": "10.100.88.214", + "ports": [ + { + "name": "http", + "protocol": "TCP", + "port": 8000 + } + ], + "externalName": "", + "annotations": { + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"viaggi\"},\"name\":\"viaggi\",\"namespace\":\"travel-portal\"},\"spec\":{\"ports\":[{\"name\":\"http\",\"port\":8000}],\"selector\":{\"app\":\"viaggi\"}}}\n" + }, + "healthAnnotations": {}, + "additionalDetails": [] + }, + "cluster": "Kubernetes", + "istioSidecar": true, + "endpoints": [ + { + "addresses": [ + { + "kind": "Pod", + "name": "viaggi-65ff94c868-jctnj", + "ip": "10.244.0.41", + "port": 0 + } + ], + "ports": [ + { + "name": "http", + "protocol": "TCP", + "port": 8000 + } + ] + } + ], + "virtualServices": [], + "destinationRules": [], + "k8sHTTPRoutes": [], + "serviceEntries": null, + "istioPermissions": { + "create": true, + "update": true, + "delete": true + }, + "workloads": [ + { + "name": "viaggi", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-02-16T11:27:39Z", + "resourceVersion": "56828", + "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": null, + "requests": { + "inbound": null, + "outbound": null, + "healthAnnotations": null + } + } + } + ], + "subServices": [ + { + "name": "viaggi", + "namespace": "", + "istioSidecar": false, + "cluster": "", + "istioAmbient": false, + "appLabel": false, + "additionalDetailSample": null, + "annotations": null, + "healthAnnotations": null, + "ports": { + "http": 8000 + }, + "labels": null, + "selector": null, + "istioReferences": null, + "kialiWizard": "", + "serviceRegistry": "", + "health": { + "requests": { + "inbound": null, + "outbound": null, + "healthAnnotations": null + } + } + } + ], + "health": { + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + }, + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": true, + "checks": [], + "references": null + } + }, + "service": { + "viaggi.travel-portal": { + "name": "viaggi", + "objectType": "service", + "valid": true, + "checks": [], + "references": null + } + }, + "virtualservice": { + "bookinfo.bookinfo": { + "name": "bookinfo", + "objectType": "virtualservice", + "valid": true, + "checks": [], + "references": null + } + }, + "workload": { + "cars-v1.travel-agency": { + "name": "cars-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "control.travel-control": { + "name": "control", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "details-v1.bookinfo": { + "name": "details-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 + }, + "grafana.istio-system": { + "name": "grafana", + "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 + }, + "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-traffic-generator.bookinfo": { + "name": "kiali-traffic-generator", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "kiali.istio-system": { + "name": "kiali", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "mysqldb-v1.travel-agency": { + "name": "mysqldb-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "productpage-v1.bookinfo": { + "name": "productpage-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "prometheus.istio-system": { + "name": "prometheus", + "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 + }, + "travels-v1.travel-agency": { + "name": "travels-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "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 + } + } + }, + "namespaceMTLS": { + "status": "MTLS_NOT_ENABLED", + "autoMTLSEnabled": true, + "minTLS": "" + } +} diff --git a/plugins/kiali/dev/__fixtures__/namespaces/travel-portal/workloads/voyages.json b/plugins/kiali/dev/__fixtures__/namespaces/travel-portal/workloads/voyages.json new file mode 100644 index 0000000000..79a25ecc4f --- /dev/null +++ b/plugins/kiali/dev/__fixtures__/namespaces/travel-portal/workloads/voyages.json @@ -0,0 +1,346 @@ +{ + "service": { + "name": "voyages", + "createdAt": "2024-02-16T11:27:39Z", + "resourceVersion": "56537", + "namespace": { + "name": "travel-portal", + "cluster": "", + "isAmbient": false, + "labels": null, + "annotations": null + }, + "labels": { + "app": "voyages" + }, + "selectors": { + "app": "voyages" + }, + "type": "ClusterIP", + "ip": "10.97.232.206", + "ports": [ + { + "name": "http", + "protocol": "TCP", + "port": 8000 + } + ], + "externalName": "", + "annotations": { + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"voyages\"},\"name\":\"voyages\",\"namespace\":\"travel-portal\"},\"spec\":{\"ports\":[{\"name\":\"http\",\"port\":8000}],\"selector\":{\"app\":\"voyages\"}}}\n" + }, + "healthAnnotations": {}, + "additionalDetails": [] + }, + "cluster": "Kubernetes", + "istioSidecar": true, + "endpoints": [ + { + "addresses": [ + { + "kind": "Pod", + "name": "voyages-59857896bf-6kgn8", + "ip": "10.244.0.40", + "port": 0 + } + ], + "ports": [ + { + "name": "http", + "protocol": "TCP", + "port": 8000 + } + ] + } + ], + "virtualServices": [], + "destinationRules": [], + "k8sHTTPRoutes": [], + "serviceEntries": null, + "istioPermissions": { + "create": true, + "update": true, + "delete": true + }, + "workloads": [ + { + "name": "voyages", + "cluster": "Kubernetes", + "type": "Deployment", + "createdAt": "2024-02-16T11:27:39Z", + "resourceVersion": "56850", + "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": null, + "requests": { + "inbound": null, + "outbound": null, + "healthAnnotations": null + } + } + } + ], + "subServices": [ + { + "name": "voyages", + "namespace": "", + "istioSidecar": false, + "cluster": "", + "istioAmbient": false, + "appLabel": false, + "additionalDetailSample": null, + "annotations": null, + "healthAnnotations": null, + "ports": { + "http": 8000 + }, + "labels": null, + "selector": null, + "istioReferences": null, + "kialiWizard": "", + "serviceRegistry": "", + "health": { + "requests": { + "inbound": null, + "outbound": null, + "healthAnnotations": null + } + } + } + ], + "health": { + "requests": { + "inbound": {}, + "outbound": {}, + "healthAnnotations": {} + } + }, + "validations": { + "gateway": { + "bookinfo-gateway.bookinfo": { + "name": "bookinfo-gateway", + "objectType": "gateway", + "valid": true, + "checks": [], + "references": null + } + }, + "service": { + "voyages.travel-portal": { + "name": "voyages", + "objectType": "service", + "valid": true, + "checks": [], + "references": null + } + }, + "virtualservice": { + "bookinfo.bookinfo": { + "name": "bookinfo", + "objectType": "virtualservice", + "valid": true, + "checks": [], + "references": null + } + }, + "workload": { + "cars-v1.travel-agency": { + "name": "cars-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "control.travel-control": { + "name": "control", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "details-v1.bookinfo": { + "name": "details-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 + }, + "grafana.istio-system": { + "name": "grafana", + "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 + }, + "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-traffic-generator.bookinfo": { + "name": "kiali-traffic-generator", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "kiali.istio-system": { + "name": "kiali", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "mysqldb-v1.travel-agency": { + "name": "mysqldb-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "productpage-v1.bookinfo": { + "name": "productpage-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "prometheus.istio-system": { + "name": "prometheus", + "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 + }, + "travels-v1.travel-agency": { + "name": "travels-v1", + "objectType": "workload", + "valid": true, + "checks": [], + "references": null + }, + "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 + } + } + }, + "namespaceMTLS": { + "status": "MTLS_NOT_ENABLED", + "autoMTLSEnabled": true, + "minTLS": "" + } +} diff --git a/plugins/kiali/dev/index.tsx b/plugins/kiali/dev/index.tsx index 1d6b94e09b..ce1d40d0ba 100644 --- a/plugins/kiali/dev/index.tsx +++ b/plugins/kiali/dev/index.tsx @@ -8,6 +8,7 @@ import { EntityProvider } from '@backstage/plugin-catalog-react'; import { TestApiProvider } from '@backstage/test-utils'; import { kialiPlugin } from '../src'; +import { pluginRoot } from '../src/components/BreadcrumbView/BreadcrumbView'; import { KialiNoPath } from '../src/pages/Kiali'; import { KialiHeader } from '../src/pages/Kiali/Header/KialiHeader'; import { KialiHeaderEntity } from '../src/pages/Kiali/Header/KialiHeaderEntity'; @@ -15,6 +16,7 @@ import { KialiEntity } from '../src/pages/Kiali/KialiEntity'; import { KialiNoAnnotation } from '../src/pages/Kiali/KialiNoAnnotation'; import { KialiNoResources } from '../src/pages/Kiali/KialiNoResources'; import { OverviewPage } from '../src/pages/Overview/OverviewPage'; +import { WorkloadDetailsPage } from '../src/pages/WorkloadDetails/WorkloadDetailsPage'; import { WorkloadListPage } from '../src/pages/WorkloadList/WorkloadListPage'; import { KialiApi, kialiApiRef } from '../src/services/Api'; import { KialiProvider } from '../src/store/KialiProvider'; @@ -29,7 +31,7 @@ import { ServiceHealth, WorkloadHealth, } from '../src/types/Health'; -import { IstioConfigsMap } from '../src/types/IstioConfigList'; +import { IstioConfigList, IstioConfigsMap } from '../src/types/IstioConfigList'; import { CanaryUpgradeStatus, OutboundTrafficPolicy, @@ -46,9 +48,11 @@ import { ServerConfig } from '../src/types/ServerConfig'; import { StatusState } from '../src/types/StatusState'; import { TLSStatus } from '../src/types/TLSStatus'; import { + Workload, WorkloadListItem, WorkloadNamespaceResponse, WorkloadOverview, + WorkloadQuery, } from '../src/types/Workload'; import { filterNsByAnnotation } from '../src/utils/entityFilter'; import { kialiData } from './__fixtures__'; @@ -114,6 +118,27 @@ class MockKialiClient implements KialiApi { ); } + async getWorkload( + namespace: string, + name: string, + _: WorkloadQuery, + __?: string, + ): Promise { + const parsedName = name.replace(/-/g, ''); + return kialiData.namespacesData[namespace].workloads[parsedName]; + } + + async getIstioConfig( + namespace: string, + _: string[], + __: boolean, + ___: string, + ____: string, + _____?: string, + ): Promise { + return kialiData.namespacesData[namespace].istioConfigList; + } + async getServerConfig(): Promise { return kialiData.config; } @@ -293,8 +318,8 @@ interface Props { export const TabsMock = () => { const [selectedTab, setSelectedTab] = React.useState(0); const tabs = [ - { label: 'Overview', route: 'overview' }, - { label: 'Workloads', route: 'workloads' }, + { label: 'Overview', route: `${pluginRoot}/overview` }, + { label: 'Workloads', route: `${pluginRoot}/workloads` }, ]; const navigate = useNavigate(); return ( @@ -314,10 +339,14 @@ export const TabsMock = () => { const RoutesList = () => ( - } /> - } /> - } /> - } /> + } /> + } /> + } /> + } + /> + } /> } /> ); @@ -361,12 +390,12 @@ createDevApp() .addPage({ element: , title: 'KialiPage', - path: '/overview', + path: `/${pluginRoot}/overview`, }) .addPage({ element: , title: 'Entity', - path: '/kiali', + path: `/${pluginRoot}/kiali/entity`, }) .addPage({ element: , diff --git a/plugins/kiali/src/Router.tsx b/plugins/kiali/src/Router.tsx index 968d761eb9..6351c9ece5 100644 --- a/plugins/kiali/src/Router.tsx +++ b/plugins/kiali/src/Router.tsx @@ -12,7 +12,13 @@ import { KialiTabs } from './pages/Kiali/Header/KialiTabs'; import { KialiEntity } from './pages/Kiali/KialiEntity'; import { KialiNoAnnotation } from './pages/Kiali/KialiNoAnnotation'; import { OverviewPage } from './pages/Overview/OverviewPage'; +import { WorkloadDetailsPage } from './pages/WorkloadDetails/WorkloadDetailsPage'; import { WorkloadListPage } from './pages/WorkloadList/WorkloadListPage'; +import { + overviewRouteRef, + workloadsDetailRouteRef, + workloadsRouteRef, +} from './routes'; import { KialiProvider } from './store/KialiProvider'; export const KUBERNETES_ANNOTATION = 'backstage.io/kubernetes-id'; @@ -61,8 +67,15 @@ export const Router = () => { } /> - } /> - } /> + } /> + } + /> + } + /> } /> diff --git a/plugins/kiali/src/assets/img/api/graphql.svg b/plugins/kiali/src/assets/img/api/graphql.svg new file mode 100644 index 0000000000..86bebc9dce --- /dev/null +++ b/plugins/kiali/src/assets/img/api/graphql.svg @@ -0,0 +1 @@ +GraphQL \ No newline at end of file diff --git a/plugins/kiali/src/assets/img/api/grpc.svg b/plugins/kiali/src/assets/img/api/grpc.svg new file mode 100644 index 0000000000..57241d7735 --- /dev/null +++ b/plugins/kiali/src/assets/img/api/grpc.svg @@ -0,0 +1 @@ +GRPC \ No newline at end of file diff --git a/plugins/kiali/src/assets/img/api/rest.svg b/plugins/kiali/src/assets/img/api/rest.svg new file mode 100644 index 0000000000..8c153a4ecd --- /dev/null +++ b/plugins/kiali/src/assets/img/api/rest.svg @@ -0,0 +1 @@ +Rest API \ No newline at end of file diff --git a/plugins/kiali/src/assets/img/runtime/go.svg b/plugins/kiali/src/assets/img/runtime/go.svg new file mode 100644 index 0000000000..253d146540 --- /dev/null +++ b/plugins/kiali/src/assets/img/runtime/go.svg @@ -0,0 +1 @@ +Go \ No newline at end of file diff --git a/plugins/kiali/src/assets/img/runtime/java.svg b/plugins/kiali/src/assets/img/runtime/java.svg new file mode 100644 index 0000000000..a7c5e629f7 --- /dev/null +++ b/plugins/kiali/src/assets/img/runtime/java.svg @@ -0,0 +1 @@ +Java \ No newline at end of file diff --git a/plugins/kiali/src/assets/img/runtime/microprofile.svg b/plugins/kiali/src/assets/img/runtime/microprofile.svg new file mode 100644 index 0000000000..ccdd9eed29 --- /dev/null +++ b/plugins/kiali/src/assets/img/runtime/microprofile.svg @@ -0,0 +1 @@ +MicroProfile \ No newline at end of file diff --git a/plugins/kiali/src/assets/img/runtime/nodejs.svg b/plugins/kiali/src/assets/img/runtime/nodejs.svg new file mode 100644 index 0000000000..ccc076ee43 --- /dev/null +++ b/plugins/kiali/src/assets/img/runtime/nodejs.svg @@ -0,0 +1 @@ +Node.js \ No newline at end of file diff --git a/plugins/kiali/src/assets/img/runtime/quarkus.svg b/plugins/kiali/src/assets/img/runtime/quarkus.svg new file mode 100644 index 0000000000..6e0290225c --- /dev/null +++ b/plugins/kiali/src/assets/img/runtime/quarkus.svg @@ -0,0 +1 @@ +Quarkus \ No newline at end of file diff --git a/plugins/kiali/src/assets/img/runtime/spring-boot.svg b/plugins/kiali/src/assets/img/runtime/spring-boot.svg new file mode 100644 index 0000000000..ba4a784bba --- /dev/null +++ b/plugins/kiali/src/assets/img/runtime/spring-boot.svg @@ -0,0 +1 @@ +Spring Boot \ No newline at end of file diff --git a/plugins/kiali/src/assets/img/runtime/thorntail.svg b/plugins/kiali/src/assets/img/runtime/thorntail.svg new file mode 100644 index 0000000000..3b8d0599f1 --- /dev/null +++ b/plugins/kiali/src/assets/img/runtime/thorntail.svg @@ -0,0 +1 @@ +Thorntail \ No newline at end of file diff --git a/plugins/kiali/src/assets/img/runtime/tomcat.svg b/plugins/kiali/src/assets/img/runtime/tomcat.svg new file mode 100644 index 0000000000..997f8187bd --- /dev/null +++ b/plugins/kiali/src/assets/img/runtime/tomcat.svg @@ -0,0 +1 @@ +Tomcat \ No newline at end of file diff --git a/plugins/kiali/src/assets/img/runtime/vertx.svg b/plugins/kiali/src/assets/img/runtime/vertx.svg new file mode 100644 index 0000000000..1349d875f9 --- /dev/null +++ b/plugins/kiali/src/assets/img/runtime/vertx.svg @@ -0,0 +1 @@ +Vert.x \ No newline at end of file diff --git a/plugins/kiali/src/components/BreadcrumbView/BreadcrumbView.tsx b/plugins/kiali/src/components/BreadcrumbView/BreadcrumbView.tsx new file mode 100644 index 0000000000..d62febe74b --- /dev/null +++ b/plugins/kiali/src/components/BreadcrumbView/BreadcrumbView.tsx @@ -0,0 +1,126 @@ +import * as React from 'react'; +import { Link, Location, useLocation } from 'react-router-dom'; + +import { Breadcrumbs } from '@material-ui/core'; + +import { HistoryManager } from '../../app/History'; +import { isMultiCluster, Paths } from '../../config'; +import { kialiStyle, linkStyle } from '../../styles/StyleUtils'; +import { dicIstioType } from '../../types/IstioConfigList'; +import { FilterSelected } from '../Filters/StatefulFilters'; + +const ItemNames = { + applications: 'App', + services: 'Service', + workloads: 'Workload', + istio: 'Istio Object', +}; + +export const pluginRoot = 'kiali'; +const IstioName = 'Istio Config'; +const namespaceRegex = + /kiali\/([a-z0-9-]+)\/([\w-.]+)\/([\w-.*]+)(\/([\w-.]+))?(\/([\w-.]+))?/; + +export const getPath = (props: Location) => { + const match = namespaceRegex.exec(props.pathname) || []; + const ns = match[2]; + // @ts-ignore + const page = Paths[match[1]?.toUpperCase()]; + const istioType = match[3]; + const urlParams = new URLSearchParams(props.search); + const itemName = page !== 'istio' ? match[3] : match[5]; + return { + cluster: HistoryManager.getClusterName(urlParams), + istioType: istioType, + item: itemName, + // @ts-ignore + itemName: ItemNames[page], + namespace: ns, + pathItem: page, + }; +}; + +const breadcrumStyle = kialiStyle({ + marginBottom: '20px', + marginTop: '-20px', +}); + +export const BreadcrumbView = () => { + const capitalize = (str: string) => { + return str?.charAt(0)?.toUpperCase() + str?.slice(1); + }; + + const path = getPath(useLocation()); + + const istioTypeF = (rawType: string) => { + const istioType = Object.keys(dicIstioType).find( + // @ts-ignore + key => dicIstioType[key] === rawType, + ); + return istioType || capitalize(rawType); + }; + + const cleanFilters = () => { + FilterSelected.resetFilters(); + }; + + const isIstioF = () => { + return path?.pathItem === 'istio'; + }; + + const getItemPage = () => { + if (path) { + let pathT = `/${pluginRoot}/${path?.pathItem}/${path?.namespace}/${path?.item}`; + if (path?.cluster && isMultiCluster) { + pathT += `?clusterName=${path.cluster}`; + } + return pathT; + } + return ''; + }; + + const namespace = path ? path.namespace : ''; + const item = path ? path.item : ''; + const istioType = path ? path.istioType : ''; + const pathItem = path ? path.pathItem : ''; + + const isIstio = isIstioF(); + const linkItem = isIstio ? ( + { item } + ) : ( + + {item} + + ); + + const linkTo = `/${pluginRoot}/${pathItem}?namespaces=${namespace}&type=${ + // @ts-ignore + dicIstioType[path?.istioType || ''] + }`; + return ( +
+ + + {isIstio ? IstioName : capitalize(pathItem)} + + + Namespace: {namespace} + + {isIstio && ( + + {istioType ? istioTypeF(istioType) : istioType} + + )} + {linkItem} + +
+ ); +}; diff --git a/plugins/kiali/src/components/DetailsDescription/DetailDescription.tsx b/plugins/kiali/src/components/DetailsDescription/DetailDescription.tsx new file mode 100644 index 0000000000..6d48820606 --- /dev/null +++ b/plugins/kiali/src/components/DetailsDescription/DetailDescription.tsx @@ -0,0 +1,350 @@ +import * as React from 'react'; +import { Link } from 'react-router-dom'; + +import { Tooltip } from '@material-ui/core'; + +import { isMultiCluster, serverConfig } from '../../config'; +import { createIcon, KialiIcon } from '../../config/KialiIcon'; +import { isGateway, isWaypoint } from '../../helpers/LabelFilterHelper'; +import { healthIndicatorStyle } from '../../styles/HealthStyle'; +import { kialiStyle } from '../../styles/StyleUtils'; +import { AppWorkload } from '../../types/App'; +import * as H from '../../types/Health'; +import { HealthSubItem } from '../../types/Health'; +import { Workload } from '../../types/Workload'; +import { renderTrafficStatus } from '../Health/HealthDetails'; +import { MissingSidecar } from '../MissingSidecar/MissingSidecar'; +import { PFBadge, PFBadges } from '../Pf/PfBadges'; + +type Props = { + apps?: string[]; + cluster?: string; + health?: H.Health; + namespace: string; + services?: string[]; + waypointWorkloads?: Workload[]; + workloads?: AppWorkload[]; +}; + +const iconStyle = kialiStyle({ + margin: 0, + padding: 0, + display: 'inline-block', +}); + +const resourceListStyle = kialiStyle({ + margin: '0 0 0.5rem 0', + $nest: { + '& > span': { + float: 'left', + width: '125px', + fontWeight: 700, + }, + }, +}); + +const containerStyle = kialiStyle({ + margin: '1rem 0 0.5rem 0', +}); + +const itemStyle = kialiStyle({ + paddingBottom: '0.25rem', +}); + +const infoStyle = kialiStyle({ + marginLeft: '0.5rem', +}); + +export const renderWaypoint = (bgsize?: string): React.ReactNode => { + const badgeSize = bgsize === 'global' || bgsize === 'sm' ? bgsize : 'global'; + return [ +
+ + Waypoint proxy + + + +
, + ]; +}; + +export const DetailDescription: React.FC = (props: Props) => { + const renderAppItem = ( + namespace: string, + appName: string, + ): React.ReactNode => { + let href = `/namespaces/${namespace}/applications/${appName}`; + + if (props.cluster && isMultiCluster) { + href = `${href}?clusterName=${props.cluster}`; + } + + const link = {appName}; + + return ( +
  • +
    + +
    + + {link} +
  • + ); + }; + + const renderServiceItem = ( + namespace: string, + serviceName: string, + ): React.ReactNode => { + let href = `/namespaces/${namespace}/services/${serviceName}`; + + if (props.cluster && isMultiCluster) { + href = `${href}?clusterName=${props.cluster}`; + } + + const link = {serviceName}; + + return ( +
  • +
    + +
    + + {link} +
  • + ); + }; + + const renderEmptyItem = (type: string): React.ReactNode => { + const message = `No ${type} found`; + + return
    {message}
    ; + }; + + const appList = (): React.ReactNode => { + const applicationList = + props.apps && props.apps.length > 0 + ? props.apps + .sort((a1: string, a2: string) => (a1 < a2 ? -1 : 1)) + .filter(name => { + if (name === undefined) { + return null; + } + + return name; + }) + .map(name => renderAppItem(props.namespace, name)) + : renderEmptyItem('applications'); + + return [ +
    +
      + {applicationList} +
    +
    , + ]; + }; + + const renderServiceAccounts = ( + workload: AppWorkload, + ): NonNullable => { + return ( +
    + {workload.serviceAccountNames && + workload.serviceAccountNames.length > 0 ? ( +
    + Service accounts + +
      + {workload.serviceAccountNames.map((serviceAccount, _) => ( +
    • + {serviceAccount} +
    • + ))} +
    +
    + ) : ( + 'Not found' + )} +
    + ); + }; + + const renderWorkloadItem = (workload: AppWorkload): React.ReactNode => { + let href = `/namespaces/${props.namespace}/workloads/${workload.workloadName}`; + + if (props.cluster && isMultiCluster) { + href = `${href}?clusterName=${props.cluster}`; + } + + const link = {workload.workloadName}; + + return ( + +
    + +
    + {link} + + + + {((!workload.istioSidecar && + !workload.istioAmbient && + !isWaypoint(workload.labels) && + serverConfig.ambientEnabled) || + (!workload.istioSidecar && !serverConfig.ambientEnabled)) && ( + + )} +
    + ); + }; + + const renderWorkloadHealthItem = (sub: HealthSubItem): React.ReactNode => { + let workload: AppWorkload | undefined = undefined; + + if (props.workloads && props.workloads.length > 0) { + for (const wk of props.workloads) { + const hWorkload = sub.text.substring(0, sub.text.indexOf(':')); + + if (hWorkload === wk.workloadName) { + workload = wk; + break; + } + } + } + + if (workload) { + let href = `/namespaces/${props.namespace}/workloads/${workload.workloadName}`; + + if (props.cluster && isMultiCluster) { + href = `${href}?clusterName=${props.cluster}`; + } + + const link = {workload.workloadName}; + + return ( + +
    + +
    + + {link} + + + + + + {sub.text}} + className={healthIndicatorStyle} + > + + {createIcon(sub.status)} + + + + {((!workload.istioSidecar && + !workload.istioAmbient && + serverConfig.ambientEnabled) || + (!workload.istioSidecar && !serverConfig.ambientEnabled)) && ( + + )} +
    + ); + } + return ( + + {createIcon(sub.status)} + {sub.text} + + ); + }; + + const renderWorkloadStatus = (): React.ReactNode => { + if (props.health) { + const item = props.health.getWorkloadStatus(); + + if (item) { + return ( +
    + {item.text} + + {item.children && ( +
      + {item.children.map((sub, _) => { + return ( +
    • + {renderWorkloadHealthItem(sub)} +
    • + ); + })} +
    + )} +
    + ); + } + return ( +
    +
      + {props.workloads + ? props.workloads + .sort((w1: AppWorkload, w2: AppWorkload) => + w1.workloadName < w2.workloadName ? -1 : 1, + ) + .map((wkd, _) => { + return ( +
    • + {renderWorkloadItem(wkd)} +
    • + ); + }) + : undefined} +
    +
    + ); + } + return undefined; + }; + + const workloadSummary = (): React.ReactNode => { + return
    {renderWorkloadStatus()}
    ; + }; + + const serviceList = (): React.ReactNode => { + const serviceListT = + props.services && props.services.length > 0 + ? props.services + .sort((s1: string, s2: string) => (s1 < s2 ? -1 : 1)) + .map(name => renderServiceItem(props.namespace, name)) + : renderEmptyItem('services'); + + return [ +
    +
      + {serviceListT} +
    +
    , + ]; + }; + + return ( +
    + {props.apps !== undefined && appList()} + {props.workloads !== undefined && workloadSummary()} + {props.services !== undefined && serviceList()} + {props.health && renderTrafficStatus(props.health)} + {props.waypointWorkloads && renderWaypoint()} +
    + ); +}; diff --git a/plugins/kiali/src/components/Health/HealthDetails.tsx b/plugins/kiali/src/components/Health/HealthDetails.tsx index a821bf5b3a..31eedef7b1 100644 --- a/plugins/kiali/src/components/Health/HealthDetails.tsx +++ b/plugins/kiali/src/components/Health/HealthDetails.tsx @@ -1,5 +1,7 @@ import * as React from 'react'; +import { Typography } from '@material-ui/core'; + import { KialiIcon } from '../../config'; import * as H from '../../types/Health'; import { PFColors } from '../Pf/PfColors'; @@ -117,3 +119,65 @@ export const HealthDetails: React.FC = ( ); // @ts-ignore }; + +export const renderTrafficStatus = (health: H.Health): React.ReactNode => { + const config = health.getStatusConfig(); + const isValueInConfig = + config && health.health.statusConfig + ? health.health.statusConfig.value > 0 + : false; + const item = health.getTrafficStatus(); + + if (item) { + const showTraffic = item.children + ? item.children.filter(sub => { + const showItem = sub.value && sub.value > 0; + + return sub.status !== H.HEALTHY && showItem; + }).length > 0 + : false; + + if (showTraffic) { + return ( +
    + Traffic + + {item.text} + + {item.children && ( +
      + {item.children.map((sub, _) => { + const showItem = sub.value && sub.value > 0; + + return sub.status !== H.HEALTHY && showItem ? ( +
    • + + {createIcon(sub.status)} + + {sub.text} +
    • + ) : ( + + ); + })} + + {config && isValueInConfig && ( +
    • + + {createIcon(H.DEGRADED)} + + : {config.degraded === 0 ? '>' : '>='} + {config.degraded}% {createIcon(H.FAILURE)}:{' '} + {config.degraded === 0 ? '>' : '>='} + {config.failure}% +
    • + )} +
    + )} +
    + ); + } + } + + return undefined; +}; diff --git a/plugins/kiali/src/components/IstioConfigCard/IstioConfigCard.tsx b/plugins/kiali/src/components/IstioConfigCard/IstioConfigCard.tsx new file mode 100644 index 0000000000..9df886ed79 --- /dev/null +++ b/plugins/kiali/src/components/IstioConfigCard/IstioConfigCard.tsx @@ -0,0 +1,103 @@ +import * as React from 'react'; + +import { EmptyState } from '@backstage/core-components'; + +import { + Card, + CardContent, + CardHeader, + TableCellProps, + Typography, +} from '@material-ui/core'; + +import { IstioConfigItem } from '../../types/IstioConfigList'; +import { PFBadge } from '../Pf/PfBadges'; +import { SimpleTable, tRow } from '../SimpleTable'; +import { ValidationObjectSummary } from '../Validations/ValidationObjectSummary'; +import { IstioTypes } from '../VirtualList/Config'; + +type IstioConfigCardProps = { + items: IstioConfigItem[]; + name: string; +}; + +export const IstioConfigCard: React.FC = ( + props: IstioConfigCardProps, +) => { + const columns: TableCellProps[] = [ + { title: 'Name' }, + { title: 'Status', width: 10 }, + ]; + + const noIstioConfig: React.ReactNode = ( + No Istio Config found for {props.name}} + /> + ); + + const overviewLink = (item: IstioConfigItem): React.ReactNode => { + return <>{item.name}; + }; + + const rows: tRow = props.items + .sort((a: IstioConfigItem, b: IstioConfigItem) => { + if (a.type < b.type) { + return -1; + } else if (a.type > b.type) { + return 1; + } + return a.name < b.name ? -1 : 1; + }) + .map((item, itemIdx) => { + return { + cells: [ + + + {overviewLink(item)} + , + , + ], + }; + }); + + return ( + + {props.items.length > 0 && ( + <> + + Istio Config + + } + /> + + + + + + )} + {props.items.length === 0 && ( + + + + )} + + ); +}; diff --git a/plugins/kiali/src/components/Label/Labels.tsx b/plugins/kiali/src/components/Label/Labels.tsx new file mode 100644 index 0000000000..a929e87931 --- /dev/null +++ b/plugins/kiali/src/components/Label/Labels.tsx @@ -0,0 +1,95 @@ +import * as React from 'react'; + +import { Tooltip } from '@material-ui/core'; +import Button from '@material-ui/core/Button'; + +import { KialiIcon } from '../../config/KialiIcon'; +import { kialiStyle } from '../../styles/StyleUtils'; +import { Label } from './Label'; + +const SHOW_MORE_TRESHOLD = 2; + +interface LabelsProps { + expanded?: boolean; + type?: string; + labels?: { [key: string]: string }; + tooltipMessage?: string; +} + +const linkStyle = kialiStyle({ + padding: '0 0.25rem', + fontSize: '0.8rem', +}); + +const infoStyle = kialiStyle({ + marginLeft: '0.25rem', + marginBottom: '0.125rem', +}); + +const labelsContainerStyle = kialiStyle({ + display: 'flex', + alignItems: 'center', + flexWrap: 'wrap', + overflow: 'hidden', +}); + +export const Labels: React.FC = (props: LabelsProps) => { + const [expanded, setExpanded] = React.useState( + props.expanded ?? false, + ); + + const labelKeys = Object.keys(props.labels ?? {}); + + const hasLabels = labelKeys.length > 0; + + const hasManyLabels = labelKeys.length > SHOW_MORE_TRESHOLD; + + const showItem = (i: number): boolean => { + return expanded || !hasManyLabels || i < SHOW_MORE_TRESHOLD; + }; + + const renderMoreLabelsLink: React.ReactNode | null = + hasManyLabels && !expanded ? ( + + ) : null; + + const renderLabels = labelKeys.map((key, i) => { + return showItem(i) ? ( +
    +
    + ) : undefined; + }); + + const renderEmptyLabels = ( + No {props.type ? props.type : 'labels'} + ); + + const tooltip = props.tooltipMessage ? ( + {props.tooltipMessage}} + > +
    + +
    +
    + ) : undefined; + + return ( +
    + {hasLabels ? [renderLabels, renderMoreLabelsLink] : renderEmptyLabels} + {tooltip} +
    + ); +}; diff --git a/plugins/kiali/src/components/Link/IstioObjectLink.ts b/plugins/kiali/src/components/Link/IstioObjectLink.ts deleted file mode 100644 index 078b356de1..0000000000 --- a/plugins/kiali/src/components/Link/IstioObjectLink.ts +++ /dev/null @@ -1,29 +0,0 @@ -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/Link/IstioObjectLink.tsx b/plugins/kiali/src/components/Link/IstioObjectLink.tsx new file mode 100644 index 0000000000..1d29ca2583 --- /dev/null +++ b/plugins/kiali/src/components/Link/IstioObjectLink.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; + +import { isMultiCluster, Paths } from '../../config'; +import { IstioTypes } from '../VirtualList/Config'; + +type ReferenceIstioObjectProps = { + cluster?: string; + name: string; + namespace: string; + query?: string; + type: string; +}; + +type IstioObjectProps = ReferenceIstioObjectProps & { + children: React.ReactNode; +}; + +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; +}; + +export const IstioObjectLink: React.FC = ( + props: IstioObjectProps, +) => { + const { name, namespace, type, cluster, query } = props; + const href = getIstioObjectUrl(name, namespace, type, cluster, query); + + return ( + + {props.children} + + ); +}; diff --git a/plugins/kiali/src/components/Logos/Logos.tsx b/plugins/kiali/src/components/Logos/Logos.tsx new file mode 100644 index 0000000000..1f2c82854f --- /dev/null +++ b/plugins/kiali/src/components/Logos/Logos.tsx @@ -0,0 +1,75 @@ +import React from 'react'; + +import { kialiStyle } from '../../styles/StyleUtils'; + +const iconStyle = kialiStyle({ + height: '1.5rem', +}); + +const GraphqlIcon = require('../../assets/img/api/graphql.svg') as string; +const GrpcIcon = require('../../assets/img/api/grpc.svg') as string; +const RestIcon = require('../../assets/img/api/rest.svg') as string; +const GoLogo = require('../../assets/img/runtime/go.svg') as string; +const JVMLogo = require('../../assets/img/runtime/java.svg') as string; +const MicroProfileLogo = + require('../../assets/img/runtime/microprofile.svg') as string; +const NodejsLogo = require('../../assets/img/runtime/nodejs.svg') as string; +const QuarkusLogo = require('../../assets/img/runtime/quarkus.svg') as string; +const SpringBootLogo = + require('../../assets/img/runtime/spring-boot.svg') as string; +const ThorntailLogo = + require('../../assets/img/runtime/thorntail.svg') as string; +const TomcatLogo = require('../../assets/img/runtime/tomcat.svg') as string; +const VertxLogo = require('../../assets/img/runtime/vertx.svg') as string; + +const renderLogo = ( + name: string, + title: string | undefined, + idx: number, + logoSet: { [key: string]: any }, + className?: string, +): React.ReactElement => { + const logo = logoSet[name]; + + if (logo) { + return ( + {name} + ); + } + + return {name}; +}; + +// API types +const apiLogos = { + grpc: GrpcIcon, + rest: RestIcon, + graphql: GraphqlIcon, +}; + +const runtimesLogos = { + Go: GoLogo, + JVM: JVMLogo, + MicroProfile: MicroProfileLogo, + 'Node.js': NodejsLogo, + Quarkus: QuarkusLogo, + 'Spring Boot': SpringBootLogo, + Thorntail: ThorntailLogo, + Tomcat: TomcatLogo, + 'Vert.x': VertxLogo, +}; + +export const renderRuntimeLogo = (name: string, idx: number): React.ReactNode => + renderLogo(name, name, idx, runtimesLogos, iconStyle); + +export const renderAPILogo = ( + name: string, + title: string | undefined, + idx: number, +): React.ReactNode => renderLogo(name, title, idx, apiLogos, iconStyle); diff --git a/plugins/kiali/src/components/MissingAuthPolicy/MissingAuthPolicy.tsx b/plugins/kiali/src/components/MissingAuthPolicy/MissingAuthPolicy.tsx new file mode 100644 index 0000000000..f8f9250223 --- /dev/null +++ b/plugins/kiali/src/components/MissingAuthPolicy/MissingAuthPolicy.tsx @@ -0,0 +1,68 @@ +import * as React from 'react'; + +import { Tooltip, TooltipPosition } from '@patternfly/react-core'; +import { SVGIconProps } from '@patternfly/react-icons/dist/js/createIcon'; + +import { icons } from '../../config/Icons'; +import { KialiIcon } from '../../config/KialiIcon'; +import { isIstioNamespace } from '../../config/ServerConfig'; +import { kialiStyle } from '../../styles/StyleUtils'; + +type MissingAuthPolicyProps = { + text?: string; + textTooltip?: string; + tooltip?: boolean; + icon?: React.ComponentClass; + color?: string; + namespace: string; + className?: string; +}; + +const infoStyle = kialiStyle({ + marginLeft: '0.5rem', +}); + +export const MissingAuthPolicy: React.FC = ({ + text = 'Missing Authorization Policy', + textTooltip = 'This workload is not covered by any authorization policy.', + tooltip = false, + icon = icons.istio.missingAuthPolicy.icon, + color = icons.istio.missingAuthPolicy.color, + namespace, + className, +}) => { + const iconComponent = ( + + {React.createElement(icon, { style: { color: color } })} + + {!tooltip && ( + + {text} + + {textTooltip}} + > + + + + )} + + ); + + if (isIstioNamespace(namespace)) { + return <>; + } + + return tooltip ? ( + {textTooltip}} + position={TooltipPosition.right} + > + {iconComponent} + + ) : ( + iconComponent + ); +}; diff --git a/plugins/kiali/src/components/MissingLabel/MissingLabel.tsx b/plugins/kiali/src/components/MissingLabel/MissingLabel.tsx new file mode 100644 index 0000000000..e6a54d8240 --- /dev/null +++ b/plugins/kiali/src/components/MissingLabel/MissingLabel.tsx @@ -0,0 +1,94 @@ +import * as React from 'react'; + +import { Tooltip } from '@material-ui/core'; + +import { icons } from '../../config/Icons'; +import { KialiIcon } from '../../config/KialiIcon'; +import { serverConfig } from '../../config/ServerConfig'; +import { kialiStyle } from '../../styles/StyleUtils'; +import { PFBadge } from '../Pf/PfBadges'; + +type MissingLabelProps = { + className?: string; + missingApp: boolean; + missingVersion: boolean; + tooltip: boolean; +}; + +const infoStyle = kialiStyle({ + marginLeft: '0.5rem', +}); + +export const MissingLabel: React.FC = ( + props: MissingLabelProps, +) => { + const appLabel = serverConfig.istioLabels.appLabelName; + const versionLabel = serverConfig.istioLabels.versionLabelName; + const icon = icons.istio.missingLabel.icon; + const color = icons.istio.missingLabel.color; + + const tooltipContent = ( +
    + {props.missingApp && ( + <> +
    + {' '} + label is missing.
    +
    +
    This workload won't be linked with an application.
    + + )} + + {props.missingVersion && ( + <> +
    + {' '} + label is missing.
    +
    +
    The label is recommended as it affects telemetry.
    + + )} + +
    + Missing labels may impact telemetry reported by the Istio proxy. +
    +
    + ); + + const missingLabel = + // eslint-disable-next-line no-nested-ternary + props.missingApp ? 'App' : props.missingVersion ? 'Version' : 'Label'; + + const iconComponent = ( + + {React.createElement(icon, { style: { color: color } })} + + {!props.tooltip && ( + + Missing {missingLabel} + +
    + +
    +
    +
    + )} +
    + ); + + return props.tooltip ? ( + +
    {iconComponent}
    +
    + ) : ( + iconComponent + ); +}; diff --git a/plugins/kiali/src/components/SimpleTable.tsx b/plugins/kiali/src/components/SimpleTable.tsx new file mode 100644 index 0000000000..fa6ec226f4 --- /dev/null +++ b/plugins/kiali/src/components/SimpleTable.tsx @@ -0,0 +1,94 @@ +import * as React from 'react'; + +import { + SortDirection, + Table, + TableBody, + TableCell, + TableCellProps, + TableHead, + TableRow, +} from '@material-ui/core'; + +import { kialiStyle } from '../styles/StyleUtils'; + +export interface SortableTh extends TableCellProps { + sortable: boolean; +} + +export type tRow = { + cells: React.JSX.Element[]; + key?: string; + className?: string; +}[]; + +interface SimpleTableProps { + className?: string; + columns: SortableTh[] | TableCellProps[]; + emptyState?: React.ReactNode; + label: string; + rows: tRow; + sort?: (columnIndex: number) => TableCellProps['sortDirection']; + sortBy?: SortDirection; + variant?: string; + verticalAlign?: string; +} + +export const SimpleTable: React.FC = ( + props: SimpleTableProps, +) => { + const tdStyle = kialiStyle({ + verticalAlign: props.verticalAlign ?? 'baseline', + }); + + return ( + + {!props.emptyState && ( + + + {props.columns.map( + (column: SortableTh | TableCellProps, index: number) => ( + + {column.title} + + ), + )} + + + )} + + {props.rows.length > 0 ? ( + props.rows.map((row, rowIndex) => ( + + {row.cells?.map((cell: React.ReactNode, colIndex: number) => ( + + {cell} + + ))} + + )) + ) : ( + <> + {props.emptyState && ( + + + {props.emptyState} + + + )} + + )} + +
    + ); +}; diff --git a/plugins/kiali/src/components/TextOrLink.tsx b/plugins/kiali/src/components/TextOrLink.tsx new file mode 100644 index 0000000000..13f99e6cf8 --- /dev/null +++ b/plugins/kiali/src/components/TextOrLink.tsx @@ -0,0 +1,29 @@ +import * as React from 'react'; + +type TextOrLinkProps = { + text: string; + urlTruncate?: number; +}; + +export const TextOrLink: React.FC = ( + props: TextOrLinkProps, +) => { + if (props.text.startsWith('http://') || props.text.startsWith('https://')) { + let truncated = props.text; + + if (props.urlTruncate && props.text.length > props.urlTruncate) { + truncated = `${props.text.substring( + 0, + props.urlTruncate / 2, + )}...${props.text.substring(props.text.length - props.urlTruncate / 2)}`; + } + + return ( + + {truncated} + + ); + } + + return <>{props.text}; +}; diff --git a/plugins/kiali/src/components/Time/LocalTime.tsx b/plugins/kiali/src/components/Time/LocalTime.tsx new file mode 100644 index 0000000000..4e4d860c3c --- /dev/null +++ b/plugins/kiali/src/components/Time/LocalTime.tsx @@ -0,0 +1,21 @@ +import * as React from 'react'; + +import { toString } from './Utils'; + +interface TimeProps { + time: string; +} + +export class LocalTime extends React.Component { + render() { + let renderedTime: string; + + if (this.props.time) { + renderedTime = toString(new Date(this.props.time).valueOf()); + } else { + renderedTime = '-'; + } + + return <>{renderedTime}; + } +} diff --git a/plugins/kiali/src/components/Time/Utils.tsx b/plugins/kiali/src/components/Time/Utils.tsx new file mode 100644 index 0000000000..062796c695 --- /dev/null +++ b/plugins/kiali/src/components/Time/Utils.tsx @@ -0,0 +1,17 @@ +import { TimeInMilliseconds } from '../../types/Common'; + +const defaultOptions = { + month: 'short', + day: 'numeric', + hour: '2-digit', + minute: '2-digit', +} as any; + +export const toString = (time: TimeInMilliseconds, options?: any): string => { + const formatOptions = { ...defaultOptions }; + const date = new Date(time); + if (date.getFullYear() !== new Date().getFullYear()) { + formatOptions.year = 'numeric'; + } + return date.toLocaleString('en-US', { ...formatOptions, ...options }); +}; diff --git a/plugins/kiali/src/components/Validations/ValidationObjectSummary.tsx b/plugins/kiali/src/components/Validations/ValidationObjectSummary.tsx new file mode 100644 index 0000000000..264f141814 --- /dev/null +++ b/plugins/kiali/src/components/Validations/ValidationObjectSummary.tsx @@ -0,0 +1,39 @@ +import * as React from 'react'; + +import { + ObjectValidation, + StatusCondition, + ValidationTypes, +} from '../../types/IstioObjects'; +import { ValidationSummary } from './ValidationSummary'; + +interface Props { + id: string; + reconciledCondition?: StatusCondition; + validations: ObjectValidation[]; +} + +export const ValidationObjectSummary: React.FC = (props: Props) => { + const numberOfChecks = (type: ValidationTypes): number => { + let numCheck = 0; + + props.validations.forEach(validation => { + if (validation.checks) { + numCheck += validation.checks.filter(i => i.severity === type).length; + } + }); + + return numCheck; + }; + + return ( + + ); +}; diff --git a/plugins/kiali/src/components/Validations/ValidationStack.tsx b/plugins/kiali/src/components/Validations/ValidationStack.tsx new file mode 100644 index 0000000000..935011c526 --- /dev/null +++ b/plugins/kiali/src/components/Validations/ValidationStack.tsx @@ -0,0 +1,45 @@ +import React from 'react'; + +import { kialiStyle } from '../../styles/StyleUtils'; +import { ObjectCheck, ValidationTypes } from '../../types/IstioObjects'; +import { highestSeverity } from '../../types/ServiceInfo'; +import { PFColors } from '../Pf/PfColors'; +import { Validation } from './Validation'; + +type ValidationStackProps = { + checks?: ObjectCheck[]; +}; + +const colorStyle = kialiStyle({ color: PFColors.White }); +const titleStyle = kialiStyle({ color: PFColors.White, fontWeight: 'bold' }); + +export const ValidationStack: React.FC = ( + props: ValidationStackProps, +) => { + const validationList = (): React.ReactNode[] => { + return (props.checks ?? []).map((check, index) => { + return ( +
    + +
    + ); + }); + }; + + const severity = highestSeverity(props.checks ?? []); + const isValid = severity === ValidationTypes.Correct; + + if (!isValid) { + return ( +
    + Istio validations + {validationList()} +
    + ); + } + return null; +}; diff --git a/plugins/kiali/src/components/Validations/ValidationSummary.tsx b/plugins/kiali/src/components/Validations/ValidationSummary.tsx index a3d0ed2729..573d43988c 100644 --- a/plugins/kiali/src/components/Validations/ValidationSummary.tsx +++ b/plugins/kiali/src/components/Validations/ValidationSummary.tsx @@ -14,6 +14,7 @@ interface Props { warnings: number; objectCount?: number; style?: CSSProperties; + type?: string; } const tooltipListStyle = kialiStyle({ diff --git a/plugins/kiali/src/components/VirtualList/Renderers.tsx b/plugins/kiali/src/components/VirtualList/Renderers.tsx index 2f7f06bda0..1396e484f6 100644 --- a/plugins/kiali/src/components/VirtualList/Renderers.tsx +++ b/plugins/kiali/src/components/VirtualList/Renderers.tsx @@ -8,6 +8,7 @@ import { isWaypoint } from '../../helpers/LabelFilterHelper'; import { infoStyle } from '../../pages/Overview/OverviewCard/CanaryUpgradeProgress'; import { ControlPlaneBadge } from '../../pages/Overview/OverviewCard/ControlPlaneBadge'; import { OverviewCardSparklineCharts } from '../../pages/Overview/OverviewCard/OverviewCardSparklineCharts'; +import { linkStyle } from '../../styles/StyleUtils'; import { Health } from '../../types/Health'; import { IstioConfigItem } from '../../types/IstioConfigList'; import { ValidationStatus } from '../../types/IstioObjects'; @@ -32,7 +33,7 @@ import { } from './Config'; const topPosition = 'top'; - +const rootPath = 'kiali'; // Istio Links const getIstioLink = (item: TResource): string => { let type = ''; @@ -50,7 +51,7 @@ const getLink = (item: TResource, config: Resource, query?: string): string => { let url = config.name === 'istio' ? getIstioLink(item) - : `/namespaces/${item.namespace}/${config.name}/${item.name}`; + : `/${rootPath}/${config.name}/${item.namespace}/${item.name}`; if (item.cluster && isMultiCluster && !url.includes('cluster')) { if (url.includes('?')) { @@ -89,9 +90,10 @@ export const actionRenderer = ( export const item: Renderer = ( resource: TResource, - _: Resource, + config: Resource, badge: PFBadgeType, ) => { + const key = `link_definition_${config.name}_${resource.namespace}_${resource.name}`; let serviceBadge = badge; if ('serviceRegistry' in resource && resource.serviceRegistry) { @@ -115,7 +117,9 @@ export const item: Renderer = ( style={{ verticalAlign: 'middle', whiteSpace: 'nowrap' }} > - {resource.name} + + {resource.name} + ); }; diff --git a/plugins/kiali/src/config/KialiIcon.tsx b/plugins/kiali/src/config/KialiIcon.tsx index 7e0aade976..845f6dad51 100644 --- a/plugins/kiali/src/config/KialiIcon.tsx +++ b/plugins/kiali/src/config/KialiIcon.tsx @@ -58,6 +58,8 @@ import { UserClockIcon, WarningTriangleIcon, } from '@patternfly/react-icons'; +import { SVGIconProps } from '@patternfly/react-icons/dist/js/createIcon'; +import { classes } from 'typestyle'; import { PFColors } from '../components/Pf/PfColors'; import { kialiStyle } from '../styles/StyleUtils'; @@ -70,9 +72,12 @@ const iconStyle = kialiStyle({ width: '10px', }); -interface IconProps { +export interface IconProps { className?: string; color?: string; + dataTest?: string; + icon?: React.ComponentClass; + size?: 'sm' | 'md' | 'lg' | 'xl'; } const conversorIconProps = ( @@ -200,3 +205,25 @@ Object.keys(KialiIcon).forEach(key => { export function createTooltipIcon(icon: any) { return {icon}; } + +export const createIcon = ( + props: IconProps, + icon?: React.ComponentClass, + colorIcon?: string, +): React.ReactElement => { + const iconComponent = props.icon ?? icon ?? React.Fragment; + + const iconColor = props.color ?? colorIcon; + + const iconStyles = iconColor ? kialiStyle({ color: iconColor }) : undefined; + + return ( + + {React.createElement(iconComponent)} + + ); +}; diff --git a/plugins/kiali/src/config/ServerConfig.ts b/plugins/kiali/src/config/ServerConfig.ts index d8951868f7..1fb600d944 100644 --- a/plugins/kiali/src/config/ServerConfig.ts +++ b/plugins/kiali/src/config/ServerConfig.ts @@ -80,6 +80,8 @@ export const defaultServerConfig: ComputedServerConfig = { }, installationTag: 'Kiali Console', istioAnnotations: { + ambientAnnotation: 'ambient.istio.io/redirection', + ambientAnnotationEnabled: 'enabled', istioInjectionAnnotation: 'sidecar.istio.io/inject', }, istioCanaryRevision: { @@ -89,6 +91,8 @@ export const defaultServerConfig: ComputedServerConfig = { istioIdentityDomain: 'svc.cluster.local', istioNamespace: 'istio-system', istioLabels: { + ambientWaypointLabel: 'gateway.istio.io/managed', + ambientWaypointLabelValue: 'istio.io-mesh-controller', appLabelName: 'app', injectionLabelName: 'istio-injection', injectionLabelRev: 'istio.io/rev', diff --git a/plugins/kiali/src/helpers/LabelFilterHelper.ts b/plugins/kiali/src/helpers/LabelFilterHelper.ts index 7e5ea1edd3..eabb8d84e0 100644 --- a/plugins/kiali/src/helpers/LabelFilterHelper.ts +++ b/plugins/kiali/src/helpers/LabelFilterHelper.ts @@ -1,3 +1,11 @@ +export const isGateway = (labels: { [key: string]: string }): boolean => { + return ( + labels && + 'istio' in labels && + (labels.istio === 'ingressgateway' || labels.istio === 'egressgateway') + ); +}; + export const isWaypoint = (labels: { [key: string]: string }): boolean => { return ( labels && diff --git a/plugins/kiali/src/pages/Kiali/Header/KialiTabs.tsx b/plugins/kiali/src/pages/Kiali/Header/KialiTabs.tsx index d501b60d76..7a3dd73673 100644 --- a/plugins/kiali/src/pages/Kiali/Header/KialiTabs.tsx +++ b/plugins/kiali/src/pages/Kiali/Header/KialiTabs.tsx @@ -1,13 +1,22 @@ import React from 'react'; -import { useNavigate } from 'react-router-dom'; +import { Location, useLocation, useNavigate } from 'react-router-dom'; import { HeaderTabs } from '@backstage/core-components'; import { useRouteRef } from '@backstage/core-plugin-api'; import { overviewRouteRef, workloadsRouteRef } from '../../../routes'; +const getPath = (loc: Location): number => { + if (loc.pathname.includes('workloads')) { + return 1; + } + return 0; +}; + export const KialiTabs = () => { - const [selectedTab, setSelectedTab] = React.useState(0); + const loc = useLocation(); + const path = getPath(loc); + const [selectedTab, setSelectedTab] = React.useState(path); const tabs = [ { label: 'Overview', route: useRouteRef(overviewRouteRef) }, { label: 'Workloads', route: useRouteRef(workloadsRouteRef) }, diff --git a/plugins/kiali/src/pages/WorkloadDetails/PodStatus.tsx b/plugins/kiali/src/pages/WorkloadDetails/PodStatus.tsx new file mode 100644 index 0000000000..ecff02f372 --- /dev/null +++ b/plugins/kiali/src/pages/WorkloadDetails/PodStatus.tsx @@ -0,0 +1,62 @@ +import React from 'react'; + +import { Tooltip } from '@material-ui/core'; + +import { ValidationStack } from '../../components/Validations/ValidationStack'; +import { createIcon } from '../../config/KialiIcon'; +import { + DEGRADED, + HEALTHY, + isProxyStatusSynced, + mergeStatus, + ProxyStatus, + Status, +} from '../../types/Health'; +import { ObjectCheck, ValidationTypes } from '../../types/IstioObjects'; +import { highestSeverity, validationToHealth } from '../../types/ServiceInfo'; +import { ProxyStatusList } from './ProxyStatusList'; + +type PodStatusProps = { + checks?: ObjectCheck[]; + proxyStatus?: ProxyStatus; +}; + +export const PodStatus: React.FC = (props: PodStatusProps) => { + const proxyStatusSeverity: Status = + props.proxyStatus && !isProxyStatusSynced(props.proxyStatus) + ? DEGRADED + : HEALTHY; + + const showTooltip = (): boolean => { + const validationSeverity: ValidationTypes = highestSeverity( + props.checks || [], + ); + return ( + proxyStatusSeverity.name !== HEALTHY.name || + validationSeverity !== ValidationTypes.Correct + ); + }; + + if (showTooltip()) { + const severityIcon = (): Status => { + const validationSeverity: Status = validationToHealth( + highestSeverity(props.checks ?? []), + ); + return mergeStatus(proxyStatusSeverity, validationSeverity); + }; + + const tooltipContent: React.ReactNode = ( + <> + + + + ); + + return ( + + {createIcon(severityIcon())} + + ); + } + return createIcon(HEALTHY); +}; diff --git a/plugins/kiali/src/pages/WorkloadDetails/ProxyStatusList.tsx b/plugins/kiali/src/pages/WorkloadDetails/ProxyStatusList.tsx new file mode 100644 index 0000000000..b2d4761648 --- /dev/null +++ b/plugins/kiali/src/pages/WorkloadDetails/ProxyStatusList.tsx @@ -0,0 +1,49 @@ +import * as React from 'react'; + +import { PFColors } from '../../components/Pf/PfColors'; +import { kialiStyle } from '../../styles/StyleUtils'; +import { + isProxyStatusComponentSynced, + isProxyStatusSynced, + ProxyStatus, +} from '../../types/Health'; + +type Props = { + status?: ProxyStatus; +}; + +const smallStyle = kialiStyle({ fontSize: '70%', color: PFColors.White }); +const colorStyle = kialiStyle({ fontSize: '1.1rem', color: PFColors.White }); + +export class ProxyStatusList extends React.Component { + statusList = () => { + if (!this.props.status) { + return []; + } + + return [ + { c: 'CDS', s: this.props.status.CDS }, + { c: 'EDS', s: this.props.status.EDS }, + { c: 'LDS', s: this.props.status.LDS }, + { c: 'RDS', s: this.props.status.RDS }, + ].map((value: { c: string; s: string }, _: number) => { + if (!isProxyStatusComponentSynced(value.s)) { + const status = value.s ? value.s : '-'; + return
    {`${value.c}: ${status}`}
    ; + } + return null; + }); + }; + + render() { + if (this.props.status && !isProxyStatusSynced(this.props.status)) { + return ( +
    + Istio Proxy Status + {this.statusList()} +
    + ); + } + return null; + } +} diff --git a/plugins/kiali/src/pages/WorkloadDetails/WorkloadDetailsPage.tsx b/plugins/kiali/src/pages/WorkloadDetails/WorkloadDetailsPage.tsx new file mode 100644 index 0000000000..c70f54c82e --- /dev/null +++ b/plugins/kiali/src/pages/WorkloadDetails/WorkloadDetailsPage.tsx @@ -0,0 +1,117 @@ +import * as React from 'react'; +import { useParams } from 'react-router-dom'; +import { useAsyncFn, useDebounce } from 'react-use'; + +import { Content } from '@backstage/core-components'; +import { useApi } from '@backstage/core-plugin-api'; + +import { CircularProgress } from '@material-ui/core'; + +import { BreadcrumbView } from '../../components/BreadcrumbView/BreadcrumbView'; +import { DefaultSecondaryMasthead } from '../../components/DefaultSecondaryMasthead/DefaultSecondaryMasthead'; +import * as FilterHelper from '../../components/FilterList/FilterHelper'; +import { TimeDurationComponent } from '../../components/Time/TimeDurationComponent'; +import { getErrorString, kialiApiRef } from '../../services/Api'; +import { KialiAppState, KialiContext } from '../../store'; +import { baseStyle } from '../../styles/StyleUtils'; +import { WorkloadHealth } from '../../types/Health'; +import { Workload, WorkloadQuery } from '../../types/Workload'; +import { WorkloadInfo } from './WorkloadInfo'; + +export const WorkloadDetailsPage = () => { + const { namespace, workload } = useParams(); + const kialiClient = useApi(kialiApiRef); + const kialiState = React.useContext(KialiContext) as KialiAppState; + const [workloadItem, setWorkloadItem] = React.useState(); + const [health, setHealth] = React.useState(); + const [duration, setDuration] = React.useState( + FilterHelper.currentDuration(), + ); + + const grids = () => { + const elements = []; + elements.push( + , + ); + return elements; + }; + + const fetchWorkload = async () => { + const query: WorkloadQuery = { + health: 'true', + rateInterval: `${duration.toString()}s`, + validate: 'false', + }; + kialiClient + .getWorkload(namespace ? namespace : '', workload ? workload : '', query) + .then((workloadResponse: Workload) => { + setWorkloadItem(workloadResponse); + + const wkHealth = WorkloadHealth.fromJson( + namespace ? namespace : '', + workloadResponse.name, + workloadResponse.health, + { + rateInterval: duration, + hasSidecar: workloadResponse.istioSidecar, + hasAmbient: workloadResponse.istioAmbient, + }, + ); + setHealth(wkHealth); + }) + .catch(err => { + kialiState.alertUtils!.add( + `Could not fetch workload: ${getErrorString(err)}`, + ); + }); + }; + + const [{ loading }, refresh] = useAsyncFn( + async () => { + // Check if the config is loaded + await fetchWorkload(); + }, + [], + { loading: true }, + ); + useDebounce(refresh, 10); + + if (loading) { + return ; + } + + const overviewTab = (): React.ReactElement => { + return ( + <> + {workloadItem && ( + + )} + + ); + }; + + return ( +
    + + + fetchWorkload()} + /> + {overviewTab()} + +
    + ); +}; diff --git a/plugins/kiali/src/pages/WorkloadDetails/WorkloadInfo.tsx b/plugins/kiali/src/pages/WorkloadDetails/WorkloadInfo.tsx new file mode 100644 index 0000000000..a4c3069147 --- /dev/null +++ b/plugins/kiali/src/pages/WorkloadDetails/WorkloadInfo.tsx @@ -0,0 +1,332 @@ +import React from 'react'; +import { useAsyncFn, useDebounce } from 'react-use'; + +import { useApi } from '@backstage/core-plugin-api'; + +import { CircularProgress, Grid } from '@material-ui/core'; + +import { IstioConfigCard } from '../../components/IstioConfigCard/IstioConfigCard'; +import { isIstioNamespace, serverConfig } from '../../config'; +import { kialiApiRef } from '../../services/Api'; +import { WorkloadHealth } from '../../types/Health'; +import { + IstioConfigItem, + IstioConfigList, + toIstioItems, +} from '../../types/IstioConfigList'; +import { + ContainerInfo, + ObjectCheck, + ObjectValidation, + Pod, + Validations, + ValidationTypes, +} from '../../types/IstioObjects'; +import { Workload } from '../../types/Workload'; +import { WorkloadPods } from './WorkloadPods'; +import { WorkloadDescription } from './WorkloadsDescription'; + +type WorkloadInfoProps = { + duration?: number; + namespace?: string; + workload: Workload; + health?: WorkloadHealth; +}; + +export const WorkloadInfo = (workloadProps: WorkloadInfoProps) => { + const pods = workloadProps.workload.pods || []; + const namespace = workloadProps.namespace ? workloadProps.namespace : ''; + const kialiClient = useApi(kialiApiRef); + const [istioValidations, setIstioValidations] = React.useState< + IstioConfigItem[] | undefined + >(); + const workloadIstioResources = [ + 'gateways', + 'authorizationpolicies', + 'peerauthentications', + 'sidecars', + 'requestauthentications', + 'envoyfilters', + ]; + const labels = workloadProps.workload.labels + ? workloadProps.workload?.labels + : {}; + const wkLabels: string[] = []; + Object.keys(labels).forEach(key => { + const label = key + (labels[key] ? `=${labels[key]}` : ''); + wkLabels.push(label); + }); + const workloadSelector = wkLabels.join(','); + + const wkIstioTypes = [ + { field: 'gateways', validation: 'gateway' }, + { field: 'sidecars', validation: 'sidecar' }, + { field: 'envoyFilters', validation: 'envoyfilter' }, + { field: 'requestAuthentications', validation: 'requestauthentication' }, + { field: 'authorizationPolicies', validation: 'authorizationpolicy' }, + { field: 'peerAuthentications', validation: 'peerauthentication' }, + ]; + + const getValidations = async (istioConfigResponse: IstioConfigList) => { + const istioConfigItems = istioConfigResponse + ? toIstioItems(istioConfigResponse, workloadProps.workload?.cluster || '') + : []; + if (workloadProps.workload) { + if (istioConfigResponse?.validations) { + const typeNames: { [key: string]: string[] } = {}; + wkIstioTypes.forEach(wkIstioType => { + if ( + istioConfigResponse && + istioConfigResponse.validations[wkIstioType.validation] + ) { + typeNames[wkIstioType.validation] = []; + // @ts-ignore + istioConfigResponse[wkIstioType.field]?.forEach(r => + typeNames[wkIstioType.validation].push(r.metadata.name), + ); + } + }); + } + } + + setIstioValidations(istioConfigItems); + }; + + const fetchIstioConfig = async () => { + kialiClient + .getIstioConfig( + namespace, + workloadIstioResources, + true, + '', + workloadSelector, + workloadProps.workload?.cluster, + ) + .then((istioConfigResponse: IstioConfigList) => { + getValidations(istioConfigResponse); + }); + }; + + const [{ loading }, refresh] = useAsyncFn( + async () => { + // Check if the config is loaded + fetchIstioConfig(); + }, + [], + { loading: true }, + ); + + useDebounce(refresh, 10); + if (loading) { + return ; + } + + // All information for validations is fetched in the workload, no need to add another call + const workloadValidations = (workload: Workload): Validations => { + const noIstiosidecar: ObjectCheck = { + message: 'Pod has no Istio sidecar', + severity: ValidationTypes.Warning, + path: '', + }; + const noAppLabel: ObjectCheck = { + message: 'Pod has no app label', + severity: ValidationTypes.Warning, + path: '', + }; + const noVersionLabel: ObjectCheck = { + message: 'Pod has no version label', + severity: ValidationTypes.Warning, + path: '', + }; + const pendingPod: ObjectCheck = { + message: 'Pod is in Pending Phase', + severity: ValidationTypes.Warning, + path: '', + }; + const unknownPod: ObjectCheck = { + message: 'Pod is in Unknown Phase', + severity: ValidationTypes.Warning, + path: '', + }; + const failedPod: ObjectCheck = { + message: 'Pod is in Failed Phase', + severity: ValidationTypes.Error, + path: '', + }; + const failingPodContainer: ObjectCheck = { + message: 'Pod has failing container', + severity: ValidationTypes.Warning, + path: '', + }; + const failingPodIstioContainer: ObjectCheck = { + message: 'Pod has failing Istio container', + severity: ValidationTypes.Warning, + path: '', + }; + const failingPodAppContainer: ObjectCheck = { + message: 'Pod has failing app container', + severity: ValidationTypes.Warning, + path: '', + }; + + const istioLabels = serverConfig.istioLabels; + const istioAnnotations = serverConfig.istioAnnotations; + + const checkPodContainers = ( + containerInfo: ContainerInfo[], + ): ObjectCheck[] => { + const validations: ObjectCheck[] = []; + containerInfo.forEach(c => { + if (!c.isReady && validations.indexOf(failingPodAppContainer) === -1) { + validations.push(failingPodAppContainer); + } + }); + return validations; + }; + + const checkIstioContainers = ( + containerInfo: ContainerInfo[], + ): ObjectCheck[] => { + const validations: ObjectCheck[] = []; + containerInfo.forEach(c => { + if ( + !c.isReady && + validations.indexOf(failingPodIstioContainer) === -1 + ) { + validations.push(failingPodIstioContainer); + } + }); + return validations; + }; + + const addMeshValidations = (pod: Pod): ObjectCheck[] => { + const validations: ObjectCheck[] = []; + if ( + !( + serverConfig.ambientEnabled && + (pod.annotations + ? pod.annotations[istioAnnotations.ambientAnnotation] === + istioAnnotations.ambientAnnotationEnabled + : false) + ) + ) { + validations.push(noIstiosidecar); + } + return validations; + }; + + const getPodValidations = (pod: Pod): ObjectValidation => { + const validations: ObjectValidation = { + name: pod.name, + objectType: 'pod', + valid: true, + checks: [], + }; + + if (!pod.istioContainers || pod.istioContainers.length === 0) { + validations.checks.concat(addMeshValidations(pod)); + } else { + validations.checks.concat(checkIstioContainers(pod.istioContainers)); + } + + if (!pod.containers || pod.containers.length === 0) { + validations.checks.push(failingPodContainer); + } else { + validations.checks.concat(checkPodContainers(pod.containers)); + } + if (!pod.labels) { + validations.checks.push(noAppLabel); + validations.checks.push(noVersionLabel); + } else { + if (!pod.appLabel) { + validations.checks.push(noAppLabel); + } + if (!pod.versionLabel) { + validations.checks.push(noVersionLabel); + } + } + return validations; + }; + + const validations: Validations = {}; + const isWaypoint = + serverConfig.ambientEnabled && + workload.labels && + workload.labels[istioLabels.ambientWaypointLabel] === + istioLabels.ambientWaypointLabelValue; + + if (workload.pods.length > 0) { + validations.pod = {}; + workload.pods.forEach(pod => { + validations.pod[pod.name] = { + name: pod.name, + objectType: 'pod', + valid: true, + checks: [], + }; + if (!isIstioNamespace(namespace) && !isWaypoint) { + validations.pod[pod.name] = getPodValidations(pod); + } + + switch (pod.status) { + case 'Pending': + validations.pod[pod.name].checks.push(pendingPod); + break; + case 'Unknown': + validations.pod[pod.name].checks.push(unknownPod); + break; + case 'Failed': + validations.pod[pod.name].checks.push(failedPod); + break; + default: + // Pod healthy + } + // If statusReason is present + if (pod.statusReason) { + validations.pod[pod.name].checks.push({ + message: pod.statusReason, + severity: ValidationTypes.Warning, + path: '', + }); + } + validations.pod[pod.name].valid = + validations.pod[pod.name].checks.length === 0; + }); + } + return validations; + }; + + return ( + <> + {workloadProps.workload && ( + + + + + + + + + + + + )} + + ); +}; diff --git a/plugins/kiali/src/pages/WorkloadDetails/WorkloadPods.tsx b/plugins/kiali/src/pages/WorkloadDetails/WorkloadPods.tsx new file mode 100644 index 0000000000..15c49af301 --- /dev/null +++ b/plugins/kiali/src/pages/WorkloadDetails/WorkloadPods.tsx @@ -0,0 +1,173 @@ +import * as React from 'react'; + +import { EmptyState } from '@backstage/core-components'; + +import { + Card, + CardContent, + CardHeader, + Tooltip, + Typography, +} from '@material-ui/core'; + +import { Labels } from '../../components/Label/Labels'; +import { PFBadge, PFBadges } from '../../components/Pf/PfBadges'; +import { SimpleTable, tRow } from '../../components/SimpleTable'; +import { LocalTime } from '../../components/Time/LocalTime'; +import { KialiIcon } from '../../config/KialiIcon'; +import { kialiStyle } from '../../styles/StyleUtils'; +import { ObjectValidation, Pod } from '../../types/IstioObjects'; +import { PodStatus } from './PodStatus'; + +type WorkloadPodsProps = { + namespace: string; + pods: Pod[]; + validations: { [key: string]: ObjectValidation }; + workload: string; +}; + +const resourceListStyle = kialiStyle({ + margin: '0 0 0.5rem 0', + $nest: { + '& > ul > li > span': { + float: 'left', + width: '125px', + fontWeight: 700, + }, + }, +}); + +const infoStyle = kialiStyle({ + marginLeft: '0.5rem', +}); + +const iconStyle = kialiStyle({ + display: 'inline-block', +}); + +export const WorkloadPods: React.FC = ( + props: WorkloadPodsProps, +) => { + const columns: any[] = [{ title: 'Name' }, { title: 'Status', width: 10 }]; + + const noPods: React.ReactNode = ( + {`No Pods in workload ${props.workload}`}} + /> + ); + + const rows: tRow = props.pods + .sort((p1: Pod, p2: Pod) => (p1.name < p2.name ? -1 : 1)) + .map((pod, _podIdx) => { + let validation: ObjectValidation = {} as ObjectValidation; + + if (props.validations[pod.name]) { + validation = props.validations[pod.name]; + } + + const podProperties = ( +
    +
      +
    • + Created +
      + +
      +
    • + +
    • + Created By +
      + {pod.createdBy && pod.createdBy.length > 0 + ? pod.createdBy + .map(ref => `${ref.name} (${ref.kind})`) + .join(', ') + : 'Not found'} +
      +
    • + +
    • + Service Account +
      + {pod.serviceAccountName ?? 'Not found'} +
      +
    • + +
    • + Istio Init Container +
      + {pod.istioInitContainers + ? pod.istioInitContainers.map(c => `${c.image}`).join(', ') + : 'Not found'} +
      +
    • + +
    • + Istio Container +
      + {pod.istioContainers + ? pod.istioContainers.map(c => `${c.image}`).join(', ') + : 'Not found'} +
      +
    • + +
    • + Labels +
      + +
      +
    • +
    +
    + ); + + return { + cells: [ + +
    + +
    + {pod.name} + {podProperties}} + > +
    + +
    +
    +
    , + , + ], + }; + }); + + return ( + + + Pods + + } + /> + + + + + + ); +}; diff --git a/plugins/kiali/src/pages/WorkloadDetails/WorkloadsDescription.tsx b/plugins/kiali/src/pages/WorkloadDetails/WorkloadsDescription.tsx new file mode 100644 index 0000000000..a88011cec4 --- /dev/null +++ b/plugins/kiali/src/pages/WorkloadDetails/WorkloadsDescription.tsx @@ -0,0 +1,279 @@ +import * as React from 'react'; + +import { + Card, + CardContent, + CardHeader, + Tooltip, + Typography, +} from '@material-ui/core'; + +import { AmbientLabel } from '../../components/Ambient/AmbientLabel'; +import { DetailDescription } from '../../components/DetailsDescription/DetailDescription'; +import { HealthIndicator } from '../../components/Health/HealthIndicator'; +import { Labels } from '../../components/Label/Labels'; +import { renderAPILogo, renderRuntimeLogo } from '../../components/Logos/Logos'; +import { MissingAuthPolicy } from '../../components/MissingAuthPolicy/MissingAuthPolicy'; +import { MissingLabel } from '../../components/MissingLabel/MissingLabel'; +import { MissingSidecar } from '../../components/MissingSidecar/MissingSidecar'; +import { PFBadge, PFBadges } from '../../components/Pf/PfBadges'; +import { TextOrLink } from '../../components/TextOrLink'; +import { LocalTime } from '../../components/Time/LocalTime'; +import { isMultiCluster, serverConfig } from '../../config'; +import { KialiIcon } from '../../config/KialiIcon'; +import { isGateway, isWaypoint } from '../../helpers/LabelFilterHelper'; +import { kialiStyle } from '../../styles/StyleUtils'; +import * as H from '../../types/Health'; +import { validationKey } from '../../types/IstioConfigList'; +import { Workload } from '../../types/Workload'; +import { hasMissingAuthPolicy } from '../../utils/IstioConfigUtils'; + +type WorkloadDescriptionProps = { + health?: H.Health; + namespace: string; + workload: Workload; +}; + +const resourceListStyle = kialiStyle({ + marginBottom: '0.75rem', + $nest: { + '& > ul > li span': { + float: 'left', + width: '125px', + fontWeight: 700, + }, + }, +}); + +const iconStyle = kialiStyle({ + display: 'inline-block', +}); + +const infoStyle = kialiStyle({ + marginLeft: '0.5rem', + verticalAlign: '-0.125rem', + display: 'inline-block', +}); + +const healthIconStyle = kialiStyle({ + marginLeft: '0.5rem', + verticalAlign: '-0.075rem', +}); + +const additionalItemStyle = kialiStyle({ + display: 'flex', + alignItems: 'center', +}); + +const runtimeInfoStyle = kialiStyle({ + display: 'flex', + alignItems: 'center', + marginTop: '0.5rem', +}); + +export const WorkloadDescription: React.FC = ( + props: WorkloadDescriptionProps, +) => { + const workload = props.workload; + const apps: string[] = []; + const services: string[] = []; + + if (workload.labels[serverConfig.istioLabels.appLabelName]) { + apps.push(workload.labels[serverConfig.istioLabels.appLabelName]); + } + + workload.services?.forEach(s => services.push(s.name)); + + const isTemplateLabels = + workload && + [ + 'Deployment', + 'ReplicaSet', + 'ReplicationController', + 'DeploymentConfig', + 'StatefulSet', + ].indexOf(workload.type) >= 0; + + const runtimes = (workload?.runtimes ?? []) + .map(r => r.name) + .filter(name => name !== ''); + + const workloadProperties = workload ? ( + <> +
    +
      + {workload.istioInjectionAnnotation !== undefined && ( +
    • + Istio Injection + {String(workload.istioInjectionAnnotation)} +
    • + )} + +
    • + Type + {workload.type ? workload.type : 'N/A'} +
    • + +
    • + Created +
      + +
      +
    • + +
    • + Version + {workload.resourceVersion} +
    • + + {workload.additionalDetails.map((additionalItem, idx) => { + return ( +
    • +
      + {additionalItem.title} + {additionalItem.icon && + renderAPILogo(additionalItem.icon, undefined, idx)} +
      + +
    • + ); + })} + + {runtimes.length > 0 && ( +
    • +
      + Runtimes +
      + {runtimes + .map((rt, idx) => renderRuntimeLogo(rt, idx)) + .reduce( + (list: React.ReactNode[], elem) => + list.length > 0 + ? [...list, | , elem] + : [elem], + [], + )} +
      +
      +
    • + )} +
    +
    + + ) : undefined; + + return ( + + + +
    + +
    + + {props.workload.name} + + {workloadProperties ? ( + + {workloadProperties} + + } + > +
    + +
    +
    + ) : undefined} + + + + + + {!props.workload.istioSidecar && + !props.workload.istioAmbient && + !isWaypoint(props.workload.labels) && ( + + )} + + {props.workload.istioAmbient && + !isWaypoint(props.workload.labels) && ( + 0 + ? true + : false + } + /> + )} + + {hasMissingAuthPolicy( + validationKey(props.workload.name, props.namespace), + props.workload.validations, + ) && ( + + )} + + {(!props.workload.appLabel || !props.workload.versionLabel) && + !isWaypoint(props.workload.labels) && ( + + )} +
    + + {props.workload?.cluster && isMultiCluster && ( +
    + {props.workload.cluster} +
    + )} + + } + /> + + + {workload.labels && ( + + )} + + +
    + ); +}; diff --git a/plugins/kiali/src/routes.ts b/plugins/kiali/src/routes.ts index 0faa5afcb5..fb77fe9bbf 100644 --- a/plugins/kiali/src/routes.ts +++ b/plugins/kiali/src/routes.ts @@ -19,3 +19,9 @@ export const workloadsRouteRef: SubRouteRef = createSubRouteRef({ path: '/workloads', parent: rootRouteRef, }); + +export const workloadsDetailRouteRef = createSubRouteRef({ + id: 'kiali-workloads-details', + parent: rootRouteRef, + path: '/workloads/:namespace/:workload', +}); diff --git a/plugins/kiali/src/services/Api.ts b/plugins/kiali/src/services/Api.ts index 939b53f4ee..a73324304c 100644 --- a/plugins/kiali/src/services/Api.ts +++ b/plugins/kiali/src/services/Api.ts @@ -15,7 +15,11 @@ import { ServiceHealth, WorkloadHealth, } from '../types/Health'; -import { IstioConfigsMap } from '../types/IstioConfigList'; +import { + IstioConfigList, + IstioConfigListQuery, + IstioConfigsMap, +} from '../types/IstioConfigList'; import { CanaryUpgradeStatus, OutboundTrafficPolicy, @@ -31,7 +35,12 @@ 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 { + Workload, + WorkloadListItem, + WorkloadNamespaceResponse, + WorkloadQuery, +} from '../types/Workload'; import { filterNsByAnnotation } from '../utils/entityFilter'; export const ANONYMOUS_USER = 'anonymous'; @@ -40,6 +49,11 @@ export interface Response { data: T; } +interface ClusterParam { + clusterName?: string; +} +type QueryParams = T & ClusterParam; + /** API URLs */ const urls = config.api.urls; @@ -115,13 +129,26 @@ export interface KialiApi { ): Promise>; getIstioStatus(cluster?: string): Promise; getIstioCertsInfo(): Promise; - setEntity(entity?: Entity): void; - status(): Promise; - + getWorkload( + namespace: string, + name: string, + params: WorkloadQuery, + cluster?: string, + ): Promise; getWorkloads( namespace: string, duration: number, ): Promise; + getIstioConfig( + namespace: string, + objects: string[], + validate: boolean, + labelSelector: string, + workloadSelector: string, + cluster?: string, + ): Promise; + setEntity(entity?: Entity): void; + status(): Promise; } export const kialiApiRef = createApiRef({ @@ -523,6 +550,64 @@ export class KialiApiClient implements KialiApi { }); }); }; + + getWorkload = async ( + namespace: string, + name: string, + params: WorkloadQuery, + cluster?: string, + ): Promise => { + const queryParams: QueryParams = { ...params }; + if (cluster) { + queryParams.clusterName = cluster; + } + return this.newRequest( + HTTP_VERBS.GET, + urls.workload(namespace, name), + queryParams, + {}, + ).then(resp => { + return resp; + }); + }; + + getIstioConfig = async ( + namespace: string, + objects: string[], + validate: boolean, + labelSelector: string, + workloadSelector: string, + cluster?: string, + ): Promise => { + const params: QueryParams = {}; + if (objects && objects.length > 0) { + params.objects = objects.join(','); + } + if (validate) { + params.validate = validate; + } + + if (labelSelector) { + params.labelSelector = labelSelector; + } + + if (workloadSelector) { + params.workloadSelector = workloadSelector; + } + + if (cluster) { + params.clusterName = cluster; + } + + return this.newRequest( + HTTP_VERBS.GET, + urls.istioConfig(namespace), + params, + {}, + ).then(resp => { + return resp; + }); + }; } export const getErrorString = (error: AxiosError): string => { diff --git a/plugins/kiali/src/styles/HealthStyle.ts b/plugins/kiali/src/styles/HealthStyle.ts new file mode 100644 index 0000000000..2f7725f83d --- /dev/null +++ b/plugins/kiali/src/styles/HealthStyle.ts @@ -0,0 +1,17 @@ +import { PFColors } from '../components/Pf/PfColors'; +import { kialiStyle } from './StyleUtils'; + +export const healthIndicatorStyle = kialiStyle({ + $nest: { + '& .pf-v5-c-tooltip__content': { + borderWidth: '1px', + textAlign: 'left', + }, + + '& .pf-v5-c-content ul': { + marginBottom: 'var(--pf-v5-c-content--ul--MarginTop)', + marginTop: 0, + color: PFColors.Color100, + }, + }, +}); diff --git a/plugins/kiali/src/styles/StyleUtils.ts b/plugins/kiali/src/styles/StyleUtils.ts index 26199ddd20..6515f08ddd 100644 --- a/plugins/kiali/src/styles/StyleUtils.ts +++ b/plugins/kiali/src/styles/StyleUtils.ts @@ -18,3 +18,8 @@ export const baseStyle = kialiStyle({ display: 'contents', overflow: 'visible', }); + +export const linkStyle = kialiStyle({ + color: '#06c', + cursor: 'pointer', +}); diff --git a/plugins/kiali/src/types/App.ts b/plugins/kiali/src/types/App.ts new file mode 100644 index 0000000000..57caba3764 --- /dev/null +++ b/plugins/kiali/src/types/App.ts @@ -0,0 +1,32 @@ +import { AppHealthResponse } from '../types/Health'; +import { Namespace } from './Namespace'; +import { Runtime } from './Workload'; + +export interface AppId { + app: string; + cluster?: string; + namespace: string; +} + +export interface AppWorkload { + istioSidecar: boolean; + istioAmbient: boolean; + labels: { [key: string]: string }; + serviceAccountNames: string[]; + workloadName: string; +} + +export interface App { + cluster?: string; + health: AppHealthResponse; + name: string; + namespace: Namespace; + runtimes: Runtime[]; + serviceNames: string[]; + workloads: AppWorkload[]; +} + +export interface AppQuery { + health: 'true' | 'false'; + rateInterval: string; +} diff --git a/plugins/kiali/src/types/IstioConfigDetails.ts b/plugins/kiali/src/types/IstioConfigDetails.ts index 8946a65c0e..41c07163ea 100644 --- a/plugins/kiali/src/types/IstioConfigDetails.ts +++ b/plugins/kiali/src/types/IstioConfigDetails.ts @@ -9,7 +9,11 @@ import { HelpMessage, IstioObject, K8sGateway, + K8sGRPCRoute, K8sHTTPRoute, + K8sReferenceGrant, + K8sTCPRoute, + K8sTLSRoute, ObjectValidation, PeerAuthentication, References, @@ -32,27 +36,31 @@ export interface IstioConfigId { } export interface IstioConfigDetails { - namespace: Namespace; + authorizationPolicy: AuthorizationPolicy; cluster?: string; + destinationRule: DestinationRule; + envoyFilter: EnvoyFilter; gateway: Gateway; + help?: HelpMessage[]; + k8sGRPCRoute: K8sGRPCRoute; k8sGateway: K8sGateway; k8sHTTPRoute: K8sHTTPRoute; - virtualService: VirtualService; - destinationRule: DestinationRule; + k8sReferenceGrant: K8sReferenceGrant; + k8sTCPRoute: K8sTCPRoute; + k8sTLSRoute: K8sTLSRoute; + namespace: Namespace; + peerAuthentication: PeerAuthentication; + permissions: ResourcePermissions; + references?: References; + requestAuthentication: RequestAuthentication; serviceEntry: ServiceEntry; sidecar: Sidecar; - workloadEntry: WorkloadEntry; - workloadGroup: WorkloadGroup; - envoyFilter: EnvoyFilter; - wasmPlugin: WasmPlugin; telemetry: Telemetry; - authorizationPolicy: AuthorizationPolicy; - peerAuthentication: PeerAuthentication; - requestAuthentication: RequestAuthentication; - permissions: ResourcePermissions; validation: ObjectValidation; - references?: References; - help?: HelpMessage[]; + virtualService: VirtualService; + wasmPlugin: WasmPlugin; + workloadEntry: WorkloadEntry; + workloadGroup: WorkloadGroup; } export const aceOptions: AceOptions = { diff --git a/plugins/kiali/src/types/IstioConfigList.ts b/plugins/kiali/src/types/IstioConfigList.ts index 81de232e0d..b21108c1ae 100644 --- a/plugins/kiali/src/types/IstioConfigList.ts +++ b/plugins/kiali/src/types/IstioConfigList.ts @@ -4,7 +4,11 @@ import { EnvoyFilter, Gateway, K8sGateway, + K8sGRPCRoute, K8sHTTPRoute, + K8sReferenceGrant, + K8sTCPRoute, + K8sTLSRoute, ObjectValidation, PeerAuthentication, RequestAuthentication, @@ -21,28 +25,32 @@ import { Namespace } from './Namespace'; import { ResourcePermissions } from './Permissions'; export interface IstioConfigItem { - namespace: string; + authorizationPolicy?: AuthorizationPolicy; cluster?: string; - type: string; - name: string; creationTimestamp?: string; - resourceVersion?: string; + destinationRule?: DestinationRule; + envoyFilter?: EnvoyFilter; gateway?: Gateway; + k8sGRPCRoute?: K8sGRPCRoute; k8sGateway?: K8sGateway; k8sHTTPRoute?: K8sHTTPRoute; - virtualService?: VirtualService; - destinationRule?: DestinationRule; + k8sReferenceGrant?: K8sReferenceGrant; + k8sTCPRoute?: K8sTCPRoute; + k8sTLSRoute?: K8sTLSRoute; + name: string; + namespace: string; + peerAuthentication?: PeerAuthentication; + requestAuthentication?: RequestAuthentication; + resourceVersion?: string; serviceEntry?: ServiceEntry; - authorizationPolicy?: AuthorizationPolicy; sidecar?: Sidecar; - wasmPlugin?: WasmPlugin; telemetry?: Telemetry; - peerAuthentication?: PeerAuthentication; - requestAuthentication?: RequestAuthentication; + type: string; + validation?: ObjectValidation; + virtualService?: VirtualService; + wasmPlugin?: WasmPlugin; workloadEntry?: WorkloadEntry; workloadGroup?: WorkloadGroup; - envoyFilter?: EnvoyFilter; - validation?: ObjectValidation; } export declare type IstioConfigsMap = { [key: string]: IstioConfigList }; @@ -68,6 +76,13 @@ export interface IstioConfigList { validations: Validations; } +export interface IstioConfigListQuery { + labelSelector?: string; + objects?: string; + validate?: boolean; + workloadSelector?: string; +} + export const dicIstioType = { Sidecar: 'sidecars', Gateway: 'gateways', diff --git a/plugins/kiali/src/types/IstioObjects.ts b/plugins/kiali/src/types/IstioObjects.ts index 5c553228f5..ea0c9c660b 100644 --- a/plugins/kiali/src/types/IstioObjects.ts +++ b/plugins/kiali/src/types/IstioObjects.ts @@ -1113,6 +1113,138 @@ export interface PeerAuthentication extends IstioObject { spec: PeerAuthenticationSpec; } +export interface K8sGRPCRoute extends IstioObject { + spec: K8sGRPCRouteSpec; +} + +export interface K8sReferenceGrant extends IstioObject { + spec: K8sReferenceGrantSpec; +} + +export interface K8sTCPRoute extends IstioObject { + spec: K8sTCPRouteSpec; +} + +export interface K8sTLSRoute extends IstioObject { + spec: K8sTLSRouteSpec; +} + +export interface K8sCommonRouteSpec { + parentRefs?: ParentRef[]; +} + +export interface K8sGRPCRouteSpec extends K8sCommonRouteSpec { + hostnames?: string[]; + rules?: K8sGRPCRouteRule[]; +} + +export interface K8sHTTPRouteSpec extends K8sCommonRouteSpec { + hostnames?: string[]; + rules?: K8sHTTPRouteRule[]; +} + +export interface K8sReferenceGrantSpec { + from?: K8sReferenceRule[]; + to?: K8sReferenceRule[]; +} + +export interface K8sTCPRouteSpec extends K8sCommonRouteSpec { + rules?: K8sTCPRouteRule[]; +} + +export interface K8sTLSRouteSpec extends K8sCommonRouteSpec { + hostnames?: string[]; + rules?: K8sTLSRouteRule[]; +} + +// rest of attributes used by k8s gateway objects +export interface K8sGRPCRouteRule { + backendRefs?: K8sRouteBackendRef[]; + matches?: K8sGRPCRouteMatch[]; +} + +export interface K8sHTTPRouteRule { + backendRefs?: K8sRouteBackendRef[]; + filters?: K8sHTTPRouteFilter[]; + matches?: K8sHTTPRouteMatch[]; +} + +export interface K8sReferenceRule { + group: string; + kind: string; + namespace?: string; +} + +export interface K8sTCPRouteRule { + backendRefs?: K8sRouteBackendRef[]; +} + +export interface K8sTLSRouteRule { + backendRefs?: K8sRouteBackendRef[]; +} + +export interface K8sGRPCHeaderMatch { + name?: string; + type?: string; + value?: string; +} + +export interface K8sGRPCMethodMatch { + method?: string; + service?: string; + type?: string; +} + +export interface K8sGRPCRouteMatch { + headers?: K8sGRPCHeaderMatch[]; + method?: K8sGRPCMethodMatch; +} + +export interface K8sHTTPMatch { + name?: string; + type?: string; + value?: string; +} + +export interface K8sHTTPRouteFilter { + requestHeaderModifier?: K8sHTTPHeaderFilter; + requestMirror?: K8sHTTPRequestMirrorFilter; + requestRedirect?: K8sHTTPRouteRequestRedirect; + type?: string; +} + +export interface K8sHTTPRouteMatch { + headers?: K8sHTTPMatch[]; + method?: string; + path?: K8sHTTPMatch; + queryParams?: K8sHTTPMatch[]; +} + +export interface K8sHTTPRouteRequestRedirect { + hostname?: string; + port?: number; + scheme?: string; + statusCode?: number; +} + +export interface K8sHTTPHeaderFilter { + add?: HTTPHeader[]; + remove?: string[]; + set?: HTTPHeader[]; +} + +export interface K8sHTTPRequestMirrorFilter { + backendRef?: K8sRouteBackendRef; +} + +export interface K8sRouteBackendRef { + filters?: K8sHTTPRouteFilter[]; + name: string; + namespace?: string; + port?: number; + weight?: number; +} + export interface PeerAuthenticationSpec { selector?: PeerAuthenticationWorkloadSelector; mtls?: PeerAuthenticationMutualTls; diff --git a/plugins/kiali/src/types/ServerConfig.ts b/plugins/kiali/src/types/ServerConfig.ts index 476d3ff5b0..faab0fe921 100644 --- a/plugins/kiali/src/types/ServerConfig.ts +++ b/plugins/kiali/src/types/ServerConfig.ts @@ -2,6 +2,8 @@ import { DurationInSeconds } from './Common'; import { MeshCluster } from './Mesh'; export type IstioLabelKey = + | 'ambientWaypointLabel' + | 'ambientWaypointLabelValue' | 'appLabelName' | 'versionLabelName' | 'injectionLabelName' @@ -12,6 +14,8 @@ interface DeploymentConfig { } interface IstioAnnotations { + ambientAnnotation: string; + ambientAnnotationEnabled: string; // this could also be the name of the pod label, both label and annotation are supported istioInjectionAnnotation: string; } diff --git a/plugins/kiali/src/types/Workload.ts b/plugins/kiali/src/types/Workload.ts index a3bd31f57c..72ed62821d 100644 --- a/plugins/kiali/src/types/Workload.ts +++ b/plugins/kiali/src/types/Workload.ts @@ -103,3 +103,9 @@ export interface AdditionalItem { value: string; icon?: string; } + +export interface WorkloadQuery { + health: 'true' | 'false'; + rateInterval: string; + validate: 'true' | 'false'; +} diff --git a/plugins/kiali/src/types/types.ts b/plugins/kiali/src/types/types.ts new file mode 100644 index 0000000000..961402ac79 --- /dev/null +++ b/plugins/kiali/src/types/types.ts @@ -0,0 +1,14 @@ +import axios, { AxiosError, AxiosResponse } from 'axios'; + +export interface KialiError { + detail: string; + error: string; +} + +export type ApiResponse = Partial> & { + data: T; +}; + +export type ApiError = AxiosError; + +export const isApiError = axios.isAxiosError; diff --git a/plugins/kiali/src/utils/IstioConfigUtils.ts b/plugins/kiali/src/utils/IstioConfigUtils.ts new file mode 100644 index 0000000000..035cdee401 --- /dev/null +++ b/plugins/kiali/src/utils/IstioConfigUtils.ts @@ -0,0 +1,169 @@ +import _ from 'lodash'; + +import { + ObjectCheck, + OutlierDetection, + Validations, +} from '../types/IstioObjects'; + +export const mergeJsonPatch = ( + objectModified: object, + object?: object, +): object => { + if (!object) { + return objectModified; + } + const customizer = ( + objValue: object | null, + srcValue: object, + ): object | null => { + if (!objValue) { + return null; + } + if (_.isObject(objValue) && _.isObject(srcValue)) { + _.mergeWith(objValue, srcValue, customizer); + } + return objValue; + }; + _.mergeWith(objectModified, object, customizer); + return objectModified; +}; + +const k8sHostRegexp = + /^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$/; +const nsRegexp = + /^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[-a-z0-9]([-a-z0-9]*[a-z0-9])?)*$/; +const hostRegexp = + /(?=^.{4,253}$)(^((?!-)(([a-zA-Z0-9-]{0,62}[a-zA-Z0-9])|\*)\.)+[a-zA-Z]{2,63}$)/; +const ipRegexp = + /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))?$/; +const durationRegexp = /^[\d]{1,10}\.?[\d]{0,10}(h|m|s|ms)$/; + +// K8s gateway hosts have only dnsName +export const isK8sGatewayHostValid = (k8sGatewayHost: string): boolean => { + if (k8sGatewayHost.length < 1 && k8sGatewayHost.length > 253) { + return false; + } + + // K8s gateway host must be fqdn but not ip address + if ( + k8sGatewayHost.split('.').length < 2 || + k8sGatewayHost.search(ipRegexp) === 0 + ) { + return false; + } + + return k8sGatewayHost.search(k8sHostRegexp) === 0; +}; + +// Used to check if Sidecar and Gateway host expressions are valid +export const isServerHostValid = ( + serverHost: string, + nsMandatory: boolean, +): boolean => { + if (serverHost.length === 0) { + return false; + } + // / + const parts = serverHost.split('/'); + // More than one / + if (parts.length > 2) { + return false; + } + // Force that namespace/dnsName are present + if (nsMandatory && parts.length < 2) { + return false; + } + + // parts[0] is a dns + let dnsValid = true; + let hostValid = true; + let dns = ''; + let host = ''; + if (parts.length === 2) { + dns = parts[0]; + host = parts[1]; + + if (dns !== '.' && dns !== '*') { + dnsValid = parts[0].search(nsRegexp) === 0; + } + } else { + host = parts[0]; + } + + if (host !== '*') { + hostValid = host.search(hostRegexp) === 0; + } + return dnsValid && hostValid; +}; + +export const isValidIp = (ip: string): boolean => { + return ipRegexp.test(ip); +}; + +export const isValidUrl = (url: string): boolean => { + try { + // eslint-disable-next-line no-new + new URL(url); + } catch (__) { + return false; + } + return true; +}; + +export const isValidDuration = (duration: string): boolean => { + if ( + duration === '0ms' || + duration === '0s' || + duration === '0m' || + duration === '0h' + ) { + return false; + } + return durationRegexp.test(duration); +}; + +export const isValidAbortStatusCode = (statusCode: number): boolean => { + return statusCode >= 100 && statusCode <= 599; +}; + +export const isValidOutlierDetection = ( + outlierDetection: OutlierDetection, +): boolean => { + if ( + outlierDetection.interval && + !isValidDuration(outlierDetection.interval) + ) { + return false; + } + if ( + outlierDetection.baseEjectionTime && + !isValidDuration(outlierDetection.baseEjectionTime) + ) { + return false; + } + return true; +}; + +export const hasMissingAuthPolicy = ( + workloadName: string, + validations: Validations | undefined, +): boolean => { + let hasMissingAP = false; + + if (!validations) { + return hasMissingAP; + } + + if (validations.workload && validations.workload[workloadName]) { + const workloadValidation = validations.workload[workloadName]; + + workloadValidation.checks.forEach((check: ObjectCheck) => { + if (check.code === 'KIA1301') { + hasMissingAP = true; + } + }); + } + + return hasMissingAP; +};