foreman_acd 0.0.6 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/foreman_acd/api/v2/app_definitions_controller.rb +1 -2
  3. data/app/controllers/foreman_acd/app_definitions_controller.rb +29 -1
  4. data/app/controllers/foreman_acd/app_instances_controller.rb +70 -21
  5. data/app/controllers/foreman_acd/concerns/app_definition_parameters.rb +1 -1
  6. data/app/controllers/foreman_acd/concerns/app_instance_parameters.rb +1 -1
  7. data/app/controllers/ui_acd_controller.rb +0 -1
  8. data/app/models/foreman_acd/app_definition.rb +0 -1
  9. data/app/views/foreman_acd/app_definitions/_form.html.erb +6 -14
  10. data/app/views/foreman_acd/app_definitions/import.html.erb +18 -0
  11. data/app/views/foreman_acd/app_definitions/index.html.erb +9 -5
  12. data/app/views/foreman_acd/app_instances/_form.html.erb +5 -5
  13. data/app/views/foreman_acd/app_instances/deploy.html.erb +19 -0
  14. data/app/views/foreman_acd/app_instances/index.html.erb +6 -5
  15. data/app/views/foreman_acd/app_instances/report.html.erb +19 -0
  16. data/app/views/ui_acd/app_definition.json.rabl +1 -1
  17. data/config/routes.rb +7 -0
  18. data/db/migrate/20190610202252_create_app_definitions.rb +1 -3
  19. data/db/migrate/20190625140305_create_app_instances.rb +1 -1
  20. data/lib/foreman_acd/plugin.rb +19 -2
  21. data/lib/foreman_acd/version.rb +1 -1
  22. data/webpack/components/ApplicationDefinition/ApplicationDefinition.js +261 -0
  23. data/webpack/components/ApplicationDefinition/ApplicationDefinition.scss +1 -0
  24. data/webpack/components/ApplicationDefinition/ApplicationDefinitionActions.js +211 -0
  25. data/webpack/components/ApplicationDefinition/ApplicationDefinitionConstants.js +9 -0
  26. data/webpack/components/ApplicationDefinition/ApplicationDefinitionReducer.js +147 -0
  27. data/webpack/components/ApplicationDefinition/ApplicationDefinitionSelectors.js +6 -0
  28. data/webpack/components/ApplicationDefinition/index.js +29 -0
  29. data/webpack/components/ApplicationInstance/ApplicationInstance.js +342 -0
  30. data/webpack/components/ApplicationInstance/ApplicationInstance.scss +11 -0
  31. data/webpack/components/ApplicationInstance/ApplicationInstanceActions.js +210 -0
  32. data/webpack/components/ApplicationInstance/ApplicationInstanceConstants.js +12 -0
  33. data/webpack/components/ApplicationInstance/ApplicationInstanceReducer.js +223 -0
  34. data/webpack/components/ApplicationInstance/ApplicationInstanceSelectors.js +8 -0
  35. data/webpack/components/ApplicationInstance/components/AppDefinitionSelector.js +49 -0
  36. data/webpack/components/ApplicationInstance/components/Service.js +30 -0
  37. data/webpack/components/ApplicationInstance/components/ServiceCounter.js +37 -0
  38. data/webpack/components/ApplicationInstance/index.js +33 -0
  39. data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReport.js +155 -0
  40. data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReport.scss +27 -0
  41. data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportActions.js +86 -0
  42. data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportConstants.js +4 -0
  43. data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportReducer.js +52 -0
  44. data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportSelectors.js +5 -0
  45. data/webpack/components/ApplicationInstanceReport/components/ReportViewer.js +26 -0
  46. data/webpack/components/ApplicationInstanceReport/index.js +27 -0
  47. data/webpack/components/ParameterSelection/ParameterSelection.js +65 -161
  48. data/webpack/components/ParameterSelection/ParameterSelection.scss +9 -0
  49. data/webpack/components/ParameterSelection/ParameterSelectionActions.js +42 -71
  50. data/webpack/components/ParameterSelection/ParameterSelectionConstants.js +12 -19
  51. data/webpack/components/ParameterSelection/ParameterSelectionHelper.js +3 -3
  52. data/webpack/components/ParameterSelection/ParameterSelectionReducer.js +76 -75
  53. data/webpack/components/ParameterSelection/ParameterSelectionSelectors.js +2 -6
  54. data/webpack/components/ParameterSelection/__fixtures__/parameterSelection.fixtures.js +12 -21
  55. data/webpack/components/ParameterSelection/__fixtures__/parameterSelectionData_1.fixtures.js +1 -1
  56. data/webpack/components/ParameterSelection/__fixtures__/parameterSelectionReducer.fixtures.js +3 -45
  57. data/webpack/components/ParameterSelection/__tests__/ParameterSelection.test.js +20 -0
  58. data/webpack/components/ParameterSelection/__tests__/ParameterSelectionReducer.test.js +22 -46
  59. data/webpack/components/ParameterSelection/__tests__/ParameterSelectionSelectors.test.js +6 -6
  60. data/webpack/components/ParameterSelection/__tests__/__snapshots__/ParameterSelection.test.js.snap +40 -265
  61. data/webpack/components/ParameterSelection/__tests__/__snapshots__/ParameterSelectionReducer.test.js.snap +11 -96
  62. data/webpack/components/ParameterSelection/__tests__/__snapshots__/ParameterSelectionSelectors.test.js.snap +3 -9
  63. data/webpack/components/ParameterSelection/index.js +4 -6
  64. data/webpack/components/common/AddTableEntry.js +30 -0
  65. data/webpack/components/common/DeleteTableEntry.js +39 -0
  66. data/webpack/components/common/ExtSelect.js +43 -0
  67. data/webpack/components/common/RailsData.js +27 -0
  68. data/webpack/components/common/__tests__/AddTableEntry.test.js +26 -0
  69. data/webpack/components/common/__tests__/DeleteTableEntry.test.js +29 -0
  70. data/webpack/components/common/__tests__/ExtSelect.test.js +38 -0
  71. data/webpack/components/common/__tests__/RailsData.test.js +16 -0
  72. data/webpack/components/common/__tests__/__snapshots__/AddParameter.test.js.snap +35 -0
  73. data/webpack/components/common/__tests__/__snapshots__/AddTableEntry.test.js.snap +35 -0
  74. data/webpack/components/common/__tests__/__snapshots__/DeleteParameter.test.js.snap +41 -0
  75. data/webpack/components/common/__tests__/__snapshots__/DeleteTableEntry.test.js.snap +41 -0
  76. data/webpack/components/common/__tests__/__snapshots__/ExtSelect.test.js.snap +18 -0
  77. data/webpack/components/common/__tests__/__snapshots__/RailsData.test.js.snap +10 -0
  78. data/webpack/helper.js +20 -0
  79. data/webpack/index.js +6 -0
  80. data/webpack/reducer.js +40 -3
  81. metadata +47 -46
