diff --git a/app/app/private-cloud/products/(product)/[licencePlate]/usage/page.tsx b/app/app/private-cloud/products/(product)/[licencePlate]/usage/page.tsx index dfb4dfb81..40ee9fd0b 100644 --- a/app/app/private-cloud/products/(product)/[licencePlate]/usage/page.tsx +++ b/app/app/private-cloud/products/(product)/[licencePlate]/usage/page.tsx @@ -51,7 +51,7 @@ export default privateCloudProductUsageMetrics(({ pathParams, queryParams, sessi const handleNamespaceChange = (namespace: string) => { setenvironment(namespace); }; - console.log('data', data); + return (
diff --git a/app/components/table/TableBodyMetrics.tsx b/app/components/table/TableBodyMetrics.tsx index dd8337700..a5f1b38c7 100644 --- a/app/components/table/TableBodyMetrics.tsx +++ b/app/components/table/TableBodyMetrics.tsx @@ -1,5 +1,6 @@ 'use client'; +import classNames from 'classnames'; import _truncate from 'lodash-es/truncate'; import React from 'react'; import { getTotalMetrics, ResourceType, Pod } from '@/services/openshift-kubernetis-metrics/helpers'; @@ -88,9 +89,12 @@ export default function TableBodyMetrics({ pods, resource, title, measurementUni {rows.map((row, index) => (
@@ -99,20 +103,20 @@ export default function TableBodyMetrics({ pods, resource, title, measurementUni
- + {_truncate(row.containerName, { length: 100 })}
-
+
{row.usage[resource]} {index !== 0 && measurementUnit}
-
+
{row.limits[resource]} {index !== 0 && measurementUnit}
-
+
{row.requests[resource]} {index !== 0 && measurementUnit}
@@ -130,11 +134,11 @@ export default function TableBodyMetrics({ pods, resource, title, measurementUni index === 0 && 'bg-gray-100' }`} > -
+
{row.totalLimit} {index !== 0 && measurementUnit}
-
+
{row.totalUsage} {index !== 0 && measurementUnit}
diff --git a/app/helpers/auto-approval-check.ts b/app/helpers/auto-approval-check.ts index 0312af7bf..4f45d5f6a 100644 --- a/app/helpers/auto-approval-check.ts +++ b/app/helpers/auto-approval-check.ts @@ -19,7 +19,7 @@ export interface Quotas { } // check if request contains quota change -export const checknoQuotaChange = (currentQuota: Quotas, requestedQuota: Quotas): boolean => { +export const checkNoQuotaChange = (currentQuota: Quotas, requestedQuota: Quotas): boolean => { let noQuotaChange = true; // @ts-ignore _each(currentQuota, (quota: Quota, envQuota: keyof Quotas) => { @@ -28,6 +28,7 @@ export const checknoQuotaChange = (currentQuota: Quotas, requestedQuota: Quotas) _each(quota, (resource: string, resourceName: keyof Quota) => { const currentVal = resource; const requestedVal = requestedQuota[envQuota][resourceName]; + if (extractNumbers(requestedVal)[0] !== extractNumbers(currentVal)[0]) { noQuotaChange = false; return; @@ -40,17 +41,18 @@ export const checknoQuotaChange = (currentQuota: Quotas, requestedQuota: Quotas) // if quota was changed check if it was undngrade quota request // TODO replace isQuotaUpgrade at app/emails/_templates/private-cloud/TeamEditRequest.tsx export const checkIfQuotaUpgrade = (currentQuota: Quotas, requestedQuota: Quotas) => { - let ifQuotaUpgrade = true; - // @ts-ignore - _each(currentQuota, (quota: Quota, envQuota: keyof Quotas) => { - // @ts-ignore - _each(quota, (resource: string, resourceName: keyof Quota) => { - const currentVal = resource; - const requestedVal = requestedQuota[envQuota][resourceName]; - ifQuotaUpgrade = extractNumbers(requestedVal)[0] > extractNumbers(currentVal)[0]; + return Object.keys(currentQuota).some((envQuota) => { + const currentEnvQuota = currentQuota[envQuota as keyof Quotas]; + const requestedEnvQuota = requestedQuota[envQuota as keyof Quotas]; + + return Object.keys(currentEnvQuota).some((resourceName) => { + const currentVal = currentEnvQuota[resourceName as keyof Quota]; + const requestedVal = requestedEnvQuota[resourceName as keyof Quota]; + + // Check if the requested value exceeds the current value + return extractNumbers(requestedVal)[0] > extractNumbers(currentVal)[0]; }); }); - return ifQuotaUpgrade; }; const resourceOrders = { @@ -61,7 +63,6 @@ const resourceOrders = { // Helper function to check resource utilization const checkUtilization = async ( - requestedQuota: Quotas, currentQuota: Quotas, licencePlate: string, cluster: Cluster, @@ -74,18 +75,20 @@ const checkUtilization = async ( const { totalUsage } = getTotalMetrics(podMetricsData, resource); const mesUnitsCoeff = resource === 'cpu' ? 1000 : 1024 * 1024; const totalLimit = extractNumbers(currentQuota[NamespaceNames[namespace]][resource])[1] * mesUnitsCoeff; + const currentUsage = extractNumbers(currentQuota[NamespaceNames[namespace]][resource])[0] * mesUnitsCoeff; // Check quota utilization + // Current Usage in Percentage=( Total Quota Limit/Current Usage)×100 // namespace already use 85% of total limit(i.e: kube_pod_container_resource_limits/namespace-total-limit)(CPU or memory, or storage) - if (totalLimit && totalUsage) { - const utilizationPercentage = (totalLimit / totalUsage) * 100; - if (utilizationPercentage < 86) { + if (totalLimit && totalUsage && currentUsage) { + const utilizationPercentage = (totalLimit / currentUsage) * 100; + if (utilizationPercentage > 86) { return true; // Auto-approval due to utilization percentage } // Check quota usage + // Utilization Rate=( Requested Resources Actual Usage )×100 // namespace has at least 35% of CPU utilization rate(namespace:container_cpu_usage/namespace_cpu:kube_pod_container_resource_requests:sum > 35%, same idea for memory) - const requestedUsage = extractNumbers(requestedQuota[NamespaceNames[namespace]][resource])[0] * mesUnitsCoeff; - const requestedUtilizationRate = (requestedUsage / totalUsage) * 100; + const requestedUtilizationRate = (currentUsage / totalUsage) * 100; if (requestedUtilizationRate > 34) { return true; // Auto-approval due to utilization percentage } @@ -94,7 +97,6 @@ const checkUtilization = async ( }; const checkIfResourceUtilized = async ( - requestedQuota: Quotas, currentQuota: Quotas, licencePlate: string, cluster: Cluster, @@ -109,7 +111,6 @@ const checkIfResourceUtilized = async ( const utilizationChecks = filteredNamespaces.flatMap((namespace) => resourceNames.map((resource) => checkUtilization( - requestedQuota, currentQuota, licencePlate, cluster, @@ -131,20 +132,21 @@ export const checkIfQuotaAutoApproval = async ( licencePlate: string, cluster: Cluster, ) => { - const noQuotaChange = checknoQuotaChange(currentQuota, requestedQuota); - let isAutoApprovalAvailable = noQuotaChange; + const castCurrentQuota = { + testQuota: currentQuota.testQuota, + toolsQuota: currentQuota.toolsQuota, + developmentQuota: currentQuota.developmentQuota, + productionQuota: currentQuota.productionQuota, + }; + + let isAutoApprovalAvailable = checkNoQuotaChange(castCurrentQuota, requestedQuota); + if (!isAutoApprovalAvailable) { + isAutoApprovalAvailable = !checkIfQuotaUpgrade(castCurrentQuota, requestedQuota); + } const namespaceNames: string[] = []; const resourceNames: string[] = []; - if (!noQuotaChange) { + if (!isAutoApprovalAvailable) { let hasIncreasedSignificantly = false; - - const castCurrentQuota = { - testQuota: currentQuota.testQuota, - toolsQuota: currentQuota.toolsQuota, - developmentQuota: currentQuota.developmentQuota, - productionQuota: currentQuota.productionQuota, - }; - // Iterate over each environment's quota // @ts-ignore _each(castCurrentQuota, (quota: Quota, envQuota: keyof Quotas) => { @@ -159,12 +161,12 @@ export const checkIfQuotaAutoApproval = async ( const requestedIndex = Object.keys(resourceOrder).indexOf(requestedResource); const isIncreased = requestedIndex > currentIndex; + if (isIncreased) { isAutoApprovalAvailable = false; // Check if the increase is significant(more than next tier) hasIncreasedSignificantly = requestedIndex - currentIndex > 1; if (hasIncreasedSignificantly) { - isAutoApprovalAvailable = false; return; } // If not a significant increase, check resource utilization @@ -175,18 +177,17 @@ export const checkIfQuotaAutoApproval = async ( } }); }); - } - // TODO remove condition if storage metrics are availiabe - if (resourceNames.indexOf('storage') === -1) { - isAutoApprovalAvailable = await checkIfResourceUtilized( - requestedQuota, - currentQuota, - licencePlate, - cluster, - namespaceNames, - resourceNames, - ); - } - return { isAutoApprovalAvailable, noQuotaChange }; + // TODO remove condition if storage metrics are availiabe + if (namespaceNames.length > 0 && resourceNames.indexOf('storage') === -1) { + isAutoApprovalAvailable = await checkIfResourceUtilized( + currentQuota, + licencePlate, + cluster, + namespaceNames, + resourceNames, + ); + } + } + return isAutoApprovalAvailable; }; diff --git a/app/request-actions/private-cloud/edit-request.ts b/app/request-actions/private-cloud/edit-request.ts index 3bcfbe56e..a94d67bee 100644 --- a/app/request-actions/private-cloud/edit-request.ts +++ b/app/request-actions/private-cloud/edit-request.ts @@ -56,7 +56,7 @@ export default async function editRequest( const hasGolddrEnabledChanged = project.cluster === Cluster.GOLD && project.golddrEnabled !== formData.golddrEnabled; - const quotaReviewResult = await checkIfQuotaAutoApproval( + const isAutoApprovalAvailable = await checkIfQuotaAutoApproval( project as Quotas, formData as Quotas, project.licencePlate, @@ -64,7 +64,7 @@ export default async function editRequest( ); // If there is no quota change or no quota upgrade and no golddr flag changes, the request is automatically approved - if (quotaReviewResult.isAutoApprovalAvailable && !hasGolddrEnabledChanged) { + if (isAutoApprovalAvailable && !hasGolddrEnabledChanged) { decisionStatus = DecisionStatus.APPROVED; } else { decisionStatus = DecisionStatus.PENDING; @@ -75,7 +75,7 @@ export default async function editRequest( const { changes, ...otherChangeMeta } = comparePrivateProductData(rest, previousRequest?.decisionData); - const quotaChangeInfo = quotaReviewResult.noQuotaChange + const quotaChangeInfo = isAutoApprovalAvailable ? {} : { quotaContactName, @@ -87,7 +87,7 @@ export default async function editRequest( data: { type: RequestType.EDIT, decisionStatus, - isQuotaChanged: !quotaReviewResult.noQuotaChange, + isQuotaChanged: !isAutoApprovalAvailable, ...quotaChangeInfo, active: true, createdBy: { connect: { email: session.user.email } },