foreman_acd 0.7.0 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -5
  3. data/app/controllers/foreman_acd/ansible_playbooks_controller.rb +17 -2
  4. data/app/controllers/foreman_acd/app_definitions_controller.rb +104 -7
  5. data/app/controllers/foreman_acd/app_instances_controller.rb +15 -30
  6. data/app/controllers/foreman_acd/concerns/app_instance_mixins.rb +36 -0
  7. data/app/controllers/ui_acd_controller.rb +42 -3
  8. data/app/lib/actions/foreman_acd/run_configurator.rb +1 -0
  9. data/app/models/concerns/foreman_acd/host_managed_extensions.rb +40 -27
  10. data/app/models/foreman_acd/app_instance.rb +47 -2
  11. data/app/models/foreman_acd/foreman_host.rb +8 -0
  12. data/app/services/foreman_acd/app_deployer.rb +19 -2
  13. data/app/services/foreman_acd/inventory_creator.rb +11 -1
  14. data/app/views/foreman_acd/app_definitions/_form.html.erb +4 -0
  15. data/app/views/foreman_acd/app_definitions/import.html.erb +20 -1
  16. data/app/views/foreman_acd/app_definitions/index.html.erb +3 -6
  17. data/app/views/foreman_acd/app_instances/_form.html.erb +4 -0
  18. data/app/views/foreman_acd/app_instances/index.html.erb +15 -11
  19. data/app/views/foreman_acd/app_instances/report.html.erb +7 -2
  20. data/app/views/ui_acd/host_report.json.rabl +4 -0
  21. data/app/views/ui_acd/report_data.json.rabl +10 -0
  22. data/app/views/ui_acd/validate_hostname.json.rabl +6 -0
  23. data/config/routes.rb +3 -0
  24. data/db/migrate/20210818125913_add_is_existing_host_to_foreman_host.rb +8 -0
  25. data/db/migrate/20210902110645_add_initial_configure_task.rb +8 -0
  26. data/lib/foreman_acd/plugin.rb +9 -9
  27. data/lib/foreman_acd/version.rb +1 -1
  28. data/lib/foreman_acd.rb +27 -9
  29. data/package.json +8 -25
  30. data/test/controllers/ui_acd_controller_test.rb +4 -1
  31. data/webpack/__mocks__/foremanReact/components/ForemanModal/ForemanModalActions.js +2 -0
  32. data/webpack/__snapshots__/helper.test.js.snap +1 -1
  33. data/webpack/components/ApplicationDefinition/ApplicationDefinition.js +34 -10
  34. data/webpack/components/ApplicationDefinition/ApplicationDefinitionActions.js +12 -0
  35. data/webpack/components/ApplicationDefinition/ApplicationDefinitionConstants.js +1 -0
  36. data/webpack/components/ApplicationDefinition/ApplicationDefinitionReducer.js +30 -9
  37. data/webpack/components/ApplicationDefinition/ApplicationDefinitionSelectors.js +4 -0
  38. data/webpack/components/ApplicationDefinition/__tests__/ApplicationDefinition.test.js +1 -0
  39. data/webpack/components/ApplicationDefinition/__tests__/ApplicationDefinitionSelectors.test.js +12 -0
  40. data/webpack/components/ApplicationDefinition/__tests__/__snapshots__/ApplicationDefinition.test.js.snap +31 -5
  41. data/webpack/components/ApplicationDefinition/__tests__/__snapshots__/ApplicationDefinitionSelectors.test.js.snap +8 -0
  42. data/webpack/components/ApplicationDefinition/components/AnsiblePlaybookSelector.js +1 -1
  43. data/webpack/components/ApplicationDefinition/components/__tests__/__snapshots__/AnsiblePlaybookSelector.test.js.snap +3 -3
  44. data/webpack/components/ApplicationDefinition/index.js +8 -0
  45. data/webpack/components/ApplicationDefinitionImport/ApplicationDefinitionImport.js +214 -0
  46. data/webpack/components/ApplicationDefinitionImport/ApplicationDefinitionImport.scss +1 -0
  47. data/webpack/components/ApplicationDefinitionImport/ApplicationDefinitionImportActions.js +161 -0
  48. data/webpack/components/ApplicationDefinitionImport/ApplicationDefinitionImportConstants.js +6 -0
  49. data/webpack/components/ApplicationDefinitionImport/ApplicationDefinitionImportReducer.js +79 -0
  50. data/webpack/components/ApplicationDefinitionImport/ApplicationDefinitionImportSelectors.js +8 -0
  51. data/webpack/components/ApplicationDefinitionImport/__fixtures__/applicationDefinitionImportConfData_1.fixtures.js +129 -0
  52. data/webpack/components/ApplicationDefinitionImport/__fixtures__/applicationDefinitionImportReducer.fixtures.js +29 -0
  53. data/webpack/components/ApplicationDefinitionImport/__tests__/ApplicationDefinitionImport.test.js +20 -0
  54. data/webpack/components/ApplicationDefinitionImport/__tests__/ApplicationDefinitionImportReducer.test.js +43 -0
  55. data/webpack/components/ApplicationDefinitionImport/__tests__/ApplicationDefinitionImportSelectors.test.js +29 -0
  56. data/webpack/components/ApplicationDefinitionImport/__tests__/__snapshots__/ApplicationDefinitionImport.test.js.snap +62 -0
  57. data/webpack/components/ApplicationDefinitionImport/__tests__/__snapshots__/ApplicationDefinitionImportReducer.test.js.snap +362 -0
  58. data/webpack/components/ApplicationDefinitionImport/__tests__/__snapshots__/ApplicationDefinitionImportSelectors.test.js.snap +130 -0
  59. data/webpack/components/ApplicationDefinitionImport/index.js +32 -0
  60. data/webpack/components/ApplicationInstance/ApplicationInstance.js +102 -26
  61. data/webpack/components/ApplicationInstance/ApplicationInstanceActions.js +118 -6
  62. data/webpack/components/ApplicationInstance/ApplicationInstanceConstants.js +4 -0
  63. data/webpack/components/ApplicationInstance/ApplicationInstanceHelper.js +15 -0
  64. data/webpack/components/ApplicationInstance/ApplicationInstanceReducer.js +71 -30
  65. data/webpack/components/ApplicationInstance/ApplicationInstanceSelectors.js +4 -0
  66. data/webpack/components/ApplicationInstance/__fixtures__/applicationInstanceReducer.fixtures.js +2 -0
  67. data/webpack/components/ApplicationInstance/__tests__/ApplicationInstance.test.js +1 -0
  68. data/webpack/components/ApplicationInstance/__tests__/ApplicationInstanceReducer.test.js +12 -0
  69. data/webpack/components/ApplicationInstance/__tests__/ApplicationInstanceSelectors.test.js +12 -0
  70. data/webpack/components/ApplicationInstance/__tests__/__snapshots__/ApplicationInstance.test.js.snap +98 -7
  71. data/webpack/components/ApplicationInstance/__tests__/__snapshots__/ApplicationInstanceReducer.test.js.snap +271 -0
  72. data/webpack/components/ApplicationInstance/__tests__/__snapshots__/ApplicationInstanceSelectors.test.js.snap +8 -0
  73. data/webpack/components/ApplicationInstance/components/AppDefinitionSelector.js +1 -0
  74. data/webpack/components/ApplicationInstance/components/ServiceCounter.js +1 -1
  75. data/webpack/components/ApplicationInstance/helper.js +0 -0
  76. data/webpack/components/ApplicationInstance/index.js +8 -0
  77. data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReport.js +81 -6
  78. data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportActions.js +35 -1
  79. data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportConstants.js +3 -0
  80. data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportReducer.js +19 -0
  81. data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportSelectors.js +4 -0
  82. data/webpack/components/ApplicationInstanceReport/__tests__/__snapshots__/ApplicationInstanceReport.test.js.snap +1 -124
  83. data/webpack/components/ApplicationInstanceReport/index.js +8 -1
  84. data/webpack/components/ExistingHostSelection/ExistingHostSelection.js +104 -0
  85. data/webpack/components/ExistingHostSelection/ExistingHostSelection.scss +15 -0
  86. data/webpack/components/ExistingHostSelection/ExistingHostSelectionActions.js +71 -0
  87. data/webpack/components/ExistingHostSelection/ExistingHostSelectionConstants.js +4 -0
  88. data/webpack/components/ExistingHostSelection/ExistingHostSelectionHelper.js +0 -0
  89. data/webpack/components/ExistingHostSelection/ExistingHostSelectionReducer.js +90 -0
  90. data/webpack/components/ExistingHostSelection/ExistingHostSelectionSelectors.js +8 -0
  91. data/webpack/components/ExistingHostSelection/__fixtures__/existingHostSelectionConfData_1.fixtures.js +191 -0
  92. data/webpack/components/ExistingHostSelection/__fixtures__/existingHostSelectionReducer.fixtures.js +203 -0
  93. data/webpack/components/ExistingHostSelection/__tests__/ExistingHostSelection.test.js +19 -0
  94. data/webpack/components/ExistingHostSelection/__tests__/ExistingHostSelectionReducer.test.js +59 -0
  95. data/webpack/components/ExistingHostSelection/__tests__/ExistingHostSelectionSelectors.test.js +36 -0
  96. data/webpack/components/ExistingHostSelection/__tests__/__snapshots__/ExistingHostSelection.test.js.snap +35 -0
  97. data/webpack/components/ExistingHostSelection/__tests__/__snapshots__/ExistingHostSelectionReducer.test.js.snap +614 -0
  98. data/webpack/components/ExistingHostSelection/__tests__/__snapshots__/ExistingHostSelectionSelectors.test.js.snap +27 -0
  99. data/webpack/components/ExistingHostSelection/components/ServiceSelector.js +48 -0
  100. data/webpack/components/ExistingHostSelection/components/__tests__/ServiceSelector.test.js +35 -0
  101. data/webpack/components/ExistingHostSelection/components/__tests__/__snapshots__/ServiceSelector.test.js.snap +77 -0
  102. data/webpack/components/ExistingHostSelection/index.js +26 -0
  103. data/webpack/components/ParameterSelection/ParameterSelection.js +103 -1
  104. data/webpack/components/ParameterSelection/ParameterSelection.scss +7 -0
  105. data/webpack/components/ParameterSelection/ParameterSelectionActions.js +46 -4
  106. data/webpack/components/ParameterSelection/ParameterSelectionConstants.js +2 -0
  107. data/webpack/components/ParameterSelection/ParameterSelectionHelper.js +5 -1
  108. data/webpack/components/ParameterSelection/ParameterSelectionReducer.js +52 -11
  109. data/webpack/components/ParameterSelection/ParameterSelectionSelectors.js +2 -0
  110. data/webpack/components/ParameterSelection/__fixtures__/parameterSelectionData_1.fixtures.js +8 -0
  111. data/webpack/components/ParameterSelection/__tests__/ParameterSelectionReducer.test.js +2 -0
  112. data/webpack/components/ParameterSelection/__tests__/ParameterSelectionSelectors.test.js +6 -0
  113. data/webpack/components/ParameterSelection/__tests__/__snapshots__/ParameterSelection.test.js.snap +96 -0
  114. data/webpack/components/ParameterSelection/__tests__/__snapshots__/ParameterSelectionReducer.test.js.snap +117 -17
  115. data/webpack/components/ParameterSelection/__tests__/__snapshots__/ParameterSelectionSelectors.test.js.snap +13 -0
  116. data/webpack/components/ParameterSelection/index.js +4 -1
  117. data/webpack/components/SyncGitRepo/SyncGitRepo.js +2 -10
  118. data/webpack/components/SyncGitRepo/SyncGitRepoActions.js +2 -3
  119. data/webpack/components/SyncGitRepo/SyncGitRepoConstants.js +0 -1
  120. data/webpack/components/SyncGitRepo/__tests__/__snapshots__/SyncGitRepo.test.js.snap +1 -0
  121. data/webpack/components/SyncGitRepo/components/FormTextInput.js +1 -1
  122. data/webpack/components/SyncGitRepo/components/ScmTypeSelector.js +3 -2
  123. data/webpack/components/common/DeleteTableEntry.js +16 -2
  124. data/webpack/components/common/__tests__/__snapshots__/DeleteTableEntry.test.js.snap +38 -0
  125. data/webpack/helper.js +21 -1
  126. data/webpack/helper.test.js +20 -1
  127. data/webpack/index.js +5 -0
  128. data/webpack/js-yaml.js +3874 -0
  129. data/webpack/reducer.js +13 -2
  130. data/webpack/test_setup.js +0 -2
  131. metadata +46 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7b616c7ba21eea339af8509f618585dcd949ddead6ecdec57278a62c751e4fd2