@@ -0,0 +1,8 @@
1
+ const applicationInstanceConf = state => state.foremanAcd.applicationInstanceConf;
2
+
3
+ export const selectEditMode = state => applicationInstanceConf(state).editMode;
4
+ export const selectAppDefinition = state => applicationInstanceConf(state).appDefinition;
5
+ export const selectHosts = state => applicationInstanceConf(state).hosts;
6
+ export const selectColumns = state => applicationInstanceConf(state).columns;
7
+ export const selectParametersData = state => applicationInstanceConf(state).parametersData;
8
+ export const selectServices = state => applicationInstanceConf(state).services;
@@ -0,0 +1,49 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import ExtSelect from '../../common/ExtSelect';
4
+ import RailsData from '../../common/RailsData'
5
+
6
+ const AppDefinitionSelector= ({
7
+ label,
8
+ hidden,
9
+ editable,
10
+ viewText,
11
+ selectValue,
12
+ onChange,
13
+ options,
14
+ additionalData,
15
+ }) =>{
16
+ return (
17
+ <div className="form-group">
18
+ <label className="col-md-2 control-label">{label}</label>
19
+ <div className="col-md-4">
20
+ <ExtSelect
21
+ editable={editable}
22
+ viewText={viewText}
23
+ selectValue={selectValue}
24
+ onChange={onChange}
25
+ options={options}
26
+ additionalData={additionalData}
27
+ />
28
+ <RailsData
29
+ key='app_instance_id'
30
+ view='app_instance'
31
+ parameter='app_definition_id'
32
+ value={selectValue}
33
+ />
34
+ </div>
35
+ </div>
36
+ );
37
+ };
38
+
39
+ AppDefinitionSelector.propTypes = {
40
+ label: PropTypes.string.isRequired,
41
+ editable: PropTypes.bool.isRequired,
42
+ viewText: PropTypes.string,
43
+ selectValue: PropTypes.string,
44
+ onChange: PropTypes.func.isRequired,
45
+ options: PropTypes.object,
46
+ additionalData: PropTypes.object,
47
+ };
48
+
49
+ export default AppDefinitionSelector;
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+
4
+ const Service= ({
5
+ name,
6
+ currentCount,
7
+ minCount,
8
+ maxCount,
9
+ }) =>{
10
+ return (
11
+ <div>
12
+ <label>{name}:</label> {currentCount} (Min/Max: {minCount}/{maxCount})
13
+ </div>
14
+ );
15
+ };
16
+
17
+ Service.defaultProps = {
18
+ minCount: 0,
19
+ maxCount: 0,
20
+ currentCount: 0,
21
+ };
22
+
23
+ Service.propTypes = {
24
+ name: PropTypes.string.isRequired,
25
+ minCount: PropTypes.number.isRequired,
26
+ maxCount: PropTypes.number.isRequired,
27
+ currentCount: PropTypes.number.isRequired,
28
+ };
29
+
30
+ export default Service;
@@ -0,0 +1,37 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import Service from './Service';
4
+ import { arrayToObjectObj } from '../../../helper';
5
+ import { cloneDeep } from 'lodash';
6
+
7
+ const ServiceCounter= ({
8
+ title,
9
+ serviceList,
10
+ }) =>{
11
+ if (serviceList == undefined || serviceList.length == 0) {
12
+ return null;
13
+ }
14
+ const services = cloneDeep(arrayToObjectObj(serviceList, "id"));
15
+
16
+ return (
17
+ <div>
18
+ <label class="service-counter-title">{title}</label>
19
+ {Object.keys(services).map(key => (
20
+ <Service
21
+ key={services[key].id}
22
+ name={services[key].name}
23
+ currentCount={services[key].currentCount}
24
+ minCount={Number(services[key].minCount)}
25
+ maxCount={Number(services[key].maxCount)}
26
+ />)
27
+ )}
28
+ </div>
29
+ );
30
+ };
31
+
32
+ ServiceCounter.propTypes = {
33
+ title: PropTypes.string.isRequired,
34
+ serviceList: PropTypes.array,
35
+ };
36
+
37
+ export default ServiceCounter;
@@ -0,0 +1,33 @@
1
+ import { bindActionCreators } from 'redux';
2
+ import { connect } from 'react-redux';
3
+
4
+ import './ApplicationInstance.scss';
5
+ import ApplicationInstance from './ApplicationInstance';
6
+ import * as ApplicationInstanceActions from './ApplicationInstanceActions';
7
+
8
+ import {
9
+ selectEditMode,
10
+ selectAppDefinition,
11
+ selectHosts,
12
+ selectServices,
13
+ selectColumns,
14
+ selectParametersData,
15
+ } from './ApplicationInstanceSelectors';
16
+
17
+ const mapStateToProps = state => ({
18
+ editMode: selectEditMode(state),
19
+ appDefinition: selectAppDefinition(state),
20
+ hosts: selectHosts(state),
21
+ services: selectServices(state),
22
+ columns: selectColumns(state),
23
+ parametersData: selectParametersData(state),
24
+ });
25
+
26
+ const mapDispatchToProps = dispatch =>
27
+ bindActionCreators(ApplicationInstanceActions, dispatch);
28
+
29
+ export default connect(
30
+ mapStateToProps,
31
+ mapDispatchToProps
32
+ )(ApplicationInstance);
33
+
@@ -0,0 +1,155 @@
1
+ import React, { useState } from 'react'
2
+ import PropTypes from 'prop-types';
3
+
4
+ import {
5
+ VerticalTabs,
6
+ } from 'patternfly-react-extensions';
7
+
8
+ import PowerStatus from 'foremanReact/components/hosts/powerStatus';
9
+ import ReportViewer from './components/ReportViewer';
10
+
11
+ class ApplicationInstanceReport extends React.Component {
12
+
13
+ constructor(props) {
14
+ super(props);
15
+ }
16
+
17
+ componentDidMount() {
18
+ const {
19
+ data: { hosts, mode },
20
+ initApplicationInstanceReport,
21
+ setActiveAndLoadLiveReport,
22
+ } = this.props;
23
+
24
+ initApplicationInstanceReport(hosts);
25
+
26
+ if (mode == 'liveReport') {
27
+ if (hosts.length > 0) {
28
+ const index = 0;
29
+ const url = `/api/v2/orchestration/${hosts[index].progress_report_id}/tasks`;
30
+ setActiveAndLoadLiveReport(index, url);
31
+ }
32
+ }
33
+ };
34
+
35
+ isActive(id) {
36
+ return (this.props.activeHostId === id);
37
+ }
38
+
39
+ collectLiveData(hosts) {
40
+ const {
41
+ setActiveAndLoadLiveReport,
42
+ } = this.props;
43
+ const tabs = []
44
+
45
+ for (const [index, value] of hosts.entries()) {
46
+ const url = `/api/v2/orchestration/${hosts[index].progress_report_id}/tasks`
47
+
48
+ tabs.push(
49
+ <VerticalTabs.Tab
50
+ id={index}
51
+ key={"vt_tab_"+index}
52
+ title={ value.name }
53
+ wrapStyle='nowrap'
54
+ onActivate={() => setActiveAndLoadLiveReport(index, url)}
55
+ active={this.isActive(index)}
56
+ />
57
+ );
58
+ }
59
+
60
+ return tabs;
61
+ }
62
+
63
+ collectLastReportData(hosts) {
64
+ const {
65
+ setActiveAndLoadLastReport,
66
+ } = this.props;
67
+ // FIXME
68
+ const url = undefined;
69
+
70
+ const tabs = []
71
+ for (const [index, value] of hosts.entries()) {
72
+ tabs.push(
73
+ <VerticalTabs.Tab
74
+ id={index}
75
+ key={"vt_tab_"+index}
76
+ title={ value.name }
77
+ wrapStyle='nowrap'
78
+ onActivate={() => setActiveAndLoadLastReport(index, url)}
79
+ active={this.isActive(index)}
80
+ />
81
+ );
82
+ }
83
+
84
+ return tabs;
85
+ }
86
+
87
+ liveReportStatus(host) {
88
+ return (
89
+ <span>Host: <a href={ host['hostUrl'] }>{ host['hostname'] }</a></span>
90
+ );
91
+ }
92
+
93
+ lastReportStatus(host) {
94
+ return (
95
+ <div>
96
+ <span>Host: <a href={ host['hostUrl'] }>{ host['hostname'] }</a></span>
97
+ <span>&nbsp;|&nbsp;</span>
98
+ <span>Power Status: <PowerStatus data={{ id: host['id'], url: host['powerStatusUrl'] }} /></span>
99
+ </div>
100
+ )
101
+ }
102
+
103
+ render() {
104
+ const {
105
+ data: { hosts, mode },
106
+ report,
107
+ activeHostId,
108
+ } = this.props;
109
+
110
+ let tabs = [];
111
+ let reportStatus = undefined;
112
+
113
+ if (mode == 'liveReport') {
114
+ tabs = this.collectLiveData(hosts);
115
+ reportStatus = this.liveReportStatus(hosts[activeHostId])
116
+ } else if (mode == 'lastReport') {
117
+ tabs = this.collectLastReportData(hosts);
118
+ reportStatus = this.lastReportStatus(hosts[activeHostId])
119
+ }
120
+
121
+ return (
122
+ <span>
123
+ <div className="deploy_report_hosts">
124
+ <VerticalTabs id="vertical_tabs">
125
+ {tabs}
126
+ </VerticalTabs>
127
+ </div>
128
+ <div className="deploy_report_status">
129
+ {reportStatus}
130
+ </div>
131
+ <div className="deploy_report_tasks">
132
+ <ReportViewer report={report} />
133
+ </div>
134
+ </span>
135
+ )};
136
+ }
137
+
138
+ ApplicationInstanceReport.defaultProps = {
139
+ error: {},
140
+ hosts: [],
141
+ report: [],
142
+ activeHostId: 0,
143
+ }
144
+
145
+ ApplicationInstanceReport.propTypes = {
146
+ initApplicationInstanceReport: PropTypes.func,
147
+ hosts: PropTypes.array,
148
+ report: PropTypes.array,
149
+ setActiveAndLoadLiveReport: PropTypes.func,
150
+ setActiveAndLoadLastReport: PropTypes.func,
151
+ activeHostId: PropTypes.number,
152
+
153
+ };
154
+
155
+ export default ApplicationInstanceReport;
@@ -0,0 +1,27 @@
1
+ @import '~@theforeman/vendor/scss/variables';
2
+
3
+ .deploy_report_hosts {
4
+ float:left;
5
+ width:25%;
6
+ height:100%;
7
+ }
8
+
9
+ .deploy_report_tasks {
10
+ float:left;
11
+ width:75%;
12
+ min-height: 300px;
13
+ background-color: #ecf9fe;
14
+ border: 1px solid #3dbff3;
15
+ }
16
+
17
+ ul.vertical-tabs-pf {
18
+ list-style-type: none;
19
+ }
20
+
21
+ .vertical-tabs-pf-tab.active {
22
+ background-color: $dropdown-link-hover-bg;
23
+ }
24
+
25
+ .vertical-tabs-pf-tab {
26
+ background-color: white;
27
+ }
@@ -0,0 +1,86 @@
1
+ import React from 'react';
2
+ import api from 'foremanReact/API';
3
+
4
+ import {
5
+ actionHeaderCellFormatter,
6
+ } from 'patternfly-react';
7
+
8
+ import {
9
+ propsToSnakeCase,
10
+ propsToCamelCase,
11
+ } from 'foremanReact/common/helpers';
12
+
13
+ import {
14
+ APPLICATION_INSTANCE_DEPLOY_INIT,
15
+ APPLICATION_INSTANCE_DEPLOY_LOAD_REPORT_REQUEST,
16
+ APPLICATION_INSTANCE_DEPLOY_LOAD_REPORT_SUCCESS,
17
+ APPLICATION_INSTANCE_DEPLOY_LOAD_REPORT_FAILURE,
18
+ } from './ApplicationInstanceReportConstants';
19
+
20
+ export const initApplicationInstanceReport = (
21
+ hosts,
22
+ ) => dispatch => {
23
+ const initialState = {};
24
+
25
+ initialState.hosts = hosts;
26
+
27
+ dispatch({
28
+ type: APPLICATION_INSTANCE_DEPLOY_INIT,
29
+ payload: initialState,
30
+ });
31
+ };
32
+
33
+ const errorHandler = (msg, err) => {
34
+ const error = {
35
+ errorMsg: 'Failed to fetch data from server.',
36
+ statusText: err,
37
+ };
38
+ return { type: msg, payload: { error } };
39
+ };
40
+
41
+ export const loadReport = (dispatch, getState, id, url, live, initial) => {
42
+
43
+ const reportState = getState().foremanAcd.applicationInstanceReport;
44
+
45
+ if (live) {
46
+ if ((reportState.activeHostId == id) || (initial == true)) {
47
+ setTimeout(() => {
48
+ loadReport(dispatch, getState, id, url, live, false);
49
+ }, 1600)
50
+ }
51
+ }
52
+
53
+ return api
54
+ .get(url, {}, {})
55
+ .then(({ data }) =>
56
+ dispatch({
57
+ type: APPLICATION_INSTANCE_DEPLOY_LOAD_REPORT_SUCCESS,
58
+ payload: data.results,
59
+ })
60
+ )
61
+ .catch(error => dispatch(errorHandler(APPLICATION_INSTANCE_DEPLOY_LOAD_REPORT_FAILURE, error)));
62
+ };
63
+
64
+ export const setActiveAndLoadLiveReport = (
65
+ id,
66
+ url,
67
+ ) => (dispatch, getState) => {
68
+ dispatch({
69
+ payload: { activeHostId: id },
70
+ type: APPLICATION_INSTANCE_DEPLOY_LOAD_REPORT_REQUEST
71
+ });
72
+
73
+ return loadReport(dispatch, getState, id, url, true, true);
74
+ }
75
+
76
+ export const setActiveAndLoadLastReport = (
77
+ id,
78
+ url,
79
+ ) => (dispatch, getState) => {
80
+ dispatch({
81
+ payload: { activeHostId: id },
82
+ type: APPLICATION_INSTANCE_DEPLOY_LOAD_REPORT_REQUEST
83
+ });
84
+
85
+ return loadReport(dispatch, getState, id, url, false, true);
86
+ };
@@ -0,0 +1,4 @@
1
+ export const APPLICATION_INSTANCE_DEPLOY_INIT = 'APPLICATION_INSTANCE_DEPLOY_INIT';
2
+ export const APPLICATION_INSTANCE_DEPLOY_LOAD_REPORT_REQUEST = 'APPLICATION_INSTANCE_DEPLOY_LOAD_REPORT_REQUEST';
3
+ export const APPLICATION_INSTANCE_DEPLOY_LOAD_REPORT_SUCCESS = 'APPLICATION_INSTANCE_DEPLOY_LOAD_REPORT_SUCCESS';
4
+ export const APPLICATION_INSTANCE_DEPLOY_LOAD_REPORT_FAILURE = 'APPLICATION_INSTANCE_DEPLOY_LOAD_REPORT_FAILURE';
@@ -0,0 +1,52 @@
1
+ import Immutable from 'seamless-immutable';
2
+
3
+ import {
4
+ cloneDeep,
5
+ findIndex,
6
+ findLastIndex,
7
+ } from 'lodash';
8
+
9
+ import {
10
+ APPLICATION_INSTANCE_DEPLOY_INIT,
11
+ APPLICATION_INSTANCE_DEPLOY_LOAD_REPORT_REQUEST,
12
+ APPLICATION_INSTANCE_DEPLOY_LOAD_REPORT_SUCCESS,
13
+ APPLICATION_INSTANCE_DEPLOY_LOAD_REPORT_FAILURE,
14
+ } from './ApplicationInstanceReportConstants';
15
+
16
+ export const initialState = Immutable({
17
+ name: false,
18
+ error: { errorMsg: '', status: '', statusText: '' },
19
+ });
20
+
21
+ const applicationInstanceReport = (state = initialState, action) => {
22
+ const { payload } = action;
23
+
24
+ switch (action.type) {
25
+ case APPLICATION_INSTANCE_DEPLOY_INIT: {
26
+ return state.merge(payload);
27
+ }
28
+ case APPLICATION_INSTANCE_DEPLOY_LOAD_REPORT_REQUEST: {
29
+ return state.merge({
30
+ loading: true,
31
+ activeHostId: payload.activeHostId,
32
+ })
33
+ }
34
+ case APPLICATION_INSTANCE_DEPLOY_LOAD_REPORT_SUCCESS: {
35
+ return state.merge({
36
+ report: payload,
37
+ loading: false,
38
+ });
39
+ }
40
+ case APPLICATION_INSTANCE_DEPLOY_LOAD_REPORT_FAILURE: {
41
+ return state.merge({
42
+ error: payload.error,
43
+ loading: false
44
+ });
45
+ }
46
+ default: {
47
+ return state;
48
+ }
49
+ }
50
+ };
51
+
52
+ export default applicationInstanceReport;