foreman_acd 0.2.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (195) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +107 -56
  3. data/app/controllers/foreman_acd/ansible_playbooks_controller.rb +199 -0
  4. data/app/controllers/foreman_acd/api/v2/ansible_playbooks_controller.rb +72 -0
  5. data/app/controllers/foreman_acd/api/v2/app_definitions_controller.rb +1 -0
  6. data/app/controllers/foreman_acd/api/v2/app_instances_controller.rb +62 -0
  7. data/app/controllers/foreman_acd/app_definitions_controller.rb +19 -10
  8. data/app/controllers/foreman_acd/app_instances_controller.rb +111 -137
  9. data/app/controllers/foreman_acd/concerns/ansible_playbook_parameters.rb +23 -0
  10. data/app/controllers/foreman_acd/concerns/app_definition_parameters.rb +1 -1
  11. data/app/controllers/foreman_acd/concerns/app_instance_parameters.rb +1 -1
  12. data/app/controllers/foreman_acd/remote_execution_controller.rb +62 -0
  13. data/app/controllers/ui_acd_controller.rb +20 -3
  14. data/app/lib/actions/foreman_acd/deploy_all_hosts.rb +47 -0
  15. data/app/lib/actions/foreman_acd/run_configurator.rb +44 -0
  16. data/app/models/concerns/foreman_acd/host_managed_extensions.rb +51 -0
  17. data/app/models/foreman_acd/acd_provider.rb +36 -0
  18. data/app/models/foreman_acd/ansible_playbook.rb +68 -0
  19. data/app/models/foreman_acd/app_definition.rb +25 -0
  20. data/app/models/foreman_acd/app_instance.rb +42 -0
  21. data/app/models/foreman_acd/foreman_host.rb +23 -0
  22. data/app/models/foreman_acd/taxonomy_extensions.rb +17 -0
  23. data/app/services/foreman_acd/acd_proxy_proxy_selector.rb +17 -0
  24. data/app/services/foreman_acd/app_configurator.rb +98 -0
  25. data/app/services/foreman_acd/app_deployer.rb +157 -0
  26. data/app/services/foreman_acd/inventory_creator.rb +68 -0
  27. data/app/views/foreman_acd/ansible_playbooks/_form.html.erb +64 -0
  28. data/app/views/foreman_acd/ansible_playbooks/edit.html.erb +11 -0
  29. data/app/views/foreman_acd/ansible_playbooks/index.html.erb +30 -0
  30. data/app/views/foreman_acd/ansible_playbooks/new.html.erb +3 -0
  31. data/app/views/foreman_acd/api/v2/ansible_playbooks/base.json.rabl +5 -0
  32. data/app/views/foreman_acd/api/v2/ansible_playbooks/index.json.rabl +5 -0
  33. data/app/views/foreman_acd/api/v2/ansible_playbooks/show.json.rabl +9 -0
  34. data/app/views/foreman_acd/api/v2/app_definitions/base.json.rabl +5 -0
  35. data/app/views/foreman_acd/api/v2/app_definitions/index.json.rabl +5 -0
  36. data/app/views/foreman_acd/api/v2/app_definitions/show.json.rabl +9 -0
  37. data/app/views/foreman_acd/api/v2/app_instances/base.json.rabl +5 -0
  38. data/app/views/foreman_acd/api/v2/app_instances/index.json.rabl +5 -0
  39. data/app/views/foreman_acd/api/v2/app_instances/show.json.rabl +5 -0
  40. data/app/views/foreman_acd/app_definitions/_form.html.erb +34 -12
  41. data/app/views/foreman_acd/app_definitions/edit.html.erb +10 -0
  42. data/app/views/foreman_acd/app_definitions/index.html.erb +4 -4
  43. data/app/views/foreman_acd/app_instances/_form.html.erb +11 -9
  44. data/app/views/foreman_acd/app_instances/edit.html.erb +10 -0
  45. data/app/views/foreman_acd/app_instances/index.html.erb +89 -10
  46. data/app/views/foreman_acd/app_instances/report.html.erb +6 -3
  47. data/app/views/templates/job/run_acd_ansible_playbook.erb +62 -0
  48. data/app/views/ui_acd/ansible_data.json.rabl +6 -0
  49. data/app/views/ui_acd/app.json.rabl +6 -2
  50. data/app/views/ui_acd/app_definition.json.rabl +1 -1
  51. data/app/views/ui_acd/{fdata.json.rabl → foreman_data.json.rabl} +1 -1
  52. data/config/routes.rb +32 -2
  53. data/db/migrate/20200916091018_create_ansible_playbooks.rb +20 -0
  54. data/db/migrate/20200917120220_add_ansible_playbook_id.rb +14 -0
  55. data/db/migrate/20201016002819_add_ansible_vars_all_to_app_definitions.rb +8 -0
  56. data/db/migrate/20201016104338_add_ansible_vars_all_to_app_instances.rb +8 -0
  57. data/db/migrate/20210112111548_add_organization_to_app_instance.rb +22 -0
  58. data/db/migrate/20210112113853_add_location_to_app_instance.rb +8 -0
  59. data/db/migrate/20210202141658_create_foreman_hosts.rb +24 -0
  60. data/db/migrate/20210204111306_remove_hosts_from_app_instances.rb +8 -0
  61. data/db/migrate/20210209091014_rename_acd_tables.rb +16 -0
  62. data/db/migrate/20210216083522_add_last_progress_report.rb +8 -0
  63. data/db/migrate/20210216091529_add_last_deploy_task.rb +8 -0
  64. data/db/migrate/20210316151145_add_git_commit_to_ansible_playbooks.rb +8 -0
  65. data/db/migrate/20210503122809_add_git_url_to_ansible_playbooks.rb +8 -0
  66. data/db/seeds.d/62_acd_proxy_feature.rb +4 -0
  67. data/db/seeds.d/75-job_templates.rb +13 -0
  68. data/lib/foreman_acd.rb +12 -0
  69. data/lib/foreman_acd/engine.rb +43 -3
  70. data/lib/foreman_acd/plugin.rb +88 -22
  71. data/lib/foreman_acd/version.rb +1 -1
  72. data/lib/tasks/foreman_acd_tasks.rake +0 -12
  73. data/locale/en/foreman_acd.edit.po +326 -0
  74. data/locale/en/foreman_acd.po +232 -2
  75. data/locale/en/foreman_acd.po.time_stamp +0 -0
  76. data/locale/foreman_acd.pot +343 -8
  77. data/package.json +9 -9
  78. data/test/controllers/ansible_playbooks_controller_test.rb +27 -0
  79. data/test/controllers/app_instances_controller_test.rb +8 -3
  80. data/test/controllers/ui_acd_controller_test.rb +22 -6
  81. data/test/factories/foreman_acd_factories.rb +18 -4
  82. data/test/models/acd_provider_test.rb +37 -0
  83. data/test/models/ansible_playbook_test.rb +11 -0
  84. data/test/models/app_definition_test.rb +1 -1
  85. data/test/models/app_instance_test.rb +2 -0
  86. data/test/models/concerns/host_extensions_test.rb +26 -0
  87. data/test/models/foreman_host_test.rb +12 -0
  88. data/webpack/__mocks__/foremanReact/API.js +2 -0
  89. data/webpack/__mocks__/foremanReact/common/I18n.js +3 -0
  90. data/webpack/__mocks__/foremanReact/common/helpers.js +2 -0
  91. data/webpack/__mocks__/foremanReact/components/ForemanModal.js +7 -0
  92. data/webpack/__mocks__/foremanReact/components/common/forms/CommonForm.js +2 -0
  93. data/webpack/__mocks__/foremanReact/components/common/forms/TextInput.js +2 -0
  94. data/webpack/__mocks__/foremanReact/components/hosts/powerStatus.js +1 -0
  95. data/webpack/__snapshots__/helper.test.js.snap +14 -0
  96. data/webpack/components/ApplicationDefinition/ApplicationDefinition.js +159 -29
  97. data/webpack/components/ApplicationDefinition/ApplicationDefinitionActions.js +106 -14
  98. data/webpack/components/ApplicationDefinition/ApplicationDefinitionConstants.js +8 -2
  99. data/webpack/components/ApplicationDefinition/ApplicationDefinitionHelper.js +26 -0
  100. data/webpack/components/ApplicationDefinition/ApplicationDefinitionReducer.js +143 -21
  101. data/webpack/components/ApplicationDefinition/ApplicationDefinitionSelectors.js +3 -0
  102. data/webpack/components/ApplicationDefinition/__fixtures__/applicationDefinitionConfData_1.fixtures.js +288 -0
  103. data/webpack/components/ApplicationDefinition/__fixtures__/applicationDefinitionReducer.fixtures.js +79 -0
  104. data/webpack/components/ApplicationDefinition/__tests__/ApplicationDefinition.test.js +25 -0
  105. data/webpack/components/ApplicationDefinition/__tests__/ApplicationDefinitionReducer.test.js +119 -0
  106. data/webpack/components/ApplicationDefinition/__tests__/ApplicationDefinitionSelectors.test.js +41 -0
  107. data/webpack/components/ApplicationDefinition/__tests__/__snapshots__/ApplicationDefinition.test.js.snap +200 -0
  108. data/webpack/components/ApplicationDefinition/__tests__/__snapshots__/ApplicationDefinitionReducer.test.js.snap +3033 -0
  109. data/webpack/components/ApplicationDefinition/__tests__/__snapshots__/ApplicationDefinitionSelectors.test.js.snap +299 -0
  110. data/webpack/components/ApplicationDefinition/components/AnsiblePlaybookSelector.js +50 -0
  111. data/webpack/components/ApplicationDefinition/components/__tests__/AnsiblePlaybookSelector.test.js +41 -0
  112. data/webpack/components/ApplicationDefinition/components/__tests__/__snapshots__/AnsiblePlaybookSelector.test.js.snap +121 -0
  113. data/webpack/components/ApplicationDefinition/index.js +6 -0
  114. data/webpack/components/ApplicationInstance/ApplicationInstance.js +151 -44
  115. data/webpack/components/ApplicationInstance/ApplicationInstanceActions.js +47 -10
  116. data/webpack/components/ApplicationInstance/ApplicationInstanceConstants.js +5 -2
  117. data/webpack/components/ApplicationInstance/ApplicationInstanceReducer.js +114 -28
  118. data/webpack/components/ApplicationInstance/ApplicationInstanceSelectors.js +3 -1
  119. data/webpack/components/ApplicationInstance/__fixtures__/applicationInstanceConfData_1.fixtures.js +263 -0
  120. data/webpack/components/ApplicationInstance/__fixtures__/applicationInstanceReducer.fixtures.js +78 -0
  121. data/webpack/components/ApplicationInstance/__tests__/ApplicationInstance.test.js +23 -0
  122. data/webpack/components/ApplicationInstance/__tests__/ApplicationInstanceReducer.test.js +119 -0
  123. data/webpack/components/ApplicationInstance/__tests__/ApplicationInstanceSelectors.test.js +44 -0
  124. data/webpack/components/ApplicationInstance/__tests__/__snapshots__/ApplicationInstance.test.js.snap +209 -0
  125. data/webpack/components/ApplicationInstance/__tests__/__snapshots__/ApplicationInstanceReducer.test.js.snap +2719 -0
  126. data/webpack/components/ApplicationInstance/__tests__/__snapshots__/ApplicationInstanceSelectors.test.js.snap +276 -0
  127. data/webpack/components/ApplicationInstance/components/Service.js +1 -1
  128. data/webpack/components/ApplicationInstance/index.js +4 -0
  129. data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReport.js +53 -60
  130. data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReport.scss +17 -0
  131. data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportActions.js +7 -51
  132. data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportConstants.js +2 -4
  133. data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportReducer.js +4 -18
  134. data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportSelectors.js +0 -1
  135. data/webpack/components/ApplicationInstanceReport/__fixtures__/applicationInstanceReportData_1.fixtures.js +349 -0
  136. data/webpack/components/ApplicationInstanceReport/__fixtures__/applicationInstanceReportReducer.fixtures.js +20 -0
  137. data/webpack/components/ApplicationInstanceReport/__tests__/ApplicationInstanceReport.test.js +47 -0
  138. data/webpack/components/ApplicationInstanceReport/__tests__/ApplicationInstanceReportReducer.test.js +41 -0
  139. data/webpack/components/ApplicationInstanceReport/__tests__/ApplicationInstanceReportSelectors.test.js +26 -0
  140. data/webpack/components/ApplicationInstanceReport/__tests__/__snapshots__/ApplicationInstanceReport.test.js.snap +130 -0
  141. data/webpack/components/ApplicationInstanceReport/__tests__/__snapshots__/ApplicationInstanceReportReducer.test.js.snap +718 -0
  142. data/webpack/components/ApplicationInstanceReport/__tests__/__snapshots__/ApplicationInstanceReportSelectors.test.js.snap +347 -0
  143. data/webpack/components/ApplicationInstanceReport/components/ReportViewer.js +1 -1
  144. data/webpack/components/ApplicationInstanceReport/components/__tests__/ReportViewer.test.js +24 -0
  145. data/webpack/components/ApplicationInstanceReport/components/__tests__/__snapshots__/ReportViewer.test.js.snap +24 -0
  146. data/webpack/components/ApplicationInstanceReport/index.js +0 -2
  147. data/webpack/components/ParameterSelection/ParameterSelection.js +85 -50
  148. data/webpack/components/ParameterSelection/ParameterSelectionActions.js +68 -62
  149. data/webpack/components/ParameterSelection/ParameterSelectionConstants.js +6 -3
  150. data/webpack/components/ParameterSelection/ParameterSelectionHelper.js +0 -32
  151. data/webpack/components/ParameterSelection/ParameterSelectionReducer.js +47 -35
  152. data/webpack/components/ParameterSelection/ParameterSelectionSelectors.js +2 -2
  153. data/webpack/components/ParameterSelection/__fixtures__/parameterSelectionData_1.fixtures.js +116 -84
  154. data/webpack/components/ParameterSelection/__fixtures__/parameterSelectionReducer.fixtures.js +10 -4
  155. data/webpack/components/ParameterSelection/__tests__/ParameterSelection.test.js +36 -46
  156. data/webpack/components/ParameterSelection/__tests__/ParameterSelectionReducer.test.js +31 -25
  157. data/webpack/components/ParameterSelection/__tests__/ParameterSelectionSelectors.test.js +6 -6
  158. data/webpack/components/ParameterSelection/__tests__/__snapshots__/ParameterSelection.test.js.snap +2 -126
  159. data/webpack/components/ParameterSelection/__tests__/__snapshots__/ParameterSelectionReducer.test.js.snap +1483 -872
  160. data/webpack/components/ParameterSelection/__tests__/__snapshots__/ParameterSelectionSelectors.test.js.snap +117 -79
  161. data/webpack/components/ParameterSelection/index.js +4 -4
  162. data/webpack/components/SyncGitRepo/SyncGitRepo.js +210 -0
  163. data/webpack/components/SyncGitRepo/SyncGitRepo.scss +1 -0
  164. data/webpack/components/SyncGitRepo/SyncGitRepoActions.js +124 -0
  165. data/webpack/components/SyncGitRepo/SyncGitRepoConstants.js +9 -0
  166. data/webpack/components/SyncGitRepo/SyncGitRepoReducer.js +80 -0
  167. data/webpack/components/SyncGitRepo/SyncGitRepoSelectors.js +6 -0
  168. data/webpack/components/SyncGitRepo/__fixtures__/syncGitRepoConfData_1.fixtures.js +7 -0
  169. data/webpack/components/SyncGitRepo/__fixtures__/syncGitRepoReducer.fixtures.js +44 -0
  170. data/webpack/components/SyncGitRepo/__tests__/SyncGitRepo.test.js +27 -0
  171. data/webpack/components/SyncGitRepo/__tests__/SyncGitRepoReducer.test.js +95 -0
  172. data/webpack/components/SyncGitRepo/__tests__/SyncGitRepoSelectors.test.js +32 -0
  173. data/webpack/components/SyncGitRepo/__tests__/__snapshots__/SyncGitRepo.test.js.snap +30 -0
  174. data/webpack/components/SyncGitRepo/__tests__/__snapshots__/SyncGitRepoReducer.test.js.snap +137 -0
  175. data/webpack/components/SyncGitRepo/__tests__/__snapshots__/SyncGitRepoSelectors.test.js.snap +13 -0
  176. data/webpack/components/SyncGitRepo/components/FormTextInput.js +42 -0
  177. data/webpack/components/SyncGitRepo/components/ScmTypeSelector.js +46 -0
  178. data/webpack/components/SyncGitRepo/index.js +28 -0
  179. data/webpack/components/common/DeleteTableEntry.js +3 -3
  180. data/webpack/components/common/EditTableEntry.js +50 -0
  181. data/webpack/components/common/ExtTextInput.js +43 -0
  182. data/webpack/components/common/LockTableEntry.js +60 -0
  183. data/webpack/components/common/__tests__/EditTableEntry.test.js +53 -0
  184. data/webpack/components/common/__tests__/LockTableEntry.test.js +35 -0
  185. data/webpack/components/common/__tests__/__snapshots__/DeleteTableEntry.test.js.snap +2 -2
  186. data/webpack/components/common/__tests__/__snapshots__/EditTableEntry.test.js.snap +81 -0
  187. data/webpack/components/common/__tests__/__snapshots__/LockTableEntry.test.js.snap +60 -0
  188. data/webpack/helper.js +15 -1
  189. data/webpack/helper.test.js +37 -0
  190. data/webpack/index.js +2 -0
  191. data/webpack/reducer.js +18 -11
  192. metadata +184 -10
  193. data/app/views/foreman_acd/app_instances/deploy.html.erb +0 -19
  194. data/webpack/components/common/__tests__/__snapshots__/AddParameter.test.js.snap +0 -35
  195. data/webpack/components/common/__tests__/__snapshots__/DeleteParameter.test.js.snap +0 -41
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ForemanAcd
4
+ module Concerns
5
+ # Parameters for AnsiblePlaybooks
6
+ module AnsiblePlaybookParameters
7
+ extend ActiveSupport::Concern
8
+
9
+ class_methods do
10
+ def ansible_playbook_params_filter
11
+ Foreman::ParameterFilter.new(::ForemanAcd::AnsiblePlaybook).tap do |filter|
12
+ filter.permit(:name, :description, :scm_type, :path, :git_commit, :git_url, :playfile, :location_ids => [], :organization_ids => [])
13
+ end
14
+ end
15
+ end
16
+
17
+ def ansible_playbook_params
18
+ param_name = parameter_filter_context.api? ? 'ansible_playbook' : 'foreman_acd_ansible_playbook'
19
+ self.class.ansible_playbook_params_filter.filter_params(params, parameter_filter_context, param_name)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -9,7 +9,7 @@ module ForemanAcd
9
9
  class_methods do