4
- data.tar.gz: 75c7d2e2c07d306dee127bc50c6b300934f90066bc4a491e49f17f65ac6b7c24
3
+ metadata.gz: a14a2b2d7f2353e0a908bf3b0de9ea3126fff402e1003f9bea648afe260c73c0
4
+ data.tar.gz: 6eecc24dc7be78b4ddc04da2b50a525dd95a2b358647d8701e1ea6479eef5332
5
5
  SHA512:
6
- metadata.gz: bba62637508bbbd4e35772a55ad1126fea7a8cb3c1658c6f734b02ccf03d44048db40fa621bb51e796220668e7a49e8b03c3169a77b818f6dbbde117d09edfe2
7
- data.tar.gz: a6e4ba1bb97250e8bcebe6180abcf4755859b73828febda64353b47021acadcc33fd99e2ab4b45ac965e5d643c368bbfb47aee421a80979c7a4c69354a18b297
6
+ metadata.gz: 372bb6339ec6ff38e80dbc8df019083ff76a660c64cd23b40ab7da5b6c035617e7395f3c2738a04f21dd327c796ca959cb250ef6bbbbfb4b23d1220177ffab6f
7
+ data.tar.gz: 5a68e1cb910e7fca00e0f44172ff391a93557a7c9febbb8eb40a2711471c7d67bfc29ea5c70e763a7b94aa7c732e406b42d2d36daa05971f950824c6eae10d80
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
 
