diff --git a/Makefile b/Makefile index 102a99f3..790ac8d5 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ SHELL=/bin/bash # Identifies the current build. Match the same version of Kiali Server and Operator. -VERSION ?= v2.2.0-SNAPSHOT +VERSION ?= v2.3.0-SNAPSHOT COMMIT_HASH ?= $(shell git rev-parse HEAD) # Directories based on the root project directory diff --git a/plugin/cypress/integration/kiali/common/apps.ts b/plugin/cypress/integration/kiali/common/apps.ts index cfb73e4f..1bb941e8 100644 --- a/plugin/cypress/integration/kiali/common/apps.ts +++ b/plugin/cypress/integration/kiali/common/apps.ts @@ -55,7 +55,7 @@ Then('user sees span details', () => { .eq(1) // take 1st row .find('td') .eq(4) // take 5th cell (kebab) - .should('be.visible'); + .should('exist'); cy.get('table') .should('be.visible') diff --git a/plugin/cypress/integration/kiali/common/workload_logs.ts b/plugin/cypress/integration/kiali/common/workload_logs.ts index 195683d8..22b7269a 100644 --- a/plugin/cypress/integration/kiali/common/workload_logs.ts +++ b/plugin/cypress/integration/kiali/common/workload_logs.ts @@ -8,8 +8,20 @@ Given( 'I am on the logs tab of the {string} workload detail page of the {string} namespace', (workload: string, namespace: string) => { cy.visit({ url: `/console/namespaces/${namespace}/workloads/${workload}?tab=logs&refresh=0` }); - cy.get('#metrics_filter_interval_duration-toggle').click(); - cy.get('#1800').click(); + + const changeIntervalDuration = (): void => { + cy.get('#metrics_filter_interval_duration-toggle').click(); + cy.get('#1800').click(); + }; + + // In OSSMC, the duration interval is configured using the time duration modal component + if (Cypress.env('OSSMC')) { + cy.get('#time_duration').click(); + changeIntervalDuration(); + cy.get('#time-duration-modal').find('button').contains('Confirm').click(); + } else { + changeIntervalDuration(); + } } ); diff --git a/plugin/package.json b/plugin/package.json index dca21ae6..675d2746 100644 --- a/plugin/package.json +++ b/plugin/package.json @@ -1,6 +1,6 @@ { "name": "ossmconsole", - "version": "2.2.0", + "version": "2.3.0", "private": true, "repository": { "type": "git", diff --git a/plugin/plugin-metadata.ts b/plugin/plugin-metadata.ts index a4b1ae89..5acaa187 100644 --- a/plugin/plugin-metadata.ts +++ b/plugin/plugin-metadata.ts @@ -2,7 +2,7 @@ import { ConsolePluginBuildMetadata } from '@openshift-console/dynamic-plugin-sd const metadata: ConsolePluginBuildMetadata = { name: 'ossmconsole', - version: '2.2.0', + version: '2.3.0', displayName: 'OpenShift Service Mesh Console', description: 'Provides Service Mesh/Istio Observability', exposedModules: { diff --git a/plugin/src/kiali/README.md b/plugin/src/kiali/README.md index 725c6c72..f64b72df 100644 --- a/plugin/src/kiali/README.md +++ b/plugin/src/kiali/README.md @@ -2,6 +2,6 @@ Copy of Kiali frontend source code Kiali frontend source originated from: -* git ref: master -* git commit: f9f04eef5bdaa9df626030a778d3ba53a40df75e -* GitHub URL: https://github.com/kiali/kiali/tree/f9f04eef5bdaa9df626030a778d3ba53a40df75e/frontend/src +* git ref: v2.2 +* git commit: cb33b859493abf0d2706068ddaaace5656dc0d4d +* GitHub URL: https://github.com/kiali/kiali/tree/cb33b859493abf0d2706068ddaaace5656dc0d4d/frontend/src diff --git a/plugin/src/kiali/components/Envoy/tables/ClusterTable.tsx b/plugin/src/kiali/components/Envoy/tables/ClusterTable.tsx index f58a90a7..4891915d 100644 --- a/plugin/src/kiali/components/Envoy/tables/ClusterTable.tsx +++ b/plugin/src/kiali/components/Envoy/tables/ClusterTable.tsx @@ -13,7 +13,7 @@ import { kialiStyle } from 'styles/StyleUtils'; import { isParentKiosk } from '../../Kiosk/KioskActions'; import { SortableTh } from 'components/Table/SimpleTable'; import { t } from 'utils/I18nUtils'; -import { dicIstioTypeToGVK } from '../../../types/IstioConfigList'; +import { dicTypeToGVK, gvkType } from '../../../types/IstioConfigList'; export class ClusterTable implements SummaryTable { kiosk: string; @@ -271,7 +271,7 @@ export class ClusterTable implements SummaryTable { value.subset, value.direction, value.type, - istioConfigLink(value.destination_rule, dicIstioTypeToGVK['DestinationRule']) + istioConfigLink(value.destination_rule, dicTypeToGVK[gvkType.DestinationRule]) ] }; } diff --git a/plugin/src/kiali/components/Envoy/tables/RouteTable.tsx b/plugin/src/kiali/components/Envoy/tables/RouteTable.tsx index 627f0d33..d5ed1a33 100644 --- a/plugin/src/kiali/components/Envoy/tables/RouteTable.tsx +++ b/plugin/src/kiali/components/Envoy/tables/RouteTable.tsx @@ -12,7 +12,7 @@ import { KialiIcon } from 'config/KialiIcon'; import { kialiStyle } from 'styles/StyleUtils'; import { isParentKiosk } from '../../Kiosk/KioskActions'; import { SortableTh } from 'components/Table/SimpleTable'; -import { dicIstioTypeToGVK } from '../../../types/IstioConfigList'; +import { dicTypeToGVK, gvkType } from '../../../types/IstioConfigList'; export class RouteTable implements SummaryTable { kiosk: string; @@ -187,7 +187,7 @@ export class RouteTable implements SummaryTable { summary.name, serviceLink(summary.domains, this.namespaces, this.namespace, true, parentKiosk), summary.match, - istioConfigLink(summary.virtual_service, dicIstioTypeToGVK['VirtualService']) + istioConfigLink(summary.virtual_service, dicTypeToGVK[gvkType.VirtualService]) ] }; } diff --git a/plugin/src/kiali/components/IstioWizards/ServiceWizard.tsx b/plugin/src/kiali/components/IstioWizards/ServiceWizard.tsx index d23dd0d3..5204a03c 100644 --- a/plugin/src/kiali/components/IstioWizards/ServiceWizard.tsx +++ b/plugin/src/kiali/components/IstioWizards/ServiceWizard.tsx @@ -73,7 +73,7 @@ import { ConfigPreviewItem, IstioConfigPreview } from 'components/IstioConfigPre import { KialiIcon } from '../../config/KialiIcon'; import { ApiResponse } from 'types/Api'; import { t } from 'utils/I18nUtils'; -import { dicIstioTypeToGVK } from '../../types/IstioConfigList'; +import { dicTypeToGVK, gvkType } from '../../types/IstioConfigList'; import { getGVKTypeString } from '../../utils/IstioConfigUtils'; const emptyServiceWizardState = (fqdnServiceName: string): ServiceWizardState => { @@ -371,7 +371,7 @@ export class ServiceWizard extends React.Component { - const dr = items.filter(it => getGVKTypeString(it.objectGVK) === getGVKTypeString('DestinationRule'))[0]; - const gw = items.filter(it => getGVKTypeString(it.objectGVK) === getGVKTypeString('Gateway'))[0]; - const k8sgateway = items.filter(it => getGVKTypeString(it.objectGVK) === getGVKTypeString('K8sGateway'))[0]; - const pa = items.filter(it => getGVKTypeString(it.objectGVK) === getGVKTypeString('PeerAuthentication'))[0]; - const vs = items.filter(it => getGVKTypeString(it.objectGVK) === getGVKTypeString('VirtualService'))[0]; - const k8shttproute = items.filter(it => getGVKTypeString(it.objectGVK) === getGVKTypeString('K8sHTTPRoute'))[0]; - const k8sgrpcroute = items.filter(it => getGVKTypeString(it.objectGVK) === getGVKTypeString('K8sGRPCRoute'))[0]; + const dr = items.filter(it => getGVKTypeString(it.objectGVK) === getGVKTypeString(gvkType.DestinationRule))[0]; + const gw = items.filter(it => getGVKTypeString(it.objectGVK) === getGVKTypeString(gvkType.Gateway))[0]; + const k8sgateway = items.filter(it => getGVKTypeString(it.objectGVK) === getGVKTypeString(gvkType.K8sGateway))[0]; + const pa = items.filter(it => getGVKTypeString(it.objectGVK) === getGVKTypeString(gvkType.PeerAuthentication))[0]; + const vs = items.filter(it => getGVKTypeString(it.objectGVK) === getGVKTypeString(gvkType.VirtualService))[0]; + const k8shttproute = items.filter( + it => getGVKTypeString(it.objectGVK) === getGVKTypeString(gvkType.K8sHTTPRoute) + )[0]; + const k8sgrpcroute = items.filter( + it => getGVKTypeString(it.objectGVK) === getGVKTypeString(gvkType.K8sGRPCRoute) + )[0]; const previews: WizardPreviews = { dr: dr ? (dr.items[0] as DestinationRule) : undefined, @@ -783,19 +787,19 @@ export class ServiceWizard extends React.Component { +export const buildWorkloadInjectionPatch = (gvk: GroupVersionKind, enable: boolean, remove: boolean): string => { const patch = {}; // environments prefer to use the pod label over the annotation @@ -2375,7 +2377,7 @@ export const buildWorkloadInjectionPatch = (workloadType: string, enable: boolea labels[serverConfig.istioAnnotations.istioInjectionAnnotation] = remove ? null : enable ? 'true' : 'false'; const annotations = {}; annotations[serverConfig.istioAnnotations.istioInjectionAnnotation] = null; - if (workloadType === 'Pod') { + if (gvk.Kind === dicTypeToGVK[gvkType.Pod].Kind) { patch['labels'] = labels; patch['annotations'] = annotations; } else { diff --git a/plugin/src/kiali/components/IstioWizards/WorkloadWizardActionsDropdownGroup.tsx b/plugin/src/kiali/components/IstioWizards/WorkloadWizardActionsDropdownGroup.tsx index ae80ea7d..c2119c61 100644 --- a/plugin/src/kiali/components/IstioWizards/WorkloadWizardActionsDropdownGroup.tsx +++ b/plugin/src/kiali/components/IstioWizards/WorkloadWizardActionsDropdownGroup.tsx @@ -15,6 +15,8 @@ import { renderDisabledDropdownOption } from 'utils/DropdownUtils'; import { MessageType } from 'types/MessageCenter'; import { groupMenuStyle } from 'styles/DropdownStyles'; import { t } from 'utils/I18nUtils'; +import { getGVKTypeString } from '../../utils/IstioConfigUtils'; +import { gvkType } from '../../types/IstioConfigList'; type Props = { actionsLabel: boolean; @@ -34,11 +36,11 @@ export const WorkloadWizardActionsDropdownGroup: React.FunctionComponent case WIZARD_REMOVE_AUTO_INJECTION: const remove = key === WIZARD_REMOVE_AUTO_INJECTION; const enable = key === WIZARD_ENABLE_AUTO_INJECTION; - const jsonInjectionPatch = buildWorkloadInjectionPatch(props.workload.type, enable, remove); + const jsonInjectionPatch = buildWorkloadInjectionPatch(props.workload.gvk, enable, remove); API.updateWorkload( props.namespace, props.workload.name, - props.workload.type, + props.workload.gvk, jsonInjectionPatch, undefined, props.workload.cluster @@ -139,7 +141,7 @@ export const WorkloadWizardActionsDropdownGroup: React.FunctionComponent } // Annotations - if (props.annotations && props.workload.type === 'Deployment') { + if (props.annotations && getGVKTypeString(props.workload.gvk) === getGVKTypeString(gvkType.Deployment)) { const annotationsAction = ( = (props: Props) => { API.updateWorkload( props.namespace, props.workload.name, - props.workload.type, + props.workload.gvk, jsonInjectionPatch, 'json', props.workload.cluster @@ -92,7 +94,9 @@ export const WorkloadWizardDropdown: React.FC = (props: Props) => { // istio actions (serverConfig.kialiFeatureFlags.istioInjectionAction && !props.workload.isAmbient) || // annotations - props.workload.type === 'Deployment'; + getGVKTypeString(props.workload.gvk) === getGVKTypeString(gvkType.Deployment); + + const supportedWorkload = isGVKSupported(props.workload.gvk); const dropdown = ( = (props: Props) => { onClick={() => onActionsToggle(!isActionsOpen)} data-test="workload-actions-toggle" isExpanded={isActionsOpen} - isDisabled={!validActions} + isDisabled={!validActions || !supportedWorkload} > {t('Actions')} @@ -144,6 +148,13 @@ export const WorkloadWizardDropdown: React.FC = (props: Props) => { t('User does not have permission on this Workload'), dropdown ) + : !supportedWorkload + ? renderDisabledDropdownOption( + 'tooltip_wizard_actions', + TooltipPosition.top, + t('This type of workload is read-only'), + dropdown + ) : dropdown} ); diff --git a/plugin/src/kiali/components/Time/TimeDurationIndicator.tsx b/plugin/src/kiali/components/Time/TimeDurationIndicator.tsx index 3a1526d7..debd77cd 100644 --- a/plugin/src/kiali/components/Time/TimeDurationIndicator.tsx +++ b/plugin/src/kiali/components/Time/TimeDurationIndicator.tsx @@ -26,6 +26,7 @@ type ReduxDispatchProps = { type Props = ReduxStateProps & ReduxDispatchProps & { + id?: string; isDuration?: boolean; onClick?: () => void; setDuration: (duration: DurationInSeconds) => void; @@ -80,6 +81,7 @@ class TimeDurationIndicatorComponent extends React.PureComponent { render(): React.ReactNode { return ( { } > - diff --git a/plugin/src/kiali/components/Time/TimeDurationModal.tsx b/plugin/src/kiali/components/Time/TimeDurationModal.tsx index e3d6e680..4c162af1 100644 --- a/plugin/src/kiali/components/Time/TimeDurationModal.tsx +++ b/plugin/src/kiali/components/Time/TimeDurationModal.tsx @@ -72,6 +72,7 @@ export const TimeDurationModal: React.FC = (props: Props) => { return ( = { export const GVKToBadge: { [gvk: string]: PFBadgeType } = {}; -Object.keys(dicIstioTypeToGVK).forEach(key => { - GVKToBadge[getGVKTypeString(key)] = PFBadges[key]; +Object.values(dicTypeToGVK).forEach(value => { + GVKToBadge[getGVKTypeString(value)] = PFBadges[kindToStringIncludeK8s(value.Group, value.Kind)]; }); export type Resource = { diff --git a/plugin/src/kiali/components/VirtualList/Renderers.tsx b/plugin/src/kiali/components/VirtualList/Renderers.tsx index e7c01a54..b76819be 100644 --- a/plugin/src/kiali/components/VirtualList/Renderers.tsx +++ b/plugin/src/kiali/components/VirtualList/Renderers.tsx @@ -443,7 +443,7 @@ export const workloadType: Renderer = (item: WorkloadListItem) key={`VirtuaItem_WorkloadType_${item.namespace}_${item.name}`} style={{ verticalAlign: 'middle' }} > - {item.type} + {item.gvk.Kind} ); }; diff --git a/plugin/src/kiali/components/VirtualList/VirtualItem.tsx b/plugin/src/kiali/components/VirtualList/VirtualItem.tsx index e4d233ec..72d307c4 100644 --- a/plugin/src/kiali/components/VirtualList/VirtualItem.tsx +++ b/plugin/src/kiali/components/VirtualList/VirtualItem.tsx @@ -68,6 +68,8 @@ export class VirtualItem extends React.Component { instanceType: InstanceType.Workload, health: emptyWorkHealth, name: 'reviews-v1', - type: 'Deployment', + gvk: dicTypeToGVK[gvkType.Deployment], istioSidecar: false, isAmbient: false, isGateway: false, @@ -300,7 +301,7 @@ describe('LabelFilter', () => { instanceType: InstanceType.Workload, health: emptyWorkHealth, name: 'reviews-v2', - type: 'Deployment', + gvk: dicTypeToGVK[gvkType.Deployment], istioSidecar: false, isAmbient: false, isGateway: false, @@ -315,7 +316,7 @@ describe('LabelFilter', () => { instanceType: InstanceType.Workload, health: emptyWorkHealth, name: 'reviews-v3', - type: 'Deployment', + gvk: dicTypeToGVK[gvkType.Deployment], istioSidecar: false, isAmbient: false, isGateway: false, diff --git a/plugin/src/kiali/hooks/services.ts b/plugin/src/kiali/hooks/services.ts index 4c2a17b5..7ca9acaa 100644 --- a/plugin/src/kiali/hooks/services.ts +++ b/plugin/src/kiali/hooks/services.ts @@ -8,6 +8,7 @@ import { DecoratedGraphNodeData, NodeType } from '../types/Graph'; import * as AlertUtils from '../utils/AlertUtils'; import { ApiError } from 'types/Api'; import { getGVKTypeString } from '../utils/IstioConfigUtils'; +import { gvkType } from '../types/IstioConfigList'; type ServiceDetailHookInfo = readonly [ ServiceDetailsInfo | null, @@ -38,10 +39,10 @@ const useServiceDetail = ( setIsLoading(true); // Mark as loading let getDetailPromise = API.getServiceDetail(namespace, serviceName, false, cluster, duration); - let getGwPromise = API.getAllIstioConfigs([getGVKTypeString('Gateway')], false, '', '', cluster); + let getGwPromise = API.getAllIstioConfigs([getGVKTypeString(gvkType.Gateway)], false, '', '', cluster); let getPeerAuthsPromise = API.getIstioConfig( namespace, - [getGVKTypeString('PeerAuthentication')], + [getGVKTypeString(gvkType.PeerAuthentication)], false, '', '', @@ -53,10 +54,12 @@ const useServiceDetail = ( .then(results => { setServiceDetails(results[0]); setGateways( - getGatewaysAsList(filterAutogeneratedGateways(results[1].data.resources[getGVKTypeString('Gateway')])).sort() + getGatewaysAsList( + filterAutogeneratedGateways(results[1].data.resources[getGVKTypeString(gvkType.Gateway)]) + ).sort() ); - setPeerAuthentications(results[2].data.resources[getGVKTypeString('PeerAuthentication')]); + setPeerAuthentications(results[2].data.resources[getGVKTypeString(gvkType.PeerAuthentication)]); setFetchError(null); setIsLoading(false); }) diff --git a/plugin/src/kiali/locales/zh/translation.json b/plugin/src/kiali/locales/zh/translation.json index 46be2683..2a121861 100644 --- a/plugin/src/kiali/locales/zh/translation.json +++ b/plugin/src/kiali/locales/zh/translation.json @@ -316,6 +316,7 @@ "This chart shows memory consumption for the istiod {{memoryMetricSource}}": "This chart shows memory consumption for the istiod {{memoryMetricSource}}", "This rule is not accessible.": "该规则将无法生效。", "This shows the meshConfig.outboundTrafficPolicy.mode setting. It controls the sidecar handling of requests to external services (services not defined in Istio’s internal service registry). When set to ALLOW_ANY the Istio proxy performs a passthrough to the unknown services. When set to REGISTRY_ONLY the Istio proxy blocks requets to hosts without a defined HTTP service or service entry.": "This shows the meshConfig.outboundTrafficPolicy.mode setting. It controls the sidecar handling of requests to external services (services not defined in Istio’s internal service registry). When set to ALLOW_ANY the Istio proxy performs a passthrough to the unknown services. When set to REGISTRY_ONLY the Istio proxy blocks requets to hosts without a defined HTTP service or service entry.", + "This type of workload is read-only": "This type of workload is read-only", "Time duration": "Time duration", "Time range": "Time range", "Time Range": "Time Range", diff --git a/plugin/src/kiali/pages/Graph/SummaryLink.tsx b/plugin/src/kiali/pages/Graph/SummaryLink.tsx index d14a6cfe..eefbf3e1 100644 --- a/plugin/src/kiali/pages/Graph/SummaryLink.tsx +++ b/plugin/src/kiali/pages/Graph/SummaryLink.tsx @@ -17,7 +17,7 @@ import { KialiPageLink } from 'components/Link/KialiPageLink'; import { kialiStyle } from 'styles/StyleUtils'; import { t } from 'utils/I18nUtils'; import { ExternalLinkAltIcon } from '@patternfly/react-icons'; -import { dicIstioTypeToGVK } from '../../types/IstioConfigList'; +import { dicTypeToGVK, gvkType } from '../../types/IstioConfigList'; interface LinkInfo { displayName: string; @@ -186,7 +186,7 @@ export const getLink = ( break; case NodeType.SERVICE: if (nodeData.isServiceEntry) { - const seGVK = dicIstioTypeToGVK['ServiceEntry']; + const seGVK = dicTypeToGVK[gvkType.ServiceEntry]; link = `/namespaces/${encodeURIComponent(nodeData.isServiceEntry.namespace)}/istio/${seGVK.Group}/${ seGVK.Version }/${seGVK.Kind}/${encodeURIComponent(service!)}`; diff --git a/plugin/src/kiali/pages/Graph/SummaryPanelNode.tsx b/plugin/src/kiali/pages/Graph/SummaryPanelNode.tsx index 530b9bf3..3f9d936a 100644 --- a/plugin/src/kiali/pages/Graph/SummaryPanelNode.tsx +++ b/plugin/src/kiali/pages/Graph/SummaryPanelNode.tsx @@ -40,7 +40,7 @@ import { groupMenuStyle, kebabToggleStyle } from 'styles/DropdownStyles'; import { isMultiCluster, serverConfig } from '../../config'; import { panelBodyStyle, panelHeadingStyle, panelStyle } from './SummaryPanelStyle'; import { renderWaypoint } from '../../components/DetailDescription/DetailDescription'; -import { dicIstioTypeToGVK } from '../../types/IstioConfigList'; +import { dicTypeToGVK, gvkType } from '../../types/IstioConfigList'; type SummaryPanelNodeState = { isActionOpen: boolean; @@ -256,7 +256,7 @@ export class SummaryPanelNodeComponent extends React.Component{renderBadgedLink(nodeData, NodeType.WORKLOAD)}; } - const weGVK = dicIstioTypeToGVK['WorkloadEntry']; + const weGVK = dicTypeToGVK[gvkType.WorkloadEntry]; const workloadEntryLinks = nodeData.hasWorkloadEntry.map(we => (
diff --git a/plugin/src/kiali/pages/GraphPF/GraphPF.tsx b/plugin/src/kiali/pages/GraphPF/GraphPF.tsx index 1b6c2e74..2ef9ce28 100644 --- a/plugin/src/kiali/pages/GraphPF/GraphPF.tsx +++ b/plugin/src/kiali/pages/GraphPF/GraphPF.tsx @@ -82,7 +82,7 @@ const DEFAULT_NODE_SIZE = 40; const ZOOM_IN = 4 / 3; const ZOOM_OUT = 3 / 4; -export const FIT_PADDING = 80; +export const FIT_PADDING = 90; export enum LayoutName { BreadthFirst = 'BreadthFirst', @@ -322,13 +322,14 @@ const TopologyContent: React.FC<{ } if (layoutInProgress !== LayoutType.LayoutNoFit) { - // On a resize, delay fit to ensure that the canvas size updates before the fit + controller.getGraph().fit(FIT_PADDING); + + // On a resize, perform a delayed second fit, this one is performed [hopefully] after + // the canvas size is updated (which needs to happen in the underlying PFT code) if (layoutInProgress === LayoutType.Resize) { setTimeout(() => { controller.getGraph().fit(FIT_PADDING); - }, 250); - } else { - controller.getGraph().fit(FIT_PADDING); + }, 500); } } @@ -886,6 +887,10 @@ const TopologyContent: React.FC<{ zoomOutCallback: () => { controller && controller.getGraph().scaleBy(ZOOM_OUT); }, + // currently unused + fitToScreenCallback: () => { + controller.getGraph().fit(FIT_PADDING); + }, resetViewCallback: () => { graphLayout(controller, LayoutType.Layout); }, diff --git a/plugin/src/kiali/pages/GraphPF/components/stylesComponentFactory.tsx b/plugin/src/kiali/pages/GraphPF/components/stylesComponentFactory.tsx index 744ddf84..92631a2b 100644 --- a/plugin/src/kiali/pages/GraphPF/components/stylesComponentFactory.tsx +++ b/plugin/src/kiali/pages/GraphPF/components/stylesComponentFactory.tsx @@ -35,6 +35,7 @@ import { ServiceDetailsInfo } from 'types/ServiceInfo'; import { kialiStyle } from 'styles/StyleUtils'; import { DropdownGroup, DropdownItem } from '@patternfly/react-core'; import { getGVKTypeString } from '../../../utils/IstioConfigUtils'; +import { gvkType } from '../../../types/IstioConfigList'; type ContextMenuOptionPF = ContextMenuOption & { altClickHandler?: (node: GraphElement, kiosk: string) => void; @@ -121,11 +122,18 @@ const nodeContextMenu = (node: GraphElement, kiosk: string): Promise((resolve, reject) => { @@ -133,9 +141,9 @@ const nodeContextMenu = (node: GraphElement, kiosk: string): Promise { const serviceDetails = results[0]; const gateways = getGatewaysAsList( - filterAutogeneratedGateways(results[1].data.resources[getGVKTypeString('Gateway')]) + filterAutogeneratedGateways(results[1].data.resources[getGVKTypeString(gvkType.Gateway)]) ).sort(); - const peerAuthentications = results[2].data.resources[getGVKTypeString('PeerAuthentication')]; + const peerAuthentications = results[2].data.resources[getGVKTypeString(gvkType.PeerAuthentication)]; items.push( item.name); const istioTypeFilters = getFilterSelectedValues(IstioConfigListFilters.istioTypeFilter, activeFilters).map(value => - getGVKTypeString(value) + getGVKTypeString(gvkType[value]) ); const istioNameFilters = getFilterSelectedValues(IstioConfigListFilters.istioNameFilter, activeFilters); diff --git a/plugin/src/kiali/pages/IstioConfigList/__tests__/IstioConfigListComponent.test.ts b/plugin/src/kiali/pages/IstioConfigList/__tests__/IstioConfigListComponent.test.ts index cd1ee30b..c3a6ced8 100644 --- a/plugin/src/kiali/pages/IstioConfigList/__tests__/IstioConfigListComponent.test.ts +++ b/plugin/src/kiali/pages/IstioConfigList/__tests__/IstioConfigListComponent.test.ts @@ -1,6 +1,7 @@ import { - dicIstioTypeToGVK, + dicTypeToGVK, filterByName, + gvkType, IstioConfigItem, IstioConfigList, toIstioItems @@ -15,40 +16,42 @@ const mockIstioConfigList = (names: string[]): IstioConfigList => { permissions: {}, resources: {} }; - Object.keys(dicIstioTypeToGVK).forEach(index => { + Object.values(dicTypeToGVK).forEach(index => { const key = getGVKTypeString(index); testData.resources[key] = []; }); names.forEach(name => { - testData.resources[getGVKTypeString('AuthorizationPolicy')].push({ + testData.resources[getGVKTypeString(gvkType.AuthorizationPolicy)].push({ metadata: { name: `${name}0` }, spec: {}, - kind: dicIstioTypeToGVK['AuthorizationPolicy'].Kind, - apiVersion: `${dicIstioTypeToGVK['AuthorizationPolicy'].Group}/${dicIstioTypeToGVK['AuthorizationPolicy'].Version}` + kind: dicTypeToGVK[gvkType.AuthorizationPolicy].Kind, + apiVersion: `${dicTypeToGVK[gvkType.AuthorizationPolicy].Group}/${ + dicTypeToGVK[gvkType.AuthorizationPolicy].Version + }` }); - testData.resources[getGVKTypeString('DestinationRule')].push({ + testData.resources[getGVKTypeString(gvkType.DestinationRule)].push({ metadata: { name: `${name}1` }, spec: {}, - kind: dicIstioTypeToGVK['DestinationRule'].Kind, - apiVersion: `${dicIstioTypeToGVK['DestinationRule'].Group}/${dicIstioTypeToGVK['DestinationRule'].Version}` + kind: dicTypeToGVK[gvkType.DestinationRule].Kind, + apiVersion: `${dicTypeToGVK[gvkType.DestinationRule].Group}/${dicTypeToGVK[gvkType.DestinationRule].Version}` }); - testData.resources[getGVKTypeString('Gateway')].push({ + testData.resources[getGVKTypeString(gvkType.Gateway)].push({ metadata: { name: `${name}2` }, spec: {}, - kind: dicIstioTypeToGVK['Gateway'].Kind, - apiVersion: `${dicIstioTypeToGVK['Gateway'].Group}/${dicIstioTypeToGVK['Gateway'].Version}` + kind: dicTypeToGVK[gvkType.Gateway].Kind, + apiVersion: `${dicTypeToGVK[gvkType.Gateway].Group}/${dicTypeToGVK[gvkType.Gateway].Version}` }); - testData.resources[getGVKTypeString('ServiceEntry')].push({ + testData.resources[getGVKTypeString(gvkType.ServiceEntry)].push({ metadata: { name: `${name}3` }, spec: {}, - kind: dicIstioTypeToGVK['ServiceEntry'].Kind, - apiVersion: `${dicIstioTypeToGVK['ServiceEntry'].Group}/${dicIstioTypeToGVK['ServiceEntry'].Version}` + kind: dicTypeToGVK[gvkType.ServiceEntry].Kind, + apiVersion: `${dicTypeToGVK[gvkType.ServiceEntry].Group}/${dicTypeToGVK[gvkType.ServiceEntry].Version}` }); - testData.resources[getGVKTypeString('VirtualService')].push({ + testData.resources[getGVKTypeString(gvkType.VirtualService)].push({ metadata: { name: `${name}4` }, spec: {}, - kind: dicIstioTypeToGVK['VirtualService'].Kind, - apiVersion: `${dicIstioTypeToGVK['VirtualService'].Group}/${dicIstioTypeToGVK['VirtualService'].Version}` + kind: dicTypeToGVK[gvkType.VirtualService].Kind, + apiVersion: `${dicTypeToGVK[gvkType.VirtualService].Group}/${dicTypeToGVK[gvkType.VirtualService].Version}` }); }); return testData; @@ -60,30 +63,30 @@ describe('IstioConfigList#filterByName', () => { it('should filter IstioConfigList by name', () => { let filtered = filterByName(unfiltered, ['white', 'red']); expect(filtered).toBeDefined(); - expect(filtered.resources[getGVKTypeString('Gateway')].length).toBe(2); - expect(filtered.resources[getGVKTypeString('VirtualService')].length).toBe(2); - expect(filtered.resources[getGVKTypeString('DestinationRule')].length).toBe(2); - expect(filtered.resources[getGVKTypeString('ServiceEntry')].length).toBe(2); + expect(filtered.resources[getGVKTypeString(gvkType.Gateway)].length).toBe(2); + expect(filtered.resources[getGVKTypeString(gvkType.VirtualService)].length).toBe(2); + expect(filtered.resources[getGVKTypeString(gvkType.DestinationRule)].length).toBe(2); + expect(filtered.resources[getGVKTypeString(gvkType.ServiceEntry)].length).toBe(2); - expect(filtered.resources[getGVKTypeString('AuthorizationPolicy')][0].metadata.name).toBe('white0'); - expect(filtered.resources[getGVKTypeString('DestinationRule')][0].metadata.name).toBe('white1'); - expect(filtered.resources[getGVKTypeString('ServiceEntry')][0].metadata.name).toBe('white3'); - expect(filtered.resources[getGVKTypeString('VirtualService')][0].metadata.name).toBe('white4'); + expect(filtered.resources[getGVKTypeString(gvkType.AuthorizationPolicy)][0].metadata.name).toBe('white0'); + expect(filtered.resources[getGVKTypeString(gvkType.DestinationRule)][0].metadata.name).toBe('white1'); + expect(filtered.resources[getGVKTypeString(gvkType.ServiceEntry)][0].metadata.name).toBe('white3'); + expect(filtered.resources[getGVKTypeString(gvkType.VirtualService)][0].metadata.name).toBe('white4'); filtered = filterByName(unfiltered, ['bad']); expect(filtered).toBeDefined(); - expect(filtered.resources[getGVKTypeString('Gateway')].length).toBe(0); - expect(filtered.resources[getGVKTypeString('VirtualService')].length).toBe(0); - expect(filtered.resources[getGVKTypeString('DestinationRule')].length).toBe(0); - expect(filtered.resources[getGVKTypeString('ServiceEntry')].length).toBe(0); - expect(filtered.resources[getGVKTypeString('WasmPlugin')].length).toBe(0); - expect(filtered.resources[getGVKTypeString('Telemetry')].length).toBe(0); - expect(filtered.resources[getGVKTypeString('K8sGateway')].length).toBe(0); - expect(filtered.resources[getGVKTypeString('K8sGRPCRoute')].length).toBe(0); - expect(filtered.resources[getGVKTypeString('K8sHTTPRoute')].length).toBe(0); - expect(filtered.resources[getGVKTypeString('K8sReferenceGrant')].length).toBe(0); - expect(filtered.resources[getGVKTypeString('K8sTCPRoute')].length).toBe(0); - expect(filtered.resources[getGVKTypeString('K8sTLSRoute')].length).toBe(0); + expect(filtered.resources[getGVKTypeString(gvkType.Gateway)].length).toBe(0); + expect(filtered.resources[getGVKTypeString(gvkType.VirtualService)].length).toBe(0); + expect(filtered.resources[getGVKTypeString(gvkType.DestinationRule)].length).toBe(0); + expect(filtered.resources[getGVKTypeString(gvkType.ServiceEntry)].length).toBe(0); + expect(filtered.resources[getGVKTypeString(gvkType.WasmPlugin)].length).toBe(0); + expect(filtered.resources[getGVKTypeString(gvkType.Telemetry)].length).toBe(0); + expect(filtered.resources[getGVKTypeString(gvkType.K8sGateway)].length).toBe(0); + expect(filtered.resources[getGVKTypeString(gvkType.K8sGRPCRoute)].length).toBe(0); + expect(filtered.resources[getGVKTypeString(gvkType.K8sHTTPRoute)].length).toBe(0); + expect(filtered.resources[getGVKTypeString(gvkType.K8sReferenceGrant)].length).toBe(0); + expect(filtered.resources[getGVKTypeString(gvkType.K8sTCPRoute)].length).toBe(0); + expect(filtered.resources[getGVKTypeString(gvkType.K8sTLSRoute)].length).toBe(0); }); }); @@ -94,11 +97,11 @@ describe('IstioConfigListContainer#toIstioItems', () => { expect(istioItems).toBeDefined(); expect(istioItems.length).toBe(15); expect(istioItems[0].resource).toBeDefined(); - expect(istioItems[0].resource.kind).toBe(dicIstioTypeToGVK['AuthorizationPolicy'].Kind); + expect(istioItems[0].resource.kind).toBe(dicTypeToGVK[gvkType.AuthorizationPolicy].Kind); expect(istioItems[3].resource).toBeDefined(); - expect(istioItems[3].resource.kind).toBe(dicIstioTypeToGVK['DestinationRule'].Kind); + expect(istioItems[3].resource.kind).toBe(dicTypeToGVK[gvkType.DestinationRule].Kind); expect(istioItems[6].resource).toBeDefined(); - expect(istioItems[6].resource.kind).toBe(dicIstioTypeToGVK['Gateway'].Kind); + expect(istioItems[6].resource.kind).toBe(dicTypeToGVK[gvkType.Gateway].Kind); }); }); @@ -114,17 +117,17 @@ describe('IstioConfigComponent#sortIstioItems', () => { const first = sorted[0]; expect(first.resource).toBeDefined(); - expect(first.resource.kind).toBe(dicIstioTypeToGVK['AuthorizationPolicy'].Kind); + expect(first.resource.kind).toBe(dicTypeToGVK[gvkType.AuthorizationPolicy].Kind); expect(first.resource.metadata.name).toBe('blue0'); const second = sorted[1]; expect(second.resource).toBeDefined(); - expect(second.resource.kind).toBe(dicIstioTypeToGVK['DestinationRule'].Kind); + expect(second.resource.kind).toBe(dicTypeToGVK[gvkType.DestinationRule].Kind); expect(second.resource.metadata.name).toBe('blue1'); const last = sorted[14]; expect(last.resource).toBeDefined(); - expect(last.resource.kind).toBe(dicIstioTypeToGVK['VirtualService'].Kind); + expect(last.resource.kind).toBe(dicTypeToGVK[gvkType.VirtualService].Kind); expect(last.resource.metadata.name).toBe('white4'); }); @@ -140,12 +143,12 @@ describe('IstioConfigComponent#sortIstioItems', () => { const first = sorted[0]; expect(first.resource).toBeDefined(); - expect(first.resource.kind).toBe(dicIstioTypeToGVK['VirtualService'].Kind); + expect(first.resource.kind).toBe(dicTypeToGVK[gvkType.VirtualService].Kind); expect(first.resource.metadata.name).toBe('white4'); const last = sorted[14]; expect(last.resource).toBeDefined(); - expect(last.resource.kind).toBe(dicIstioTypeToGVK['AuthorizationPolicy'].Kind); + expect(last.resource.kind).toBe(dicTypeToGVK[gvkType.AuthorizationPolicy].Kind); expect(last.resource.metadata.name).toBe('blue0'); }); @@ -160,17 +163,17 @@ describe('IstioConfigComponent#sortIstioItems', () => { const first = sorted[0]; expect(first.resource).toBeDefined(); - expect(first.resource.kind).toBe(dicIstioTypeToGVK['AuthorizationPolicy'].Kind); + expect(first.resource.kind).toBe(dicTypeToGVK[gvkType.AuthorizationPolicy].Kind); expect(first.resource.metadata.name).toBe('blue0'); const second = sorted[3]; expect(second.resource).toBeDefined(); - expect(second.resource.kind).toBe(dicIstioTypeToGVK['DestinationRule'].Kind); + expect(second.resource.kind).toBe(dicTypeToGVK[gvkType.DestinationRule].Kind); expect(second.resource.metadata.name).toBe('blue1'); const last = sorted[14]; expect(last.resource).toBeDefined(); - expect(last.resource.kind).toBe(dicIstioTypeToGVK['VirtualService'].Kind); + expect(last.resource.kind).toBe(dicTypeToGVK[gvkType.VirtualService].Kind); expect(last.resource.metadata.name).toBe('white4'); }); @@ -185,7 +188,7 @@ describe('IstioConfigComponent#sortIstioItems', () => { const first = sorted[0]; expect(first.resource).toBeDefined(); - expect(first.resource.kind).toBe(dicIstioTypeToGVK['VirtualService'].Kind); + expect(first.resource.kind).toBe(dicTypeToGVK[gvkType.VirtualService].Kind); expect(first.resource.metadata.name).toBe('white4'); }); }); diff --git a/plugin/src/kiali/pages/IstioConfigNew/IstioConfigNewPage.tsx b/plugin/src/kiali/pages/IstioConfigNew/IstioConfigNewPage.tsx index 7c925b8c..2665a6a8 100644 --- a/plugin/src/kiali/pages/IstioConfigNew/IstioConfigNewPage.tsx +++ b/plugin/src/kiali/pages/IstioConfigNew/IstioConfigNewPage.tsx @@ -72,7 +72,7 @@ import { NamespaceDropdown } from '../../components/Dropdown/NamespaceDropdown'; import { Labels } from '../../components/Label/Labels'; import { WizardLabels } from '../../components/IstioWizards/WizardLabels'; import { isParentKiosk, kioskContextMenuAction } from 'components/Kiosk/KioskActions'; -import { dicIstioTypeToGVK } from '../../types/IstioConfigList'; +import { dicTypeToGVK, gvkType } from '../../types/IstioConfigList'; import { getGVKTypeString } from '../../utils/IstioConfigUtils'; import { GroupVersionKind } from '../../types/IstioObjects'; @@ -126,14 +126,14 @@ const editStyle = kialiStyle({ // Used in the Istio Config list Actions export const NEW_ISTIO_RESOURCE = [ - { value: dicIstioTypeToGVK['AuthorizationPolicy'], disabled: false }, - { value: dicIstioTypeToGVK['Gateway'], disabled: false }, - { value: dicIstioTypeToGVK['K8sGateway'], disabled: false }, - { value: dicIstioTypeToGVK['K8sReferenceGrant'], disabled: false }, - { value: dicIstioTypeToGVK['PeerAuthentication'], disabled: false }, - { value: dicIstioTypeToGVK['RequestAuthentication'], disabled: false }, - { value: dicIstioTypeToGVK['ServiceEntry'], disabled: false }, - { value: dicIstioTypeToGVK['Sidecar'], disabled: false } + { value: dicTypeToGVK[gvkType.AuthorizationPolicy], disabled: false }, + { value: dicTypeToGVK[gvkType.Gateway], disabled: false }, + { value: dicTypeToGVK[gvkType.K8sGateway], disabled: false }, + { value: dicTypeToGVK[gvkType.K8sReferenceGrant], disabled: false }, + { value: dicTypeToGVK[gvkType.PeerAuthentication], disabled: false }, + { value: dicTypeToGVK[gvkType.RequestAuthentication], disabled: false }, + { value: dicTypeToGVK[gvkType.ServiceEntry], disabled: false }, + { value: dicTypeToGVK[gvkType.Sidecar], disabled: false } ]; const initState = (): State => ({ @@ -336,7 +336,7 @@ class IstioConfigNewPageComponent extends React.Component { const items: ConfigPreviewItem[] = []; this.props.activeNamespaces.forEach(ns => { switch (getGVKTypeString(this.props.objectGVK)) { - case getGVKTypeString('AuthorizationPolicy'): + case getGVKTypeString(gvkType.AuthorizationPolicy): items.push({ title: 'Authorization Policy', objectGVK: this.props.objectGVK, @@ -351,7 +351,7 @@ class IstioConfigNewPageComponent extends React.Component { ] }); break; - case getGVKTypeString('Gateway'): + case getGVKTypeString(gvkType.Gateway): items.push({ title: 'Gateway', objectGVK: this.props.objectGVK, @@ -360,7 +360,7 @@ class IstioConfigNewPageComponent extends React.Component { ] }); break; - case getGVKTypeString('K8sGateway'): + case getGVKTypeString(gvkType.K8sGateway): items.push({ title: 'K8s Gateway', objectGVK: this.props.objectGVK, @@ -375,7 +375,7 @@ class IstioConfigNewPageComponent extends React.Component { ] }); break; - case getGVKTypeString('K8sReferenceGrant'): + case getGVKTypeString(gvkType.K8sReferenceGrant): items.push({ title: 'K8s Reference Grant', objectGVK: this.props.objectGVK, @@ -390,7 +390,7 @@ class IstioConfigNewPageComponent extends React.Component { ] }); break; - case getGVKTypeString('PeerAuthentication'): + case getGVKTypeString(gvkType.PeerAuthentication): items.push({ title: 'Peer Authentication', objectGVK: this.props.objectGVK, @@ -405,7 +405,7 @@ class IstioConfigNewPageComponent extends React.Component { ] }); break; - case getGVKTypeString('RequestAuthentication'): + case getGVKTypeString(gvkType.RequestAuthentication): items.push({ title: 'Request Authentication', objectGVK: this.props.objectGVK, @@ -420,7 +420,7 @@ class IstioConfigNewPageComponent extends React.Component { ] }); break; - case getGVKTypeString('ServiceEntry'): + case getGVKTypeString(gvkType.ServiceEntry): items.push({ title: 'Service Entry', objectGVK: this.props.objectGVK, @@ -435,7 +435,7 @@ class IstioConfigNewPageComponent extends React.Component { ] }); break; - case getGVKTypeString('Sidecar'): + case getGVKTypeString(gvkType.Sidecar): items.push({ title: 'Sidecar', objectGVK: this.props.objectGVK, @@ -465,21 +465,21 @@ class IstioConfigNewPageComponent extends React.Component { isIstioFormValid = (): boolean => { switch (getGVKTypeString(this.props.objectGVK)) { - case getGVKTypeString('AuthorizationPolicy'): + case getGVKTypeString(gvkType.AuthorizationPolicy): return isAuthorizationPolicyStateValid(this.state.authorizationPolicy); - case getGVKTypeString('Gateway'): + case getGVKTypeString(gvkType.Gateway): return isGatewayStateValid(this.state.gateway); - case getGVKTypeString('K8sGateway'): + case getGVKTypeString(gvkType.K8sGateway): return isK8sGatewayStateValid(this.state.k8sGateway); - case getGVKTypeString('K8sReferenceGrant'): + case getGVKTypeString(gvkType.K8sReferenceGrant): return isK8sReferenceGrantStateValid(this.state.k8sReferenceGrant); - case getGVKTypeString('PeerAuthentication'): + case getGVKTypeString(gvkType.PeerAuthentication): return isPeerAuthenticationStateValid(this.state.peerAuthentication); - case getGVKTypeString('RequestAuthentication'): + case getGVKTypeString(gvkType.RequestAuthentication): return isRequestAuthenticationStateValid(this.state.requestAuthentication); - case getGVKTypeString('ServiceEntry'): + case getGVKTypeString(gvkType.ServiceEntry): return isServiceEntryValid(this.state.serviceEntry); - case getGVKTypeString('Sidecar'): + case getGVKTypeString(gvkType.Sidecar): return isSidecarStateValid(this.state.sidecar); default: return false; @@ -625,47 +625,47 @@ class IstioConfigNewPageComponent extends React.Component { )} - {getGVKTypeString(this.props.objectGVK) === getGVKTypeString('AuthorizationPolicy') && ( + {getGVKTypeString(this.props.objectGVK) === getGVKTypeString(gvkType.AuthorizationPolicy) && ( )} - {getGVKTypeString(this.props.objectGVK) === getGVKTypeString('Gateway') && ( + {getGVKTypeString(this.props.objectGVK) === getGVKTypeString(gvkType.Gateway) && ( )} - {getGVKTypeString(this.props.objectGVK) === getGVKTypeString('K8sGateway') && ( + {getGVKTypeString(this.props.objectGVK) === getGVKTypeString(gvkType.K8sGateway) && ( )} - {getGVKTypeString(this.props.objectGVK) === getGVKTypeString('K8sReferenceGrant') && ( + {getGVKTypeString(this.props.objectGVK) === getGVKTypeString(gvkType.K8sReferenceGrant) && ( )} - {getGVKTypeString(this.props.objectGVK) === getGVKTypeString('PeerAuthentication') && ( + {getGVKTypeString(this.props.objectGVK) === getGVKTypeString(gvkType.PeerAuthentication) && ( )} - {getGVKTypeString(this.props.objectGVK) === getGVKTypeString('RequestAuthentication') && ( + {getGVKTypeString(this.props.objectGVK) === getGVKTypeString(gvkType.RequestAuthentication) && ( )} - {getGVKTypeString(this.props.objectGVK) === getGVKTypeString('ServiceEntry') && ( + {getGVKTypeString(this.props.objectGVK) === getGVKTypeString(gvkType.ServiceEntry) && ( )} - {getGVKTypeString(this.props.objectGVK) === getGVKTypeString('Sidecar') && ( + {getGVKTypeString(this.props.objectGVK) === getGVKTypeString(gvkType.Sidecar) && ( )} diff --git a/plugin/src/kiali/pages/IstioConfigNew/K8sReferenceGrantForm.tsx b/plugin/src/kiali/pages/IstioConfigNew/K8sReferenceGrantForm.tsx index 312c7bae..39ce521b 100644 --- a/plugin/src/kiali/pages/IstioConfigNew/K8sReferenceGrantForm.tsx +++ b/plugin/src/kiali/pages/IstioConfigNew/K8sReferenceGrantForm.tsx @@ -7,14 +7,14 @@ import { namespaceItemsSelector } from '../../store/Selectors'; import { KialiDispatch } from '../../types/Redux'; import { NamespaceThunkActions } from '../../actions/NamespaceThunkActions'; import { connect } from 'react-redux'; -import { dicIstioTypeToGVK } from '../../types/IstioConfigList'; +import { dicTypeToGVK, gvkType } from '../../types/IstioConfigList'; export const FROM_KINDS = [ - dicIstioTypeToGVK['K8sHTTPRoute'], - dicIstioTypeToGVK['K8sGateway'], - dicIstioTypeToGVK['K8sGRPCRoute'], - dicIstioTypeToGVK['K8sTCPRoute'], - dicIstioTypeToGVK['K8sTLSRoute'] + dicTypeToGVK[gvkType.K8sHTTPRoute], + dicTypeToGVK[gvkType.K8sGateway], + dicTypeToGVK[gvkType.K8sGRPCRoute], + dicTypeToGVK[gvkType.K8sTCPRoute], + dicTypeToGVK[gvkType.K8sTLSRoute] ]; export const TO_KINDS = { @@ -70,7 +70,7 @@ export class K8sReferenceGrantFormComponent extends React.Component { this.setState( { - from: [{ group: dicIstioTypeToGVK[`K8s${value}`].Group, kind: value, namespace: this.state.from[0].namespace }] + from: [{ group: dicTypeToGVK[`K8s${value}`].Group, kind: value, namespace: this.state.from[0].namespace }] }, () => this.props.onChange(this.state) ); diff --git a/plugin/src/kiali/pages/Mesh/Mesh.tsx b/plugin/src/kiali/pages/Mesh/Mesh.tsx index b1848e77..35c6c234 100644 --- a/plugin/src/kiali/pages/Mesh/Mesh.tsx +++ b/plugin/src/kiali/pages/Mesh/Mesh.tsx @@ -211,13 +211,14 @@ const TopologyContent: React.FC<{ } if (layoutInProgress !== LayoutType.LayoutNoFit) { - // On a resize, delay fit to ensure that the canvas size updates before the fit + controller.getGraph().fit(FIT_PADDING); + + // On a resize, perform a delayed second fit, this one is performed [hopefully] after + // the canvas size is updated (which needs to happen in the underlying PFT code) if (layoutInProgress === LayoutType.Resize) { setTimeout(() => { controller.getGraph().fit(FIT_PADDING); - }, 250); - } else { - controller.getGraph().fit(FIT_PADDING); + }, 500); } } @@ -553,6 +554,10 @@ const TopologyContent: React.FC<{ zoomOutCallback: () => { controller && controller.getGraph().scaleBy(ZOOM_OUT); }, + // currently unused + fitToScreenCallback: () => { + controller.getGraph().fit(FIT_PADDING); + }, resetViewCallback: () => { meshLayout(controller, LayoutType.Layout); }, diff --git a/plugin/src/kiali/pages/Overview/OverviewPage.tsx b/plugin/src/kiali/pages/Overview/OverviewPage.tsx index cda0b1cb..782222c9 100644 --- a/plugin/src/kiali/pages/Overview/OverviewPage.tsx +++ b/plugin/src/kiali/pages/Overview/OverviewPage.tsx @@ -73,7 +73,7 @@ import { ControlPlaneVersionBadge } from './ControlPlaneVersionBadge'; import { AmbientBadge } from '../../components/Ambient/AmbientBadge'; import { PFBadge, PFBadges } from 'components/Pf/PfBadges'; import { ApiError } from 'types/Api'; -import { IstioConfigList } from 'types/IstioConfigList'; +import { gvkType, IstioConfigList } from 'types/IstioConfigList'; import { t } from 'utils/I18nUtils'; import { getGVKTypeString } from '../../utils/IstioConfigUtils'; @@ -880,7 +880,7 @@ export class OverviewPageComponent extends React.Component }); } - const aps = nsInfo.istioConfig?.resources[getGVKTypeString('AuthorizationPolicy')] ?? []; + const aps = nsInfo.istioConfig?.resources[getGVKTypeString(gvkType.AuthorizationPolicy)] ?? []; const addAuthorizationAction = { isGroup: false, diff --git a/plugin/src/kiali/pages/Overview/OverviewTrafficPolicies.tsx b/plugin/src/kiali/pages/Overview/OverviewTrafficPolicies.tsx index 4dfea491..9728f000 100644 --- a/plugin/src/kiali/pages/Overview/OverviewTrafficPolicies.tsx +++ b/plugin/src/kiali/pages/Overview/OverviewTrafficPolicies.tsx @@ -15,7 +15,7 @@ import { buildNamespaceInjectionPatch, buildGraphSidecars } from 'components/IstioWizards/WizardActions'; -import { dicIstioTypeToGVK } from '../../types/IstioConfigList'; +import { dicTypeToGVK, gvkType } from '../../types/IstioConfigList'; import { getGVKTypeString } from '../../utils/IstioConfigUtils'; type OverviewTrafficPoliciesProps = { @@ -73,8 +73,8 @@ export class OverviewTrafficPolicies extends React.Component remove.map(key => delete sdc.metadata[key])); authorizationPolicies.map(ap => remove.map(key => delete ap.metadata[key])); @@ -83,8 +83,8 @@ export class OverviewTrafficPolicies extends React.Component this.fetchPermission(true) ); @@ -101,7 +101,7 @@ export class OverviewTrafficPolicies extends React.Component { - const permission = result.data[this.props.nsTarget][getGVKTypeString('AuthorizationPolicy')]; + const permission = result.data[this.props.nsTarget][getGVKTypeString(gvkType.AuthorizationPolicy)]; const disableOp = !(permission.create && permission.update && permission.delete); this.setState({ confirmationModal, @@ -183,10 +183,10 @@ export class OverviewTrafficPolicies extends React.Component - API.deleteIstioConfigDetail(ns, dicIstioTypeToGVK['AuthorizationPolicy'], ap.metadata.name, cluster) + API.deleteIstioConfigDetail(ns, dicTypeToGVK[gvkType.AuthorizationPolicy], ap.metadata.name, cluster) ) .concat( - sdsP.map(sc => API.deleteIstioConfigDetail(ns, dicIstioTypeToGVK['Sidecar'], sc.metadata.name, cluster)) + sdsP.map(sc => API.deleteIstioConfigDetail(ns, dicTypeToGVK[gvkType.Sidecar], sc.metadata.name, cluster)) ) ) .then(_ => { @@ -224,10 +224,10 @@ export class OverviewTrafficPolicies extends React.Component - API.createIstioConfigDetail(ns, dicIstioTypeToGVK['AuthorizationPolicy'], JSON.stringify(ap), cluster) + API.createIstioConfigDetail(ns, dicTypeToGVK[gvkType.AuthorizationPolicy], JSON.stringify(ap), cluster) ) .concat( - sds.map(sc => API.createIstioConfigDetail(ns, dicIstioTypeToGVK['Sidecar'], JSON.stringify(sc), cluster)) + sds.map(sc => API.createIstioConfigDetail(ns, dicTypeToGVK[gvkType.Sidecar], JSON.stringify(sc), cluster)) ) ) .then(results => { @@ -262,20 +262,20 @@ export class OverviewTrafficPolicies extends React.Component 0 && items.push({ - objectGVK: dicIstioTypeToGVK['AuthorizationPolicy'], + objectGVK: dicTypeToGVK[gvkType.AuthorizationPolicy], items: this.state.authorizationPolicies, title: 'Authorization Policies' }); this.state.sidecars.length > 0 && - items.push({ objectGVK: dicIstioTypeToGVK['Sidecar'], items: this.state.sidecars, title: 'Sidecars' }); + items.push({ objectGVK: dicTypeToGVK[gvkType.Sidecar], items: this.state.sidecars, title: 'Sidecars' }); return items; }; onConfirmPreviewPoliciesModal = (items: ConfigPreviewItem[]): void => { - const aps = items.filter(i => getGVKTypeString(i.objectGVK) === getGVKTypeString('AuthorizationPolicy'))[0]; - const sds = items.filter(i => getGVKTypeString(i.objectGVK) === getGVKTypeString('Sidecar'))[0]; + const aps = items.filter(i => getGVKTypeString(i.objectGVK) === getGVKTypeString(gvkType.AuthorizationPolicy))[0]; + const sds = items.filter(i => getGVKTypeString(i.objectGVK) === getGVKTypeString(gvkType.Sidecar))[0]; this.setState( { diff --git a/plugin/src/kiali/pages/ServiceDetails/ServiceDetailsPage.tsx b/plugin/src/kiali/pages/ServiceDetails/ServiceDetailsPage.tsx index 84157508..cdd38539 100644 --- a/plugin/src/kiali/pages/ServiceDetails/ServiceDetailsPage.tsx +++ b/plugin/src/kiali/pages/ServiceDetails/ServiceDetailsPage.tsx @@ -35,6 +35,7 @@ import { durationSelector } from 'store/Selectors'; import { basicTabStyle } from 'styles/TabStyles'; import { serverConfig } from 'config'; import { getGVKTypeString } from '../../utils/IstioConfigUtils'; +import { gvkType } from '../../types/IstioConfigList'; type ServiceDetailsState = { cluster?: string; @@ -119,7 +120,7 @@ class ServiceDetailsPageComponent extends React.Component { - this.setState({ gateways: response.data.resources[getGVKTypeString('Gateway')] }); - this.setState({ k8sGateways: response.data.resources[getGVKTypeString('K8sGateway')] }); + this.setState({ gateways: response.data.resources[getGVKTypeString(gvkType.Gateway)] }); + this.setState({ k8sGateways: response.data.resources[getGVKTypeString(gvkType.K8sGateway)] }); }) .catch(gwError => { AlertUtils.addError('Could not fetch Gateways list.', gwError); @@ -157,10 +158,17 @@ class ServiceDetailsPageComponent extends React.Component { this.setState({ - peerAuthentications: results.data.resources[getGVKTypeString('PeerAuthentication')] + peerAuthentications: results.data.resources[getGVKTypeString(gvkType.PeerAuthentication)] }); }) .catch(error => { diff --git a/plugin/src/kiali/pages/WorkloadDetails/WorkloadDescription.tsx b/plugin/src/kiali/pages/WorkloadDetails/WorkloadDescription.tsx index 61da158b..903ad4ec 100644 --- a/plugin/src/kiali/pages/WorkloadDetails/WorkloadDescription.tsx +++ b/plugin/src/kiali/pages/WorkloadDetails/WorkloadDescription.tsx @@ -15,12 +15,13 @@ import { MissingSidecar } from '../../components/MissingSidecar/MissingSidecar'; import { PFBadge, PFBadges } from '../../components/Pf/PfBadges'; import { MissingLabel } from '../../components/MissingLabel/MissingLabel'; import { MissingAuthPolicy } from 'components/MissingAuthPolicy/MissingAuthPolicy'; -import { hasMissingAuthPolicy } from 'utils/IstioConfigUtils'; +import { getGVKTypeString, hasMissingAuthPolicy, isGVKSupported } from 'utils/IstioConfigUtils'; import { DetailDescription } from '../../components/DetailDescription/DetailDescription'; import { isWaypoint } from '../../helpers/LabelFilterHelper'; import { AmbientLabel, tooltipMsgType } from '../../components/Ambient/AmbientLabel'; -import { validationKey } from '../../types/IstioConfigList'; +import { gvkType, validationKey } from '../../types/IstioConfigList'; import { infoStyle } from 'styles/IconStyle'; +import { addInfo } from 'utils/AlertUtils'; import { classes } from 'typestyle'; type WorkloadDescriptionProps = { @@ -30,12 +31,16 @@ type WorkloadDescriptionProps = { }; const resourceListStyle = kialiStyle({ - marginBottom: '0.75rem', + display: 'flex', $nest: { - '& > ul > li span': { - float: 'left', - width: '125px', - fontWeight: 700 + '& > ul > li': { + display: 'flex', + $nest: { + '& span': { + minWidth: '125px', + fontWeight: 700 + } + } } } }); @@ -80,11 +85,20 @@ export const WorkloadDescription: React.FC = (props: W workload.services?.forEach(s => services.push(s.name)); const isTemplateLabels = - ['Deployment', 'ReplicaSet', 'ReplicationController', 'DeploymentConfig', 'StatefulSet'].indexOf(workload.type) >= - 0; + [ + getGVKTypeString(gvkType.Deployment), + getGVKTypeString(gvkType.ReplicaSet), + getGVKTypeString(gvkType.ReplicationController), + getGVKTypeString(gvkType.DeploymentConfig), + getGVKTypeString(gvkType.StatefulSet) + ].indexOf(getGVKTypeString(workload.gvk)) >= 0; const runtimes = (workload.runtimes ?? []).map(r => r.name).filter(name => name !== ''); + if (!isGVKSupported(workload.gvk)) { + addInfo('This type of workload is not fully supported by Kiali, only limited information is available for display'); + } + const workloadProperties = ( <>
@@ -96,9 +110,16 @@ export const WorkloadDescription: React.FC = (props: W )} + {!isGVKSupported(workload.gvk) && ( +
  • + API Version + {`${workload.gvk.Group}.${workload.gvk.Version}`} +
  • + )} +
  • Type - {workload.type ? workload.type : 'N/A'} + {workload.gvk.Kind || 'N/A'}
  • diff --git a/plugin/src/kiali/pages/WorkloadDetails/WorkloadDetailsPage.tsx b/plugin/src/kiali/pages/WorkloadDetails/WorkloadDetailsPage.tsx index 1f804fb2..aba1d4e9 100644 --- a/plugin/src/kiali/pages/WorkloadDetails/WorkloadDetailsPage.tsx +++ b/plugin/src/kiali/pages/WorkloadDetails/WorkloadDetailsPage.tsx @@ -270,6 +270,8 @@ class WorkloadDetailsPageComponent extends React.Component { if (pod.istioContainers && pod.istioContainers.length > 0) { hasIstioSidecars = true; + } else if (pod.istioInitContainers && pod.istioInitContainers.some(cont => cont.name === 'istio-proxy')) { + hasIstioSidecars = true; } else { hasIstioSidecars = hasIstioSidecars || (!!pod.containers && pod.containers.some(cont => cont.name === 'istio-proxy')); @@ -304,7 +306,7 @@ class WorkloadDetailsPageComponent extends React.Component diff --git a/plugin/src/kiali/pages/WorkloadDetails/WorkloadInfo.tsx b/plugin/src/kiali/pages/WorkloadDetails/WorkloadInfo.tsx index f0fc2ab4..c35a9877 100644 --- a/plugin/src/kiali/pages/WorkloadDetails/WorkloadInfo.tsx +++ b/plugin/src/kiali/pages/WorkloadDetails/WorkloadInfo.tsx @@ -12,7 +12,7 @@ import { RenderComponentScroll } from '../../components/Nav/Page'; import { GraphDataSource } from '../../services/GraphDataSource'; import { DurationInSeconds } from 'types/Common'; import { isIstioNamespace, serverConfig } from '../../config/ServerConfig'; -import { IstioConfigList, toIstioItems } from '../../types/IstioConfigList'; +import { gvkType, IstioConfigList, toIstioItems } from '../../types/IstioConfigList'; import { WorkloadPods } from './WorkloadPods'; import { GraphEdgeTapEvent } from '../../components/CytoscapeGraph/CytoscapeGraph'; import { location, router, URLParam } from '../../app/History'; @@ -44,12 +44,12 @@ const tabName = 'list'; const defaultTab = 'pods'; const workloadIstioResources = [ - getGVKTypeString('Gateway'), - getGVKTypeString('AuthorizationPolicy'), - getGVKTypeString('PeerAuthentication'), - getGVKTypeString('Sidecar'), - getGVKTypeString('RequestAuthentication'), - getGVKTypeString('EnvoyFilter') + getGVKTypeString(gvkType.Gateway), + getGVKTypeString(gvkType.AuthorizationPolicy), + getGVKTypeString(gvkType.PeerAuthentication), + getGVKTypeString(gvkType.Sidecar), + getGVKTypeString(gvkType.RequestAuthentication), + getGVKTypeString(gvkType.EnvoyFilter) ]; export class WorkloadInfo extends React.Component { diff --git a/plugin/src/kiali/pages/WorkloadDetails/WorkloadPodLogs.tsx b/plugin/src/kiali/pages/WorkloadDetails/WorkloadPodLogs.tsx index b02106ad..9bd1e63f 100644 --- a/plugin/src/kiali/pages/WorkloadDetails/WorkloadPodLogs.tsx +++ b/plugin/src/kiali/pages/WorkloadDetails/WorkloadPodLogs.tsx @@ -480,8 +480,8 @@ export class WorkloadPodLogsComponent extends React.Component - - + + diff --git a/plugin/src/kiali/pages/WorkloadList/FiltersAndSorts.ts b/plugin/src/kiali/pages/WorkloadList/FiltersAndSorts.ts index beafa15d..89d7920d 100644 --- a/plugin/src/kiali/pages/WorkloadList/FiltersAndSorts.ts +++ b/plugin/src/kiali/pages/WorkloadList/FiltersAndSorts.ts @@ -6,7 +6,7 @@ import { AllFilterTypes, ToggleType } from '../../types/Filters'; -import { WorkloadListItem, WorkloadType } from '../../types/Workload'; +import { WorkloadListItem } from '../../types/Workload'; import { hasMissingSidecar } from 'components/VirtualList/Config'; import { SortField } from '../../types/SortFilters'; import { hasHealth } from '../../types/Health'; @@ -26,6 +26,7 @@ import { istioConfigTypeFilter } from '../IstioConfigList/FiltersAndSorts'; import { compareObjectReferences } from '../AppList/FiltersAndSorts'; import { serverConfig } from 'config'; import { getGVKTypeString, istioTypesToGVKString } from '../../utils/IstioConfigUtils'; +import { dicTypeToGVK } from '../../types/IstioConfigList'; const missingLabels = (r: WorkloadListItem): number => { return r.appLabel && r.versionLabel ? 0 : r.appLabel || r.versionLabel ? 1 : 2; @@ -57,7 +58,7 @@ export const sortFields: SortField[] = [ title: 'Workload Type', isNumeric: false, param: 'wt', - compare: (a: WorkloadListItem, b: WorkloadListItem) => a.type.localeCompare(b.type) + compare: (a: WorkloadListItem, b: WorkloadListItem) => a.gvk.Kind.localeCompare(b.gvk.Kind) }, { id: 'details', @@ -236,40 +237,40 @@ const workloadTypeFilter: FilterType = { action: FILTER_ACTION_APPEND, filterValues: [ { - id: WorkloadType.CronJob, - title: WorkloadType.CronJob + id: dicTypeToGVK.CronJob.Kind, + title: dicTypeToGVK.CronJob.Kind }, { - id: WorkloadType.DaemonSet, - title: WorkloadType.DaemonSet + id: dicTypeToGVK.DaemonSet.Kind, + title: dicTypeToGVK.DaemonSet.Kind }, { - id: WorkloadType.Deployment, - title: WorkloadType.Deployment + id: dicTypeToGVK.Deployment.Kind, + title: dicTypeToGVK.Deployment.Kind }, { - id: WorkloadType.DeploymentConfig, - title: WorkloadType.DeploymentConfig + id: dicTypeToGVK.DeploymentConfig.Kind, + title: dicTypeToGVK.DeploymentConfig.Kind }, { - id: WorkloadType.Job, - title: WorkloadType.Job + id: dicTypeToGVK.Job.Kind, + title: dicTypeToGVK.Job.Kind }, { - id: WorkloadType.Pod, - title: WorkloadType.Pod + id: dicTypeToGVK.Pod.Kind, + title: dicTypeToGVK.Pod.Kind }, { - id: WorkloadType.ReplicaSet, - title: WorkloadType.ReplicaSet + id: dicTypeToGVK.ReplicaSet.Kind, + title: dicTypeToGVK.ReplicaSet.Kind }, { - id: WorkloadType.ReplicationController, - title: WorkloadType.ReplicationController + id: dicTypeToGVK.ReplicationController.Kind, + title: dicTypeToGVK.ReplicationController.Kind }, { - id: WorkloadType.StatefulSet, - title: WorkloadType.StatefulSet + id: dicTypeToGVK.StatefulSet.Kind, + title: dicTypeToGVK.StatefulSet.Kind } ] }; @@ -299,7 +300,7 @@ const filterByType = (items: WorkloadListItem[], filter: string[]): WorkloadList if (filter && filter.length === 0) { return items; } - return items.filter(item => includeName(item.type, filter)); + return items.filter(item => includeName(item.gvk.Kind, filter)); }; const filterByLabelPresence = ( diff --git a/plugin/src/kiali/pages/WorkloadList/WorkloadListPage.tsx b/plugin/src/kiali/pages/WorkloadList/WorkloadListPage.tsx index 623cc2be..5f37dc83 100644 --- a/plugin/src/kiali/pages/WorkloadList/WorkloadListPage.tsx +++ b/plugin/src/kiali/pages/WorkloadList/WorkloadListPage.tsx @@ -120,7 +120,7 @@ class WorkloadListPageComponent extends FilterComponent.Component< namespace: deployment.namespace, name: deployment.name, instanceType: InstanceType.Workload, - type: deployment.type, + gvk: deployment.gvk, appLabel: deployment.appLabel, versionLabel: deployment.versionLabel, istioSidecar: deployment.istioSidecar, diff --git a/plugin/src/kiali/services/Api.ts b/plugin/src/kiali/services/Api.ts index 0bba276e..5e733a60 100644 --- a/plugin/src/kiali/services/Api.ts +++ b/plugin/src/kiali/services/Api.ts @@ -24,7 +24,8 @@ import { IstioPermissionsQuery } from '../types/IstioConfigDetails'; import { - dicIstioTypeToGVK, + dicTypeToGVK, + gvkType, IstioConfigList, IstioConfigListQuery, IstioConfigsMapQuery @@ -70,6 +71,7 @@ import { } from '../types/Workload'; import { CertsInfo } from 'types/CertsInfo'; import { ApiError, ApiResponse } from 'types/Api'; +import { getGVKTypeString } from '../utils/IstioConfigUtils'; export const ANONYMOUS_USER = 'anonymous'; @@ -1020,12 +1022,12 @@ export const getWorkload = ( export const updateWorkload = ( namespace: string, name: string, - type: string, + type: GroupVersionKind | gvkType, jsonPatch: string, patchType?: string, cluster?: string ): Promise> => { - const params: QueryParams = { type: type }; + const params: QueryParams = { gvk: getGVKTypeString(type) }; if (patchType) { params.patchType = patchType; @@ -1284,7 +1286,7 @@ export function deleteServiceTrafficRouting( deletePromises.push( deleteIstioConfigDetail( vs.metadata.namespace ?? '', - dicIstioTypeToGVK['VirtualService'], + dicTypeToGVK[gvkType.VirtualService], vs.metadata.name, cluster ) @@ -1295,7 +1297,7 @@ export function deleteServiceTrafficRouting( deletePromises.push( deleteIstioConfigDetail( k8sr.metadata.namespace ?? '', - dicIstioTypeToGVK['K8sHTTPRoute'], + dicTypeToGVK[gvkType.K8sHTTPRoute], k8sr.metadata.name, cluster ) @@ -1306,7 +1308,7 @@ export function deleteServiceTrafficRouting( deletePromises.push( deleteIstioConfigDetail( k8sr.metadata.namespace ?? '', - dicIstioTypeToGVK['K8sGRPCRoute'], + dicTypeToGVK[gvkType.K8sGRPCRoute], k8sr.metadata.name, cluster ) @@ -1316,7 +1318,7 @@ export function deleteServiceTrafficRouting( deletePromises.push( deleteIstioConfigDetail( dr.metadata.namespace ?? '', - dicIstioTypeToGVK['DestinationRule'], + dicTypeToGVK[gvkType.DestinationRule], dr.metadata.name, cluster ) @@ -1325,7 +1327,7 @@ export function deleteServiceTrafficRouting( const paName = dr.hasPeerAuthentication(); if (!!paName) { deletePromises.push( - deleteIstioConfigDetail(dr.metadata.namespace ?? '', dicIstioTypeToGVK['PeerAuthentication'], paName, cluster) + deleteIstioConfigDetail(dr.metadata.namespace ?? '', dicTypeToGVK[gvkType.PeerAuthentication], paName, cluster) ); } }); diff --git a/plugin/src/kiali/services/__mockData__/getServiceDetail.ts b/plugin/src/kiali/services/__mockData__/getServiceDetail.ts index 3c640318..2d5d5c90 100644 --- a/plugin/src/kiali/services/__mockData__/getServiceDetail.ts +++ b/plugin/src/kiali/services/__mockData__/getServiceDetail.ts @@ -1,6 +1,6 @@ import { ServiceDetailsInfo } from '../../types/ServiceInfo'; import { ValidationTypes } from '../../types/IstioObjects'; -import { dicIstioTypeToGVK } from '../../types/IstioConfigList'; +import { dicTypeToGVK, gvkType } from '../../types/IstioConfigList'; export const SERVICE_DETAILS: ServiceDetailsInfo = { service: { @@ -136,7 +136,7 @@ export const SERVICE_DETAILS: ServiceDetailsInfo = { 'networking.istio.io/v1, Kind=DestinationRule': { reviews: { name: 'details', - objectGVK: dicIstioTypeToGVK['DestinationRule'], + objectGVK: dicTypeToGVK[gvkType.DestinationRule], valid: false, checks: [ { diff --git a/plugin/src/kiali/types/IstioConfigList.ts b/plugin/src/kiali/types/IstioConfigList.ts index 60086372..9a28a401 100644 --- a/plugin/src/kiali/types/IstioConfigList.ts +++ b/plugin/src/kiali/types/IstioConfigList.ts @@ -46,30 +46,76 @@ export interface IstioConfigsMapQuery extends IstioConfigListQuery { namespaces?: string; } -export const dicIstioTypeToGVK: { [key: string]: GroupVersionKind } = { - AuthorizationPolicy: { Group: 'security.istio.io', Version: 'v1', Kind: 'AuthorizationPolicy' }, - PeerAuthentication: { Group: 'security.istio.io', Version: 'v1', Kind: 'PeerAuthentication' }, - RequestAuthentication: { Group: 'security.istio.io', Version: 'v1', Kind: 'RequestAuthentication' }, - - DestinationRule: { Group: 'networking.istio.io', Version: 'v1', Kind: 'DestinationRule' }, - Gateway: { Group: 'networking.istio.io', Version: 'v1', Kind: 'Gateway' }, - EnvoyFilter: { Group: 'networking.istio.io', Version: 'v1alpha3', Kind: 'EnvoyFilter' }, - Sidecar: { Group: 'networking.istio.io', Version: 'v1', Kind: 'Sidecar' }, - ServiceEntry: { Group: 'networking.istio.io', Version: 'v1', Kind: 'ServiceEntry' }, - VirtualService: { Group: 'networking.istio.io', Version: 'v1', Kind: 'VirtualService' }, - WorkloadEntry: { Group: 'networking.istio.io', Version: 'v1', Kind: 'WorkloadEntry' }, - WorkloadGroup: { Group: 'networking.istio.io', Version: 'v1', Kind: 'WorkloadGroup' }, - - WasmPlugin: { Group: 'extensions.istio.io', Version: 'v1alpha1', Kind: 'WasmPlugin' }, - Telemetry: { Group: 'telemetry.istio.io', Version: 'v1', Kind: 'Telemetry' }, - - K8sGateway: { Group: 'gateway.networking.k8s.io', Version: 'v1', Kind: 'Gateway' }, - K8sGatewayClass: { Group: 'gateway.networking.k8s.io', Version: 'v1', Kind: 'GatewayClass' }, - K8sGRPCRoute: { Group: 'gateway.networking.k8s.io', Version: 'v1', Kind: 'GRPCRoute' }, - K8sHTTPRoute: { Group: 'gateway.networking.k8s.io', Version: 'v1', Kind: 'HTTPRoute' }, - K8sReferenceGrant: { Group: 'gateway.networking.k8s.io', Version: 'v1', Kind: 'ReferenceGrant' }, - K8sTCPRoute: { Group: 'gateway.networking.k8s.io', Version: 'v1alpha2', Kind: 'TCPRoute' }, - K8sTLSRoute: { Group: 'gateway.networking.k8s.io', Version: 'v1alpha2', Kind: 'TLSRoute' } +export enum gvkType { + AuthorizationPolicy = 'AuthorizationPolicy', + PeerAuthentication = 'PeerAuthentication', + RequestAuthentication = 'RequestAuthentication', + + DestinationRule = 'DestinationRule', + Gateway = 'Gateway', + EnvoyFilter = 'EnvoyFilter', + Sidecar = 'Sidecar', + ServiceEntry = 'ServiceEntry', + VirtualService = 'VirtualService', + WorkloadEntry = 'WorkloadEntry', + WorkloadGroup = 'WorkloadGroup', + + WasmPlugin = 'WasmPlugin', + Telemetry = 'Telemetry', + + K8sGateway = 'K8sGateway', + K8sGatewayClass = 'K8sGatewayClass', + K8sGRPCRoute = 'K8sGRPCRoute', + K8sHTTPRoute = 'K8sHTTPRoute', + K8sReferenceGrant = 'K8sReferenceGrant', + K8sTCPRoute = 'K8sTCPRoute', + K8sTLSRoute = 'K8sTLSRoute', + + CronJob = 'CronJob', + DaemonSet = 'DaemonSet', + Deployment = 'Deployment', + DeploymentConfig = 'DeploymentConfig', + Job = 'Job', + Pod = 'Pod', + ReplicaSet = 'ReplicaSet', + ReplicationController = 'ReplicationController', + StatefulSet = 'StatefulSet' +} + +export const dicTypeToGVK: { [key in gvkType]: GroupVersionKind } = { + [gvkType.AuthorizationPolicy]: { Group: 'security.istio.io', Version: 'v1', Kind: gvkType.AuthorizationPolicy }, + [gvkType.PeerAuthentication]: { Group: 'security.istio.io', Version: 'v1', Kind: gvkType.PeerAuthentication }, + [gvkType.RequestAuthentication]: { Group: 'security.istio.io', Version: 'v1', Kind: gvkType.RequestAuthentication }, + + [gvkType.DestinationRule]: { Group: 'networking.istio.io', Version: 'v1', Kind: gvkType.DestinationRule }, + [gvkType.Gateway]: { Group: 'networking.istio.io', Version: 'v1', Kind: gvkType.Gateway }, + [gvkType.EnvoyFilter]: { Group: 'networking.istio.io', Version: 'v1alpha3', Kind: gvkType.EnvoyFilter }, + [gvkType.Sidecar]: { Group: 'networking.istio.io', Version: 'v1', Kind: gvkType.Sidecar }, + [gvkType.ServiceEntry]: { Group: 'networking.istio.io', Version: 'v1', Kind: gvkType.ServiceEntry }, + [gvkType.VirtualService]: { Group: 'networking.istio.io', Version: 'v1', Kind: gvkType.VirtualService }, + [gvkType.WorkloadEntry]: { Group: 'networking.istio.io', Version: 'v1', Kind: gvkType.WorkloadEntry }, + [gvkType.WorkloadGroup]: { Group: 'networking.istio.io', Version: 'v1', Kind: gvkType.WorkloadGroup }, + + [gvkType.WasmPlugin]: { Group: 'extensions.istio.io', Version: 'v1alpha1', Kind: gvkType.WasmPlugin }, + [gvkType.Telemetry]: { Group: 'telemetry.istio.io', Version: 'v1', Kind: gvkType.Telemetry }, + + [gvkType.K8sGateway]: { Group: 'gateway.networking.k8s.io', Version: 'v1', Kind: 'Gateway' }, + [gvkType.K8sGatewayClass]: { Group: 'gateway.networking.k8s.io', Version: 'v1', Kind: 'GatewayClass' }, + [gvkType.K8sGRPCRoute]: { Group: 'gateway.networking.k8s.io', Version: 'v1', Kind: 'GRPCRoute' }, + [gvkType.K8sHTTPRoute]: { Group: 'gateway.networking.k8s.io', Version: 'v1', Kind: 'HTTPRoute' }, + [gvkType.K8sReferenceGrant]: { Group: 'gateway.networking.k8s.io', Version: 'v1', Kind: 'ReferenceGrant' }, + [gvkType.K8sTCPRoute]: { Group: 'gateway.networking.k8s.io', Version: 'v1alpha2', Kind: 'TCPRoute' }, + [gvkType.K8sTLSRoute]: { Group: 'gateway.networking.k8s.io', Version: 'v1alpha2', Kind: 'TLSRoute' }, + + [gvkType.CronJob]: { Group: 'batch', Version: 'v1', Kind: 'CronJob' }, + [gvkType.DaemonSet]: { Group: 'apps', Version: 'v1', Kind: 'DaemonSet' }, + [gvkType.Deployment]: { Group: 'apps', Version: 'v1', Kind: 'Deployment' }, + [gvkType.DeploymentConfig]: { Group: 'apps.openshift.io', Version: 'v1', Kind: 'DeploymentConfig' }, + [gvkType.Job]: { Group: 'batch', Version: 'v1', Kind: 'Job' }, + [gvkType.Pod]: { Group: '', Version: 'v1', Kind: 'Pod' }, + [gvkType.ReplicaSet]: { Group: 'apps', Version: 'v1', Kind: 'ReplicaSet' }, + [gvkType.ReplicationController]: { Group: '', Version: 'v1', Kind: 'ReplicationController' }, + [gvkType.StatefulSet]: { Group: 'apps', Version: 'v1', Kind: 'StatefulSet' } }; export function validationKey(name: string, namespace?: string): string { @@ -101,9 +147,9 @@ export const filterByNamespaces = (unfiltered: IstioConfigList, namespaces: stri const namespaceSet = new Set(namespaces); const filteredResources: { [key: string]: any[] } = {}; - // Iterate over dicIstioTypeToGVK to dynamically filter each resource by namespace - Object.keys(dicIstioTypeToGVK).forEach(key => { - const resourceKey = getGVKTypeString(key); + // Iterate over dicTypeToGVK to dynamically filter each resource by namespace + Object.values(dicTypeToGVK).forEach(value => { + const resourceKey = getGVKTypeString(value); // Check if the resource exists in the unfiltered list, then filter by namespace if (unfiltered.resources[resourceKey]) { @@ -127,9 +173,9 @@ export const filterByName = (unfiltered: IstioConfigList, names: string[]): Isti const filteredResources: { [key: string]: any[] } = {}; - // Iterate over the dicIstioTypeToGVK to access each resource type dynamically - Object.keys(dicIstioTypeToGVK).forEach(key => { - const resourceKey = getGVKTypeString(key); + // Iterate over the dicTypeToGVK to access each resource type dynamically + Object.values(dicTypeToGVK).forEach(value => { + const resourceKey = getGVKTypeString(value); // Check if the resource exists in the unfiltered list, then filter by names if (unfiltered.resources[resourceKey]) { diff --git a/plugin/src/kiali/types/Workload.ts b/plugin/src/kiali/types/Workload.ts index 0858be83..31d56313 100644 --- a/plugin/src/kiali/types/Workload.ts +++ b/plugin/src/kiali/types/Workload.ts @@ -1,5 +1,5 @@ import { WorkloadHealth, WorkloadHealthResponse } from './Health'; -import { ObjectReference, Pod, Service, Validations } from './IstioObjects'; +import { GroupVersionKind, ObjectReference, Pod, Service, Validations } from './IstioObjects'; import { InstanceType } from 'types/Common'; export type WorkloadId = { @@ -14,6 +14,7 @@ export interface Workload { availableReplicas: Number; cluster?: string; createdAt: string; + gvk: GroupVersionKind; health?: WorkloadHealthResponse; instanceType: InstanceType.Workload; isAmbient: boolean; @@ -28,7 +29,6 @@ export interface Workload { resourceVersion: string; runtimes: Runtime[]; services: Service[]; - type: string; validations?: Validations; versionLabel: boolean; waypointWorkloads: Workload[]; @@ -40,6 +40,7 @@ export const emptyWorkload: Workload = { appLabel: false, availableReplicas: 0, createdAt: '', + gvk: { Group: '', Version: '', Kind: '' }, isAmbient: false, isGateway: false, istioSidecar: true, // true until proven otherwise @@ -52,27 +53,15 @@ export const emptyWorkload: Workload = { resourceVersion: '', runtimes: [], services: [], - type: '', versionLabel: false, waypointWorkloads: [] }; -export const WorkloadType = { - CronJob: 'CronJob', - DaemonSet: 'DaemonSet', - Deployment: 'Deployment', - DeploymentConfig: 'DeploymentConfig', - Job: 'Job', - Pod: 'Pod', - ReplicaSet: 'ReplicaSet', - ReplicationController: 'ReplicationController', - StatefulSet: 'StatefulSet' -}; - export interface WorkloadListItem { additionalDetailSample?: AdditionalItem; appLabel: boolean; cluster?: string; + gvk: GroupVersionKind; health: WorkloadHealth; instanceType: InstanceType.Workload; isAmbient: boolean; @@ -83,7 +72,6 @@ export interface WorkloadListItem { name: string; namespace: string; notCoveredAuthPolicy: boolean; - type: string; versionLabel: boolean; } @@ -94,8 +82,8 @@ export interface WorkloadQuery { } export interface WorkloadUpdateQuery { + gvk: string; patchType?: string; - type: string; } export interface WorkloadListQuery { diff --git a/plugin/src/kiali/types/__tests__/AceValidations.test.ts b/plugin/src/kiali/types/__tests__/AceValidations.test.ts index d4a70cef..cb2bc515 100644 --- a/plugin/src/kiali/types/__tests__/AceValidations.test.ts +++ b/plugin/src/kiali/types/__tests__/AceValidations.test.ts @@ -1,12 +1,12 @@ import { ObjectValidation, ValidationTypes } from '../IstioObjects'; import { parseKialiValidations } from '../AceValidations'; -import { dicIstioTypeToGVK } from '../IstioConfigList'; +import { dicTypeToGVK, gvkType } from '../IstioConfigList'; const fs = require('fs'); const destinationRuleValidations: ObjectValidation = { name: 'details', - objectGVK: dicIstioTypeToGVK['DestinationRule'], + objectGVK: dicTypeToGVK[gvkType.DestinationRule], valid: false, checks: [ { @@ -19,7 +19,7 @@ const destinationRuleValidations: ObjectValidation = { const vsInvalidHosts: ObjectValidation = { name: 'productpage', - objectGVK: dicIstioTypeToGVK['VirtualService'], + objectGVK: dicTypeToGVK[gvkType.VirtualService], valid: false, checks: [ { @@ -32,7 +32,7 @@ const vsInvalidHosts: ObjectValidation = { const vsInvalidHttpFirstRoute: ObjectValidation = { name: 'productpage', - objectGVK: dicIstioTypeToGVK['VirtualService'], + objectGVK: dicTypeToGVK[gvkType.VirtualService], valid: false, checks: [ { @@ -45,7 +45,7 @@ const vsInvalidHttpFirstRoute: ObjectValidation = { const vsInvalidHttpSecondRoute: ObjectValidation = { name: 'productpage', - objectGVK: dicIstioTypeToGVK['VirtualService'], + objectGVK: dicTypeToGVK[gvkType.VirtualService], valid: false, checks: [ { @@ -58,7 +58,7 @@ const vsInvalidHttpSecondRoute: ObjectValidation = { const vsInvalidHttpThirdRoute: ObjectValidation = { name: 'productpage', - objectGVK: dicIstioTypeToGVK['VirtualService'], + objectGVK: dicTypeToGVK[gvkType.VirtualService], valid: false, checks: [ { @@ -71,7 +71,7 @@ const vsInvalidHttpThirdRoute: ObjectValidation = { const vsInvalidHttpSecondSecondDestinationField: ObjectValidation = { name: 'productpage', - objectGVK: dicIstioTypeToGVK['VirtualService'], + objectGVK: dicTypeToGVK[gvkType.VirtualService], valid: false, checks: [ { @@ -84,7 +84,7 @@ const vsInvalidHttpSecondSecondDestinationField: ObjectValidation = { const vsInvalidHttpThirdFirstDestinationField: ObjectValidation = { name: 'productpage', - objectGVK: dicIstioTypeToGVK['VirtualService'], + objectGVK: dicTypeToGVK[gvkType.VirtualService], valid: false, checks: [ { @@ -97,7 +97,7 @@ const vsInvalidHttpThirdFirstDestinationField: ObjectValidation = { const vsInvalidHttpThirdFirstSubsetNotFound: ObjectValidation = { name: 'productpage', - objectGVK: dicIstioTypeToGVK['VirtualService'], + objectGVK: dicTypeToGVK[gvkType.VirtualService], valid: false, checks: [ { @@ -110,7 +110,7 @@ const vsInvalidHttpThirdFirstSubsetNotFound: ObjectValidation = { const vsInvalidHttpFirstSecondSubsetNotFound: ObjectValidation = { name: 'productpage', - objectGVK: dicIstioTypeToGVK['VirtualService'], + objectGVK: dicTypeToGVK[gvkType.VirtualService], valid: false, checks: [ { @@ -123,7 +123,7 @@ const vsInvalidHttpFirstSecondSubsetNotFound: ObjectValidation = { const vsInvalidHttpFourthFirstWeigth: ObjectValidation = { name: 'productpage', - objectGVK: dicIstioTypeToGVK['VirtualService'], + objectGVK: dicTypeToGVK[gvkType.VirtualService], valid: false, checks: [ { @@ -136,7 +136,7 @@ const vsInvalidHttpFourthFirstWeigth: ObjectValidation = { const vsInvalidHttpFifthSecondWeigth: ObjectValidation = { name: 'productpage', - objectGVK: dicIstioTypeToGVK['VirtualService'], + objectGVK: dicTypeToGVK[gvkType.VirtualService], valid: false, checks: [ { diff --git a/plugin/src/kiali/utils/IstioConfigUtils.ts b/plugin/src/kiali/utils/IstioConfigUtils.ts index 4008fbe6..21cdf75e 100644 --- a/plugin/src/kiali/utils/IstioConfigUtils.ts +++ b/plugin/src/kiali/utils/IstioConfigUtils.ts @@ -9,7 +9,7 @@ import { Validations } from '../types/IstioObjects'; import _ from 'lodash'; -import { dicIstioTypeToGVK, IstioConfigItem } from 'types/IstioConfigList'; +import { dicTypeToGVK, gvkType, IstioConfigItem } from 'types/IstioConfigList'; export const mergeJsonPatch = (objectModified: object, object?: object): object => { if (!object) { @@ -194,14 +194,18 @@ export function getIstioObjectGVK(apiVersion?: string, kind?: string): GroupVers const parts = apiVersion.split('/'); if (parts.length !== 2) { // should not happen, but not the best way, only an alternative - return dicIstioTypeToGVK[kind]; + return dicTypeToGVK[kind]; } return { Group: parts[0], Version: parts[1], Kind: kind! }; } -export function getGVKTypeString(gvk: GroupVersionKind | string): string { +export function getGVKTypeString(gvk: GroupVersionKind | gvkType): string { if (typeof gvk === 'string') { - return gvkToString(dicIstioTypeToGVK[gvk]); + const gvkEntry = dicTypeToGVK[gvk]; + if (!gvkEntry) { + throw new Error(`GVK type '${gvk}' not found in dicTypeToGVK.`); + } + return gvkToString(gvkEntry); } else { return gvkToString(gvk); } @@ -243,6 +247,10 @@ export function kindToStringIncludeK8s(apiVersion?: string, kind?: string): stri export function istioTypesToGVKString(istioTypes: string[]): string[] { return istioTypes.map(type => { - return gvkToString(dicIstioTypeToGVK[type]); + return gvkToString(dicTypeToGVK[type]); }); } + +export function isGVKSupported(gvk: GroupVersionKind): boolean { + return getGVKTypeString(gvk) === getGVKTypeString(gvkType[gvk.Kind]); +} diff --git a/plugin/src/kiali/utils/__tests__/IstioConfigUtils.test.ts b/plugin/src/kiali/utils/__tests__/IstioConfigUtils.test.ts index b68bc5b7..45625145 100644 --- a/plugin/src/kiali/utils/__tests__/IstioConfigUtils.test.ts +++ b/plugin/src/kiali/utils/__tests__/IstioConfigUtils.test.ts @@ -8,7 +8,7 @@ import { mergeJsonPatch, stringToGVK } from '../IstioConfigUtils'; -import { dicIstioTypeToGVK } from '../../types/IstioConfigList'; +import { dicTypeToGVK, gvkType } from '../../types/IstioConfigList'; describe('Validate JSON Patchs', () => { const gateway: object = { @@ -119,7 +119,7 @@ describe('Validate returned GoupVersionKind for IstioObject', () => { it('Invalid apiVersion, valid Kind', () => { const result = getIstioObjectGVK('invalidApiVersion', 'AuthorizationPolicy'); - expect(result).toEqual(dicIstioTypeToGVK['AuthorizationPolicy']); + expect(result).toEqual(dicTypeToGVK[gvkType.AuthorizationPolicy]); }); it('Empty apiVersion, valid kind', () => { @@ -139,6 +139,21 @@ describe('Validate converting GroupVersionKind To String', () => { expect(result).toBe('networking.istio.io/v1, Kind=VirtualService'); }); + it('Correct Workload GroupVersionKind properties', () => { + const result = getGVKTypeString({ Group: 'apps', Version: 'v1', Kind: 'Deployment' }); + expect(result).toBe('apps/v1, Kind=Deployment'); + }); + + it('Correct Workload Kind properties', () => { + const result = getGVKTypeString(gvkType.Deployment); + expect(result).toBe('apps/v1, Kind=Deployment'); + }); + + it('Correct Gateway Kind properties', () => { + const result = getGVKTypeString(gvkType.Gateway); + expect(result).toBe('networking.istio.io/v1, Kind=Gateway'); + }); + it('Validate empty string when Group, Version, and Kind are all empty', () => { const result = getGVKTypeString({ Group: '', Version: '', Kind: '' }); expect(result).toBe('');