foreman_acd 0.6.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (197) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -5
  3. data/app/controllers/foreman_acd/ansible_playbooks_controller.rb +90 -0
  4. data/app/controllers/foreman_acd/app_definitions_controller.rb +104 -7
  5. data/app/controllers/foreman_acd/app_instances_controller.rb +34 -32
  6. data/app/controllers/foreman_acd/concerns/ansible_playbook_parameters.rb +1 -1
  7. data/app/controllers/foreman_acd/concerns/app_instance_mixins.rb +36 -0
  8. data/app/controllers/foreman_acd/remote_execution_controller.rb +37 -21
  9. data/app/controllers/ui_acd_controller.rb +42 -3
  10. data/app/lib/actions/foreman_acd/deploy_all_hosts.rb +12 -7
  11. data/app/lib/actions/foreman_acd/run_configurator.rb +11 -7
  12. data/app/models/concerns/foreman_acd/host_managed_extensions.rb +41 -28
  13. data/app/models/foreman_acd/acd_provider.rb +7 -1
  14. data/app/models/foreman_acd/ansible_playbook.rb +2 -1
  15. data/app/models/foreman_acd/app_instance.rb +48 -3
  16. data/app/models/foreman_acd/foreman_host.rb +8 -0
  17. data/app/services/foreman_acd/acd_proxy_proxy_selector.rb +17 -0
  18. data/app/services/foreman_acd/app_configurator.rb +59 -15
  19. data/app/services/foreman_acd/app_deployer.rb +27 -4
  20. data/app/services/foreman_acd/inventory_creator.rb +25 -1
  21. data/app/views/foreman_acd/ansible_playbooks/_form.html.erb +41 -7
  22. data/app/views/foreman_acd/app_definitions/_form.html.erb +5 -1
  23. data/app/views/foreman_acd/app_definitions/import.html.erb +20 -1
  24. data/app/views/foreman_acd/app_definitions/index.html.erb +3 -6
  25. data/app/views/foreman_acd/app_instances/_form.html.erb +5 -1
  26. data/app/views/foreman_acd/app_instances/index.html.erb +18 -12
  27. data/app/views/foreman_acd/app_instances/report.html.erb +8 -3
  28. data/app/views/templates/job/run_acd_ansible_playbook.erb +1 -1
  29. data/app/views/ui_acd/host_report.json.rabl +4 -0
  30. data/app/views/ui_acd/report_data.json.rabl +10 -0
  31. data/app/views/ui_acd/validate_hostname.json.rabl +6 -0
  32. data/config/routes.rb +6 -0
  33. data/db/migrate/20210316151145_add_git_commit_to_ansible_playbooks.rb +8 -0
  34. data/db/migrate/20210503122809_add_git_url_to_ansible_playbooks.rb +8 -0
  35. data/db/migrate/20210818125913_add_is_existing_host_to_foreman_host.rb +8 -0
  36. data/db/migrate/20210902110645_add_initial_configure_task.rb +8 -0
  37. data/db/seeds.d/75-job_templates.rb +1 -1
  38. data/lib/foreman_acd/engine.rb +26 -4
  39. data/lib/foreman_acd/plugin.rb +9 -18
  40. data/lib/foreman_acd/version.rb +1 -1
  41. data/lib/foreman_acd.rb +30 -0
  42. data/lib/tasks/foreman_acd_tasks.rake +0 -12
  43. data/package.json +8 -22
  44. data/test/controllers/ansible_playbooks_controller_test.rb +1 -1
  45. data/test/controllers/app_instances_controller_test.rb +8 -3
  46. data/test/controllers/ui_acd_controller_test.rb +25 -6
  47. data/test/factories/foreman_acd_factories.rb +18 -4
  48. data/test/models/acd_provider_test.rb +37 -0
  49. data/test/models/ansible_playbook_test.rb +11 -0
  50. data/test/models/app_definition_test.rb +1 -1
  51. data/test/models/app_instance_test.rb +2 -0
  52. data/test/models/concerns/host_extensions_test.rb +26 -0
  53. data/test/models/foreman_host_test.rb +12 -0
  54. data/webpack/__mocks__/foremanReact/API.js +2 -0
  55. data/webpack/__mocks__/foremanReact/common/I18n.js +3 -0
  56. data/webpack/__mocks__/foremanReact/common/helpers.js +2 -0
  57. data/webpack/__mocks__/foremanReact/components/ForemanModal/ForemanModalActions.js +2 -0
  58. data/webpack/__mocks__/foremanReact/components/ForemanModal.js +7 -0
  59. data/webpack/__mocks__/foremanReact/components/common/forms/CommonForm.js +2 -0
  60. data/webpack/__mocks__/foremanReact/components/common/forms/TextInput.js +2 -0
  61. data/webpack/__mocks__/foremanReact/components/hosts/powerStatus.js +1 -0
  62. data/webpack/__snapshots__/helper.test.js.snap +14 -0
  63. data/webpack/components/ApplicationDefinition/ApplicationDefinition.js +35 -11
  64. data/webpack/components/ApplicationDefinition/ApplicationDefinitionActions.js +12 -0
  65. data/webpack/components/ApplicationDefinition/ApplicationDefinitionConstants.js +1 -0
  66. data/webpack/components/ApplicationDefinition/ApplicationDefinitionReducer.js +30 -9
  67. data/webpack/components/ApplicationDefinition/ApplicationDefinitionSelectors.js +4 -0
  68. data/webpack/components/ApplicationDefinition/__fixtures__/applicationDefinitionConfData_1.fixtures.js +288 -0
  69. data/webpack/components/ApplicationDefinition/__fixtures__/applicationDefinitionReducer.fixtures.js +79 -0
  70. data/webpack/components/ApplicationDefinition/__tests__/ApplicationDefinition.test.js +26 -0
  71. data/webpack/components/ApplicationDefinition/__tests__/ApplicationDefinitionReducer.test.js +119 -0
  72. data/webpack/components/ApplicationDefinition/__tests__/ApplicationDefinitionSelectors.test.js +53 -0
  73. data/webpack/components/ApplicationDefinition/__tests__/__snapshots__/ApplicationDefinition.test.js.snap +226 -0
  74. data/webpack/components/ApplicationDefinition/__tests__/__snapshots__/ApplicationDefinitionReducer.test.js.snap +3033 -0
  75. data/webpack/components/ApplicationDefinition/__tests__/__snapshots__/ApplicationDefinitionSelectors.test.js.snap +307 -0
  76. data/webpack/components/ApplicationDefinition/components/AnsiblePlaybookSelector.js +2 -1
  77. data/webpack/components/ApplicationDefinition/components/__tests__/AnsiblePlaybookSelector.test.js +41 -0
  78. data/webpack/components/ApplicationDefinition/components/__tests__/__snapshots__/AnsiblePlaybookSelector.test.js.snap +121 -0
  79. data/webpack/components/ApplicationDefinition/index.js +8 -0
  80. data/webpack/components/ApplicationDefinitionImport/ApplicationDefinitionImport.js +214 -0
  81. data/webpack/components/ApplicationDefinitionImport/ApplicationDefinitionImport.scss +1 -0
  82. data/webpack/components/ApplicationDefinitionImport/ApplicationDefinitionImportActions.js +161 -0
  83. data/webpack/components/ApplicationDefinitionImport/ApplicationDefinitionImportConstants.js +6 -0
  84. data/webpack/components/ApplicationDefinitionImport/ApplicationDefinitionImportReducer.js +79 -0
  85. data/webpack/components/ApplicationDefinitionImport/ApplicationDefinitionImportSelectors.js +8 -0
  86. data/webpack/components/ApplicationDefinitionImport/__fixtures__/applicationDefinitionImportConfData_1.fixtures.js +129 -0
  87. data/webpack/components/ApplicationDefinitionImport/__fixtures__/applicationDefinitionImportReducer.fixtures.js +29 -0
  88. data/webpack/components/ApplicationDefinitionImport/__tests__/ApplicationDefinitionImport.test.js +20 -0
  89. data/webpack/components/ApplicationDefinitionImport/__tests__/ApplicationDefinitionImportReducer.test.js +43 -0
  90. data/webpack/components/ApplicationDefinitionImport/__tests__/ApplicationDefinitionImportSelectors.test.js +29 -0
  91. data/webpack/components/ApplicationDefinitionImport/__tests__/__snapshots__/ApplicationDefinitionImport.test.js.snap +62 -0
  92. data/webpack/components/ApplicationDefinitionImport/__tests__/__snapshots__/ApplicationDefinitionImportReducer.test.js.snap +362 -0
  93. data/webpack/components/ApplicationDefinitionImport/__tests__/__snapshots__/ApplicationDefinitionImportSelectors.test.js.snap +130 -0
  94. data/webpack/components/ApplicationDefinitionImport/index.js +32 -0
  95. data/webpack/components/ApplicationInstance/ApplicationInstance.js +105 -31
  96. data/webpack/components/ApplicationInstance/ApplicationInstanceActions.js +118 -6
  97. data/webpack/components/ApplicationInstance/ApplicationInstanceConstants.js +4 -0
  98. data/webpack/components/ApplicationInstance/ApplicationInstanceHelper.js +15 -0
  99. data/webpack/components/ApplicationInstance/ApplicationInstanceReducer.js +71 -30
  100. data/webpack/components/ApplicationInstance/ApplicationInstanceSelectors.js +4 -0
  101. data/webpack/components/ApplicationInstance/__fixtures__/applicationInstanceConfData_1.fixtures.js +263 -0
  102. data/webpack/components/ApplicationInstance/__fixtures__/applicationInstanceReducer.fixtures.js +80 -0
  103. data/webpack/components/ApplicationInstance/__tests__/ApplicationInstance.test.js +24 -0
  104. data/webpack/components/ApplicationInstance/__tests__/ApplicationInstanceReducer.test.js +131 -0
  105. data/webpack/components/ApplicationInstance/__tests__/ApplicationInstanceSelectors.test.js +56 -0
  106. data/webpack/components/ApplicationInstance/__tests__/__snapshots__/ApplicationInstance.test.js.snap +300 -0
  107. data/webpack/components/ApplicationInstance/__tests__/__snapshots__/ApplicationInstanceReducer.test.js.snap +2990 -0
  108. data/webpack/components/ApplicationInstance/__tests__/__snapshots__/ApplicationInstanceSelectors.test.js.snap +284 -0
  109. data/webpack/components/ApplicationInstance/components/AppDefinitionSelector.js +1 -0
  110. data/webpack/components/ApplicationInstance/components/ServiceCounter.js +1 -1
  111. data/webpack/components/ApplicationInstance/helper.js +0 -0
  112. data/webpack/components/ApplicationInstance/index.js +8 -0
  113. data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReport.js +81 -6
  114. data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportActions.js +35 -1
  115. data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportConstants.js +3 -0
  116. data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportReducer.js +19 -0
  117. data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportSelectors.js +4 -0
  118. data/webpack/components/ApplicationInstanceReport/__fixtures__/applicationInstanceReportData_1.fixtures.js +349 -0
  119. data/webpack/components/ApplicationInstanceReport/__fixtures__/applicationInstanceReportReducer.fixtures.js +20 -0
  120. data/webpack/components/ApplicationInstanceReport/__tests__/ApplicationInstanceReport.test.js +47 -0
  121. data/webpack/components/ApplicationInstanceReport/__tests__/ApplicationInstanceReportReducer.test.js +41 -0
  122. data/webpack/components/ApplicationInstanceReport/__tests__/ApplicationInstanceReportSelectors.test.js +26 -0
  123. data/webpack/components/ApplicationInstanceReport/__tests__/__snapshots__/ApplicationInstanceReport.test.js.snap +7 -0
  124. data/webpack/components/ApplicationInstanceReport/__tests__/__snapshots__/ApplicationInstanceReportReducer.test.js.snap +718 -0
  125. data/webpack/components/ApplicationInstanceReport/__tests__/__snapshots__/ApplicationInstanceReportSelectors.test.js.snap +347 -0
  126. data/webpack/components/ApplicationInstanceReport/components/__tests__/ReportViewer.test.js +24 -0
  127. data/webpack/components/ApplicationInstanceReport/components/__tests__/__snapshots__/ReportViewer.test.js.snap +24 -0
  128. data/webpack/components/ApplicationInstanceReport/index.js +8 -1
  129. data/webpack/components/ExistingHostSelection/ExistingHostSelection.js +104 -0
  130. data/webpack/components/ExistingHostSelection/ExistingHostSelection.scss +15 -0
  131. data/webpack/components/ExistingHostSelection/ExistingHostSelectionActions.js +71 -0
  132. data/webpack/components/ExistingHostSelection/ExistingHostSelectionConstants.js +4 -0
  133. data/webpack/components/ExistingHostSelection/ExistingHostSelectionHelper.js +0 -0
  134. data/webpack/components/ExistingHostSelection/ExistingHostSelectionReducer.js +90 -0
  135. data/webpack/components/ExistingHostSelection/ExistingHostSelectionSelectors.js +8 -0
  136. data/webpack/components/ExistingHostSelection/__fixtures__/existingHostSelectionConfData_1.fixtures.js +191 -0
  137. data/webpack/components/ExistingHostSelection/__fixtures__/existingHostSelectionReducer.fixtures.js +203 -0
  138. data/webpack/components/ExistingHostSelection/__tests__/ExistingHostSelection.test.js +19 -0
  139. data/webpack/components/ExistingHostSelection/__tests__/ExistingHostSelectionReducer.test.js +59 -0
  140. data/webpack/components/ExistingHostSelection/__tests__/ExistingHostSelectionSelectors.test.js +36 -0
  141. data/webpack/components/ExistingHostSelection/__tests__/__snapshots__/ExistingHostSelection.test.js.snap +35 -0
  142. data/webpack/components/ExistingHostSelection/__tests__/__snapshots__/ExistingHostSelectionReducer.test.js.snap +614 -0
  143. data/webpack/components/ExistingHostSelection/__tests__/__snapshots__/ExistingHostSelectionSelectors.test.js.snap +27 -0
  144. data/webpack/components/ExistingHostSelection/components/ServiceSelector.js +48 -0
  145. data/webpack/components/ExistingHostSelection/components/__tests__/ServiceSelector.test.js +35 -0
  146. data/webpack/components/ExistingHostSelection/components/__tests__/__snapshots__/ServiceSelector.test.js.snap +77 -0
  147. data/webpack/components/ExistingHostSelection/index.js +26 -0
  148. data/webpack/components/ParameterSelection/ParameterSelection.js +103 -1
  149. data/webpack/components/ParameterSelection/ParameterSelection.scss +7 -0
  150. data/webpack/components/ParameterSelection/ParameterSelectionActions.js +46 -4
  151. data/webpack/components/ParameterSelection/ParameterSelectionConstants.js +2 -0
  152. data/webpack/components/ParameterSelection/ParameterSelectionHelper.js +5 -1
  153. data/webpack/components/ParameterSelection/ParameterSelectionReducer.js +51 -29
  154. data/webpack/components/ParameterSelection/ParameterSelectionSelectors.js +2 -0
  155. data/webpack/components/ParameterSelection/__fixtures__/parameterSelectionData_1.fixtures.js +124 -84
  156. data/webpack/components/ParameterSelection/__fixtures__/parameterSelectionReducer.fixtures.js +10 -4
  157. data/webpack/components/ParameterSelection/__tests__/ParameterSelection.test.js +36 -46
  158. data/webpack/components/ParameterSelection/__tests__/ParameterSelectionReducer.test.js +33 -25
  159. data/webpack/components/ParameterSelection/__tests__/ParameterSelectionSelectors.test.js +12 -6
  160. data/webpack/components/ParameterSelection/__tests__/__snapshots__/ParameterSelection.test.js.snap +84 -112
  161. data/webpack/components/ParameterSelection/__tests__/__snapshots__/ParameterSelectionReducer.test.js.snap +1589 -878
  162. data/webpack/components/ParameterSelection/__tests__/__snapshots__/ParameterSelectionSelectors.test.js.snap +130 -79
  163. data/webpack/components/ParameterSelection/index.js +4 -1
  164. data/webpack/components/SyncGitRepo/SyncGitRepo.js +202 -0
  165. data/webpack/components/SyncGitRepo/SyncGitRepo.scss +1 -0
  166. data/webpack/components/SyncGitRepo/SyncGitRepoActions.js +123 -0
  167. data/webpack/components/SyncGitRepo/SyncGitRepoConstants.js +8 -0
  168. data/webpack/components/SyncGitRepo/SyncGitRepoReducer.js +80 -0
  169. data/webpack/components/SyncGitRepo/SyncGitRepoSelectors.js +6 -0
  170. data/webpack/components/SyncGitRepo/__fixtures__/syncGitRepoConfData_1.fixtures.js +7 -0
  171. data/webpack/components/SyncGitRepo/__fixtures__/syncGitRepoReducer.fixtures.js +44 -0
  172. data/webpack/components/SyncGitRepo/__tests__/SyncGitRepo.test.js +27 -0
  173. data/webpack/components/SyncGitRepo/__tests__/SyncGitRepoReducer.test.js +95 -0
  174. data/webpack/components/SyncGitRepo/__tests__/SyncGitRepoSelectors.test.js +32 -0
  175. data/webpack/components/SyncGitRepo/__tests__/__snapshots__/SyncGitRepo.test.js.snap +31 -0
  176. data/webpack/components/SyncGitRepo/__tests__/__snapshots__/SyncGitRepoReducer.test.js.snap +137 -0
  177. data/webpack/components/SyncGitRepo/__tests__/__snapshots__/SyncGitRepoSelectors.test.js.snap +13 -0
  178. data/webpack/components/SyncGitRepo/components/FormTextInput.js +42 -0
  179. data/webpack/components/SyncGitRepo/components/ScmTypeSelector.js +47 -0
  180. data/webpack/components/SyncGitRepo/index.js +28 -0
  181. data/webpack/components/common/DeleteTableEntry.js +16 -2
  182. data/webpack/components/common/ExtTextInput.js +43 -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 +40 -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 +35 -1
  189. data/webpack/helper.test.js +56 -0
  190. data/webpack/index.js +7 -0
  191. data/webpack/js-yaml.js +3874 -0
  192. data/webpack/reducer.js +16 -1
  193. data/webpack/test_setup.js +0 -2
  194. metadata +136 -11
  195. data/webpack/components/common/EasyHeaderFormatter.js +0 -18
  196. data/webpack/components/common/__tests__/__snapshots__/AddParameter.test.js.snap +0 -35
  197. data/webpack/components/common/__tests__/__snapshots__/DeleteParameter.test.js.snap +0 -41
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 809d34cc7063d66a26a1c0c913cd95287a6329cce55a75c1684046bb47b1afe1
4
- data.tar.gz: d32aa387545b3fac503996354baae6704c30118a923d95706668895cad6440f5
3
+ metadata.gz: 2a738c9828728db02d8d06460befc527ffbf7c3662d0bd0ba4d252ffe416be7e
4
+ data.tar.gz: 30c1d33e7525179ac752d949116fd99bc9fefb27e11c1a6f69567803ef5a0e25
5
5
  SHA512:
6
- metadata.gz: 996b0c911e71e045b232b3d6b27776eaa9c7e6c52f23f78d29c911f899d4a60024990fa8866e2f401e2d91f1c2a0c06a7264f16ffc2e4bd0b97c0e794a0a7c63
7
- data.tar.gz: 445dc99ec810e7fbbdf33d2c1e69b3a60942c58938212105033e472981bee85e626ce51d7e2f36359bbfb9148dca240fff134d183481d1d8312a3e9f933f4815
6
+ metadata.gz: 8311857ec1c4a78e95e4f6a401548fa975b876fba04839a0641d84c4dd138d20858717df830d391651bb517ce07e423fc4faa2b4abc42e25a8fe332233e7460c
7
+ data.tar.gz: 455afe8abd543214663b4a621d92893a8e63e28a44e412b1de65695c32e712432906324b73b09c64ee94a6091bc4470f8b8640eb3f1507c3a21d804526ee45a8
data/README.md CHANGED
@@ -1,5 +1,3 @@
1
- [![Build Status master](https://travis-ci.org/ATIX-AG/foreman_acd.svg?branch=master)](https://travis-ci.org/ATIX-AG/foreman_acd)
2
-
3
1
  # Foreman Application Centric Deployment
4
2
 
5
3
  A Foreman plugin providing application centric deployment and a self-service portal.
@@ -55,6 +53,10 @@ This plugin aims to setup all six hosts and to deploy the application.
55
53
 
56
54
  :warning: This plugin is still in development.
57
55
 
56
+ ## Documentation
57
+
58
+ See [Application Centric Deployment Guide](https://docs.theforeman.org/nightly/Application_Centric_Deployment/index-foreman-el.html)
59
+
58
60
  ## Installation
59
61
 
60
62
  See the [installation](https://theforeman.org/plugins/#2.Installation) chapter of the Foreman plugins documentation on how to install Foreman plugins.
@@ -88,7 +90,7 @@ You need to refresh the smart proxy features in *Infrastructure > Smart Proxies
88
90
  ### Ansible Playbook
89
91
 
90
92
  * Copy (or checkout a git repository) an Ansible playbook.
91
- Store it in `/etc/foreman/plugins/foreman_acd/ansible-playbooks/` so that SELinux is able to read it.
93
+ Store it in `/var/lib/foreman/foreman_acd/ansible-playbooks/` so that SELinux is able to read it.
92
94
  * Add a new Ansible Playbook via *Applications > Ansible Playbooks*.
93
95
  * Specify the path to the Ansible playbook and name of the playbook file. (e.g. `site.yml`).
94
96
  * Save it and press *Import group variables* for this newly created Ansible playbook.
@@ -117,8 +119,6 @@ All Foreman parameters require a value.
117
119
 
118
120
  ## TODO
119
121
 
120
- * Add `git` support for the Ansible playbooks.
121
- * Provide application templates which contains application definition and the required Ansible-playbook.
122
122
  * Add Saltstack support to configure the application.
123
123
  * Extend the Foreman parameter and value validation.
124
124
 
@@ -7,6 +7,7 @@ module ForemanAcd
7
7
  include ::ForemanAcd::Concerns::AnsiblePlaybookParameters
8
8
 
9
9
  before_action :find_resource, :only => [:edit, :update, :destroy, :import_vars]
10
+ after_action :delete_synced_repo, :only => [:new, :edit, :create, :update, :destroy, :index]
10
11
 
11
12
  def index
12
13
  @ansible_playbooks = resource_base.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page])
@@ -18,6 +19,11 @@ module ForemanAcd
18
19
 
19
20
  def create
20
21
  @ansible_playbook = AnsiblePlaybook.new(ansible_playbook_params)
22
+ if session[:git_path]
23
+ @ansible_playbook.update(:path => ansible_playbook_full_path(ansible_playbook_rename(@ansible_playbook[:name])))
24
+ FileUtils.mv(session[:git_path], @ansible_playbook[:path])
25
+ session[:git_path] = nil
26
+ end
21
27
  if @ansible_playbook.save
22
28
  process_success :success_msg => _("Successfully created %s. You need to press the \"Import groups\" button
23
29
  before this ansible playbook can be used in App Definitions!") % @ansible_playbook
@@ -29,6 +35,20 @@ module ForemanAcd
29
35
  def edit; end
30
36
 
31
37
  def update
38
+ # Move synced repo to new path if ansible_playbook name is changed
39
+ if !session[:git_path].nil? && ansible_playbook_params[:name] != @ansible_playbook[:name]
40
+ FileUtils.mv(@ansible_playbook[:path], ansible_playbook_full_path(ansible_playbook_rename(ansible_playbook_params[:name])))
41
+ @ansible_playbook.update(:path => ansible_playbook_full_path(ansible_playbook_rename(ansible_playbook_params[:name])))
42
+ session[:git_path] = nil
43
+
44
+ # Remove old version and copy new version of synced repository
45
+ elsif !session[:git_path].nil? && ansible_playbook_params[:name] == @ansible_playbook.name
46
+ remove_ansible_dir(@ansible_playbook[:path]) if @ansible_playbook.path
47
+ @ansible_playbook.update(:path => ansible_playbook_full_path(ansible_playbook_rename(@ansible_playbook[:name])))
48
+ FileUtils.mv(session[:git_path], @ansible_playbook[:path])
49
+ session[:git_path] = nil
50
+ end
51
+
32
52
  if @ansible_playbook.update(ansible_playbook_params)
33
53
  process_success
34
54
  else
@@ -44,10 +64,62 @@ module ForemanAcd
44
64
  end
45
65
  end
46
66
 
67
+ def sync_git_repo
68
+ @ansible_playbook = AnsiblePlaybook.new
69
+ sync_params = params[:ansible_playbook]
70
+ dir = Dir.mktmpdir
71
+
72
+ begin
73
+ git = Git.init(dir)
74
+
75
+ if ForemanAcd.proxy_setting.present?
76
+ git.config('http.proxy', ForemanAcd.proxy_setting)
77
+ logger.info("HTTP Proxy used: #{git.config['http.proxy']}")
78
+ end
79
+
80
+ if sync_params[:git_commit].empty?
81
+ if ForemanAcd.proxy_setting.present?
82
+ err_msg = _('Please set the Git Branch/Tag/Commit. This setting is necessary if a HTTP proxy is used!')
83
+ raise StandardError.new err_msg
84
+ else
85
+ commit = Git.ls_remote(sync_params[:git_url])['head'][:sha]
86
+ end
87
+ else
88
+ commit = sync_params[:git_commit]
89
+ end
90
+
91
+ git.add_remote('origin', sync_params[:git_url])
92
+ git.fetch
93
+ git.checkout(commit)
94
+
95
+ session[:git_path] = git.dir.path
96
+ rescue StandardError => e
97
+ logger.error("Failed to sync git repository: #{e}")
98
+ render :json => { :status => 'error', :message => e }, :status => :internal_server_error
99
+ end
100
+ end
101
+
102
+ # Remove abandoned synced git repositories
103
+ def delete_synced_repo
104
+ names = []
105
+ AnsiblePlaybook.all.each do |ansible_playbook|
106
+ names.push(ansible_playbook_rename(ansible_playbook.name))
107
+ end
108
+ names.push('.', '..')
109
+ return unless Dir.exist?(ForemanAcd.ansible_playbook_path)
110
+ Dir.foreach(ForemanAcd.ansible_playbook_path) do |dirname|
111
+ next if names.include? dirname
112
+ remove_ansible_dir(ansible_playbook_full_path(dirname))
113
+ logger.info("Successfully removed #{dirname}")
114
+ end
115
+ end
116
+
47
117
  def action_permission
48
118
  case params[:action]
49
119
  when 'import_vars'
50
120
  :import_vars
121
+ when 'sync_git_repo'
122
+ :sync_git_repo
51
123
  when 'grab'
52
124
  :grab
53
125
  else
@@ -120,5 +192,23 @@ module ForemanAcd
120
192
  process_error :error_msg => _(errors.join(' ')), :redirect => ansible_playbooks_path
121
193
  end
122
194
  end
195
+
196
+ private
197
+
198
+ def ansible_playbook_rename(name)
199
+ name.split(/\W+/).join('_')
200
+ end
201
+
202
+ def remove_ansible_dir(dirpath)
203
+ unless dirpath.start_with? ForemanAcd.ansible_playbook_path
204
+ logger.error("Sorry, the directory #{dirpath} is not within #{ForemanAcd.ansible_playbook_path} and will therefore not be cleaned up!")
205
+ return false
206
+ end
207
+ FileUtils.rm_rf(dirpath) if Dir.exist?(dirpath)
208
+ end
209
+
210
+ def ansible_playbook_full_path(dirname)
211
+ File.join(ForemanAcd.ansible_playbook_path, dirname)
212
+ end
123
213
  end
124
214
  end
@@ -9,7 +9,7 @@ module ForemanAcd
9
9
  before_action :find_resource, :only => [:edit, :update, :destroy, :export]
10
10
  before_action :read_hostgroups, :only => [:edit, :new, :import]
11
11
  before_action :read_ansible_playbooks, :only => [:edit, :new]
12
- before_action :handle_file_upload, :only => [:create]
12
+ before_action :assign_ansible_playbook, :only => [:create]
13
13
 
14
14
  def index
15
15
  @app_definitions = resource_base.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page])
@@ -37,6 +37,9 @@ module ForemanAcd
37
37
  process_error
38
38
  end
39
39
  rescue StandardError, ValidationError => e
40
+ if params[:foreman_acd_app_definition_import].present?
41
+ AnsiblePlaybook.find(params[:foreman_acd_app_definition][:acd_ansible_playbook_id]).delete if params[:foreman_acd_app_definition][:acd_ansible_playbook_id].present?
42
+ end
40
43
  redirect_to new_app_definition_path, :flash => { :error => _(e.message) }
41
44
  end
42
45
  end
@@ -75,16 +78,110 @@ module ForemanAcd
75
78
  end
76
79
 
77
80
  def export
78
- filename = "#{@app_definition.name}.yaml"
79
- data = JSON.parse(@app_definition.services).to_yaml
80
- send_data data, :type => 'text/yaml', :disposition => 'attachment', :filename => filename
81
+ dir_path = "#{Dir.mktmpdir}/#{@app_definition.name}.tar"
82
+ `tar -cvf "#{dir_path}" "#{@app_definition.ansible_playbook.path}" #{export_app_template_data.path}`
83
+ logger.info("Successfully created application template tar file for #{@app_definition.name}")
84
+ send_file dir_path
85
+ rescue StandardError => e
86
+ logger.info("Export of #{@app_definition.name} failed with the error: #{e}")
87
+ redirect_to app_definitions_path, :flash => { :error => _(e.message) }
88
+ end
89
+
90
+ def handle_file_upload
91
+ return unless params[:app_definition_file] && (raw_directory = params[:app_definition_file])
92
+ begin
93
+ dir = Dir.mktmpdir
94
+ untar_import_directory(raw_directory, dir)
95
+ ansible_file = Dir.glob("#{dir}/tmp/*.yaml")
96
+ data = JSON.parse(YAML.load_file(ansible_file[0]).to_json) if ansible_file
97
+ ansible_playbook_import = data.find { |d| d if d['ansible_playbook'] }
98
+
99
+ session[:ansible_playbook_params] = { :dir => dir, :ansible_playbook => ansible_playbook_import['ansible_playbook'] }
100
+ render :json => { :ansible_services => create_ansible_services(data, ansible_playbook_import) }, :status => :ok
101
+ rescue StandardError => e
102
+ render :json => { :status => 'error', :message => e }, :status => :internal_server_error
103
+ end
81
104
  end
82
105
 
83
106
  private
84
107
 
85
- def handle_file_upload
86
- return unless params[:foreman_acd_app_definition] && (raw_file = params[:foreman_acd_app_definition][:app_definition_file])
87
- params[:foreman_acd_app_definition][:services] = YAML.load_file(raw_file.tempfile).to_json
108
+ def export_app_template_data
109
+ file = Tempfile.open([@app_definition.name, '.yaml'])
110
+ data = JSON.parse(@app_definition.services).append(:ansible_playbook => @app_definition.ansible_playbook.attributes.except('id', 'created_at', 'updated_at').as_json).to_yaml
111
+ file.write(data)
112
+ file.close
113
+ logger.info("Successfully created yaml file for app_template data: #{file.path}")
114
+ file
115
+ rescue StandardError => e
116
+ logger.info("Creation of app template data failed: #{e}")
117
+ redirect_to app_definitions_path, :flash => { :error => _(e.message) }
118
+ end
119
+
120
+ def rename_path_name(dir_path)
121
+ return dir_path unless Dir.exist?(dir_path)
122
+ ind = 1
123
+ loop do
124
+ path = dir_path
125
+ path += ind.to_s
126
+ return path unless Dir.exist?(path)
127
+ ind += 1
128
+ end
129
+ end
130
+
131
+ def create_ansible_playbook(dir, ansible_playbook, dir_path)
132
+ FileUtils.cp_r "#{dir}/#{ansible_playbook['path']}/.", dir_path
133
+ ansible_playbook['path'] = dir_path
134
+ ansible_playbook['name'] = File.basename(dir_path)
135
+ new_playbook = AnsiblePlaybook.create(ansible_playbook)
136
+ new_playbook
137
+ rescue StandardError => e
138
+ logger.info("Error while creating AnsiblePlaybook: #{e}")
139
+ end
140
+
141
+ def create_ansible_services(data, ansible_playbook_import)
142
+ ansible_services = []
143
+ session[:data_services] = data - [ansible_playbook_import]
144
+ session[:data_services].each do |d|
145
+ ansible_services.append({ :id => d['id'], :value => d['name'] })
146
+ end
147
+ ansible_services
148
+ end
149
+
150
+ def untar_import_directory(directory, dir)
151
+ `tar -xvf #{directory.path} -C #{dir}`
152
+ rescue StandardError => e
153
+ logger.info("Failed to untar imported directory: #{e}")
154
+ end
155
+
156
+ def assign_ansible_playbook
157
+ return unless params[:foreman_acd_app_definition_import]
158
+ dir_path = "#{ForemanAcd.ansible_playbook_path}/#{session[:ansible_playbook_params][:ansible_playbook]['name'].split(/\W+/).join('_')}"
159
+
160
+ # Append path and name of ansible with n + 1 if ansible_playbook with same name or name[n] exists
161
+ ansible_playbook = create_ansible_playbook(session[:ansible_playbook_params][:dir], session[:ansible_playbook_params][:ansible_playbook], rename_path_name(dir_path))
162
+ params[:foreman_acd_app_definition][:acd_ansible_playbook_id] = ansible_playbook.id
163
+
164
+ begin
165
+ services = JSON.parse(params[:foreman_acd_app_definition_import][:services])
166
+ flag = 0
167
+ session[:data_services].each do |d|
168
+ hostgroup = services.find { |service| service['name'] == d['name'] }['hostgroup']
169
+ if hostgroup == ''
170
+ flag += 1
171
+ break
172
+ else
173
+ d['hostgroup'] = hostgroup
174
+ end
175
+ end
176
+ params[:foreman_acd_app_definition][:services] = session[:data_services].to_json
177
+ rescue StandardError => e
178
+ ansible_playbook&.destroy
179
+ redirect_to({ :action => 'import' }, :error => _("Hostgroups are not configured properly: #{e}"))
180
+ else
181
+ ansible_playbook&.destroy
182
+ redirect_to({ :action => 'import' }, :error => _('Some services are not assigned Hostgroups')) if flag.positive?
183
+ end
184
+ session[:data_services] = nil
88
185
  end
89
186
  end
90
187
  end
@@ -3,8 +3,9 @@
3
3
  module ForemanAcd
4
4
  # Application Instance Controller
5
5
  class AppInstancesController < ::ForemanAcd::ApplicationController
6
- include Foreman::Controller::AutoCompleteSearch
6
+ include ::Foreman::Controller::AutoCompleteSearch
7
7
  include ::ForemanAcd::Concerns::AppInstanceParameters
8
+ include ::ForemanAcd::Concerns::AppInstanceMixins
8
9
 
9
10
  before_action :find_resource, :only => [:edit, :update, :destroy_with_hosts, :deploy, :report]
10
11
  before_action :read_applications, :only => [:new, :edit]
@@ -72,33 +73,58 @@ module ForemanAcd
72
73
  end
73
74
 
74
75
  def deploy
76
+ value = false
77
+ @app_instance.update!({ :last_deploy_task_id => nil,
78
+ :initial_configure_task_id => nil })
79
+ @app_instance.foreman_hosts.each { |f| f.update!(:last_progress_report => nil) }
75
80
  @app_instance.clean_all_hosts if params[:delete_hosts]
76
-
81
+ value = safe_deploy? if params[:safe_deploy]
82
+ session.delete(:remember_hosts)
77
83
  logger.info('Run async foreman task to deploy hosts')
78
- async_task = ForemanTasks.async_task(::Actions::ForemanAcd::DeployAllHosts, @app_instance)
84
+ async_task = ForemanTasks.async_task(::Actions::ForemanAcd::DeployAllHosts, @app_instance, value)
79
85
  @app_instance.update!(:last_deploy_task => async_task)
80
- process_success(:success_msg => _('Started task to deploy hosts for %s') % @app_instance)
86
+
87
+ redirect_to report_app_instance_path, :success => _('Started task to deploy hosts for %s') % @app_instance
81
88
  rescue StandardError => e
82
89
  error_msg = "Error happend while deploying hosts of #{@app_instance}: #{e.message}"
83
90
  logger.error("#{error_msg} - #{e.class}\n#{e.backtrace.join($INPUT_RECORD_SEPARATOR)}")
84
91
  process_error :error_msg => error_msg
85
92
  end
86
93
 
94
+ def safe_deploy?
95
+ return false if session[:remember_hosts].empty?
96
+ session[:remember_hosts]
97
+ end
98
+
87
99
  def report
88
- @report_hosts = collect_host_report_data
100
+ @report_hosts = collect_host_report_data(@app_instance)
89
101
  logger.debug("app instance host details: #{@report_hosts.inspect}")
90
102
  end
91
103
 
92
104
  def app_instance_has_foreman_hosts
93
105
  hosts = JSON.parse(@app_instance.hosts)
106
+ session[:remember_hosts] = []
94
107
  hosts.each do |h|
95
108
  if @app_instance.foreman_hosts.where(:hostname => h['hostname']).exists?
109
+ old_host = @app_instance.foreman_hosts.find_by(:hostname => h['hostname'])
110
+
96
111
  @app_instance.foreman_hosts.where(:hostname => h['hostname']).
97
112
  update(:service => h['service'], :description => h['description'],
98
113
  :foremanParameters => JSON.dump(h['foremanParameters']), :ansibleParameters => JSON.dump(h['ansibleParameters']))
114
+
115
+ updated_host = @app_instance.foreman_hosts.find_by(:hostname => h['hostname'])
116
+
117
+ # Store hosts if updated for safe deploy
118
+ session[:remember_hosts] << updated_host.id if updated_host.updated_at != old_host.updated_at
99
119
  else
100
- @app_instance.foreman_hosts.create(:hostname => h['hostname'], :service => h['service'], :description => h['description'],
101
- :foremanParameters => JSON.dump(h['foremanParameters']), :ansibleParameters => JSON.dump(h['ansibleParameters']))
120
+ @app_instance.foreman_hosts.create(:hostname => h['hostname'],
121
+ :service => h['service'],
122
+ :description => h['description'],
123
+ :is_existing_host => h['isExistingHost'],
124
+ :foremanParameters => JSON.dump(h['foremanParameters']),
125
+ :ansibleParameters => JSON.dump(h['ansibleParameters']))
126
+ # Store new hosts for safe deploy
127
+ session[:remember_hosts] << @app_instance.foreman_hosts.find_by(:hostname => h['hostname']).id
102
128
  end
103
129
  end
104
130
 
@@ -115,6 +141,7 @@ module ForemanAcd
115
141
  :hostname => h.hostname,
116
142
  :service => h.service,
117
143
  :description => h.description,
144
+ :isExistingHost => h.is_existing_host,
118
145
  :foremanParameters => JSON.parse(h.foremanParameters),
119
146
  :ansibleParameters => JSON.parse(h.ansibleParameters)
120
147
  }