@@ -77,9 +77,20 @@ module ForemanAcd
77
77
  logger.info("HTTP Proxy used: #{git.config['http.proxy']}")
78
78
  end
79
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
+
80
91
  git.add_remote('origin', sync_params[:git_url])
81
92
  git.fetch
82
- git.checkout(sync_params[:git_commit])
93
+ git.checkout(commit)
83
94
 
84
95
  session[:git_path] = git.dir.path
85
96
  rescue StandardError => e
@@ -189,7 +200,11 @@ module ForemanAcd
189
200
  end
190
201
 
191
202
  def remove_ansible_dir(dirpath)
192
- FileUtils.remove_dir(dirpath) if Dir.exist?(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)
193
208
  end
194
209
 
195
210
  def ansible_playbook_full_path(dirname)
@@ -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]
@@ -73,13 +74,17 @@ module ForemanAcd
73
74
 
74
75
  def deploy
75
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) }
76
80
  @app_instance.clean_all_hosts if params[:delete_hosts]
77
81
  value = safe_deploy? if params[:safe_deploy]
78
82
  session.delete(:remember_hosts)
79
83
  logger.info('Run async foreman task to deploy hosts')
80
84
  async_task = ForemanTasks.async_task(::Actions::ForemanAcd::DeployAllHosts, @app_instance, value)
