foreman_puppet 3.0.5 → 4.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -1
  3. data/db/migrate/20220421204325_drop_environment_from_host_and_hg.foreman_puppet.rb +8 -0
  4. data/lib/foreman_puppet/register.rb +4 -4
  5. data/lib/foreman_puppet/version.rb +1 -1
  6. data/locale/ca/foreman_puppet.edit.po +237 -111
  7. data/locale/cs_CZ/foreman_puppet.edit.po +213 -105
  8. data/locale/de/foreman_puppet.edit.po +326 -148
  9. data/locale/en/foreman_puppet.edit.po +80 -172
  10. data/locale/en_GB/foreman_puppet.edit.po +200 -100
  11. data/locale/es/foreman_puppet.edit.po +304 -133
  12. data/locale/fr/foreman_puppet.edit.po +313 -135
  13. data/locale/gl/foreman_puppet.edit.po +210 -102
  14. data/locale/it/foreman_puppet.edit.po +250 -116
  15. data/locale/ja/foreman_puppet.edit.po +223 -107
  16. data/locale/ko/foreman_puppet.edit.po +202 -101
  17. data/locale/messages.mo +0 -0
  18. data/locale/nl_NL/foreman_puppet.edit.po +243 -115
  19. data/locale/pl/foreman_puppet.edit.po +260 -121
  20. data/locale/pt_BR/foreman_puppet.edit.po +317 -139
  21. data/locale/ru/foreman_puppet.edit.po +258 -122
  22. data/locale/sv_SE/foreman_puppet.edit.po +209 -103
  23. data/locale/zh_CN/foreman_puppet.edit.po +209 -105
  24. data/locale/zh_TW/foreman_puppet.edit.po +202 -103
  25. data/package.json +7 -7
  26. data/test/controllers/foreman_puppet/api/v2/override_values_controller_test.rb +6 -2
  27. data/test/models/foreman_puppet/host_test.rb +6 -0
  28. data/webpack/global_index.js +11 -0
  29. data/webpack/index.js +0 -7
  30. data/webpack/legacy.js +31 -0
  31. data/webpack/src/Extends/Fills/index.js +26 -0
  32. data/webpack/src/Extends/Host/PuppetTab/Routes.js +47 -0
  33. data/webpack/src/Extends/Host/PuppetTab/SubTabs/ENCPreview/ENCTab.js +61 -0
  34. data/webpack/src/Extends/Host/PuppetTab/SubTabs/ENCPreview/index.js +53 -0
  35. data/webpack/src/Extends/Host/PuppetTab/SubTabs/EmptyPage.js +19 -0
  36. data/webpack/src/Extends/Host/PuppetTab/SubTabs/Reports/components/DescriptionCard.js +71 -0
  37. data/webpack/src/Extends/Host/PuppetTab/SubTabs/Reports/index.js +45 -0
  38. data/webpack/src/Extends/Host/PuppetTab/SubTabs/Reports/styles.scss +7 -0
  39. data/webpack/src/Extends/Host/PuppetTab/constants.js +8 -0
  40. data/webpack/src/Extends/Host/PuppetTab/helpers.js +3 -0
  41. data/webpack/src/Extends/Host/PuppetTab/index.js +51 -0
  42. metadata +67 -55
  43. data/app/views/smart_proxies/plugins/_puppet_ca.html.erb +0 -44
  44. data/webpack/fills_index.js +0 -30
data/package.json CHANGED
@@ -21,17 +21,17 @@
21
21
  "url": "http://projects.theforeman.org/projects/foreman_puppet/issues"
22
22
  },