@@ -135,30 +162,5 @@ module ForemanAcd
135
162
  def read_applications
136
163
  @applications = AppDefinition.all.map { |elem| { elem.id => elem.name } }.reduce({}) { |h, v| h.merge v }
137
164
  end
138
-
139
- def collect_host_report_data
140
- report_data = []
141
-
142
- @app_instance.foreman_hosts.each do |foreman_host|
143
- a_host = {
144
- :id => nil,
145
- :name => foreman_host.hostname,
146
- :build => nil,
147
- :hostUrl => nil,
148
- :progress_report => foreman_host.last_progress_report.empty? ? [] : JSON.parse(foreman_host.last_progress_report)
149
- }
150
-
151
- if foreman_host.host.present?
152
- a_host.update({
153
- :id => foreman_host.host.id,
154
- :build => foreman_host.host.build,
155
- :hostUrl => host_path(foreman_host.host),
156
- :powerStatusUrl => power_api_host_path(foreman_host.host)
157
- })
158
- end
159
- report_data << a_host
160
- end
161
- report_data
162
- end
163
165
  end
164
166
  end
@@ -9,7 +9,7 @@ module ForemanAcd
9
9
  class_methods do
10
10
  def ansible_playbook_params_filter
11
11
  Foreman::ParameterFilter.new(::ForemanAcd::AnsiblePlaybook).tap do |filter|
