Skip to content

Commit eeeb083

Browse files
celdrakerawagner
andauthored
[release-1.2] Backports for 1.2-rc2 (#674)
* EDM-4058: Correctly identify RBAC exclusions (#671) Made-with: Cursor (cherry picked from commit afa4951) * EDM-3976 vulnerabilities empty state (#651) * No need to return placeholder when no configs exist Made-with: Cursor (cherry picked from commit 15c4ddc) * EDM-4008: Enforce only valid date range selection (#660) Made-with: Cursor (cherry picked from commit 5ee7434) * EDM-4020: Refresh vulnerability summary in Overview (#657) Made-with: Cursor (cherry picked from commit 4204596) * EDM-4063: Add docker:// protocol if link does not contain any (#675) (cherry picked from commit 24c25bf) Made-with: Cursor * EDM-4014 device logs cancel (#659) * EDM-4014: Add cancel button and improve overall UX * EDM-4012: Reset filters when logType changes * Fix disconnect banner for dark theme with suggested token Made-with: Cursor (cherry picked from commit 8c875e0) * EDM-4018: Allow search for vulnerabilities be case-insensitive (#656) * EDM-4018: Allow search for vulnerabilities be case-insensitive * Fix space not being an allowed character Made-with: Cursor (cherry picked from commit 623d65a) * EDM-4011: Fix parser missing footer depending on formatting (#663) Made-with: Cursor (cherry picked from commit 0ce4600) * EDM-4013 device logs search validations (#662) * EDM-4013: Block file paths with dots for moving away of /var/log * EDM-4013: Strengthen validations for systemd unit names Made-with: Cursor (cherry picked from commit ac370df) --------- Co-authored-by: Rastislav Wagner <rawagner@redhat.com>
1 parent a20987c commit eeeb083

21 files changed

Lines changed: 610 additions & 234 deletions

libs/i18n/locales/en/translation.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1631,6 +1631,9 @@
16311631
"Total active vulnerabilities.": "Total active vulnerabilities.",
16321632
"CVEs affecting images deployed across your managed fleet and devices.": "CVEs affecting images deployed across your managed fleet and devices.",
16331633
"Vulnerability counts by severity": "Vulnerability counts by severity",
1634+
"No vulnerabilities were found affecting the image currently deployed on this device. This reflects the most recent scan data available.": "No vulnerabilities were found affecting the image currently deployed on this device. This reflects the most recent scan data available.",
1635+
"No vulnerabilities were found affecting images currently deployed across all devices in this fleet. This reflects the most recent scan data available.": "No vulnerabilities were found affecting images currently deployed across all devices in this fleet. This reflects the most recent scan data available.",
1636+
"No vulnerabilities detected": "No vulnerabilities detected",
16341637
"No CVEs detected": "No CVEs detected",
16351638
"All managed devices have been scanned. No CVEs were found affecting images currently deployed across your fleets and devices.": "All managed devices have been scanned. No CVEs were found affecting images currently deployed across your fleets and devices.",
16361639
"No vulnerability data to display": "No vulnerability data to display",
@@ -1751,7 +1754,8 @@
17511754
"Info and above": "Info and above",
17521755
"Debug and above": "Debug and above",
17531756
"Must be at most {{max}} characters.": "Must be at most {{max}} characters.",
1754-
"Enter a single unit name using alphanumeric characters, colons (:), at signs (@), dots (.), underscores (_), and hyphens (-).": "Enter a single unit name using alphanumeric characters, colons (:), at signs (@), dots (.), underscores (_), and hyphens (-).",
1757+
"Unit name must not consist of only dots.": "Unit name must not consist of only dots.",
1758+
"Enter a unit name or pattern. Use alphanumeric characters, dots (.), underscores (_), hyphens (-), at signs (@) and colons (:). Glob characters are allowed: (*, ?, [...]).": "Enter a unit name or pattern. Use alphanumeric characters, dots (.), underscores (_), hyphens (-), at signs (@) and colons (:). Glob characters are allowed: (*, ?, [...]).",
17551759
"Log file path is required.": "Log file path is required.",
17561760
"Log file path must be relative to {{basePath}}. It cannot be absolute or point outside the log directory.": "Log file path must be relative to {{basePath}}. It cannot be absolute or point outside the log directory.",
17571761
"Log file path must not end with \"/\". Enter a file path, not a directory.": "Log file path must not end with \"/\". Enter a file path, not a directory.",

libs/ui-components/src/components/Catalog/InstallWizard/steps/SelectTargetStep.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@ const FleetTarget = () => {
206206
);
207207
};
208208

209+
const PROTOCOL_REGEX = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//;
210+
209211
type NewDeviceTargetProps = {
210212
catalogItem: CatalogItem;
211213
};
@@ -234,6 +236,8 @@ const NewDeviceTarget = ({ catalogItem }: NewDeviceTargetProps) => {
234236

235237
const artifactUrl = currentVersion && artifact ? getFullArtifactURI(artifact, currentVersion) : undefined;
236238

239+
const hasProtocol = PROTOCOL_REGEX.test(artifactUrl || '');
240+
237241
return (
238242
<FlightCtlForm>
239243
<Grid hasGutter>
@@ -274,7 +278,7 @@ const NewDeviceTarget = ({ catalogItem }: NewDeviceTargetProps) => {
274278
icon={<ExternalLinkAltIcon />}
275279
target="_blank"
276280
rel="noopener noreferrer"
277-
href={artifactUrl}
281+
href={hasProtocol ? artifactUrl : `docker://${artifactUrl}`}
278282
>
279283
{artifactUrl}
280284
</Button>

libs/ui-components/src/components/Device/DeviceDetails/DeviceLogsEmptyState.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ import { ExclamationCircleIcon } from '@patternfly/react-icons/dist/js/icons/exc
1818
import { DEVICE_LOG_BASE_PATH, DeviceLogErrorType } from '../../../utils/deviceLogs';
1919
import { useTranslation } from '../../../hooks/useTranslation';
2020

21+
// Fixes the color of the text in the "red" banner button.
22+
const redBannerButtonFixStyle = {
23+
'--pf-v6-c-button--m-link--Color': 'var(--pf-t--global--text--color--nonstatus--on-red--default)',
24+
'--pf-v6-c-button--m-link--hover--Color': 'var(--pf-t--global--text--color--nonstatus--on-red--default)',
25+
} as React.CSSProperties;
26+
2127
// Note: Despite the function signature, errorType can actually be a plain string.
2228
// This happens when we receive an uncontrolled error, and instead of the errorType we store the actual error message.
2329
const getErrorDetails = (t: TFunction, errorType: DeviceLogErrorType) => {
@@ -94,7 +100,7 @@ export const DeviceLogsDisconnectedBanner = ({ onRetry }: { onRetry: VoidFunctio
94100
<Flex justifyContent={{ default: 'justifyContentSpaceBetween' }}>
95101
<FlexItem>{t('Offline/ device lost connection')}</FlexItem>
96102
<FlexItem>
97-
<Button variant="link" onClick={onRetry}>
103+
<Button variant="link" onClick={onRetry} style={redBannerButtonFixStyle}>
98104
{t('Retry')}
99105
</Button>
100106
</FlexItem>

libs/ui-components/src/components/Device/DeviceDetails/DeviceLogsInnerToolbar.tsx

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,31 @@ import {
2222
DeviceLogSearchParams,
2323
getActiveTimeFilterLabel,
2424
getDeviceLogLevelLabel,
25+
getDeviceLogsFormResetValues,
2526
} from '../../../utils/deviceLogs';
2627

2728
import './DeviceLogsInnerToolbar.css';
2829

2930
export type DeviceLogsInnerToolbarProps = {
3031
lastSearchParams: DeviceLogSearchParams;
31-
onSearchUpdate: (params: DeviceLogSearchParams) => Promise<boolean>;
32-
onResetForm: VoidFunction;
32+
isStreaming: boolean;
33+
onApplySearchParams: (params: DeviceLogSearchParams) => Promise<boolean>;
34+
onStartLiveStream: () => Promise<boolean>;
35+
onStopLiveStream: VoidFunction;
36+
onClearLiveLogsPreference: VoidFunction;
37+
onClearSession: VoidFunction;
3338
onDownload: VoidFunction;
3439
onOpenRaw: VoidFunction;
3540
};
3641

3742
const DeviceLogsInnerToolbar = ({
3843
lastSearchParams,
39-
onSearchUpdate,
40-
onResetForm,
44+
isStreaming,
45+
onApplySearchParams,
46+
onStartLiveStream,
47+
onStopLiveStream,
48+
onClearLiveLogsPreference,
49+
onClearSession,
4150
onDownload,
4251
onOpenRaw,
4352
children,
@@ -46,38 +55,31 @@ const DeviceLogsInnerToolbar = ({
4655
const { setValues, values } = useFormikContext<DeviceLogSearchParams>();
4756
const hasChildren = Boolean(children);
4857

49-
const updateLiveLogs = React.useCallback(
50-
(checked: boolean) => {
51-
const newParams = { ...lastSearchParams, showLiveLogs: checked };
52-
void setValues(newParams);
53-
return newParams;
54-
},
55-
[lastSearchParams, setValues],
56-
);
58+
const onClearSearch = React.useCallback(() => {
59+
void setValues(getDeviceLogsFormResetValues(values.category));
60+
onClearSession();
61+
}, [onClearSession, setValues, values.category]);
5762

58-
// The "show live logs" switch can only be applied after the logs have been retrieved and it triggers a new search.
5963
const onShowLiveLogsChange = React.useCallback(
6064
(_event: React.FormEvent<HTMLInputElement>, checked: boolean) => {
61-
const newParams = updateLiveLogs(checked);
62-
void onSearchUpdate(newParams);
65+
if (checked) {
66+
void onStartLiveStream();
67+
} else if (isStreaming) {
68+
onStopLiveStream();
69+
} else if (lastSearchParams.showLiveLogs) {
70+
onClearLiveLogsPreference();
71+
}
6372
},
64-
[updateLiveLogs, onSearchUpdate],
73+
[isStreaming, lastSearchParams.showLiveLogs, onClearLiveLogsPreference, onStartLiveStream, onStopLiveStream],
6574
);
6675

67-
const doResetForm = React.useCallback(() => {
68-
updateLiveLogs(false);
69-
onResetForm();
70-
}, [onResetForm, updateLiveLogs]);
71-
7276
const onRemoveFilter = React.useCallback(
7377
(filterKey: keyof DeviceLogSearchParams, defaultValue: string | undefined) => () => {
74-
// We sync the form values with the search params with the current filter removed.
75-
// That syncs the form controls to the updated search (including reverting non-persisted form changes)
7678
const newParams = { ...lastSearchParams, [filterKey]: defaultValue };
7779
void setValues(newParams);
78-
void onSearchUpdate(newParams);
80+
void onApplySearchParams(newParams);
7981
},
80-
[onSearchUpdate, lastSearchParams, setValues],
82+
[onApplySearchParams, lastSearchParams, setValues],
8183
);
8284

8385
const activeFilters = React.useMemo(() => {
@@ -147,7 +149,7 @@ const DeviceLogsInnerToolbar = ({
147149
</LabelGroup>
148150
</ToolbarItem>
149151
<ToolbarItem>
150-
<Button variant="link" isInline onClick={doResetForm}>
152+
<Button variant="link" isInline onClick={onClearSearch}>
151153
{t('Clear search')}
152154
</Button>
153155
</ToolbarItem>
@@ -157,7 +159,7 @@ const DeviceLogsInnerToolbar = ({
157159
<Switch
158160
id="device-logs-show-live-logs"
159161
label={t('Show live logs')}
160-
isChecked={values.showLiveLogs}
162+
isChecked={isStreaming || lastSearchParams.showLiveLogs}
161163
onChange={onShowLiveLogsChange}
162164
/>
163165
</ToolbarItem>

libs/ui-components/src/components/Device/DeviceDetails/DeviceLogsSearchToolbar.tsx

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ import {
1616
import { useFormikContext } from 'formik';
1717

1818
import { useTranslation } from '../../../hooks/useTranslation';
19-
import { DEVICE_LOG_BASE_PATH, DeviceLogCategory, DeviceLogSearchParams } from '../../../utils/deviceLogs';
19+
import {
20+
DEVICE_LOG_BASE_PATH,
21+
DeviceLogCategory,
22+
DeviceLogSearchParams,
23+
getDeviceLogsFormResetValues,
24+
} from '../../../utils/deviceLogs';
2025
import FormSelect from '../../form/FormSelect';
2126
import TextField from '../../form/TextField';
2227
import DeviceLogsPriorityField from './DeviceLogsLevelField';
@@ -31,13 +36,24 @@ const getCategoryItems = (t: TFunction) => ({
3136

3237
export type DeviceLogsToolbarProps = {
3338
onLogTypeChange: VoidFunction;
39+
onCancelSearch: VoidFunction;
40+
isSubmitting: boolean;
3441
};
3542

36-
const DeviceLogsToolbar = ({ onLogTypeChange }: DeviceLogsToolbarProps) => {
43+
const DeviceLogsToolbar = ({ onLogTypeChange, isSubmitting, onCancelSearch }: DeviceLogsToolbarProps) => {
3744
const { t } = useTranslation();
3845
const categoryItems = React.useMemo(() => getCategoryItems(t), [t]);
3946

40-
const { submitForm, isSubmitting, values, errors } = useFormikContext<DeviceLogSearchParams>();
47+
const { submitForm, setValues, values, errors } = useFormikContext<DeviceLogSearchParams>();
48+
49+
const onCategoryChange = React.useCallback(
50+
(value: string) => {
51+
void setValues(getDeviceLogsFormResetValues(value as DeviceLogCategory));
52+
onLogTypeChange();
53+
},
54+
[onLogTypeChange, setValues],
55+
);
56+
4157
const validationError = React.useMemo(() => {
4258
const allErrors = Object.values(errors).filter(Boolean);
4359
return allErrors.join(', ');
@@ -63,7 +79,7 @@ const DeviceLogsToolbar = ({ onLogTypeChange }: DeviceLogsToolbarProps) => {
6379
flexWrap={{ default: 'nowrap' }}
6480
>
6581
<FlexItem>
66-
<FormSelect name="category" items={categoryItems} onChange={onLogTypeChange} />
82+
<FormSelect name="category" items={categoryItems} onChange={onCategoryChange} />
6783
</FlexItem>
6884
{values.category === DeviceLogCategory.SYSTEM && (
6985
<FlexItem style={{ minWidth: '12rem' }}>
@@ -108,6 +124,13 @@ const DeviceLogsToolbar = ({ onLogTypeChange }: DeviceLogsToolbarProps) => {
108124
</ToolbarItem>
109125
</ToolbarGroup>
110126
<ToolbarGroup variant="action-group" align={{ default: 'alignEnd' }}>
127+
{isSubmitting && (
128+
<ToolbarItem>
129+
<Button variant="link" onClick={onCancelSearch}>
130+
{t('Cancel')}
131+
</Button>
132+
</ToolbarItem>
133+
)}
111134
<ToolbarItem>
112135
<Button
113136
variant="primary"

0 commit comments

Comments
 (0)