From 6a613e067e3fadda2a031f65b6a2f7b80cb3667f Mon Sep 17 00:00:00 2001 From: ashutosh16 <11219262+ashutosh16@users.noreply.github.com> Date: Mon, 30 Oct 2023 13:09:21 -0700 Subject: [PATCH] bug: add exec check to avoid API calls --- .../resource-details/resource-details.tsx | 52 ++++---- ui/src/app/applications/components/utils.tsx | 119 ++++++++---------- 2 files changed, 80 insertions(+), 91 deletions(-) diff --git a/ui/src/app/applications/components/resource-details/resource-details.tsx b/ui/src/app/applications/components/resource-details/resource-details.tsx index 6477509370905..fa59fe668792d 100644 --- a/ui/src/app/applications/components/resource-details/resource-details.tsx +++ b/ui/src/app/applications/components/resource-details/resource-details.tsx @@ -50,7 +50,7 @@ export const ResourceDetails = (props: ResourceDetailsProps) => { tabs: Tab[], execEnabled: boolean, execAllowed: boolean, - logsAllowed: boolean + logsAllowed: boolean, ) => { if (!node || node === undefined) { return []; @@ -66,7 +66,7 @@ export const ResourceDetails = (props: ResourceDetailsProps) => {
- ) + ), }); } if (podState && podState.metadata && podState.spec) { @@ -74,14 +74,14 @@ export const ResourceDetails = (props: ResourceDetailsProps) => { { offset: 0, title: 'CONTAINERS', - containers: podState.spec.containers || [] - } + containers: podState.spec.containers || [], + }, ]; if (podState.spec.initContainers?.length > 0) { containerGroups.push({ offset: (podState.spec.containers || []).length, title: 'INIT CONTAINERS', - containers: podState.spec.initContainers || [] + containers: podState.spec.initContainers || [], }); } @@ -111,8 +111,8 @@ export const ResourceDetails = (props: ResourceDetailsProps) => { onClickContainer={onClickContainer} /> - ) - } + ), + }, ]); } if (selectedNode.kind === 'Pod' && execEnabled && execAllowed) { @@ -131,8 +131,8 @@ export const ResourceDetails = (props: ResourceDetailsProps) => { containerName={AppUtils.getContainerName(podState, activeContainer)} onClickContainer={onClickContainer} /> - ) - } + ), + }, ]); } } @@ -146,7 +146,7 @@ export const ResourceDetails = (props: ResourceDetailsProps) => { ), - icon: tabExtensions.icon + icon: tabExtensions.icon, }); }); } @@ -158,7 +158,7 @@ export const ResourceDetails = (props: ResourceDetailsProps) => { { title: 'SUMMARY', key: 'summary', - content: updateApp(app, query)} /> + content: updateApp(app, query)} />, }, { title: 'PARAMETERS', @@ -170,7 +170,7 @@ export const ResourceDetails = (props: ResourceDetailsProps) => { load={app => services.repos.appDetails(AppUtils.getAppDefaultSource(app), app.metadata.name, app.spec.project).catch(() => ({ type: 'Directory' as AppSourceType, - path: AppUtils.getAppDefaultSource(app).path + path: AppUtils.getAppDefaultSource(app).path, })) }> {(details: RepoAppDetails) => ( @@ -181,7 +181,7 @@ export const ResourceDetails = (props: ResourceDetailsProps) => { /> )} - ) + ), }, { title: 'MANIFEST', @@ -195,8 +195,8 @@ export const ResourceDetails = (props: ResourceDetailsProps) => { return services.applications.updateSpec(application.metadata.name, application.metadata.namespace, jsonMergePatch.apply(spec, JSON.parse(patch))); }} /> - ) - } + ), + }, ]; if (application.status.sync.status !== SyncStatuses.Synced) { @@ -209,26 +209,26 @@ export const ResourceDetails = (props: ResourceDetailsProps) => { key='diff' load={async () => await services.applications.managedResources(application.metadata.name, application.metadata.namespace, { - fields: ['items.normalizedLiveState', 'items.predictedLiveState', 'items.group', 'items.kind', 'items.namespace', 'items.name'] + fields: ['items.normalizedLiveState', 'items.predictedLiveState', 'items.group', 'items.kind', 'items.namespace', 'items.name'], }) }> {managedResources => } - ) + ), }); } tabs.push({ title: 'EVENTS', key: 'event', - content: + content: , }); const extensionTabs = services.extensions.getResourceTabs('argoproj.io', 'Application').map((ext, i) => ({ title: ext.title, key: `extension-${i}`, content: , - icon: ext.icon + icon: ext.icon, })); return tabs.concat(extensionTabs); @@ -248,8 +248,8 @@ export const ResourceDetails = (props: ResourceDetailsProps) => { name: selectedNode.name, namespace: selectedNode.namespace, kind: selectedNode.kind, - group: selectedNode.group - } + group: selectedNode.group, + }, }); const controlled = managedResources.find(item => AppUtils.isSameNode(selectedNode, item)); const summary = application.status.resources.find(item => AppUtils.isSameNode(selectedNode, item)); @@ -264,7 +264,7 @@ export const ResourceDetails = (props: ResourceDetailsProps) => { (await services.applications.resourceEvents(application.metadata.name, application.metadata.namespace, { name: liveState.metadata.name, namespace: liveState.metadata.namespace, - uid: liveState.metadata.uid + uid: liveState.metadata.uid, }))) || []; let podState: State; @@ -280,7 +280,7 @@ export const ResourceDetails = (props: ResourceDetailsProps) => { const settings = await services.authService.settings(); const execEnabled = settings.execEnabled; const logsAllowed = await services.accounts.canI('logs', 'get', application.spec.project + '/' + application.metadata.name); - const execAllowed = await services.accounts.canI('exec', 'create', application.spec.project + '/' + application.metadata.name); + const execAllowed = settings.execEnabled && (await services.accounts.canI('exec', 'create', application.spec.project + '/' + application.metadata.name)); const links = await services.applications.getResourceLinks(application.metadata.name, application.metadata.namespace, selectedNode).catch(() => null); return {controlledState, liveState, events, podState, execEnabled, execAllowed, logsAllowed, links}; }}> @@ -343,12 +343,12 @@ export const ResourceDetails = (props: ResourceDetailsProps) => { node={selectedNode} links={data.links} /> - ) - } + ), + }, ], data.execEnabled, data.execAllowed, - data.logsAllowed + data.logsAllowed, )} selectedTabKey={props.tab} onTabSelected={selected => appContext.navigation.goto('.', {tab: selected}, {replace: true})} diff --git a/ui/src/app/applications/components/utils.tsx b/ui/src/app/applications/components/utils.tsx index 674ffc6728db4..216b2db037ac5 100644 --- a/ui/src/app/applications/components/utils.tsx +++ b/ui/src/app/applications/components/utils.tsx @@ -53,16 +53,16 @@ export async function deleteApplication(appName: string, appNamespace: string, a const propagationPolicies: {name: string; message: string}[] = [ { name: 'Foreground', - message: `Cascade delete the application's resources using foreground propagation policy` + message: `Cascade delete the application's resources using foreground propagation policy`, }, { name: 'Background', - message: `Cascade delete the application's resources using background propagation policy` + message: `Cascade delete the application's resources using background propagation policy`, }, { name: 'Non-cascading', - message: `Only delete the application, but do not cascade delete its resources` - } + message: `Only delete the application, but do not cascade delete its resources`, + }, ]; await apis.popup.prompt( 'Delete application', @@ -91,7 +91,7 @@ export async function deleteApplication(appName: string, appNamespace: string, a component={PropagationPolicyOption} componentProps={{ policy: policy.name, - message: policy.message + message: policy.message, }} /> ); @@ -101,7 +101,7 @@ export async function deleteApplication(appName: string, appNamespace: string, a ), { validate: vals => ({ - applicationName: vals.applicationName !== appName && 'Enter the application name to confirm the deletion' + applicationName: vals.applicationName !== appName && 'Enter the application name to confirm the deletion', }), submit: async (vals, _, close) => { try { @@ -111,14 +111,14 @@ export async function deleteApplication(appName: string, appNamespace: string, a } catch (e) { apis.notifications.show({ content: , - type: NotificationType.Error + type: NotificationType.Error, }); } - } + }, }, {name: 'argo-icon-warning', color: 'warning'}, 'yellow', - {propagationPolicy: 'foreground'} + {propagationPolicy: 'foreground'}, ); return confirmed; } @@ -148,7 +148,7 @@ export async function confirmSyncingAppOfApps(apps: appModels.Application[], api ), { validate: vals => ({ - applicationName: vals.applicationName !== appNameList && 'Enter the application name(s) to confirm syncing' + applicationName: vals.applicationName !== appNameList && 'Enter the application name(s) to confirm syncing', }), submit: async (_vals, _, close) => { try { @@ -158,20 +158,20 @@ export async function confirmSyncingAppOfApps(apps: appModels.Application[], api } catch (e) { apis.notifications.show({ content: , - type: NotificationType.Error + type: NotificationType.Error, }); } - } + }, }, {name: 'argo-icon-warning', color: 'warning'}, - 'yellow' + 'yellow', ); return confirmed; } const PropagationPolicyOption = ReactForm.FormField((props: {fieldApi: ReactForm.FieldApi; policy: string; message: string}) => { const { - fieldApi: {setValue} + fieldApi: {setValue}, } = props; return (
@@ -224,7 +224,7 @@ export const ComparisonStatusIcon = ({ status, resource, label, - noSpin + noSpin, }: { status: appModels.SyncStatusCode; resource?: {requiresPruning?: boolean}; @@ -316,18 +316,18 @@ export const deletePodAction = async (pod: appModels.Pod, appContext: AppContext } catch (e) { appContext.apis.notifications.show({ content: , - type: NotificationType.Error + type: NotificationType.Error, }); } - } - } + }, + }, ); }; export const deletePopup = async (ctx: ContextApis, resource: ResourceTreeNode, application: appModels.Application, appChanged?: BehaviorSubject) => { const isManaged = !!resource.status; const deleteOptions = { - option: 'foreground' + option: 'foreground', }; function handleStateChange(option: string) { deleteOptions.option = option; @@ -371,7 +371,7 @@ export const deletePopup = async (ctx: ContextApis, resource: ResourceTreeNode, { validate: vals => isManaged && { - resourceName: vals.resourceName !== resource.name && 'Enter the resource name to confirm the deletion' + resourceName: vals.resourceName !== resource.name && 'Enter the resource name to confirm the deletion', }, submit: async (vals, _, close) => { const force = deleteOptions.option === 'force'; @@ -385,13 +385,13 @@ export const deletePopup = async (ctx: ContextApis, resource: ResourceTreeNode, } catch (e) { ctx.notifications.show({ content: , - type: NotificationType.Error + type: NotificationType.Error, }); } - } + }, }, {name: 'argo-icon-warning', color: 'warning'}, - 'yellow' + 'yellow', ); }; @@ -414,11 +414,11 @@ function getResourceActionsMenuItems(resource: ResourceTreeNode, metadata: model } catch (e) { apis.notifications.show({ content: , - type: NotificationType.Error + type: NotificationType.Error, }); } - } - } as MenuItem) + }, + }) as MenuItem, ); }) .catch(() => [] as MenuItem[]); @@ -430,7 +430,7 @@ function getActionItems( tree: appModels.ApplicationTree, apis: ContextApis, appChanged: BehaviorSubject, - isQuickStart: boolean + isQuickStart: boolean, ): Observable { const isRoot = resource.root && nodeKey(resource.root) === nodeKey(resource); const items: MenuItem[] = [ @@ -438,8 +438,8 @@ function getActionItems( { title: 'Sync', iconClassName: 'fa fa-fw fa-sync', - action: () => showDeploy(nodeKey(resource), null, apis) - } + action: () => showDeploy(nodeKey(resource), null, apis), + }, ]) || []), { @@ -447,14 +447,14 @@ function getActionItems( iconClassName: 'fa fa-fw fa-times-circle', action: async () => { return deletePopup(apis, resource, application, appChanged); - } - } + }, + }, ]; if (!isQuickStart) { items.unshift({ title: 'Details', iconClassName: 'fa fa-fw fa-info-circle', - action: () => apis.navigation.goto('.', {node: nodeKey(resource)}) + action: () => apis.navigation.goto('.', {node: nodeKey(resource)}), }); } @@ -462,7 +462,7 @@ function getActionItems( items.push({ title: 'Logs', iconClassName: 'fa fa-fw fa-align-left', - action: () => apis.navigation.goto('.', {node: nodeKey(resource), tab: 'logs'}, {replace: true}) + action: () => apis.navigation.goto('.', {node: nodeKey(resource), tab: 'logs'}, {replace: true}), }); } @@ -473,14 +473,14 @@ function getActionItems( const execAction = services.authService .settings() .then(async settings => { - const execAllowed = await services.accounts.canI('exec', 'create', application.spec.project + '/' + application.metadata.name); + const execAllowed = settings.execEnabled && (await services.accounts.canI('exec', 'create', application.spec.project + '/' + application.metadata.name)); if (resource.kind === 'Pod' && settings.execEnabled && execAllowed) { return [ { title: 'Exec', iconClassName: 'fa fa-fw fa-terminal', - action: async () => apis.navigation.goto('.', {node: nodeKey(resource), tab: 'exec'}, {replace: true}) - } as MenuItem + action: async () => apis.navigation.goto('.', {node: nodeKey(resource), tab: 'exec'}, {replace: true}), + } as MenuItem, ]; } return [] as MenuItem[]; @@ -498,8 +498,8 @@ function getActionItems( title: link.title, iconClassName: `fa fa-fw ${link.iconClass ? link.iconClass : 'fa-external-link'}`, action: () => window.open(link.url, '_blank'), - tooltip: link.description - } as MenuItem) + tooltip: link.description, + }) as MenuItem, ); }) .catch(() => [] as MenuItem[]); @@ -508,7 +508,7 @@ function getActionItems( from([items]), // this resolves immediately concat([[] as MenuItem[]], resourceActions), // this resolves at first to [] and then whatever the API returns concat([[] as MenuItem[]], execAction), // this resolves at first to [] and then whatever the API returns - concat([[] as MenuItem[]], links) // this resolves at first to [] and then whatever the API returns + concat([[] as MenuItem[]], links), // this resolves at first to [] and then whatever the API returns ).pipe(map(res => ([] as MenuItem[]).concat(...res))); } @@ -518,7 +518,7 @@ export function renderResourceMenu( tree: appModels.ApplicationTree, apis: ContextApis, appChanged: BehaviorSubject, - getApplicationActionMenu: () => any + getApplicationActionMenu: () => any, ): React.ReactNode { let menuItems: Observable; @@ -593,7 +593,7 @@ export function renderResourceButtons( application: appModels.Application, tree: appModels.ApplicationTree, apis: ContextApis, - appChanged: BehaviorSubject + appChanged: BehaviorSubject, ): React.ReactNode { let menuItems: Observable; menuItems = getActionItems(resource, application, tree, apis, appChanged, true); @@ -613,12 +613,7 @@ export function renderResourceButtons( } }} icon={item.iconClassName} - tooltip={ - item.title - .toString() - .charAt(0) - .toUpperCase() + item.title.toString().slice(1) - } + tooltip={item.title.toString().charAt(0).toUpperCase() + item.title.toString().slice(1)} /> ))}
@@ -810,13 +805,13 @@ export const getAppOperationState = (app: appModels.Application): appModels.Oper message: (app.status && app.status.operationState && app.status.operationState.message) || 'waiting to start', startedAt: new Date().toISOString(), operation: { - sync: {} - } + sync: {}, + }, } as appModels.OperationState; } else if (app.metadata.deletionTimestamp) { return { phase: appModels.OperationPhases.Running, - startedAt: app.metadata.deletionTimestamp + startedAt: app.metadata.deletionTimestamp, } as appModels.OperationState; } else { return app.status.operationState; @@ -952,7 +947,7 @@ export const getPodReadinessGatesState = (pod: appModels.State): {nonExistingCon if (!pod.spec?.readinessGates?.length) { return { nonExistingConditions: [], - notPassedConditions: [] + notPassedConditions: [], }; } @@ -991,7 +986,7 @@ export const getPodReadinessGatesState = (pod: appModels.State): {nonExistingCon return { nonExistingConditions, - notPassedConditions: failedConditions + notPassedConditions: failedConditions, }; }; @@ -1160,7 +1155,7 @@ export function handlePageVisibility(src: () => Observable): Observable subscription = src().subscribe( (item: T) => observer.next(item), err => observer.error(err), - () => observer.complete() + () => observer.complete(), ); }; @@ -1215,15 +1210,15 @@ export const BASE_COLORS = [ '#FF9500', // orange '#4B0082', // purple '#F5d905', // yellow - '#964B00' // brown + '#964B00', // brown ]; export const urlPattern = new RegExp( new RegExp( // tslint:disable-next-line:max-line-length /^(https?:\/\/(?:www\.|(?!www))[a-z0-9][a-z0-9-]+[a-z0-9]\.[^\s]{2,}|www\.[a-z0-9][a-z0-9-]+[a-z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-z0-9]+\.[^\s]{2,}|www\.[a-z0-9]+\.[^\s]{2,})$/, - 'gi' - ) + 'gi', + ), ); export function appQualifiedName(app: appModels.Application, nsEnabled: boolean): string { @@ -1235,14 +1230,8 @@ export function appInstanceName(app: appModels.Application): string { } export function formatCreationTimestamp(creationTimestamp: string) { - const createdAt = moment - .utc(creationTimestamp) - .local() - .format('MM/DD/YYYY HH:mm:ss'); - const fromNow = moment - .utc(creationTimestamp) - .local() - .fromNow(); + const createdAt = moment.utc(creationTimestamp).local().format('MM/DD/YYYY HH:mm:ss'); + const fromNow = moment.utc(creationTimestamp).local().fromNow(); return ( {createdAt} @@ -1264,5 +1253,5 @@ export function getUsrMsgKeyToDisplay(appName: string, msgKey: string, usrMessag export const userMsgsList: {[key: string]: string} = { groupNodes: `Since the number of pods has surpassed the threshold pod count of 15, you will now be switched to the group node view. - If you prefer the tree view, you can simply click on the Group Nodes toolbar button to deselect the current view.` + If you prefer the tree view, you can simply click on the Group Nodes toolbar button to deselect the current view.`, };