Skip to content

Commit

Permalink
Refs #36738 - Re-use host selection box from core
Browse files Browse the repository at this point in the history
  • Loading branch information
ofedoren committed Mar 11, 2024
1 parent 78bf926 commit 6299ea1
Show file tree
Hide file tree
Showing 12 changed files with 133 additions and 220 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<%#
name: Run OpenSCAP remediation - Ansible Default
job_category: OpenSCAP Ansible Commands
description_format: Run OpenSCAP remediation on given host. NOTE: Not meant to be used directly.
description_format: Run OpenSCAP remediation on given host. Please note, it is not meant to be used directly.
snippet: false
provider_type: Ansible
kind: job_template
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<%#
name: Run OpenSCAP remediation - Script Default
job_category: OpenSCAP
description_format: Run OpenSCAP remediation on given host. NOTE: Not meant to be used directly.
description_format: Run OpenSCAP remediation on given host. Please note, it is not meant to be used directly.
snippet: false
provider_type: script
kind: job_template
Expand Down
2 changes: 1 addition & 1 deletion lib/foreman_openscap/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class Engine < ::Rails::Engine

initializer 'foreman_openscap.register_plugin', :before => :finisher_hook do |app|
Foreman::Plugin.register :foreman_openscap do
requires_foreman '>= 3.9'
requires_foreman '>= 3.11'
register_gettext