81
85
  @app_instance.update!(:last_deploy_task => async_task)
82
- 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
83
88
  rescue StandardError => e
84
89
  error_msg = "Error happend while deploying hosts of #{@app_instance}: #{e.message}"
85
90
  logger.error("#{error_msg} - #{e.class}\n#{e.backtrace.join($INPUT_RECORD_SEPARATOR)}")
@@ -92,7 +97,7 @@ module ForemanAcd
92
97
  end
93
98
 
94
99
  def report
95
- @report_hosts = collect_host_report_data
100
+ @report_hosts = collect_host_report_data(@app_instance)
96
101
  logger.debug("app instance host details: #{@report_hosts.inspect}")
97
102
  end
98
103
 
@@ -112,8 +117,12 @@ module ForemanAcd
112
117
  # Store hosts if updated for safe deploy
113
118
  session[:remember_hosts] << updated_host.id if updated_host.updated_at != old_host.updated_at
114
119
  else
115
- @app_instance.foreman_hosts.create(:hostname => h['hostname'], :service => h['service'], :description => h['description'],
116
- :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']))
117
126
  # Store new hosts for safe deploy
118
127
  session[:remember_hosts] << @app_instance.foreman_hosts.find_by(:hostname => h['hostname']).id
119
128
  end
@@ -132,6 +141,7 @@ module ForemanAcd
132
141
  :hostname => h.hostname,
133
142
  :service => h.service,
134
143
  :description => h.description,
144
+ :isExistingHost => h.is_existing_host,
135
145
  :foremanParameters => JSON.parse(h.foremanParameters),
136
146
  :ansibleParameters => JSON.parse(h.ansibleParameters)
137
147
  }
