Skip to content

Commit

Permalink
feat(diagnostics): Add Dashboard card for invoking diagnostic operati…
Browse files Browse the repository at this point in the history
…ons (#1426)

Co-authored-by: Andrew Azores <[email protected]>
  • Loading branch information
Josh-Matsuoka and andrewazores authored Oct 1, 2024
1 parent 2336e55 commit 7a22d65
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 1 deletion.
11 changes: 10 additions & 1 deletion locales/en/public.json
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,6 @@
},
"EVALUATING_EXPRESSION": "Evaluating Match Expression...",
"FAILING_EVALUATION": "The expression matching failed.",
"MATCH_EXPRESSION_HELPER_TEXT": "Enter a Match Expression. This is a <a>Common Expression Language (CEL)</a> code snippet that is evaluated against each target application to determine whether the rule should be applied.",
"MATCH_EXPRESSION_HINT_BODY": "Try an expression like:",
"MATCH_EXPRESSION_HINT_MODAL_HEADER": "Match Expression hint",
"MODAL_DESCRIPTION": "Create Stored Credentials for target JVMs. Cryostat will use these credentials to connect to Cryostat agents or target JVMs over JMX (if required).",
Expand Down Expand Up @@ -328,6 +327,16 @@
},
"DATETIME": "Date and Time"
},
"DiagnosticsCard": {
"DIAGNOSTICS_ACTION_FAILURE": "Diagnostics Failure: {{kind}}",
"DIAGNOSTICS_CARD_DESCRIPTION": "Perform diagnostic operations on the target.",
"DIAGNOSTICS_CARD_DESCRIPTION_FULL": "Perform diagonstic operations from a list of supported operations on the target.",
"DIAGNOSTICS_CARD_TITLE": "Diagnostics",
"DIAGNOSTICS_GC_BUTTON": "Start Garbage Collection",
"KINDS": {
"GC": "Garbage Collection"
}
},
"DurationFilter": {
"ARIA_LABELS": {
"FROM_DURATION": "duration-from",
Expand Down
154 changes: 154 additions & 0 deletions src/app/Dashboard/Diagnostics/DiagnosticsCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*
* Copyright The Cryostat Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {
DashboardCardTypeProps,
DashboardCardFC,
DashboardCardSizes,
DashboardCardDescriptor,
} from '@app/Dashboard/types';
import { NotificationsContext } from '@app/Shared/Services/Notifications.service';
import { FeatureLevel } from '@app/Shared/Services/service.types';
import { ServiceContext } from '@app/Shared/Services/Services';
import { useSubscriptions } from '@app/utils/hooks/useSubscriptions';
import {
Bullseye,
Button,
CardBody,
CardHeader,
CardTitle,
EmptyState,
EmptyStateBody,
EmptyStateIcon,
EmptyStateVariant,
EmptyStateHeader,
EmptyStateFooter,
} from '@patternfly/react-core';
import { WrenchIcon } from '@patternfly/react-icons';
import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { DashboardCard } from '../DashboardCard';

export interface DiagnosticsCardProps extends DashboardCardTypeProps {}

export const DiagnosticsCard: DashboardCardFC<DiagnosticsCardProps> = (props) => {
const { t } = useTranslation();
const serviceContext = React.useContext(ServiceContext);
const notifications = React.useContext(NotificationsContext);
const addSubscription = useSubscriptions();
const [running, setRunning] = React.useState(false);

const handleError = React.useCallback(
(kind, error) => {
notifications.danger(t('DiagnosticsCard.DIAGNOSTICS_ACTION_FAILURE', { kind }), error?.message || error);
},
[notifications, t],
);

const handleGC = React.useCallback(() => {
setRunning(true);
addSubscription(
serviceContext.api.runGC(true).subscribe({
error: (err) => handleError(t('DiagnosticsCard.KINDS.GC'), err),
complete: () => setRunning(false),
}),
);
}, [addSubscription, serviceContext.api, handleError, setRunning, t]);

const header = React.useMemo(() => {
return (
<CardHeader actions={{ actions: <>{...props.actions || []}</>, hasNoOffset: false, className: undefined }}>
<CardTitle>{t('DiagnosticsCard.DIAGNOSTICS_CARD_TITLE')}</CardTitle>
</CardHeader>
);
}, [props.actions, t]);

return (
<>
<DashboardCard
id={'diagnostics-card'}
dashboardId={props.dashboardId}
cardSizes={DiagnosticsCardSizes}
isCompact
cardHeader={header}
isDraggable={props.isDraggable}
isResizable={props.isResizable}
isFullHeight={props.isFullHeight}
>
<CardBody>
<Bullseye>
<EmptyState variant={EmptyStateVariant.lg}>
<EmptyStateHeader
titleText={<>{t('DiagnosticsCard.DIAGNOSTICS_CARD_TITLE')}</>}
icon={<EmptyStateIcon icon={WrenchIcon} />}
headingLevel="h2"
/>
<EmptyStateBody>{t('DiagnosticsCard.DIAGNOSTICS_CARD_DESCRIPTION')}</EmptyStateBody>
<EmptyStateFooter>
<Button
variant="primary"
onClick={handleGC}
spinnerAriaValueText="Invoke GC"
spinnerAriaLabel="invoke-gc"
isLoading={running}
>
{t('DiagnosticsCard.DIAGNOSTICS_GC_BUTTON')}
</Button>
</EmptyStateFooter>
</EmptyState>
</Bullseye>
</CardBody>
</DashboardCard>
</>
);
};

DiagnosticsCard.cardComponentName = 'DiagnosticsCard';

export const DiagnosticsCardSizes: DashboardCardSizes = {
span: {
minimum: 3,
default: 4,
maximum: 12,
},
height: {
// TODO: implement height resizing
minimum: Number.NaN,
default: Number.NaN,
maximum: Number.NaN,
},
};

export const DiagnosticsCardDescriptor: DashboardCardDescriptor = {
featureLevel: FeatureLevel.BETA,
title: 'DiagnosticsCard.DIAGNOSTICS_CARD_TITLE',
cardSizes: DiagnosticsCardSizes,
description: 'DiagnosticsCard.DIAGNOSTICS_CARD_DESCRIPTION',
descriptionFull: 'DiagnosticsCard.DIAGNOSTICS_CARD_DESCRIPTION_FULL',
component: DiagnosticsCard,
propControls: [],
icon: <WrenchIcon />,
labels: [
{
content: 'Beta',
color: 'cyan',
},
{
content: 'Diagnostics',
color: 'blue',
},
],
};
2 changes: 2 additions & 0 deletions src/app/Dashboard/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { useDispatch } from 'react-redux';
import { AutomatedAnalysisCardDescriptor } from './AutomatedAnalysis/AutomatedAnalysisCard';
import { JFRMetricsChartCardDescriptor } from './Charts/jfr/JFRMetricsChartCard';
import { MBeanMetricsChartCardDescriptor } from './Charts/mbean/MBeanMetricsChartCard';
import { DiagnosticsCardDescriptor } from './Diagnostics/DiagnosticsCard';
import { JvmDetailsCardDescriptor } from './JvmDetails/JvmDetailsCard';
import {
SerialLayoutTemplate,
Expand Down Expand Up @@ -165,6 +166,7 @@ export const getDashboardCards: (featureLevel?: FeatureLevel) => DashboardCardDe
AutomatedAnalysisCardDescriptor,
JFRMetricsChartCardDescriptor,
MBeanMetricsChartCardDescriptor,
DiagnosticsCardDescriptor,
];
return cards.filter((card) => card.featureLevel >= featureLevel);
};
Expand Down
30 changes: 30 additions & 0 deletions src/app/Shared/Services/Api.service.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,7 @@ export class ApiService {
first(),
),
),
first(),
);
}