10
10
  def app_definition_params_filter
11
11
  Foreman::ParameterFilter.new(::ForemanAcd::AppDefinition).tap do |filter|
12
- filter.permit(:name, :description, :services)
12
+ filter.permit(:name, :description, :acd_ansible_playbook_id, :services, :ansible_vars_all, :location_ids => [], :organization_ids => [])
13
13
  end
14
14
  end
15
15
  end
@@ -9,7 +9,7 @@ module ForemanAcd
9
9
  class_methods do
10
10
  def app_instance_params_filter
11
11
  Foreman::ParameterFilter.new(::ForemanAcd::AppInstance).tap do |filter|
12
- filter.permit(:name, :app_definition_id, :description, :hosts)
12
+ filter.permit(:name, :app_definition_id, :description, :hosts, :ansible_vars_all, :organization_id, :location_id)
13
13
  end
14
14
  end
15
15
  end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ForemanAcd
4
+ # Class to run remote execution jobs
5
+ class RemoteExecutionController < JobInvocationsController
6
+ def new
7
+ set_app_instance
8
+ result, job = init_configuration
9
+
10
+ if result.success == true
11
+ @composer = job
12
+ else
13
+ redirect_to(app_instances_path, :error => _("Coult not create remote execution job to configure the app '%{app_instance}': %{msg}") % {
14
+ :app_instance => @app_instance, :msg => result.error
15
+ })
16
+ end
17
+ end
18
+
19
+ def create
20
+ customize_first = params[:customize] || false
21
+ begin
22
+ set_app_instance
23
+ result, job = init_configuration
24
+
25
+ unless result.success == true
26
+ return redirect_to(app_instances_path, :error => _("Coult not create remote execution job to configure the app '%{app_instance}': %{msg}") % {
27
+ :app_instance => @app_instance, :msg => result.error
28
+ })
29
+ end
30
+
31
+ @composer = job
32
+ if customize_first == false
33
+ @composer.trigger!
34
+ redirect_to job_invocation_path(@composer.job_invocation)
35
+ else
36
+ # redirect to the job itself if we want to customize the job
37
+ render :action => 'new'
38
+ end
39
+ rescue StandardError => e
40
+ redirect_to app_instances_path, :error => _("#{job}, #{e}")
41
+ end
42
+ end
43
+
44
+ # to overcome the isolated namespace engine difficulties with paths
45
+ helper Rails.application.routes.url_helpers
46
+ def _routes
47
+ Rails.application.routes
48
+ end
49
+
50
+ private
51
+
52
+ def set_app_instance
53
+ @app_instance = ForemanAcd::AppInstance.find_by(:id => params[:id])
54
+ end
55
+
56
+ def init_configuration
57
+ logger.debug("Creating job to configure the app #{@app_instance}. Customize first: #{params[:customize]}")
58
+ app_configurator = ForemanAcd::AppConfigurator.new(@app_instance)
59
+ app_configurator.configure
60
+ end
61
+ end
62
+ end
@@ -8,13 +8,26 @@ class UiAcdController < ::Api::V2::BaseController
8
8
  @app_data['app_definition'] = app_definition