@@ -152,30 +162,5 @@ module ForemanAcd
152
162
  def read_applications
153
163
  @applications = AppDefinition.all.map { |elem| { elem.id => elem.name } }.reduce({}) { |h, v| h.merge v }
154
164
  end
155
-
156
- def collect_host_report_data
157
- report_data = []
158
-
159
- @app_instance.foreman_hosts.each do |foreman_host|
160
- a_host = {
161
- :id => nil,
162
- :name => foreman_host.hostname,
163
- :build => nil,
164
- :hostUrl => nil,
165
- :progress_report => foreman_host.last_progress_report.empty? ? [] : JSON.parse(foreman_host.last_progress_report)
166
- }
167
-
168
- if foreman_host.host.present?
169
- a_host.update({
170
- :id => foreman_host.host.id,
171
- :build => foreman_host.host.build,
172
- :hostUrl => host_path(foreman_host.host),
173
- :powerStatusUrl => power_api_host_path(foreman_host.host)
174
- })
175
- end
176
- report_data << a_host
177
- end
178
- report_data
179
- end
180
165
  end
181
166
  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
@@ -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
@@ -19,6 +19,7 @@ module Actions
19
19
  if result.success
20
20
  ::Foreman::Logging.logger('foreman_acd').info "Creating job to configure the app #{app_instance}"
21
21
  job.trigger!
22
+ output[:configure_job_id] = job.job_invocation.id
22
23
  else
23
24
  ::Foreman::Logging.logger('foreman_acd').error "Could not create the job to configure the app #{app_instance}: #{result.error}"
24
25
  end
@@ -5,47 +5,60 @@ module ForemanAcd
5
5
  module HostManagedExtensions
6
6
  extend ActiveSupport::Concern
7
7
 
8
- RUN_CONFIGURATOR_DELAY = 240
9
-
10
8
  def self.prepended(base)
11
9
  base.instance_eval do
