Skip to content

Commit

Permalink
Quota charts now show all resource types
Browse files Browse the repository at this point in the history
  • Loading branch information
rawagner committed Jun 21, 2022
1 parent 2ad4e17 commit b41238b
Show file tree
Hide file tree
Showing 26 changed files with 537 additions and 412 deletions.
1 change: 1 addition & 0 deletions frontend/packages/console-app/locales/en/console-app.json
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@
"Leave quick start?": "Leave quick start?",
"Leave": "Leave",
"Your progress will be saved.": "Your progress will be saved.",
"No quota": "No quota",
"Get started": "Get started",
"Skip tour": "Skip tour",
"Okay, got it!": "Okay, got it!",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { DonutChart } from '@console/internal/components/graphs/donut';
import { AppliedClusterResourceQuotaKind } from '@console/internal/module/k8s';
import { getUsedPercentage, getLabelAndUsage } from './utils';

import './resource-quota.scss';

type AppliedClusterResourceQuotaChartsProps = {
appliedClusterResourceQuota: AppliedClusterResourceQuotaKind;
namespace: string;
};

const AppliedClusterResourceQuotaCharts = ({
appliedClusterResourceQuota,
namespace,
}: AppliedClusterResourceQuotaChartsProps): JSX.Element => {
const { t } = useTranslation();
const nsQuotas = appliedClusterResourceQuota.status?.namespaces?.find(
(ns) => ns.namespace === namespace,
);

const charts = Object.keys(nsQuotas?.status?.hard ?? {}).map((resourceName) => {
const clusterHard = appliedClusterResourceQuota.status.total?.hard?.[resourceName];
const clusterUsed = appliedClusterResourceQuota.status.total?.used?.[resourceName];
const nsUsed = nsQuotas.status.used?.[resourceName];
const clusterUsage = getUsedPercentage(clusterHard, clusterUsed);
const unused = 100 - clusterUsage;

const { label, percent: nsUsage } = getLabelAndUsage({
resourceName,
used: nsUsed,
hard: clusterHard,
});

const percentOtherNamespaces = clusterUsage - nsUsage;

return (
<div
key={resourceName}
className="co-resource-quota__chart"
data-test="resource-quota-gauge-chart"
>
<DonutChart
data={[
{
x: 'Namespace',
y: nsUsage,
},
{
x: 'Other namespaces',
y: percentOtherNamespaces,
},
{
x: 'Unused',
y: unused,
},
]}
title={resourceName}
label={label}
/>
</div>
);
});

return (
<div className="co-resource-quota-chart-row">
{charts.length ? charts : <>{t('console-app~No quota')}</>}
</div>
);
};

export default AppliedClusterResourceQuotaCharts;
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { DonutChart } from '@console/internal/components/graphs/donut';
import { ClusterResourceQuotaKind } from '@console/internal/module/k8s';
import { getLabelAndUsage } from './utils';

import './resource-quota.scss';

type ClusterResourceQuotaChartsProps = {
clusterResourceQuota: ClusterResourceQuotaKind;
};

const ClusterResourceQuotaCharts = ({
clusterResourceQuota,
}: ClusterResourceQuotaChartsProps): JSX.Element => {
const { t } = useTranslation();
const charts = Object.keys(clusterResourceQuota.status?.total?.hard ?? {}).map((resourceName) => {
const clusterHard = clusterResourceQuota.status.total.hard[resourceName];
const clusterUsed = clusterResourceQuota.status.total.used?.[resourceName];

const { label, percent } = getLabelAndUsage({
resourceName,
used: clusterUsed,
hard: clusterHard,
});

return (
<div
key={resourceName}
className="co-resource-quota__chart"
data-test="resource-quota-gauge-chart"
>
<DonutChart
data={[
{
x: 'Used',
y: percent,
},
{
x: 'Unused',
y: 100 - percent,
},
]}
title={resourceName}
label={label}
/>
</div>
);
});

return (
<div className="co-resource-quota-chart-row">
{charts.length ? charts : <>{t('console-app~No quota')}</>}
</div>
);
};

export default ClusterResourceQuotaCharts;
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { GaugeChart } from '@console/internal/components/graphs/gauge';
import { ResourceQuotaKind } from '@console/internal/module/k8s';
import { getLabelAndUsage } from './utils';

import './resource-quota.scss';

type ResourceQuotaChartsProps = {
resourceQuota: ResourceQuotaKind;
};

const ResourceQuotaCharts = ({ resourceQuota }: ResourceQuotaChartsProps): JSX.Element => {
const { t } = useTranslation();
const charts = Object.keys(resourceQuota.status?.hard ?? {}).map((resourceName) => {
const hard = resourceQuota.status.hard[resourceName];
const used = resourceQuota.status.used?.[resourceName];

const { label, percent } = getLabelAndUsage({ resourceName, used, hard });
return (
<div
key={resourceName}
className="co-resource-quota__chart"
data-test="resource-quota-gauge-chart"
>
<GaugeChart
data={{
x: `${percent}%`,
y: percent,
}}
thresholds={[{ value: 90 }, { value: 101 }]}
title={resourceName}
label={label}
/>
</div>
);
});

return (
<div className="co-resource-quota-chart-row">
{charts.length ? charts : <>{t('console-app~No quota')}</>}
</div>
);
};