9
9
  end
10
10
 
11
- def fdata
12
- @fdata = collect_fdata(params['id'])
11
+ def foreman_data
12
+ @foreman_data = collect_foreman_data(params['id'])
13
+ end
14
+
15
+ def ansible_data
16
+ @ansible_data = collect_ansible_data(params['id'])
17
+ end
18
+
19
+ def action_permission
20
+ case params[:action]
21
+ when 'app', 'foreman_data', 'ansible_data'
22
+ :view
23
+ else
24
+ super
25
+ end
13
26
  end
14
27
 
15
28
  private
16
29
 
17
- def collect_fdata(hostgroup_id)
30
+ def collect_foreman_data(hostgroup_id)
18
31
  hg = Hostgroup.find(hostgroup_id)
19
32
  fdata = OpenStruct.new(
20
33
  :environments => Environment.all,
@@ -26,4 +39,8 @@ class UiAcdController < ::Api::V2::BaseController
26
39
  )
27
40
  fdata
28
41
  end
42
+
43
+ def collect_ansible_data(playbook_id)
44
+ ForemanAcd::AnsiblePlaybook.find(playbook_id).as_unified_structobj
45
+ end
29
46
  end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Actions
4
+ module ForemanAcd
5
+ # DeployAllHosts implements a Foreman Task EntryAction
6
+ class DeployAllHosts < Actions::EntryAction
7
+ def plan(app_instance, safe_deploy)
8
+ action_subject(app_instance, :safe_deploy => safe_deploy)
9
+ plan_self(:id => app_instance.id)
10
+ end
11
+
12
+ def run
13
+ output[:status] = 'IN PROGRESS'
14
+ app_instance = ::ForemanAcd::AppInstance.find(input.fetch(:id))
15
+ safe_deploy = input.fetch(:safe_deploy)
16
+
17
+ # Goal: all, safe_deploy or nothing
18
+ begin
19
+ if safe_deploy
20
+ ::Foreman::Logging.logger('foreman_acd').info "Start to safe deploy hosts of the app #{app_instance}"
21
+ else
22
+ ::Foreman::Logging.logger('foreman_acd').info "Start to deploy all hosts of the app #{app_instance}"
23
+ end
24
+ app_deployer = ::ForemanAcd::AppDeployer.new(app_instance)
25
+ output[:data] = app_deployer.deploy(safe_deploy)
26
+ output[:status] = 'SUCCESS'
27
+ rescue StandardError => e
28
+ ::Foreman::Logging.logger('foreman_acd').error "Error while deploying hosts for application instance '#{app_instance.name}'. Clean up all other hosts: #{e}"
29
+ app_instance.clean_all_hosts
30
+
31
+ output[:status] = 'FAILURE'
32
+ raise "Error while deploying hosts for application instance '#{app_instance.name}': (#{e.message})"
33
+ end
34
+ end
35
+
36
+ def finalize; end
37
+
38
+ def rescue_strategy
39
+ Dynflow::Action::Rescue::Fail
40
+ end
41
+
42
+ def humanized_name
43
+ _('Deploy application instance hosts')
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Actions
4
+ module ForemanAcd
5
+ # RunConfigurator implements a Foreman Task EntryAction
6
+ class RunConfigurator < Actions::EntryAction
7
+ def plan(app_instance)
8
+ action_subject(app_instance)
9
+ plan_self(:id => app_instance.id)
10
+ end
11
+
12
+ def run
13
+ output[:status] = 'SUCCESS'
14
+ begin
15
+ app_instance = ::ForemanAcd::AppInstance.find(input.fetch(:id))
16
+ app_configurator = ::ForemanAcd::AppConfigurator.new(app_instance)
17
+
18
+ result, job = app_configurator.configure
19
+ if result.success
20
+ ::Foreman::Logging.logger('foreman_acd').info "Creating job to configure the app #{app_instance}"
21
+ job.trigger!
22
+ else
23
+ ::Foreman::Logging.logger('foreman_acd').error "Could not create the job to configure the app #{app_instance}: #{result.error}"
24
+ end
25
+ rescue StandardError => e
26
+ ::Foreman::Logging.logger('foreman_acd').error "Error while configuring application instance '#{app_instance.name}': #{e}"
27
+
28
+ output[:status] = 'FAILURE'
29
+ raise "Error while configuring hosts via ansible playbook for application instance '#{app_instance.name}': (#{e.message})"
30
+ end
31
+ end
32
+
33
+ def finalize; end
34
+
35
+ def rescue_strategy
36
+ Dynflow::Action::Rescue::Fail
37
+ end
38
+
39
+ def humanized_name
40
+ _('Configure application instance after hosts deployment')
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ForemanAcd
4
+ # Extends the Host Managed
5
+ module HostManagedExtensions
6
+ extend ActiveSupport::Concern
7
+
8
+ RUN_CONFIGURATOR_DELAY = 240
9
+
10
+ def self.prepended(base)
11
+ base.instance_eval do
12
+ before_provision :initiate_acd_app_configurator, :if => :deployed_via_acd?
13
+ end
14
+ end
15
+
16
+ def deployed_via_acd?
17
+ find_app_instance_host
18
+ @app_instance_host.present?
19
+ end
20
+
21
+ private
22
+
23
+ def find_app_instance_host
24
+ @app_instance_host = ForemanAcd::ForemanHost.find_by(:host_id => id)
25
+ end
26
+
27
+ def initiate_acd_app_configurator
28
+ return false if @app_instance_host.blank?
29
+
30
+ run_it = true
31
+ @app_instance_host.app_instance.foreman_hosts.each do |foreman_host|
32
+ # if there is ONE host, which is still in build phase we don't let the app_configuator run
33
+ next unless foreman_host.host.build?
34
+ ::Foreman::Logging.logger('foreman_acd').info("Another host (#{foreman_host.host.name} is still in build-phase. Wait for it...")
35
+ run_it = false
36
+ break
37
+ end
38
+
39
+ return unless run_it
40
+
41
+ ::Foreman::Logging.logger('foreman_acd').info("All hosts of app (#{@app_instance_host.app_instance.name}) were built. Schedule app configurator...")
42
+ start_acd_app_configurator
43
+ end
44
+
45
+ def start_acd_app_configurator
46
+ ForemanTasks.delay(::Actions::ForemanAcd::RunConfigurator,
47
+ { :start_at => Time.zone.now + RUN_CONFIGURATOR_DELAY },
48
+ @app_instance_host.app_instance)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ForemanAcd
4
+ # Implement a RemoteExecutionProvider
5
+ class AcdProvider < ::RemoteExecutionProvider
6
+ class << self
7
+ def supports_effective_user?
8
+ true
9
+ end
10
+
11
+ def proxy_operation_name
12
+ 'acd'
13
+ end
14
+
15
+ def humanized_name
16
+ 'ACD'
17
+ end
18
+
19
+ def proxy_command_options(template_invocation, host)
20
+ super(template_invocation, host).merge(:name => host.name)
21
+ end
22
+
23
+ def ssh_password(_host); end
24
+
25
+ def ssh_key_passphrase(_host); end
26
+
27
+ def sudo_password(_host); end
28
+
29
+ # Workaround till infrastructure jobs on proxies are possible. See
30
+ # configure in services/foreman_acd/app_configurator.rb for more details.
31
+ # def required_proxy_selector_for(_template)
32
+ # AcdProxyProxySelector.new
33
+ # end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ForemanAcd
4
+ # Ansible playbook
5
+ class AnsiblePlaybook < ApplicationRecord
6
+ include Authorizable
7
+ include Taxonomix
8
+ extend FriendlyId
9
+ friendly_id :name
10
+ include Parameterizable::ByIdName
11
+
12
+ self.table_name = 'acd_ansible_playbooks'
13
+ has_many :app_definitions, :inverse_of => :ansible_playbook, :foreign_key => 'acd_ansible_playbook_id', :dependent => :restrict_with_error
14
+ validates :name, :presence => true, :uniqueness => true
15
+ validates :scm_type, :presence => true
16
+ scoped_search :on => :name
17
+
18
+ default_scope do
19
+ with_taxonomy_scope do
20
+ order('acd_ansible_playbooks.name')
21
+ end
22
+ end
23
+
24
+ def used_location_ids
25
+ Location.joins(:taxable_taxonomies).where(
26
+ 'taxable_taxonomies.taxable_type' => 'ForemanAcd::AnsiblePlaybook',
27
+ 'taxable_taxonomies.taxable_id' => id
28
+ ).pluck("#{Taxonomy.table_name}.id")
29
+ end
30
+
31
+ def used_organization_ids
32
+ Organization.joins(:taxable_taxonomies).where(
33
+ 'taxable_taxonomies.taxable_type' => 'ForemanAcd::AnsiblePlaybook',
34
+ 'taxable_taxonomies.taxable_id' => id
35
+ ).pluck("#{Taxonomy.table_name}.id")
36
+ end
37
+
38
+ def self.humanize_class_name(_name = nil)
39
+ _('Ansible playbook')
40
+ end
41
+
42
+ def self.permission_name
43
+ 'ansible_playbooks'
44
+ end
45
+
46
+ def content
47
+ case scm_type
48
+ when 'directory' || 'git'
49
+ File.read(File.join(path, playfile))
50
+ else
51
+ raise NotImplementedError.new "scm_type #{scm_type.inspect} not supported!"
52
+ end
53
+ end
54
+
55
+ def as_unified_structobj
56
+ # FIXME: For now, we convert all values to string - even booleans and dicts
57
+ pretty_groups = JSON.parse(vars).each do |_, params|
58
+ params.transform_values!(&:to_s)
59
+ end
60
+
61
+ OpenStruct.new(
62
+ :id => id,
63
+ :name => name,
64
+ :groups => pretty_groups
65
+ )
66
+ end
67
+ end
68
+ end
@@ -4,13 +4,38 @@ module ForemanAcd
4
4
  # Application Definition