12
- before_provision :initiate_acd_app_configurator, :if => :deployed_via_acd?
10
+ before_provision :initiate_acd_app_configurator_after_host_deployment, :if => :deployed_via_acd?
11
+ before_destroy :check_deletable, :prepend => true, :if => :deployed_via_acd?
12
+
13
+ has_many :app_instances, :through => :foreman_hosts, :class_name => 'ForemanAcd::AppInstance'
14
+
15
+ scoped_search :relation => :app_instances,
16
+ :on => :name,
17
+ :rename => :acd_app_instance,
18
+ :only_explicit => true,
19
+ :complete_value => true,
20
+ :operators => ['= '],
21
+ :ext_method => :find_by_acd_app_instance_name
22
+ end
23
+
24
+ base.singleton_class.prepend ClassMethods
25
+ end
26
+
27
+ # New class methods for Host::Managed
28
+ module ClassMethods
29
+ def find_by_acd_app_instance_name(_key, operator, acd_instance_name)
30
+ cond = sanitize_sql_for_conditions(["acd_app_instances.name #{operator} ?", value_to_sql(operator, acd_instance_name)])
31
+ hosts = ForemanAcd::AppInstance.where(cond).joins(:foreman_hosts).pluck(:host_id)
32
+ if hosts.empty?
33
+ { :condition => '1=0' }
34
+ else
35
+ { :conditions => Host::Managed.arel_table[:id].in(hosts).to_sql }
36
+ end
13
37
  end
14
38
  end
15
39
 
16
40
  def deployed_via_acd?
17
- find_app_instance_host
18
- @app_instance_host.present?
41
+ find_foreman_host
42
+ @foreman_host.present?
19
43
  end
20
44
 
21
45
  private
22
46
 
23
- def find_app_instance_host
24
- @app_instance_host = ForemanAcd::ForemanHost.find_by(:host_id => id)
47
+ def check_deletable
48
+ return if @foreman_host.blank?
49
+ ::Foreman::Logging.logger('foreman_acd').warn "Could not delete host '#{name}' because it is used in Applications > App Instances '#{@foreman_host.app_instance.name}'"
50
+ raise _("Could not delete host '%{host_name}' because it is used in Applications > App Instances '%{app_instance_name}'") % {
51
+ :host_name => name, :app_instance_name => @foreman_host.app_instance.name
52
+ }
25
53
  end
26
54
 
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
55
+ def find_foreman_host
56
+ @foreman_host = ForemanAcd::ForemanHost.find_by(:host_id => id)
43
57
  end
44
58
 
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)
59
+ def initiate_acd_app_configurator_after_host_deployment
60
+ return if @foreman_host.blank?
61
+ ForemanAcd.initiate_acd_app_configurator(@foreman_host.app_instance)
49
62
  end
50
63
  end
51
64
  end
@@ -11,6 +11,7 @@ module ForemanAcd
11
11
 
12
12
  self.table_name = 'acd_app_instances'
13
13
  belongs_to :last_deploy_task, :class_name => 'ForemanTasks::Task'
14
+ belongs_to :initial_configure_task, :class_name => 'ForemanTasks::Task'
14
15
  validates :name, :presence => true, :uniqueness => true
15
16
  validates :app_definition, :presence => true
16
17
  belongs_to :app_definition, :inverse_of => :app_instances
@@ -32,7 +33,7 @@ module ForemanAcd
32
33
  end
33
34
 
34
35
  def clean_all_hosts
35
- remember_host_ids = foreman_hosts.map(&:host_id)
36
+ remember_host_ids = foreman_hosts.select(&:fresh_host?).map(&:host_id)
36
37
 
37
38
  # Clean the app instance association first
38
39
  foreman_hosts.update_all(:host_id => nil)
@@ -43,12 +44,56 @@ module ForemanAcd
43
44
 
44
45
  def clean_hosts_by_id(ids = [])
45
46
  # Clean the app instance association first
46
- foreman_hosts.where(:host_id => ids).update_all(:host_id => nil)
47
+ foreman_hosts.where(:host_id => ids, :is_existing_host => false).update_all(:host_id => nil)
47
48
 
48
49
  # Remove all hosts afterwards
49
50
  delete_hosts(ids)