12
- filter.permit(:name, :description, :scm_type, :path, :playfile, :location_ids => [], :organization_ids => [])
12
+ filter.permit(:name, :description, :scm_type, :path, :git_commit, :git_url, :playfile, :location_ids => [], :organization_ids => [])
13
13
  end
14
14
  end
15
15
  end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ForemanAcd
4
+ module Concerns
5
+ # Shared code for AppInstance API and UI controller
6
+ module AppInstanceMixins
7
+ extend ActiveSupport::Concern
8
+
9
+ def collect_host_report_data(app_instance)
10
+ report_data = []
11
+
12
+ app_instance.foreman_hosts.each do |foreman_host|
13
+ a_host = {
14
+ :id => nil,
15
+ :name => foreman_host.hostname,
16
+ :build => nil,
17
+ :hostUrl => nil,
18
+ :progress_report => foreman_host.last_progress_report.empty? ? [] : JSON.parse(foreman_host.last_progress_report)
19
+ }
20
+
21
+ if foreman_host.host.present?
22
+ a_host.update({
23
+ :id => foreman_host.host.id,
24
+ :build => foreman_host.host.build,
25
+ :hostUrl => host_path(foreman_host.host),
26
+ :isExistingHost => foreman_host.is_existing_host,
27
+ :powerStatusUrl => power_api_host_path(foreman_host.host)
28
+ })
29
+ end
30
+ report_data << OpenStruct.new(a_host)
31
+ end
32
+ report_data
33
+ end
34
+ end
35
+ end
36
+ end
@@ -4,25 +4,40 @@ module ForemanAcd
4
4
  # Class to run remote execution jobs