5
5
  class AppDefinition < ApplicationRecord
6
6
  include Authorizable
7
+ include Taxonomix
7
8
  extend FriendlyId
8
9
  friendly_id :name
10
+ include Parameterizable::ByIdName
9
11
 
12
+ self.table_name = 'acd_app_definitions'
10
13
  validates :name, :presence => true, :uniqueness => true
14
+ validates :ansible_playbook, :presence => true
11
15
  has_many :app_instances, :inverse_of => :app_definition, :dependent => :destroy
16
+ belongs_to :ansible_playbook, :inverse_of => :app_definitions, :foreign_key => :acd_ansible_playbook_id
12
17
  scoped_search :on => :name
13
18
 
19
+ default_scope do
20
+ with_taxonomy_scope do
21
+ order('acd_app_definitions.name')
22
+ end
23
+ end
24
+
25
+ def used_location_ids
26
+ Location.joins(:taxable_taxonomies).where(
27
+ 'taxable_taxonomies.taxable_type' => 'ForemanAcd::AppDefinition',
28
+ 'taxable_taxonomies.taxable_id' => id
29
+ ).pluck("#{Taxonomy.table_name}.id")
30
+ end
31
+
32
+ def used_organization_ids
33
+ Organization.joins(:taxable_taxonomies).where(
34
+ 'taxable_taxonomies.taxable_type' => 'ForemanAcd::AppDefinition',
35
+ 'taxable_taxonomies.taxable_id' => id
36
+ ).pluck("#{Taxonomy.table_name}.id")
37
+ end
38
+
14
39
  def self.humanize_class_name(_name = nil)