50
51
  end
51
52
 
53
+ def hosts_deployment_finished?
54
+ return true if all_hosts_deployed?
55
+
56
+ ::Foreman::Logging.logger('foreman_acd').info('Another host is still in build-phase. Wait for it...')
57
+ false
58
+ end
59
+
60
+ def deployment_state
61
+ return :new if last_deploy_task.nil?
62
+ return :initiated if !last_deploy_task.nil? && last_deploy_task.ended_at.nil?
63
+
64
+ state = if all_hosts_deployed?
65
+ :finished
66
+ elsif last_deploy_task.ended_at? && last_deploy_task.result != 'success'
67
+ :failed
68
+ else
69
+ :pending
70
+ end
71
+ state
72
+ end
73
+
74
+ def initial_configure_job
75
+ return nil if initial_configure_task.nil?
76
+ return JobInvocation.find(initial_configure_task.output['configure_job_id']) if initial_configure_task.output.key?('configure_job_id') &&
77
+ !initial_configure_task.output['configure_job_id'].nil?
78
+ nil
79
+ end
80
+
81
+ def initial_configure_state
82
+ return :unconfigured if initial_configure_job.nil? && initial_configure_task.nil?
83
+ return :scheduled if initial_configure_job.nil?
84
+ return :pending unless initial_configure_job.finished?
85
+ initial_configure_job.status_label.to_sym
86
+ end
87
+
88
+ private
89
+
90
+ def all_hosts_deployed?
91
+ foreman_hosts.each do |foreman_host|
92
+ return false if foreman_host.host.nil? || foreman_host.host.build?
93
+ end
94
+ true
95
+ end
96
+
52
97
  def delete_hosts(ids = [])
53
98
  return if ids.empty?
54
99
  ids.each do |host_id|
@@ -16,6 +16,14 @@ module ForemanAcd
16
16
  _('Foreman Host')
17
17
  end
18
18
 
19
+ def fresh_host?
20
+ !is_existing_host
21
+ end
22
+
23
+ def existing_host?
24
+ is_existing_host
25
+ end
26
+
19
27
  def self.permission_name
20
28
  'foreman_hosts'
21
29
  end
@@ -22,8 +22,17 @@ module ForemanAcd
22
22
 
23
23
  foreman_hosts.each do |foreman_host|
24
24
  service_data = services.select { |k| k['id'] == foreman_host.service.to_i }.first
25
- host_params = set_host_params(foreman_host, service_data)
26
25
 
26
+ # Handle already deployed hosts
27
+ if foreman_host.existing_host?
28
+ domain = Hostgroup.find(service_data['hostgroup']).domain.name
29
+ fqdn = "#{foreman_host.hostname}.#{domain}"
30
+ h = Host.find_by(:name => fqdn)
31
+ foreman_host.update!(:host_id => h.id)
32
+ next
33
+ end
34
+
35
+ host_params = set_host_params(foreman_host, service_data)
27
36
  host = foreman_host.host.presence
28
37
 
29
38
  is_rebuild = false
@@ -45,7 +54,10 @@ module ForemanAcd
45
54
  end
46
55
 
47
56
  # REMOVE ME (but very nice for testing)
48
- # host.mac = "00:11:22:33:44:55"
57
+ # prng = Random.new
58
+ # x = prng.rand(100)
59
+ # y = prng.rand(100)
60
+ # host.mac = "00:11:22:33:#{x}:#{y}"
49
61
 
50
62
  apply_compute_profile(host)
51
63
  host.suggest_default_pxe_loader
@@ -73,6 +85,11 @@ module ForemanAcd
73
85
  output << msg
74
86
  end
75
87
  end
88
+
89
+ # Try to start the configuration, too. In case of a app instance including only already deployed hosts
90
+ # this would start the configuration job then.
91
+ ForemanAcd.initiate_acd_app_configurator(@app_instance)
92
+
76
93
  output
77
94
  end
78
95