Expand All @@ -376,6 +377,7 @@ export class ApiService {
first(),
),
),
first(),
);
}

Expand All @@ -395,6 +397,7 @@ export class ApiService {
first(),
),
),
first(),
);
}

Expand All @@ -410,6 +413,7 @@ export class ApiService {
first(),
),
),
first(),
);
}

Expand All @@ -424,6 +428,7 @@ export class ApiService {
first(),
),
),
first(),
);
}

Expand Down Expand Up @@ -451,6 +456,7 @@ export class ApiService {
first(),
),
),
first(),
);
}

Expand All @@ -467,6 +473,7 @@ export class ApiService {
first(),
),
),
first(),
);
}

Expand Down Expand Up @@ -617,6 +624,27 @@ export class ApiService {
first(),
),
),
first(),
);
}

runGC(suppressNotifications = false): Observable<boolean> {
return this.target.target().pipe(
concatMap((target) =>
this.sendRequest(
'beta',
`diagnostics/targets/${target?.id}/gc`,
{
method: 'POST',
},
undefined,
suppressNotifications,
).pipe(
map((resp) => resp.ok),
first(),
),
),
first(),
);
}

Expand All @@ -640,6 +668,7 @@ export class ApiService {
first(),
),
),
first(),
);
}

Expand Down Expand Up @@ -736,6 +765,7 @@ export class ApiService {
first(),
),
),
first(),
);
}

Expand Down

0 comments on commit 7a22d65

Please sign in to comment.