5
5
  class RemoteExecutionController < JobInvocationsController
6
6
  def new
7
- jobs = init_configuration
8
- @composer = jobs.first
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
9
17
  end
10
18
 
11
19
  def create
12
20
  customize_first = params[:customize] || false
13
- jobs = init_configuration
14
-
15
- if jobs.count == 1 && customize_first == false
16
- @composer = jobs.first
17
- @composer.trigger!
18
- redirect_to job_invocation_path(@composer.job_invocation)
19
- elsif customize_first == false
20
- jobs.each(&:trigger)
21
- redirect_to job_invocations_path
22
- else
23
- # redirect to the job itself if we want to customize the job
24
- @composer = jobs.first
25
- render :action => 'new'
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}")
26
41
  end
27
42
  end
28
43
 
@@ -34,13 +49,14 @@ module ForemanAcd
34
49
 
35
50
  private
36
51
 
37
- def init_configuration
38
- app_instance = ForemanAcd::AppInstance.find_by(:id => params[:id])
39
- app_configurator = ForemanAcd::AppConfigurator.new(app_instance)
52
+ def set_app_instance
53
+ @app_instance = ForemanAcd::AppInstance.find_by(:id => params[:id])
54
+ end
40
55
 
41
- jobs = app_configurator.configure
42
- logger.debug("Creating #{jobs.count} job(s) to configure the app #{app_instance}. Customize first: #{params[:customize]}")
43
- jobs
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
44
60
  end
