From aff7d46d7fe8765426d79b2124e2ae351a6322f7 Mon Sep 17 00:00:00 2001 From: Aritra Roy Date: Wed, 24 Feb 2021 14:43:14 +0530 Subject: [PATCH] visualize serverless function in topology --- frontend/packages/knative-plugin/src/const.ts | 1 + .../components/groups/KnativeService.scss | 4 +++ .../components/groups/KnativeServiceGroup.tsx | 10 +++++-- .../src/topology/knative-topology-utils.ts | 17 +++++++++++- .../listView/KnativeServiceListViewNode.tsx | 7 ++++- .../__tests__/knative-topology-utils.spec.ts | 25 ++++++++++++++++++ .../components/catalog/catalog-item-icon.tsx | 2 ++ frontend/public/imgs/logos/serverlessfx.png | Bin 0 -> 2461 bytes 8 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 frontend/public/imgs/logos/serverlessfx.png 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 0000000000000000000000000000000000000000..1529fb6c14000aecac1be637a830bbc534362eff GIT binary patch literal 2461 zcmV;O31aq%P)Px;S4l)cR9FekS$j;J#TMTe%kHwvvMjIC@`hayN|i#PR_c;c`m{(I8jZ#_&}t01 zNj16phsN|DQ|~ouqNZMwi>NVetcgveX{t8hBfeTSH7c5__=+0)RGYH|l<9!uUSXi>v>2!YXa=G49ftrS* zX&X$klbD$JORzmnuzGc1R3g1zZw=ckR;+kH!bDwPUw^9(fR2)rlT)DrDTUc=zAOPo z*D0` z_^i6Tyxa<4crKE3~{-sBORBKOU7Jh*VNiQl3XUvPPh?Wsvc_91xLf zLPElLO-)UCL;{%ey`WDjY^P;q?F&mxh2v`la*FMuqM}t{2?;DIDOnBQ{ekVizCH_U z7PuCTGE<=Vi9jGwN(hyXLEj`LC4B<9&&*Iww?fq+j3EYUC}%tuK%dViI1^u2SC`LP zaDo1=$0GhBAXw-ePB=~{9yy2sg%yTAhuV36W=yIHG+(yN_#8kw1y8KCE$8JaH>3mt<@ zqJ0cQm6eqR@d&5V!X2Ds4kp8pQs+Y|QM?W4@DQ1zeq4?%C@9Dz%w#qX!rvT(FI~FS z!)*X#D%h$yl6F=dBBjnCd3kva7$8(+v)QU1v5Lx7sJRZ+WfX^xw6&2gs+uKvMh-J%``6{cb)@xyg+}!;C$mL*{Tnrtg zBaYG086XC&VaJXgw(kZ84v&qFx=V_SoA&SDe<9YGsQ8sDR~E~afKVDI5Oq*ABm)YJ zBLWIS@Ync0CS&;JkD-Q)#z%)k)^R(TAfBHiqC60A(gP@YOyAUT3U+ve+idpJ=qgz` zIrmB$10z=Q~WUiKAT2{U@(HEs>cpR%&DHc_frU%cm%5ywX}{EvypR-B$sZ~}z2-z3a#x3?TTcu=3riDk|42Vw6h znWm+sk@in&2a#CW*Mcb0 z4>H$Rf#Y%Vq?U$39KQPvaWEhgYhFH>>At9?3)eOwV2qKtrlFxclwj4WRW3-(SSs8P zle|q|mULmaDcs7A4PeExFCOph?U%%|Ub}Yf2qyha_95}GY)7n-Z1OtBQG2yy%{b_;R{(Qfr z697mc!7SThhyx=eo)P$6;!r? zzYO0vyk^ZBZ)~m$Oo6SCjmbZh!l0gn_WGNfo3EqOAy=$)y4}Yl9>QJ%{y=wkw{3w% ziOddxHN%~fA_4*hK~NZXx!t`zJw4)5z>!RKnAan`{XrUj2rgeB(A-4)RWD5CvFCSb z%sqwN>)Z9~Z_5vz97mM3sHpTtkH`BGt^{2OWUHZVH4cZ;FJ#|{*iwje>w7R2T7tmm z5gm6eyXb+_^I|l#YEY{yN0trw}zh!!i7PfzP)B7Mg#I-MDdMmfi073B2V_KZ{5H+rv$c zV>1VfI3;OWBQg0E>Dm1dLO4GX7#p4uZbbsar&YOlg~zM*EjAqgXfP`h_WLy29ETvi z;j|fKV#FeARt(rqSc|zASy6f?^?;pwXw0_-98GBr2|{sU^eVbgHKi zeG}*R9Z?4X8Nky}UQtmC7WFH-VZ$ATbtNbF6#D(uS7dTbKyCg@t{z&YHs6is7LBt$ z3%S96u|S=$=no-(?SO#tt`TePk?|WgY)FR*ReuBXb9lYm9(lwJhM5x&5RCsox(%>8 zVWEIY+^$~1qw&oDVlyn}+)BWXy;o`zqb8xv_d7c~)if>=CulJjo98*L79s2;4uFsR z6%}pBVUJ_1Dl~j_(3_tA7+$!hFHB^N47&rTh9ClgQd(LXcK717Fcl2HkPI==2;yd} z49N%@KjJNK!dgLoXLyrl#yg%D+17(th>^bH2wHSf`AiH`u2Q2i|e zIrEc7ajgzE>@8|k zW8Va|vv=>_d1XLXvZ`6f6HTyg-8%o+*jVe};LyXT&zv@v`b)bl7IWv(qenmICb*Ib b=R5Xq2A(=z_?cUS00000NkvXXu0mjfXR4A~ literal 0 HcmV?d00001