apipie_documented_controllers ["#{ForemanOpenscap::Engine.root}/app/controllers/api/v2/compliance/*.rb"]
Expand Down
6 changes: 5 additions & 1 deletion webpack/components/EmptyState.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,11 @@ EmptyStateIcon.defaultProps = {
EmptyState.propTypes = {
title: PropTypes.string,
body: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
error: PropTypes.oneOfType([PropTypes.shape({}), PropTypes.string]),
error: PropTypes.oneOfType([
PropTypes.shape({}),
PropTypes.string,
PropTypes.bool,
]),
search: PropTypes.bool,
lock: PropTypes.bool,
primaryButton: PropTypes.node,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,9 @@ import {
} from 'foremanReact/redux/API/APISelectors';
import { STATUS } from 'foremanReact/constants';
import {

Check failure on line 7 in webpack/components/OpenscapRemediationWizard/OpenscapRemediationSelectors.js

View workflow job for this annotation

GitHub Actions / test_js (14)

Replace `⏎··REPORT_LOG_REQUEST_KEY,⏎` with `·REPORT_LOG_REQUEST_KEY·`

Check failure on line 7 in webpack/components/OpenscapRemediationWizard/OpenscapRemediationSelectors.js

View workflow job for this annotation

GitHub Actions / test_js (18)

Replace `⏎··REPORT_LOG_REQUEST_KEY,⏎` with `·REPORT_LOG_REQUEST_KEY·`
JOB_INVOCATION_API_REQUEST_KEY,
REPORT_LOG_REQUEST_KEY,
} from './constants';

export const selectRemediationResponse = state =>
selectAPIResponse(state, JOB_INVOCATION_API_REQUEST_KEY) || {};

export const selectRemediationStatus = state =>
selectAPIStatus(state, JOB_INVOCATION_API_REQUEST_KEY) || STATUS.PENDING;

export const selectRemediationError = state =>
selectAPIError(state, JOB_INVOCATION_API_REQUEST_KEY);

export const selectLogResponse = state =>
selectAPIResponse(state, REPORT_LOG_REQUEST_KEY) || {};

Expand Down
1 change: 0 additions & 1 deletion webpack/components/OpenscapRemediationWizard/constants.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export const OPENSCAP_REMEDIATION_MODAL_ID = 'openscapRemediationModal';
export const NEW_HOSTS_PATH = '/new/hosts';
export const HOSTS_PATH = '/hosts';
export const FAIL_RULE_SEARCH = 'fails_xccdf_rule';

Expand Down
10 changes: 7 additions & 3 deletions webpack/components/OpenscapRemediationWizard/helpers.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { join, find, map, compact, includes, filter } from 'lodash';
import { join, find, map, compact, includes, filter, isString } from 'lodash';

const getResponseErrorMsgs = ({ data } = {}) => {
if (data) {
Expand All @@ -9,8 +9,10 @@ const getResponseErrorMsgs = ({ data } = {}) => {
return [];
};

export const errorMsg = error => {
join(getResponseErrorMsgs(error?.response || {}), '\n');
export const errorMsg = data => {
if (isString(data)) return data;

return join(getResponseErrorMsgs({ data }), '\n');
};

export const findFixBySnippet = (fixes, snippet) =>
Expand All @@ -29,3 +31,5 @@ export const supportedRemediationSnippets = (
)
);
};

export const reviewHostCount = hostIdsParam => hostIdsParam.split(',').length;
15 changes: 8 additions & 7 deletions webpack/components/OpenscapRemediationWizard/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { isEmpty } from 'lodash';
import PropTypes from 'prop-types';
import { Button, Wizard } from '@patternfly/react-core';

Expand Down Expand Up @@ -29,11 +28,12 @@ const OpenscapRemediationWizard = ({
const fixes = JSON.parse(log?.message?.fixes || null) || [];
const source = log?.source?.value || '';
const title = log?.message?.value || '';
const defaultHostIdsParam = `id ^ (${hostId})`;

const [isRemediationWizardOpen, setIsRemediationWizardOpen] = useState(false);
const [snippet, setSnippet] = useState('');
const [method, setMethod] = useState('job');
const [hostIds, setHostIds] = useState([hostId]);
const [hostIdsParam, setHostIdsParam] = useState(defaultHostIdsParam);
const [isRebootSelected, setIsRebootSelected] = useState(false);

const onModalButtonClick = e => {
Expand All @@ -52,7 +52,7 @@ const OpenscapRemediationWizard = ({

const onWizardClose = () => {
// Reset to defaults
setHostIds([hostId]);
setHostIdsParam(defaultHostIdsParam);
setSnippet('');
setMethod('job');
setIsRebootSelected(false);
Expand All @@ -64,7 +64,7 @@ const OpenscapRemediationWizard = ({
name: __('Review hosts'),
component: <ReviewHosts />,
canJumpTo: Boolean(snippet && method === 'job'),
enableNext: Boolean(snippet && method && !isEmpty(hostIds)),
enableNext: Boolean(snippet && method),
};
const steps = [
{
Expand All @@ -79,7 +79,7 @@ const OpenscapRemediationWizard = ({
id: 3,
name: __('Review remediation'),
component: <ReviewRemediation />,
canJumpTo: Boolean(snippet && method && !isEmpty(hostIds)),
canJumpTo: Boolean(snippet && method),
enableNext: method === 'job',
nextButtonText: __('Run'),
},
Expand All @@ -101,15 +101,16 @@ const OpenscapRemediationWizard = ({
setSnippet,
method,
setMethod,
hostIds,
setHostIds,
hostName,
source,
logStatus,
logError,
supportedJobSnippets,
isRebootSelected,
setIsRebootSelected,
hostId,
hostIdsParam,
setHostIdsParam,
}}
>
<Wizard
Expand Down
71 changes: 31 additions & 40 deletions webpack/components/OpenscapRemediationWizard/steps/Finish.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
/* eslint-disable camelcase */
import React, { useContext, useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import { join } from 'lodash';
import { Button, Bullseye } from '@patternfly/react-core';
import ExternalLinkSquareAltIcon from '@patternfly/react-icons/dist/esm/icons/external-link-square-alt-icon';

import { sprintf, translate as __ } from 'foremanReact/common/I18n';
import { foremanUrl } from 'foremanReact/common/helpers';
import { STATUS } from 'foremanReact/constants';
import { API_OPERATIONS, post } from 'foremanReact/redux/API';
import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
import Loading from 'foremanReact/components/Loading';
import PermissionDenied from 'foremanReact/components/PermissionDenied';

import OpenscapRemediationWizardContext from '../OpenscapRemediationWizardContext';
import EmptyState from '../../EmptyState';
import { errorMsg, findFixBySnippet } from '../helpers';
import { errorMsg, findFixBySnippet, reviewHostCount } from '../helpers';

import {
JOB_INVOCATION_PATH,
Expand All @@ -24,21 +23,11 @@ import {
SNIPPET_ANSIBLE,
} from '../constants';

import {
selectRemediationResponse,
selectRemediationError,
selectRemediationStatus,
} from '../OpenscapRemediationSelectors';

const Finish = ({ onClose }) => {
const { fixes, snippet, hostIds, isRebootSelected } = useContext(
const { fixes, snippet, isRebootSelected, hostIdsParam } = useContext(
OpenscapRemediationWizardContext
);
const dispatch = useDispatch();
const [isDispatched, setIsDispatched] = useState(false);
const response = useSelector(state => selectRemediationResponse(state));
const status = useSelector(state => selectRemediationStatus(state));
const error = useSelector(state => selectRemediationError(state));

const snippetText = findFixBySnippet(fixes, snippet)?.full_text;

const remediationJobParams = () => {
Expand All @@ -61,26 +50,19 @@ const Finish = ({ onClose }) => {
job_invocation: {
feature,
inputs,
search_query: `id ^ (${join(hostIds, ',')})`,
search_query: hostIdsParam,
},
};
};

const runCommand = () => {
setIsDispatched(true);
dispatch(
post({
type: API_OPERATIONS.POST,
key: JOB_INVOCATION_API_REQUEST_KEY,
url: JOB_INVOCATION_API_PATH,
params: remediationJobParams(),
})
);
};

useEffect(() => {
if (!isDispatched) runCommand();
const response = useAPI('post', JOB_INVOCATION_API_PATH, {
key: JOB_INVOCATION_API_REQUEST_KEY,
params: remediationJobParams(),
});
const {
response: { response: { status: statusCode, data } = {} },
status = STATUS.PENDING,
} = response;

const linkToJob = (
<Button
Expand All @@ -89,31 +71,40 @@ const Finish = ({ onClose }) => {
iconPosition="right"
target="_blank"
component="a"
href={foremanUrl(`${JOB_INVOCATION_PATH}/${response.id}`)}
href={foremanUrl(`${JOB_INVOCATION_PATH}/${response?.response?.id}`)}
>
{__('Job details')}
</Button>
);
const closeBtn = <Button onClick={onClose}>{__('Close')}</Button>;
const errorComponent =
statusCode === 403 ? (
<PermissionDenied
missingPermissions={data?.error?.missing_permissions}
primaryButton={closeBtn}
/>
) : (
<EmptyState
error
title={__('Error!')}
body={errorMsg(data)}
primaryButton={closeBtn}
/>
);
const body =
status === STATUS.RESOLVED ? (
<EmptyState
title={sprintf(
__(
'The job has started on %s host(s), you can check the status on the job details page.'
),
hostIds.length
reviewHostCount(hostIdsParam)
)}
body={linkToJob}
primaryButton={closeBtn}
/>
) : (
<EmptyState
error={error}
title={__('Error!')}
body={errorMsg(error)}
primaryButton={closeBtn}
/>
errorComponent
);

return <Bullseye>{status === STATUS.PENDING ? <Loading /> : body}</Bullseye>;
Expand Down
Loading

0 comments on commit 6299ea1

Please sign in to comment.