45
61
  end
46
62
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  # Controller to create JSON data to be used in react app
4
4
  class UiAcdController < ::Api::V2::BaseController
5
+ include ::ForemanAcd::Concerns::AppInstanceMixins
6
+
5
7
  def app
6
8
  @app_data = {}
7
9
  app_definition = ForemanAcd::AppDefinition.find(params[:id])
@@ -16,9 +18,17 @@ class UiAcdController < ::Api::V2::BaseController
16
18
  @ansible_data = collect_ansible_data(params['id'])
17
19
  end
18
20
 
21
+ def report_data
22
+ @report_data = collect_report_data(params['id'])
23
+ end
24
+
25
+ def validate_hostname
26
+ @host_validation = hostname_duplicate?(params['appDefId'].to_i, params['serviceId'].to_i, params['hostname'])
27
+ end
28
+
19
29
  def action_permission
20
30
  case params[:action]
21
- when 'app', 'foreman_data', 'ansible_data'
31
+ when 'app', 'foreman_data', 'ansible_data', 'validate_hostname'
22
32
  :view
23
33
  else
24
34
  super
@@ -30,17 +40,46 @@ class UiAcdController < ::Api::V2::BaseController
30
40
  def collect_foreman_data(hostgroup_id)
31
41
  hg = Hostgroup.find(hostgroup_id)