15
40
  _('App Definition')
16
41
  end
@@ -4,11 +4,24 @@ module ForemanAcd
4
4
  # Application Instance
5
5
  class AppInstance < ApplicationRecord
6
6
  include Authorizable
7
+ include ForemanTasks::Concerns::ActionSubject
7
8
  extend FriendlyId
8
9
  friendly_id :name
10
+ include Parameterizable::ByIdName
11
+
12
+ self.table_name = 'acd_app_instances'
13
+ belongs_to :last_deploy_task, :class_name => 'ForemanTasks::Task'
9
14
  validates :name, :presence => true, :uniqueness => true
15
+ validates :app_definition, :presence => true
10
16
  belongs_to :app_definition, :inverse_of => :app_instances
17
+ belongs_to :organization
18
+ validates :organization, :presence => true
19
+ belongs_to :location
20
+ validates :location, :presence => true
21
+ has_many :foreman_hosts, :inverse_of => :app_instance, :dependent => :destroy
11
22
  scoped_search :on => :name
23
+ default_scope -> { order('acd_app_instances.name') }
24
+ attr_accessor :hosts
12
25
 
13
26
  def self.humanize_class_name(_name = nil)
14
27
  _('App Instance')
@@ -17,5 +30,34 @@ module ForemanAcd
17
30
  def self.permission_name
18
31
  'app_instances'
19
32
  end
33
+
34
+ def clean_all_hosts
35
+ remember_host_ids = foreman_hosts.map(&:host_id)
36
+
37
+ # Clean the app instance association first
38
+ foreman_hosts.update_all(:host_id => nil)
39
+
40
+ # Remove all hosts afterwards
41
+ delete_hosts(remember_host_ids)
42
+ end
43
+
44
+ def clean_hosts_by_id(ids = [])
45
+ # Clean the app instance association first
46
+ foreman_hosts.where(:host_id => ids).update_all(:host_id => nil)
47
+
48
+ # Remove all hosts afterwards
49
+ delete_hosts(ids)
50
+ end
51
+
52
+ def delete_hosts(ids = [])
53
+ return if ids.empty?
54
+ ids.each do |host_id|
55
+ h = ::Host.find(host_id) unless host_id.nil?
56
+ if h
57
+ Katello::RegistrationManager.unregister_host(h, :unregistering => false) if ForemanAcd.with_katello?
58
+ h.destroy
59
+ end
60
+ end
61
+ end
20
62
  end
21
63
  end