export default ResourceQuotaCharts;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.co-resource-quota__chart {
min-width: 170px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { convertToBaseValue, humanizePercentage } from '@console/internal/components/utils';

const genericCountResources = [
'configmaps',
'persistentvolumeclaims',
'pods',
'replicationcontrollers',
'resourcequotas',
'services',
'services.loadbalancers',
'services.nodeports',
'secrets',
'openshift.io/imagestreams',
];

export const getUsedPercentage = (hard: string, used: string) => {
let usedNum = convertToBaseValue(used);
let hardNum = convertToBaseValue(hard);

if (!usedNum || !hardNum) {
// try to get the value without unit
usedNum = parseInt(usedNum, 10);
hardNum = parseInt(hardNum, 10);
}

return !usedNum || !hardNum ? 0 : (usedNum / hardNum) * 100;
};

export const getLabelAndUsage = ({
resourceName,
used,
hard,
}: {
resourceName: string;
used: string;
hard: string;
}) => {
const useCount =
resourceName.startsWith('count/') || genericCountResources.includes(resourceName);

const percent = getUsedPercentage(hard, used);

return {
label: useCount ? `${used || 0} of ${hard}` : humanizePercentage(percent).string,
percent,
};
};
34 changes: 33 additions & 1 deletion frontend/packages/console-dynamic-plugin-sdk/docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -591,10 +591,42 @@ A tuple containing the data filtered by all static filteres, the data filtered b

### Summary

[For more details please refer the implementation](https://github.com/openshift/console/tree/release-4.11/frontend/public/components/utils/resource-link.tsx)
Component that creates a link to a specific resource type with an icon badge



### Example


```tsx
<ResourceLink
kind="Pod"
name="testPod"
title={metadata.uid}
/>
```





### Parameters

| Parameter Name | Description |
| -------------- | ----------- |
| `kind` | (optional) the kind of resource i.e. Pod, Deployment, Namespace |
| `groupVersionKind` | (optional) object with groupd, version, and kind |
| `className` | (optional) class style for component |
| `displayName` | (optional) display name for component, overwrites the resource name if set |
| `inline` | (optional) flag to create icon badge and name inline with children |
| `linkTo` | (optional) flag to create a Link object - defaults to true |
| `name` | (optional) name of resource |
| `namesapce` | (optional) specific namespace for the kind resource to link to |
| `hideIcon` | (optional) flag to hide the icon badge |
| `title` | (optional) title for the link object (not displayed) |
| `dataTest` | (optional) identifier for testing |
| `onClick` | (optional) callback function for when component is clicked |
| `truncate` | (optional) flag to truncate the link if too long |



Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,31 @@ export const ListPageFilter: React.FC<ListPageFilterProps> = require('@console/i
*/
export const useListPageFilter: UseListPageFilter = require('@console/internal/components/factory/ListPage/filter-hook')
.useListPageFilter;

/**
* Component that creates a link to a specific resource type with an icon badge
* @param {K8sResourceKindReference} [kind] - (optional) the kind of resource i.e. Pod, Deployment, Namespace
* @param {K8sGroupVersionKind} [groupVersionKind] - (optional) object with groupd, version, and kind
* @param {string} [className] - (optional) class style for component
* @param {string} [displayName] - (optional) display name for component, overwrites the resource name if set
* @param {boolean} [inline=false] - (optional) flag to create icon badge and name inline with children
* @param {boolean} [linkTo=true] - (optional) flag to create a Link object - defaults to true
* @param {string} [name] - (optional) name of resource
* @param {string} [namesapce] - (optional) specific namespace for the kind resource to link to
* @param {boolean} [hideIcon] - (optional) flag to hide the icon badge
* @param {string} [title] - (optional) title for the link object (not displayed)
* @param {string} [dataTest] - (optional) identifier for testing
* @param {function} [onClick] - (optional) callback function for when component is clicked
* @param {boolean} [truncate=false] - (optional) flag to truncate the link if too long
* @example
* ```tsx
* <ResourceLink
* kind="Pod"
* name="testPod"
* title={metadata.uid}
* />
* ```
*/
export const ResourceLink: React.FC<ResourceLinkProps> = require('@console/internal/components/utils/resource-link')
.ResourceLink;
export { default as ResourceStatus } from '../app/components/utils/resource-status';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,7 @@ export type ResourceLinkProps = {
title?: string;
dataTest?: string;
onClick?: () => void;
truncate?: boolean;
};

export type UseK8sModel = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@
"There are no ongoing activities.": "There are no ongoing activities.",
"Ongoing": "Ongoing",
"Not available": "Not available",
"{{count}} resource": "{{count}} resource",
"{{count}} resource_plural": "{{count}} resources",
"{{count}} resource reached quota": "{{count}} resource reached quota",
"{{count}} resource reached quota_plural": "{{count}} resources reached quota",
"none are at quota": "none are at quota",
"No ResourceQuotas": "No ResourceQuotas",
"View details": "View details",
"Alerts could not be loaded.": "Alerts could not be loaded.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const mapCardsToGrid = (

const DashboardGrid: React.FC<OverviewGridProps> = ({ mainCards, leftCards, rightCards }) => {
const [containerRef, width] = useRefWidth();
const smallGrid = !!containerRef.current && width <= parseInt(breakpointLG.value, 10);
const smallGrid = !!width && width <= parseInt(breakpointLG.value, 10);

const mainGridCards = React.useMemo(() => mapCardsToGrid(mainCards, 'main', smallGrid), [
mainCards,
Expand Down
Loading

0 comments on commit b41238b

Please sign in to comment.