32
42
  fdata = OpenStruct.new(
33
- :environments => Environment.all,
34
- :lifecycle_environments => Katello::KTEnvironment.all,
35
43
  :domains => Domain.all,
36
44
  :computeprofiles => ComputeProfile.all,
37
45
  :hostgroup_id => hg.id,
38
46
  :ptables => hg&.operatingsystem&.ptables
39
47
  )
48
+
49
+ fdata[:environments] = Environment.all if defined?(ForemanPuppet)
50
+ fdata[:lifecycle_environments] = Katello::KTEnvironment.all if defined?(Katello)
51
+
40
52
  fdata
41
53
  end
42
54
 
43
55
  def collect_ansible_data(playbook_id)
44
56
  ForemanAcd::AnsiblePlaybook.find(playbook_id).as_unified_structobj
45
57
  end
58
+
59
+ def collect_report_data(app_instance_id)
60
+ app_instance = ForemanAcd::AppInstance.find(app_instance_id)
61
+
62
+ report_data = {
63
+ :hosts => collect_host_report_data(app_instance),
64
+ :deploymentState => app_instance.deployment_state.to_s,
65
+ :initialConfigureState => app_instance.initial_configure_state.to_s
66
+ }
67
+ report_data['initialConfigureJobUrl'] = job_invocation_path(app_instance.initial_configure_job) unless app_instance.initial_configure_job.nil?
68
+
69
+ OpenStruct.new(report_data)
70
+ end
71
+
72
+ def hostname_duplicate?(app_def_id, service_id, hostname)
73
+ app_definition = ForemanAcd::AppDefinition.find(app_def_id)
74
+ service_data = JSON.parse(app_definition.services).select { |k| k['id'] == service_id }.first
75
+ domain_name = Hostgroup.find(service_data['hostgroup']).domain.name
76
+ validation_hostname = "#{hostname}.#{domain_name}"
77
+
78
+ vdata = OpenStruct.new(
79
+ :hostname => hostname,
80
+ :fqdn => validation_hostname,
81
+ :result => Host.find_by(:name => validation_hostname).nil?
82
+ )
83
+ vdata
84
+ end
46
85
  end