23
23
  "peerDependencies": {
24
- "@theforeman/vendor": "^8.15.0"
24
+ "@theforeman/vendor": "^10.1.0"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@babel/core": "^7.7.0",
28
28
  "@sheerun/mutationobserver-shim": "^0.3.3",
29
- "@theforeman/builder": "^8.15.0",
30
- "@theforeman/eslint-plugin-foreman": "^8.15.0",
31
- "@theforeman/find-foreman": "^8.15.0",
32
- "@theforeman/stories": "^8.15.0",
33
- "@theforeman/test": "^8.15.0",
34
- "@theforeman/vendor-dev": "^8.15.0",
29
+ "@theforeman/builder": "^10.1.0",
30
+ "@theforeman/eslint-plugin-foreman": "^10.1.0",
31
+ "@theforeman/find-foreman": "^10.1.0",
32
+ "@theforeman/stories": "^10.1.0",
33
+ "@theforeman/test": "^10.1.0",
34
+ "@theforeman/vendor-dev": "^10.1.0",
35
35
  "babel-eslint": "^10.0.3",
36
36
  "eslint": "^6.7.2",
37
37
  "jed": "^1.1.1",
@@ -75,8 +75,12 @@ module ForemanPuppet
75
75
  post :create, params: { smart_class_parameter_id: lookup_key.id, override_value: override_value }
76
76
  end
77
77
  response = ActiveSupport::JSON.decode(@response.body)
78
- param_not_posted = (override_value.keys.first.to_s == 'match') ? 'Value' : 'Match' # The opposite of override_value is missing
79
- assert_match(/Validation failed: #{param_not_posted} can't be blank/, response['error']['message'])
78
+ if override_value.keys.first.to_s == 'match'
79
+ # The opposite of override_value is missing and should fail
80
+ assert_match(/Validation failed: Value can't be blank/, response['error']['message'])
81
+ else # match is missing
82
+ assert_match(/Failed to save the record/, response['error']['message'])
83
+ end
80
84
  assert_response :error
81
85
  end
82
86
  end
@@ -110,6 +110,12 @@ module ForemanPuppet
110
110
  end
111
111
 
112
112
  describe '#info puppet bits' do
113
+ test 'ENC YAML omits environment if no puppet facet' do
114
+ host = FactoryBot.build_stubbed(:host)
115
+ enc = host.info
116
+ assert_not_includes enc.keys, 'environment'
117
+ end
118
+
113
119
  test 'ENC YAML uses Classification::ClassParam for parameterized output' do
114
120
  skip 'No idea whats wrong here'
115
121
  host = FactoryBot.build_stubbed(:host, :with_environment)
@@ -0,0 +1,11 @@
1
+ import { registerReducer } from 'foremanReact/common/MountingService';
2
+ import reducers from './src/reducers';
3
+ import { registerFills } from './src/Extends/Fills';
4
+ import { registerLegacy } from './legacy';
5
+
6
+ // register reducers
7
+ registerReducer('puppet', reducers);
8
+ // add fills
9
+ registerFills();
10
+ // TODO: the checkForUnavailablePuppetclasses is very nasty
11
+ registerLegacy();
data/webpack/index.js CHANGED
@@ -2,16 +2,9 @@
2
2
  /* eslint-disable import/no-extraneous-dependencies */
3
3
  /* eslint-disable import/extensions */
4
4
  import componentRegistry from 'foremanReact/components/componentRegistry';
5
- import { registerReducer } from 'foremanReact/common/MountingService';
6
- import reducers from './src/reducers';
7
5
  import ForemanPuppet from './src/ForemanPuppet';
8
6
  import { WelcomeEnv } from './src/Components/Environments/Welcome';
9
7
 
10
- // register reducers
11
- Object.entries(reducers).forEach(([key, reducer]) =>
12
- registerReducer(key, reducer)
13
- );
14
-
15
8
  // register components for erb mounting
16
9
  componentRegistry.register({ name: 'WelcomeEnv', type: WelcomeEnv });
17
10
  componentRegistry.register({ name: 'ForemanPuppet', type: ForemanPuppet });
data/webpack/legacy.js ADDED
@@ -0,0 +1,31 @@
1
+ import $ from 'jquery';
2
+ import * as classEditor from './src/foreman_class_edit';
3
+ import * as hostForm from './src/foreman_puppet_host_form';
4
+
5
+ export const registerLegacy = () => {
6
+ window.tfm = Object.assign(window.tfm || {}, {
7
+ classEditor,
8
+ puppetEnc: {
9
+ hostForm,
10
+ },
11
+ });
12
+
13
+ // TODO: the checkForUnavailablePuppetclasses is very nasty
14
+ $(document)
15
+ .on('change', '.hostgroup-select', evt => {
16
+ const form = $('form.host-form')[0];
17
+ if (form && form.dataset.id) hostForm.updatePuppetclasses(evt.target);
18
+ })
19
+ .on('change', '.interface_domain', evt => {
20
+ hostForm.reloadPuppetclassParams();
21
+ })
22
+ .on('change', '.host-architecture-os-select', evt => {
23
+ hostForm.reloadPuppetclassParams();
24
+ })
25
+ .on('ContentLoad', evt => {
26
+ hostForm.checkForUnavailablePuppetclasses();
27
+ });
28
+ $(window).on('load', evt => {
29
+ hostForm.checkForUnavailablePuppetclasses();
30
+ });
31
+ };
@@ -0,0 +1,26 @@
1
+ import React from 'react';
2
+ import { translate as __ } from 'foremanReact/common/I18n';
3
+ import { addGlobalFill } from 'foremanReact/components/common/Fill/GlobalFill';
4
+ import PuppetTab from '../Host/PuppetTab';
5
+
6
+ const fills = [
7
+ {
8
+ slot: 'host-details-page-tabs',
9
+ name: 'Puppet',
10
+ component: props => <PuppetTab {...props} />,
11
+ weight: 500,
12
+ metadata: { title: __('Puppet') },
13
+ },
14
+ ];
15
+
16
+ export const registerFills = () => {
17
+ fills.forEach(({ slot, name, component: Component, weight, metadata }) =>
18
+ addGlobalFill(
19
+ slot,
20
+ name,
21
+ <Component key={`puppet-fill-${name}`} />,
22
+ weight,
23
+ metadata
24
+ )
25
+ );
26
+ };
@@ -0,0 +1,47 @@
1
+ import PropTypes from 'prop-types';
2
+ import React from 'react';
3
+ import { Route, Switch, Redirect } from 'react-router-dom';
4
+ import { route } from './helpers';
5
+ import EmptyPage from './SubTabs/EmptyPage';
6
+ import Reports from './SubTabs/Reports';
7
+ import ENCPreview from './SubTabs/ENCPreview';
8
+
9
+ const SecondaryTabRoutes = ({ hostName, hostInfo, status }) => (
10
+ <Switch>
11
+ <Route path={route('reports')}>
12
+ {hostName ? (
13
+ <Reports hostName={hostName} hostInfo={hostInfo} status={status} />
14
+ ) : (
15
+ <EmptyPage header="Reports" />
16
+ )}
17
+ </Route>
18
+ <Route path={route('assigned')}>
19
+ <EmptyPage header="Assigned classes" />
20
+ </Route>
21
+ <Route path={route('smart-classes')}>
22
+ <EmptyPage header="Smart class parameters" />
23
+ </Route>
24
+ <Route path={route('yaml')}>
25
+ {hostName ? (
26
+ <ENCPreview hostName={hostName} />
27
+ ) : (
28
+ <EmptyPage header="ENC Preview" />
29
+ )}
30
+ </Route>
31
+ <Redirect to={route('reports')} />
32
+ </Switch>
33
+ );
34
+
35
+ SecondaryTabRoutes.propTypes = {
36
+ hostName: PropTypes.string,
37
+ hostInfo: PropTypes.object,
38
+ status: PropTypes.string,
39
+ };
40
+
41
+ SecondaryTabRoutes.defaultProps = {
42
+ hostName: '',
43
+ hostInfo: {},
44
+ status: undefined,
45
+ };
46
+
47
+ export default SecondaryTabRoutes;
@@ -0,0 +1,61 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import {
4
+ CodeBlock,
5
+ CodeBlockAction,
6
+ CodeBlockCode,
7
+ ClipboardCopyButton,
8
+ } from '@patternfly/react-core';
9
+
10
+ export const ENCTab = ({ encData }) => {
11
+ const [copied, setCopied] = React.useState(false);
12
+
13
+ const code = `${encData}`;
14
+
15
+ const clipboardCopyFunc = (event, text) => {
16
+ const clipboard = event.currentTarget.parentElement;
17
+ const el = document.createElement('textarea');
18
+ el.value = text.toString();
19
+ clipboard.appendChild(el);
20
+ el.select();
21
+ document.execCommand('copy');
22
+ clipboard.removeChild(el);
23
+ };
24
+
25
+ const onClick = (event, text) => {
26
+ clipboardCopyFunc(event, text);
27
+ setCopied(true);
28
+ };
29
+
30
+ const actions = (
31
+ <CodeBlockAction>
32
+ <ClipboardCopyButton
33
+ id="copy-button"
34
+ textId="code-content"
35
+ aria-label="Copy to clipboard"
36
+ onClick={e => onClick(e, code)}
37
+ exitDelay={600}
38
+ maxWidth="110px"
39
+ variant="plain"
40
+ >
41
+ {copied ? 'Successfully copied to clipboard!' : 'Copy to clipboard'}
42
+ </ClipboardCopyButton>
43
+ </CodeBlockAction>
44
+ );
45
+
46
+ return (
47
+ <CodeBlock actions={actions}>
48
+ <CodeBlockCode id="code-content">{code}</CodeBlockCode>
49
+ </CodeBlock>
50
+ );
51
+ };
52
+
53
+ ENCTab.propTypes = {
54
+ encData: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
55
+ };
56
+
57
+ ENCTab.defaultProps = {
58
+ encData: undefined,
59
+ };
60
+
61
+ export default ENCTab;
@@ -0,0 +1,53 @@
1
+ import React from 'react';
2
+ import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
3
+ import PropTypes from 'prop-types';
4
+
5
+ import Skeleton from 'react-loading-skeleton';
6
+ import EmptyState from 'foremanReact/components/common/EmptyState/EmptyStatePattern';
7
+ import { STATUS } from 'foremanReact/constants';
8
+ import { EmptyStateIcon } from '@patternfly/react-core';
9
+ import { ExclamationCircleIcon } from '@patternfly/react-icons';
10
+ import { global_danger_color_200 as dangerColor } from '@patternfly/react-tokens';
11
+ import { translate as __ } from 'foremanReact/common/I18n';
12
+ import { ENCTab } from './ENCTab';
13
+
14
+ const ENCPreview = ({ hostName }) => {
15
+ const options = {
16
+ params: { name: hostName, format: 'yml' },
17
+ key: 'PUPPET_ENC_PREVIEW',
18
+ };
19
+ const url = `${window.location.origin.toString()}/foreman_puppet/hosts/${hostName}/externalNodes`;
20
+ const { response, status } = useAPI('get', url, options);
21
+
22
+ if (status === STATUS.PENDING) {
23
+ return <Skeleton count={5} />;
24
+ }
25
+
26
+ if (status === STATUS.ERROR) {
27
+ const icon = (
28
+ <EmptyStateIcon icon={ExclamationCircleIcon} color={dangerColor.value} />
29
+ );
30
+ return (
31
+ <EmptyState
32
+ header={__('Error!')}
33
+ icon={icon}
34
+ description={response?.response?.data?.message}
35
+ />
36
+ );
37
+ }
38
+ if (response !== '' || response !== undefined) {
39
+ return (
40
+ <div className="yaml-tab" padding="16px 24px">
41
+ <ENCTab encData={response} />
42
+ </div>
43
+ );
44
+ }
45
+
46
+ return null;
47
+ };
48
+
49
+ ENCPreview.propTypes = {
50
+ hostName: PropTypes.string.isRequired,
51
+ };
52
+
53
+ export default ENCPreview;
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import PFEmptyPage from 'foremanReact/components/common/EmptyState/EmptyStatePattern';
4
+
5
+ const EmptyPage = ({ header }) => (
6
+ <div className="host-details-tab-item">
7
+ <PFEmptyPage
8
+ icon="enterprise"
9
+ header={header}
10
+ description="This is a demo for adding content to the new host page"
11
+ />
12
+ </div>
13
+ );
14
+
15
+ EmptyPage.propTypes = {
16
+ header: PropTypes.string.isRequired,
17
+ };
18
+
19
+ export default EmptyPage;
@@ -0,0 +1,71 @@
1
+ import PropTypes from 'prop-types';
2
+ import React from 'react';
3
+ import CardTemplate from 'foremanReact/components/HostDetails/Templates/CardItem/CardTemplate';
4
+ import {
5
+ DescriptionList,
6
+ DescriptionListTerm,
7
+ DescriptionListGroup,
8
+ DescriptionListDescription,
9
+ } from '@patternfly/react-core';
10
+ import SkeletonLoader from 'foremanReact/components/common/SkeletonLoader';
11
+ import DefaultLoaderEmptyState from 'foremanReact/components/HostDetails/DetailsCard/DefaultLoaderEmptyState';
12
+ import { STATUS } from 'foremanReact/constants';
13
+ import { translate as __ } from 'foremanReact/common/I18n';
14
+
15
+ const DescriptionCard = ({ proxyName, caProxy, env, status }) => (
16
+ <CardTemplate header={__('Puppet details')} expandable>
17
+ <DescriptionList isCompact>
18
+ <DescriptionListGroup>
19
+ <DescriptionListTerm>{__('Puppet environment')}</DescriptionListTerm>
20
+ <DescriptionListDescription>
21
+ <SkeletonLoader
22
+ emptyState={<DefaultLoaderEmptyState />}
23
+ status={status}
24
+ >
25
+ {env && (
26
+ <a href={`/foreman_puppet/environments/${env}/edit`}>{env}</a>
27
+ )}
28
+ </SkeletonLoader>
29
+ </DescriptionListDescription>
30
+ </DescriptionListGroup>
31
+ <DescriptionListGroup>
32
+ <DescriptionListTerm>{__('Puppet smart proxy')}</DescriptionListTerm>
33
+ <DescriptionListDescription>
34
+ <SkeletonLoader
35
+ emptyState={<DefaultLoaderEmptyState />}
36
+ status={status}
37
+ >
38
+ {proxyName}
39
+ </SkeletonLoader>
40
+ </DescriptionListDescription>
41
+ </DescriptionListGroup>
42
+ <DescriptionListGroup>
43
+ <DescriptionListTerm>{__('Puppet server CA')}</DescriptionListTerm>
44
+ <DescriptionListDescription>
45
+ <SkeletonLoader
46
+ emptyState={<DefaultLoaderEmptyState />}
47
+ status={status}
48
+ >
49
+ {caProxy}
50
+ </SkeletonLoader>
51
+ </DescriptionListDescription>
52
+ </DescriptionListGroup>
53
+ </DescriptionList>
54
+ </CardTemplate>
55
+ );
56
+
57
+ DescriptionCard.propTypes = {
58
+ caProxy: PropTypes.string,
59
+ env: PropTypes.string,
60
+ proxyName: PropTypes.string,
61
+ status: PropTypes.string,
62
+ };
63
+
64
+ DescriptionCard.defaultProps = {
65
+ caProxy: undefined,
66
+ env: undefined,
67
+ proxyName: undefined,
68
+ status: STATUS.PENDING,
69
+ };
70
+
71
+ export default DescriptionCard;
@@ -0,0 +1,45 @@
1
+ import PropTypes from 'prop-types';
2
+ import React from 'react';
3
+ import { Grid, GridItem } from '@patternfly/react-core';
4
+ import ReportsTab from 'foremanReact/components/HostDetails/Tabs/ReportsTab';
5
+ import DescriptionCard from './components/DescriptionCard';
6
+ import './styles.scss';
7
+
8
+ const Reports = ({
9
+ hostName,
10
+ status,
11
+ hostInfo: {
12
+ puppet_proxy_name: proxyName,
13
+ puppet_ca_proxy_name: caProxy,
14
+ environment_name: env,
15
+ },
16
+ }) => (
17
+ <div className="report-tab">
18
+ <Grid hasGutter>
19
+ <GridItem span={4}>
20
+ <DescriptionCard
21
+ proxyName={proxyName}
22
+ caProxy={caProxy}
23
+ env={env}
24
+ status={status}
25
+ />
26
+ </GridItem>
27
+ <GridItem span={12}>
28
+ <ReportsTab hostName={hostName} origin="Puppet" />
29
+ </GridItem>
30
+ </Grid>
31
+ </div>
32
+ );
33
+
34
+ Reports.propTypes = {
35
+ hostName: PropTypes.string.isRequired,
36
+ hostInfo: PropTypes.object,
37
+ status: PropTypes.string,
38
+ };
39
+
40
+ Reports.defaultProps = {
41
+ hostInfo: {},
42
+ status: undefined,
43
+ };
44
+
45
+ export default Reports;
@@ -0,0 +1,7 @@
1
+ .report-tab {
2
+ padding: 16px 24px;
3
+
4
+ .pf-c-card {
5
+ height: 100%;
6
+ }
7
+ }
@@ -0,0 +1,8 @@
1
+ import { translate as __ } from 'foremanReact/common/I18n';
2
+
3
+ export const SECONDARY_TABS = [
4
+ { key: 'reports', title: __('Reports') },
5
+ { key: 'assigned', title: __('Assigned classes') },
6
+ { key: 'smart-classes', title: __('Smart class parameters') },
7
+ { key: 'yaml', title: __('ENC Preview') },
8
+ ];
@@ -0,0 +1,3 @@
1
+ export const hashRoute = subpath => `#/Puppet/${subpath}`;
2
+ export const route = subpath => hashRoute(subpath).substring(1);
3
+ export const activeTab = path => path.split('/')[2];
@@ -0,0 +1,51 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { useHistory } from 'react-router-dom';
4
+ import { Tabs, Tab, TabTitleText } from '@patternfly/react-core';
5
+ import { STATUS } from 'foremanReact/constants';
6
+
7
+ import SecondaryTabRoutes from './Routes';
8
+ import { activeTab } from './helpers';
9
+ import { SECONDARY_TABS } from './constants';
10
+
11
+ const PuppetTab = ({ response, status, location: { pathname } }) => {
12
+ const hashHistory = useHistory();
13
+ return (
14
+ <>
15
+ <Tabs
16
+ className="margin-0-24"
17
+ onSelect={(evt, subTab) => hashHistory.push(subTab)}
18
+ isSecondary
19
+ activeKey={activeTab(pathname)}
20
+ >
21
+ {SECONDARY_TABS.map(({ key, title }) => (
22
+ <Tab
23
+ key={key}
24
+ eventKey={key}
25
+ title={<TabTitleText>{title}</TabTitleText>}
26
+ />
27
+ ))}
28
+ </Tabs>
29
+ <SecondaryTabRoutes
30
+ hostName={response.name}
31
+ hostInfo={response}
32
+ status={status}
33
+ />
34
+ </>
35
+ );
36
+ };
37
+
38
+ PuppetTab.propTypes = {
39
+ response: PropTypes.object,
40
+ status: PropTypes.string,
41
+ location: PropTypes.shape({
42
+ pathname: PropTypes.string,
43
+ }),
44
+ };
45
+ PuppetTab.defaultProps = {
46
+ location: { pathname: '' },
47
+ response: { name: '' },
48
+ status: STATUS.PENDING,
49
+ };
50
+
51
+ export default PuppetTab;