diff --git a/frontend/packages/knative-plugin/src/const.ts b/frontend/packages/knative-plugin/src/const.ts index 667134a8cfe..15e87bbe1ff 100644 --- a/frontend/packages/knative-plugin/src/const.ts +++ b/frontend/packages/knative-plugin/src/const.ts @@ -25,3 +25,4 @@ export const KNATIVE_MAXSCALE_ANNOTATION = `${KNATIVE_AUTOSCALING_APIGROUP}/maxS export const KNATIVE_CONCURRENCYTARGET_ANNOTATION = `${KNATIVE_AUTOSCALING_APIGROUP}/target`; export const KNATIVE_CONCURRENCYUTILIZATION_ANNOTATION = `${KNATIVE_AUTOSCALING_APIGROUP}/targetUtilizationPercentage`; export const KNATIVE_AUTOSCALEWINDOW_ANNOTATION = `${KNATIVE_AUTOSCALING_APIGROUP}/window`; +export const SERVERLESS_FUNCTION_LABEL = 'boson.dev/function'; diff --git a/frontend/packages/knative-plugin/src/topology/components/groups/KnativeService.scss b/frontend/packages/knative-plugin/src/topology/components/groups/KnativeService.scss index 6bbd1fb35fe..628ae5b6cad 100644 --- a/frontend/packages/knative-plugin/src/topology/components/groups/KnativeService.scss +++ b/frontend/packages/knative-plugin/src/topology/components/groups/KnativeService.scss @@ -16,6 +16,10 @@ stroke-dasharray: $group-node-stroke-dasharray; } + &.is-function &__bg { + fill: var(--pf-global--palette--purple-50); + } + &.is-filtered &__bg { stroke: $filtered-stroke-color; stroke-width: $group-node-filtered-stroke-width; diff --git a/frontend/packages/knative-plugin/src/topology/components/groups/KnativeServiceGroup.tsx b/frontend/packages/knative-plugin/src/topology/components/groups/KnativeServiceGroup.tsx index 74a79ecf5bb..deeb77730c8 100644 --- a/frontend/packages/knative-plugin/src/topology/components/groups/KnativeServiceGroup.tsx +++ b/frontend/packages/knative-plugin/src/topology/components/groups/KnativeServiceGroup.tsx @@ -32,9 +32,10 @@ import { SHOW_LABELS_FILTER_ID, } from '@console/topology/src/filters'; import SvgBoxedText from '@console/topology/src/components/svg/SvgBoxedText'; +import { getNodeDecorators } from '@console/topology/src/components/graph-view/components/nodes/decorators/getNodeDecorators'; import { TYPE_KNATIVE_SERVICE, EVENT_MARKER_RADIUS } from '../../const'; import RevisionTrafficSourceAnchor from '../anchors/RevisionTrafficSourceAnchor'; -import { getNodeDecorators } from '@console/topology/src/components/graph-view/components/nodes/decorators/getNodeDecorators'; +import { isServerlessFunction } from '../../knative-topology-utils'; export type KnativeServiceGroupProps = { element: Node; @@ -119,6 +120,10 @@ const KnativeServiceGroup: React.FC = ({ height, ); + const typeIconClass: string = isServerlessFunction(element) + ? 'icon-serverless-function' + : 'icon-knative'; + return ( = ({ 'is-highlight': canDrop || edgeDragging, 'is-dropTarget': canDrop && dropTarget, 'is-filtered': filtered, + 'is-function': isServerlessFunction(element), })} > = ({ paddingY={4} kind={data.kind} dragRef={dragLabelRef} - typeIconClass="icon-knative" + typeIconClass={typeIconClass} > {element.getLabel()} diff --git a/frontend/packages/knative-plugin/src/topology/knative-topology-utils.ts b/frontend/packages/knative-plugin/src/topology/knative-topology-utils.ts index 1487aa1305e..1e1901e3fe8 100644 --- a/frontend/packages/knative-plugin/src/topology/knative-topology-utils.ts +++ b/frontend/packages/knative-plugin/src/topology/knative-topology-utils.ts @@ -26,7 +26,11 @@ import { import { getImageForIconClass } from '@console/internal/components/catalog/catalog-item-icon'; import { DeploymentModel, PodModel } from '@console/internal/models'; import { RootState } from '@console/internal/redux'; -import { FLAG_KNATIVE_EVENTING, CAMEL_SOURCE_INTEGRATION } from '../const'; +import { + FLAG_KNATIVE_EVENTING, + CAMEL_SOURCE_INTEGRATION, + SERVERLESS_FUNCTION_LABEL, +} from '../const'; import { KnativeItem } from '../utils/get-knative-resources'; import { KNATIVE_GROUP_NODE_HEIGHT, @@ -1144,3 +1148,14 @@ export const createSinkPubSubConnection = ( const targetObj = getTopologyResourceObject(target); return createEventingPubSubSink(resources.obj, targetObj); }; + +export const isServerlessFunction = (element: Node): boolean => { + if (!element) { + return false; + } + + const { + metadata: { labels }, + } = getResource(element); + return !!labels?.[SERVERLESS_FUNCTION_LABEL]; +}; diff --git a/frontend/packages/knative-plugin/src/topology/listView/KnativeServiceListViewNode.tsx b/frontend/packages/knative-plugin/src/topology/listView/KnativeServiceListViewNode.tsx index 1f2cce6a519..25611894562 100644 --- a/frontend/packages/knative-plugin/src/topology/listView/KnativeServiceListViewNode.tsx +++ b/frontend/packages/knative-plugin/src/topology/listView/KnativeServiceListViewNode.tsx @@ -5,6 +5,7 @@ import { TopologyListViewNode, TypedResourceBadgeCell, } from '@console/topology/src/components/list-view'; +import { isServerlessFunction } from '../knative-topology-utils'; interface KnativeServiceListViewNodeProps { item: Node; @@ -20,8 +21,12 @@ const ObservedKnativeServiceListViewNode: React.FC { const kind = getResourceKind(item); + const typeIconClass: string = isServerlessFunction(item) + ? 'icon-serverless-function' + : 'icon-knative'; + const badgeCell = ( - + ); return ( diff --git a/frontend/packages/knative-plugin/src/utils/__tests__/knative-topology-utils.spec.ts b/frontend/packages/knative-plugin/src/utils/__tests__/knative-topology-utils.spec.ts index 546fb4a0d06..cb4d690da7d 100644 --- a/frontend/packages/knative-plugin/src/utils/__tests__/knative-topology-utils.spec.ts +++ b/frontend/packages/knative-plugin/src/utils/__tests__/knative-topology-utils.spec.ts @@ -1,5 +1,6 @@ import * as _ from 'lodash'; import * as k8s from '@console/internal/module/k8s'; +import { Node } from '@patternfly/react-topology'; import { MockKnativeResources, getEventSourceResponse, @@ -17,11 +18,13 @@ import { getSinkUriTopologyNodeItems, getSinkUriTopologyEdgeItems, isOperatorBackedKnResource, + isServerlessFunction, } from '../../topology/knative-topology-utils'; import { EdgeType, NodeType } from '../../topology/topology-types'; import { mockServiceData, mockRevisions } from '../__mocks__/traffic-splitting-utils-mock'; import { EventSourceCronJobModel } from '../../models'; import * as knativefetchutils from '../fetch-dynamic-eventsources-utils'; +import { SERVERLESS_FUNCTION_LABEL } from '../../const'; describe('knative topology utils', () => { it('expect getKnativeServiceData to return knative resources', () => { @@ -162,6 +165,28 @@ describe('knative topology utils', () => { ); expect(isOperatorbacked).toBe(false); }); + + it('expect isServerlessFunction to return false if node is undefined', () => { + expect(isServerlessFunction(undefined)).toBe(false); + }); + + it('expect isServerlessFunction to return false if a node does not have necessary labels', () => { + const sampleKnNode: Node = ({ + getResource: () => MockKnativeResources.ksservices.data[0], + } as any) as Node; + expect(isServerlessFunction(sampleKnNode)).toBe(false); + }); + + it('expect isServerlessFunction to return true if a node has necessary labels', () => { + const sampleKnResource: k8s.K8sResourceKind = { + ...MockKnativeResources.ksservices.data[0], + metadata: { labels: { [SERVERLESS_FUNCTION_LABEL]: 'true' } }, + }; + const sampleKnNode: Node = ({ + getResource: () => sampleKnResource, + } as any) as Node; + expect(isServerlessFunction(sampleKnNode)).toBe(true); + }); }); describe('Knative Topology Utils', () => { diff --git a/frontend/public/components/catalog/catalog-item-icon.tsx b/frontend/public/components/catalog/catalog-item-icon.tsx index a02741c62e7..d934929acbe 100644 --- a/frontend/public/components/catalog/catalog-item-icon.tsx +++ b/frontend/public/components/catalog/catalog-item-icon.tsx @@ -46,6 +46,7 @@ import * as joomlaImg from '../../imgs/logos/joomla.svg'; import * as jrubyImg from '../../imgs/logos/jruby.svg'; import * as jsImg from '../../imgs/logos/js.svg'; import * as knativeImg from '../../imgs/logos/knative.svg'; +import * as serverlessFuncImage from '../../imgs/logos/serverlessfx.png'; import * as kubevirtImg from '../../imgs/logos/kubevirt.svg'; import * as laravelImg from '../../imgs/logos/laravel.svg'; import * as loadBalancerImg from '../../imgs/logos/load-balancer.svg'; @@ -171,6 +172,7 @@ const logos = new Map() .set('icon-redis', redisImg) .set('icon-rh-integration', rhIntegrationImg) .set('icon-rh-spring-boot', rhSpringBoot) + .set('icon-serverless-function', serverlessFuncImage) .set('icon-java', openjdkImg) // Use the upstream icon. .set('icon-redhat', redhatImg) diff --git a/frontend/public/imgs/logos/serverlessfx.png b/frontend/public/imgs/logos/serverlessfx.png new file mode 100644 index 00000000000..1529fb6c140 Binary files /dev/null and b/frontend/public/imgs/logos/serverlessfx.png differ