@@ -4,27 +4,32 @@ module Actions
4
4
  module ForemanAcd
5
5
  # DeployAllHosts implements a Foreman Task EntryAction
6
6
  class DeployAllHosts < Actions::EntryAction
7
- def plan(app_instance)
8
- action_subject(app_instance)
7
+ def plan(app_instance, safe_deploy)
8
+ action_subject(app_instance, :safe_deploy => safe_deploy)
9
9
  plan_self(:id => app_instance.id)
10
10
  end
11
11
 
12
12
  def run
13
13
  output[:status] = 'IN PROGRESS'
14
14
  app_instance = ::ForemanAcd::AppInstance.find(input.fetch(:id))
15
+ safe_deploy = input.fetch(:safe_deploy)
15
16
 
16
- # Goal: all or nothing
17
+ # Goal: all, safe_deploy or nothing
17
18
  begin
18
- ::Foreman::Logging.logger('foreman_acd').info "Start to deploy all hosts of the app #{app_instance}"
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
19
24
  app_deployer = ::ForemanAcd::AppDeployer.new(app_instance)
20
- output[:data] = app_deployer.deploy
25
+ output[:data] = app_deployer.deploy(safe_deploy)
21
26
  output[:status] = 'SUCCESS'
22
27
  rescue StandardError => e
23
- ::Foreman::Logging.logger('foreman_acd').error "Error while deploying hosts for application instance. Clean up all other hosts: #{e}"
28
+ ::Foreman::Logging.logger('foreman_acd').error "Error while deploying hosts for application instance '#{app_instance.name}'. Clean up all other hosts: #{e}"
24
29
  app_instance.clean_all_hosts
25
30
 
26
- output[:error] = e.to_s
27
31
  output[:status] = 'FAILURE'
32
+ raise "Error while deploying hosts for application instance '#{app_instance.name}': (#{e.message})"
28
33
  end
29
34
  end
30
35