diff --git a/frontend/packages/console-shared/src/types/resource.ts b/frontend/packages/console-shared/src/types/resource.ts index b9b037e138f..7160daf1658 100644 --- a/frontend/packages/console-shared/src/types/resource.ts +++ b/frontend/packages/console-shared/src/types/resource.ts @@ -1,4 +1,4 @@ -import { K8sResourceKind, PodKind, RouteKind } from '@console/internal/module/k8s'; +import { K8sResourceKind, PodKind, RouteKind, EventKind } from '@console/internal/module/k8s'; import { DEPLOYMENT_STRATEGY } from '../constants'; import { OverviewItemAlerts, PodControllerOverviewItem } from './pod'; @@ -21,7 +21,7 @@ export type OverviewItem = { configurations?: K8sResourceKind[]; ksservices?: K8sResourceKind[]; revisions?: K8sResourceKind[]; - events?: K8sResourceKind[]; + events?: EventKind[]; }; export type DeploymentStrategy = DEPLOYMENT_STRATEGY.recreate | DEPLOYMENT_STRATEGY.rolling; diff --git a/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringEvents.scss b/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringEvents.scss new file mode 100644 index 00000000000..83be8c2807c --- /dev/null +++ b/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringEvents.scss @@ -0,0 +1,14 @@ +.odc-monitoring-events { + &__event-item { + margin-bottom: var(--pf-global--spacer--md); + } + &__warning-icon { + font-size: var(--pf-global--FontSize--xl); + } + &__event-message { + font-size: var(--pf-global--FontSize--sm); + color: var(--pf-global--Color--100); + margin-top: var(--pf-global--spacer--sm); + word-wrap: break-word; + } +} diff --git a/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringEvents.tsx b/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringEvents.tsx new file mode 100644 index 00000000000..981b8814c15 --- /dev/null +++ b/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringEvents.tsx @@ -0,0 +1,47 @@ +import * as React from 'react'; +import * as _ from 'lodash'; +import { Flex, FlexItem, FlexModifiers } from '@patternfly/react-core'; +import { twentyFourHourTime } from '@console/internal/components/utils/datetime'; +import { YellowExclamationTriangleIcon } from '@console/shared'; +import { referenceFor, EventKind } from '@console/internal/module/k8s'; +import { ResourceLink } from '@console/internal/components/utils'; +import MonitoringEventsWarning from './MonitoringEventsWarning'; +import './MonitoringEvents.scss'; + +type MonitoringEventsProps = React.ComponentProps; + +const MonitoringEvents: React.FC = ({ events }) => ( +
+ {!_.isEmpty(events) ? ( + _.map(events, (e: EventKind) => { + return ( +
+ + + {twentyFourHourTime(new Date(e.lastTimestamp))} + + {e.type === 'Warning' && ( + + + + )} + + + + +
{e.message}
+
+ ); + }) + ) : ( +
There are no recent events.
+ )} +
+); + +export default MonitoringEvents; diff --git a/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringEventsWarning.scss b/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringEventsWarning.scss new file mode 100644 index 00000000000..800af1df6f6 --- /dev/null +++ b/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringEventsWarning.scss @@ -0,0 +1,8 @@ +.odc-monitoring-events-warning { + &__no-alerts { + padding-left: var(--pf-global--spacer--xl); + } + &__timestamp { + margin-top: var(--pf-global--spacer--sm); + } +} diff --git a/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringEventsWarning.tsx b/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringEventsWarning.tsx new file mode 100644 index 00000000000..e82722d1ffa --- /dev/null +++ b/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringEventsWarning.tsx @@ -0,0 +1,33 @@ +import * as React from 'react'; +import { Alert } from '@patternfly/react-core'; +import * as _ from 'lodash'; +import { fromNow } from '@console/internal/components/utils/datetime'; +import { EventKind } from '@console/internal/module/k8s'; +import './MonitoringEventsWarning.scss'; + +interface MonitoringEventsWarningProps { + events: EventKind[]; +} + +const MonitoringEventsWarning: React.FC = ({ events }) => ( +
+ {!_.isEmpty(events) ? ( + _.map(events, (e: EventKind) => { + return ( + + {e.message} +
+ {fromNow(e.lastTimestamp)} +
+
+ ); + }) + ) : ( +
+ There are no events warning. +
+ )} +
+); + +export default MonitoringEventsWarning; diff --git a/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringMetrics.tsx b/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringMetrics.tsx new file mode 100644 index 00000000000..847ce1e391f --- /dev/null +++ b/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringMetrics.tsx @@ -0,0 +1,28 @@ +import * as React from 'react'; +import * as _ from 'lodash'; +import { requirePrometheus } from '@console/internal/components/graphs'; +import { workloadMetricQueries } from './queries'; +import MonitoringDashboardGraph from '../dashboard/MonitoringDashboardGraph'; + +const WorkloadGraphs = requirePrometheus(({ resource }) => { + const ns = resource?.metadata?.namespace; + const workloadName = resource?.metadata?.name; + const workloadType = resource?.kind?.toLowerCase(); + return ( + <> + {_.map(workloadMetricQueries, (q) => ( + + ))} + + ); +}); + +export default WorkloadGraphs; diff --git a/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringMetricsSection.tsx b/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringMetricsSection.tsx deleted file mode 100644 index 3da9c50f9a9..00000000000 --- a/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringMetricsSection.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import * as React from 'react'; -import * as _ from 'lodash'; -import { - Accordion, - AccordionItem, - AccordionToggle, - AccordionContent, - Grid, - GridItem, -} from '@patternfly/react-core'; -import { K8sResourceKind } from '@console/internal/module/k8s'; -import { requirePrometheus } from '@console/internal/components/graphs'; -import MonitoringDashboardGraph from '../dashboard/MonitoringDashboardGraph'; -import { workloadMetricQueries } from './queries'; -import './MonitoringSection.scss'; - -const WorkloadGraphs = requirePrometheus(({ resource }) => { - const ns = resource.metadata.namespace; - const workloadName = resource.metadata.name; - const workloadType = resource.kind.toLowerCase(); - return ( - - {_.map(workloadMetricQueries, (q) => ( - - - - ))} - - ); -}); - -type MonitoringMetricsSectionProps = { - resource: K8sResourceKind; -}; - -const MonitoringMetricsSection: React.FC = ({ resource }) => { - const [expanded, setExpanded] = React.useState(''); - - const onToggle = (id: string) => { - setExpanded(id === expanded ? '' : id); - }; - - return ( -
- - - { - onToggle('metrics'); - }} - isExpanded={expanded === 'metrics'} - id="metrics" - > - Metrics - - - - - - -
- ); -}; - -export default MonitoringMetricsSection; diff --git a/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringOverview.scss b/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringOverview.scss new file mode 100644 index 00000000000..fb2ca00b7bc --- /dev/null +++ b/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringOverview.scss @@ -0,0 +1,24 @@ +.odc-monitoring-overview { + &__metric-accordion { + padding: 0; + } + &__event-warning-toggle { + .pf-c-accordion__toggle-text { + width: 100% + } + } + &__event-warning-body { + .pf-c-accordion__expanded-content-body { + padding: var(--pf-global--spacer--sm) 0; + } + } + h5 { + margin: 0; + } + .pf-c-accordion__expanded-content.pf-m-expanded { + --pf-c-accordion__expanded-content--BorderLeftColor: transparent; + } + .pf-c-accordion__toggle.pf-m-expanded { + --pf-c-accordion__toggle--BorderLeftColor: transparent; + } +} diff --git a/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringOverview.tsx b/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringOverview.tsx new file mode 100644 index 00000000000..ae41ea1eea6 --- /dev/null +++ b/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringOverview.tsx @@ -0,0 +1,104 @@ +import * as React from 'react'; +import * as _ from 'lodash'; +import { + Accordion, + AccordionItem, + AccordionToggle, + AccordionContent, + Split, + SplitItem, + Badge, +} from '@patternfly/react-core'; +import { K8sResourceKind, EventKind } from '@console/internal/module/k8s'; +import MonitoringEventsWarning from './MonitoringEventsWarning'; +import MonitoringEvents from './MonitoringEvents'; +import WorkloadGraphs from './MonitoringMetrics'; +import './MonitoringOverview.scss'; + +type MonitoringOverviewProps = { + resource: K8sResourceKind; + events: EventKind[]; +}; + +const MonitoringOverview: React.FC = ({ resource, events }) => { + const [expanded, setExpanded] = React.useState(['metrics']); + const eventWarning = _.filter(events, ['type', 'Warning']); + + const onToggle = (id: string) => { + const index = expanded.indexOf(id); + const newExpanded = + index >= 0 + ? [...expanded.slice(0, index), ...expanded.slice(index + 1, expanded.length)] + : [...expanded, id]; + setExpanded(newExpanded); + }; + + return ( +
+ + + { + onToggle('events-warning'); + }} + isExpanded={expanded.includes('events-warning')} + id="events-warning" + className="odc-monitoring-overview__event-warning-toggle" + > + + Events (Warning) + + + {eventWarning.length} + + + + + + + + + + { + onToggle('metrics'); + }} + isExpanded={expanded.includes('metrics')} + id="metrics" + > + Metrics + + + + + + + + { + onToggle('all-events'); + }} + isExpanded={expanded.includes('all-events')} + id="all-events" + > + All Events + + + + + + +
+ ); +}; + +export default MonitoringOverview; diff --git a/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringSection.scss b/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringSection.scss deleted file mode 100644 index f44b7910bce..00000000000 --- a/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringSection.scss +++ /dev/null @@ -1,14 +0,0 @@ -.odc-monitoring-section { - &__metric-accordion { - padding: 0; - } - h5 { - margin: 0; - } - .pf-c-accordion__expanded-content.pf-m-expanded { - --pf-c-accordion__expanded-content--BorderLeftColor: transparent; - } - .pf-c-accordion__toggle.pf-m-expanded { - --pf-c-accordion__toggle--BorderLeftColor: transparent; - } -} diff --git a/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringTab.tsx b/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringTab.tsx index 15b1af6ec19..00f23871873 100644 --- a/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringTab.tsx +++ b/frontend/packages/dev-console/src/components/monitoring/overview/MonitoringTab.tsx @@ -1,13 +1,13 @@ import * as React from 'react'; import { OverviewItem } from '@console/shared'; -import MonitoringMetricsSection from './MonitoringMetricsSection'; +import MonitoringOverview from './MonitoringOverview'; type MonitoringTabProps = { item: OverviewItem; }; -const MonitoringTab: React.FC = ({ item: { obj: res } }) => { - return ; +const MonitoringTab: React.FC = ({ item: { obj: res, events } }) => { + return ; }; export default MonitoringTab; diff --git a/frontend/packages/dev-console/src/components/monitoring/overview/__tests__/MonitoringMetricsSection.spec.tsx b/frontend/packages/dev-console/src/components/monitoring/overview/__tests__/MonitoringMetricsSection.spec.tsx deleted file mode 100644 index 9e5b4f8c3e7..00000000000 --- a/frontend/packages/dev-console/src/components/monitoring/overview/__tests__/MonitoringMetricsSection.spec.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import * as React from 'react'; -import { shallow } from 'enzyme'; -import { AccordionContent, AccordionToggle } from '@patternfly/react-core'; -import MonitoringMetricsSection from '../MonitoringMetricsSection'; - -describe('Monitoring Metric Section', () => { - const metSecProps: React.ComponentProps = { - resource: { - metadata: { - name: 'workload-name', - namespace: 'test', - }, - spec: {}, - status: {}, - kind: 'Deployment', - }, - }; - - it('should render Metric Section for workload of type Deployment', () => { - const component = shallow(); - expect(component.find(AccordionContent).exists()).toBe(true); - expect(component.find(AccordionContent).prop('isExpanded')).toBe(undefined); - }); - - it('should expand & collapse Metric Section accordion', () => { - const component = shallow(); - component.find(AccordionToggle).simulate('click'); - expect(component.find(AccordionToggle).prop('isExpanded')).toBe(true); - expect(component.find(AccordionContent).prop('isHidden')).toBe(false); - component.find(AccordionToggle).simulate('click'); - expect(component.find(AccordionToggle).prop('isExpanded')).toBe(false); - expect(component.find(AccordionContent).prop('isHidden')).toBe(true); - }); -}); diff --git a/frontend/packages/dev-console/src/components/monitoring/overview/__tests__/MonitoringOverview.spec.tsx b/frontend/packages/dev-console/src/components/monitoring/overview/__tests__/MonitoringOverview.spec.tsx new file mode 100644 index 00000000000..87fe25b6f88 --- /dev/null +++ b/frontend/packages/dev-console/src/components/monitoring/overview/__tests__/MonitoringOverview.spec.tsx @@ -0,0 +1,52 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import MonitoringOverview from '../MonitoringOverview'; +import { Badge } from '@patternfly/react-core'; + +describe('Monitoring Metric Section', () => { + const metSecProps: React.ComponentProps = { + resource: { + metadata: { + name: 'workload-name', + namespace: 'test', + }, + spec: {}, + status: {}, + kind: 'Deployment', + }, + events: [], + }; + + it('metrics accordion should be expanded by default', () => { + const component = shallow(); + expect(component.find('#metrics').prop('isExpanded')).toBe(true); + expect(component.find('#metrics-content').prop('isHidden')).toBe(false); + }); + + it('events warning accordion should not be expanded by default', () => { + const component = shallow(); + expect(component.find('#events-warning').prop('isExpanded')).toBe(false); + expect(component.find('#events-warning-content').prop('isHidden')).toBe(true); + }); + + it('all events accordion should not be expanded by default', () => { + const component = shallow(); + expect(component.find('#all-events').prop('isExpanded')).toBe(false); + expect(component.find('#all-events-content').prop('isHidden')).toBe(true); + }); + + it('should expand & collapse Metric Section accordion', () => { + const component = shallow(); + component.find('#metrics').simulate('click'); + expect(component.find('#metrics').prop('isExpanded')).toBe(false); + expect(component.find('#metrics-content').prop('isHidden')).toBe(true); + component.find('#metrics').simulate('click'); + expect(component.find('#metrics').prop('isExpanded')).toBe(true); + expect(component.find('#metrics-content').prop('isHidden')).toBe(false); + }); + + it('event warning should be 0', () => { + const component = shallow(); + expect(component.find(Badge).props().children).toBe(0); + }); +}); diff --git a/frontend/packages/dev-console/src/components/monitoring/overview/__tests__/MonitoringTab.spec.tsx b/frontend/packages/dev-console/src/components/monitoring/overview/__tests__/MonitoringTab.spec.tsx index 606ac091cf1..d1d6115ef3b 100644 --- a/frontend/packages/dev-console/src/components/monitoring/overview/__tests__/MonitoringTab.spec.tsx +++ b/frontend/packages/dev-console/src/components/monitoring/overview/__tests__/MonitoringTab.spec.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { shallow } from 'enzyme'; import MonitoringTab from '../MonitoringTab'; -import MonitoringMetricsSection from '../MonitoringMetricsSection'; +import MonitoringOverview from '../MonitoringOverview'; describe('Monitoring Tab', () => { const monTabProps: React.ComponentProps = { @@ -31,6 +31,6 @@ describe('Monitoring Tab', () => { it('should render Monitoring tab with Metrics section for selected workload', () => { const component = shallow(); - expect(component.find(MonitoringMetricsSection).exists()).toBe(true); + expect(component.find(MonitoringOverview).exists()).toBe(true); }); }); diff --git a/frontend/packages/dev-console/src/components/monitoring/overview/queries.ts b/frontend/packages/dev-console/src/components/monitoring/overview/queries.ts index 2085ab9d348..6ad7986ca7b 100644 --- a/frontend/packages/dev-console/src/components/monitoring/overview/queries.ts +++ b/frontend/packages/dev-console/src/components/monitoring/overview/queries.ts @@ -16,7 +16,7 @@ export const workloadMetricQueries = [ query: template( `sum( node_namespace_pod_container:container_cpu_usage_seconds_total:sum_rate{cluster='', namespace='<%= ns %>'} - * on(namespace,pod) group_left(workload, workload_type) mixin_pod_workload{cluster='', + * on(namespace,pod) group_left(workload, workload_type) mixin_pod_workload{cluster='', namespace='<%= ns %>', workload='<%= workloadName %>', workload_type='<%= workloadType %>'}) by (pod)`, ), }, @@ -26,8 +26,8 @@ export const workloadMetricQueries = [ humanize: humanizeBinaryBytes, byteDataType: ByteDataTypes.BinaryBytes, query: template( - `sum(container_memory_working_set_bytes{cluster='', namespace='<%= ns %>', container!=""} - * on(namespace,pod) group_left(workload, workload_type) mixin_pod_workload{cluster='', + `sum(container_memory_working_set_bytes{cluster='', namespace='<%= ns %>', container!=""} + * on(namespace,pod) group_left(workload, workload_type) mixin_pod_workload{cluster='', namespace='<%= ns %>', workload='<%= workloadName %>', workload_type='<%= workloadType %>'}) by (pod)`, ), }, @@ -37,8 +37,8 @@ export const workloadMetricQueries = [ humanize: humanizeDecimalBytesPerSec, byteDataType: ByteDataTypes.DecimalBytes, query: template( - `sum(irate(container_network_receive_bytes_total{cluster="", namespace=~'<%= ns %>'}[4h]) - * on (namespace,pod) group_left(workload,workload_type) mixin_pod_workload{cluster="", + `sum(irate(container_network_receive_bytes_total{cluster="", namespace=~'<%= ns %>'}[4h]) + * on (namespace,pod) group_left(workload,workload_type) mixin_pod_workload{cluster="", namespace=~'<%= ns %>', workload=~'<%= workloadName %>', workload_type='<%= workloadType %>'}) by (pod)`, ), }, @@ -49,7 +49,7 @@ export const workloadMetricQueries = [ byteDataType: ByteDataTypes.DecimalBytes, query: template( `sum(irate(container_network_receive_bytes_total{cluster="", namespace=~'<%= ns %>'}[4h]) - * on (namespace,pod) group_left(workload,workload_type) mixin_pod_workload{cluster="", + * on (namespace,pod) group_left(workload,workload_type) mixin_pod_workload{cluster="", namespace=~'<%= ns %>', workload=~'<%= workloadName %>', workload_type='<%= workloadType %>'}) by (pod)`, ), },