foreman_acd 0.7.0 → 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -5
- data/app/controllers/foreman_acd/ansible_playbooks_controller.rb +17 -2
- data/app/controllers/foreman_acd/app_definitions_controller.rb +104 -7
- data/app/controllers/foreman_acd/app_instances_controller.rb +15 -30
- data/app/controllers/foreman_acd/concerns/app_instance_mixins.rb +36 -0
- data/app/controllers/ui_acd_controller.rb +42 -3
- data/app/lib/actions/foreman_acd/run_configurator.rb +1 -0
- data/app/models/concerns/foreman_acd/host_managed_extensions.rb +40 -27
- data/app/models/foreman_acd/app_instance.rb +47 -2
- data/app/models/foreman_acd/foreman_host.rb +8 -0
- data/app/services/foreman_acd/app_deployer.rb +19 -2
- data/app/services/foreman_acd/inventory_creator.rb +11 -1
- data/app/views/foreman_acd/app_definitions/_form.html.erb +4 -0
- data/app/views/foreman_acd/app_definitions/import.html.erb +20 -1
- data/app/views/foreman_acd/app_definitions/index.html.erb +3 -6
- data/app/views/foreman_acd/app_instances/_form.html.erb +4 -0
- data/app/views/foreman_acd/app_instances/index.html.erb +15 -11
- data/app/views/foreman_acd/app_instances/report.html.erb +7 -2
- data/app/views/ui_acd/host_report.json.rabl +4 -0
- data/app/views/ui_acd/report_data.json.rabl +10 -0
- data/app/views/ui_acd/validate_hostname.json.rabl +6 -0
- data/config/routes.rb +3 -0
- data/db/migrate/20210818125913_add_is_existing_host_to_foreman_host.rb +8 -0
- data/db/migrate/20210902110645_add_initial_configure_task.rb +8 -0
- data/lib/foreman_acd/plugin.rb +9 -9
- data/lib/foreman_acd/version.rb +1 -1
- data/lib/foreman_acd.rb +27 -9
- data/package.json +8 -25
- data/test/controllers/ui_acd_controller_test.rb +4 -1
- data/webpack/__mocks__/foremanReact/components/ForemanModal/ForemanModalActions.js +2 -0
- data/webpack/__snapshots__/helper.test.js.snap +1 -1
- data/webpack/components/ApplicationDefinition/ApplicationDefinition.js +34 -10
- data/webpack/components/ApplicationDefinition/ApplicationDefinitionActions.js +12 -0
- data/webpack/components/ApplicationDefinition/ApplicationDefinitionConstants.js +1 -0
- data/webpack/components/ApplicationDefinition/ApplicationDefinitionReducer.js +30 -9
- data/webpack/components/ApplicationDefinition/ApplicationDefinitionSelectors.js +4 -0
- data/webpack/components/ApplicationDefinition/__tests__/ApplicationDefinition.test.js +1 -0
- data/webpack/components/ApplicationDefinition/__tests__/ApplicationDefinitionSelectors.test.js +12 -0
- data/webpack/components/ApplicationDefinition/__tests__/__snapshots__/ApplicationDefinition.test.js.snap +31 -5
- data/webpack/components/ApplicationDefinition/__tests__/__snapshots__/ApplicationDefinitionSelectors.test.js.snap +8 -0
- data/webpack/components/ApplicationDefinition/components/AnsiblePlaybookSelector.js +1 -1
- data/webpack/components/ApplicationDefinition/components/__tests__/__snapshots__/AnsiblePlaybookSelector.test.js.snap +3 -3
- data/webpack/components/ApplicationDefinition/index.js +8 -0
- data/webpack/components/ApplicationDefinitionImport/ApplicationDefinitionImport.js +214 -0
- data/webpack/components/ApplicationDefinitionImport/ApplicationDefinitionImport.scss +1 -0
- data/webpack/components/ApplicationDefinitionImport/ApplicationDefinitionImportActions.js +161 -0
- data/webpack/components/ApplicationDefinitionImport/ApplicationDefinitionImportConstants.js +6 -0
- data/webpack/components/ApplicationDefinitionImport/ApplicationDefinitionImportReducer.js +79 -0
- data/webpack/components/ApplicationDefinitionImport/ApplicationDefinitionImportSelectors.js +8 -0
- data/webpack/components/ApplicationDefinitionImport/__fixtures__/applicationDefinitionImportConfData_1.fixtures.js +129 -0
- data/webpack/components/ApplicationDefinitionImport/__fixtures__/applicationDefinitionImportReducer.fixtures.js +29 -0
- data/webpack/components/ApplicationDefinitionImport/__tests__/ApplicationDefinitionImport.test.js +20 -0
- data/webpack/components/ApplicationDefinitionImport/__tests__/ApplicationDefinitionImportReducer.test.js +43 -0
- data/webpack/components/ApplicationDefinitionImport/__tests__/ApplicationDefinitionImportSelectors.test.js +29 -0
- data/webpack/components/ApplicationDefinitionImport/__tests__/__snapshots__/ApplicationDefinitionImport.test.js.snap +62 -0
- data/webpack/components/ApplicationDefinitionImport/__tests__/__snapshots__/ApplicationDefinitionImportReducer.test.js.snap +362 -0
- data/webpack/components/ApplicationDefinitionImport/__tests__/__snapshots__/ApplicationDefinitionImportSelectors.test.js.snap +130 -0
- data/webpack/components/ApplicationDefinitionImport/index.js +32 -0
- data/webpack/components/ApplicationInstance/ApplicationInstance.js +102 -26
- data/webpack/components/ApplicationInstance/ApplicationInstanceActions.js +118 -6
- data/webpack/components/ApplicationInstance/ApplicationInstanceConstants.js +4 -0
- data/webpack/components/ApplicationInstance/ApplicationInstanceHelper.js +15 -0
- data/webpack/components/ApplicationInstance/ApplicationInstanceReducer.js +71 -30
- data/webpack/components/ApplicationInstance/ApplicationInstanceSelectors.js +4 -0
- data/webpack/components/ApplicationInstance/__fixtures__/applicationInstanceReducer.fixtures.js +2 -0
- data/webpack/components/ApplicationInstance/__tests__/ApplicationInstance.test.js +1 -0
- data/webpack/components/ApplicationInstance/__tests__/ApplicationInstanceReducer.test.js +12 -0
- data/webpack/components/ApplicationInstance/__tests__/ApplicationInstanceSelectors.test.js +12 -0
- data/webpack/components/ApplicationInstance/__tests__/__snapshots__/ApplicationInstance.test.js.snap +98 -7
- data/webpack/components/ApplicationInstance/__tests__/__snapshots__/ApplicationInstanceReducer.test.js.snap +271 -0
- data/webpack/components/ApplicationInstance/__tests__/__snapshots__/ApplicationInstanceSelectors.test.js.snap +8 -0
- data/webpack/components/ApplicationInstance/components/AppDefinitionSelector.js +1 -0
- data/webpack/components/ApplicationInstance/components/ServiceCounter.js +1 -1
- data/webpack/components/ApplicationInstance/helper.js +0 -0
- data/webpack/components/ApplicationInstance/index.js +8 -0
- data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReport.js +81 -6
- data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportActions.js +35 -1
- data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportConstants.js +3 -0
- data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportReducer.js +19 -0
- data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportSelectors.js +4 -0
- data/webpack/components/ApplicationInstanceReport/__tests__/__snapshots__/ApplicationInstanceReport.test.js.snap +1 -124
- data/webpack/components/ApplicationInstanceReport/index.js +8 -1
- data/webpack/components/ExistingHostSelection/ExistingHostSelection.js +104 -0
- data/webpack/components/ExistingHostSelection/ExistingHostSelection.scss +15 -0
- data/webpack/components/ExistingHostSelection/ExistingHostSelectionActions.js +71 -0
- data/webpack/components/ExistingHostSelection/ExistingHostSelectionConstants.js +4 -0
- data/webpack/components/ExistingHostSelection/ExistingHostSelectionHelper.js +0 -0
- data/webpack/components/ExistingHostSelection/ExistingHostSelectionReducer.js +90 -0
- data/webpack/components/ExistingHostSelection/ExistingHostSelectionSelectors.js +8 -0
- data/webpack/components/ExistingHostSelection/__fixtures__/existingHostSelectionConfData_1.fixtures.js +191 -0
- data/webpack/components/ExistingHostSelection/__fixtures__/existingHostSelectionReducer.fixtures.js +203 -0
- data/webpack/components/ExistingHostSelection/__tests__/ExistingHostSelection.test.js +19 -0
- data/webpack/components/ExistingHostSelection/__tests__/ExistingHostSelectionReducer.test.js +59 -0
- data/webpack/components/ExistingHostSelection/__tests__/ExistingHostSelectionSelectors.test.js +36 -0
- data/webpack/components/ExistingHostSelection/__tests__/__snapshots__/ExistingHostSelection.test.js.snap +35 -0
- data/webpack/components/ExistingHostSelection/__tests__/__snapshots__/ExistingHostSelectionReducer.test.js.snap +614 -0
- data/webpack/components/ExistingHostSelection/__tests__/__snapshots__/ExistingHostSelectionSelectors.test.js.snap +27 -0
- data/webpack/components/ExistingHostSelection/components/ServiceSelector.js +48 -0
- data/webpack/components/ExistingHostSelection/components/__tests__/ServiceSelector.test.js +35 -0
- data/webpack/components/ExistingHostSelection/components/__tests__/__snapshots__/ServiceSelector.test.js.snap +77 -0
- data/webpack/components/ExistingHostSelection/index.js +26 -0
- data/webpack/components/ParameterSelection/ParameterSelection.js +103 -1
- data/webpack/components/ParameterSelection/ParameterSelection.scss +7 -0
- data/webpack/components/ParameterSelection/ParameterSelectionActions.js +46 -4
- data/webpack/components/ParameterSelection/ParameterSelectionConstants.js +2 -0
- data/webpack/components/ParameterSelection/ParameterSelectionHelper.js +5 -1
- data/webpack/components/ParameterSelection/ParameterSelectionReducer.js +52 -11
- data/webpack/components/ParameterSelection/ParameterSelectionSelectors.js +2 -0
- data/webpack/components/ParameterSelection/__fixtures__/parameterSelectionData_1.fixtures.js +8 -0
- data/webpack/components/ParameterSelection/__tests__/ParameterSelectionReducer.test.js +2 -0
- data/webpack/components/ParameterSelection/__tests__/ParameterSelectionSelectors.test.js +6 -0
- data/webpack/components/ParameterSelection/__tests__/__snapshots__/ParameterSelection.test.js.snap +96 -0
- data/webpack/components/ParameterSelection/__tests__/__snapshots__/ParameterSelectionReducer.test.js.snap +117 -17
- data/webpack/components/ParameterSelection/__tests__/__snapshots__/ParameterSelectionSelectors.test.js.snap +13 -0
- data/webpack/components/ParameterSelection/index.js +4 -1
- data/webpack/components/SyncGitRepo/SyncGitRepo.js +2 -10
- data/webpack/components/SyncGitRepo/SyncGitRepoActions.js +2 -3
- data/webpack/components/SyncGitRepo/SyncGitRepoConstants.js +0 -1
- data/webpack/components/SyncGitRepo/__tests__/__snapshots__/SyncGitRepo.test.js.snap +1 -0
- data/webpack/components/SyncGitRepo/components/FormTextInput.js +1 -1
- data/webpack/components/SyncGitRepo/components/ScmTypeSelector.js +3 -2
- data/webpack/components/common/DeleteTableEntry.js +16 -2
- data/webpack/components/common/__tests__/__snapshots__/DeleteTableEntry.test.js.snap +38 -0
- data/webpack/helper.js +21 -1
- data/webpack/helper.test.js +20 -1
- data/webpack/index.js +5 -0
- data/webpack/js-yaml.js +3874 -0
- data/webpack/reducer.js +13 -2
- data/webpack/test_setup.js +0 -2
- metadata +46 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a14a2b2d7f2353e0a908bf3b0de9ea3126fff402e1003f9bea648afe260c73c0
|
4
|
+
data.tar.gz: 6eecc24dc7be78b4ddc04da2b50a525dd95a2b358647d8701e1ea6479eef5332
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 `/
|
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(
|
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
|
-
|
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 :
|
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
|
-
|
79
|
-
|
80
|
-
|
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
|
86
|
-
|
87
|
-
|
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
|
-
|
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'],
|
116
|
-
:
|
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 :
|
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
|
-
|
18
|
-
@
|
41
|
+
find_foreman_host
|
42
|
+
@foreman_host.present?
|
19
43
|
end
|
20
44
|
|
21
45
|
private
|
22
46
|
|
23
|
-
def
|
24
|
-
|
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
|
28
|
-
|
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
|
46
|
-
|
47
|
-
|
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|
|
@@ -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
|
-
#
|
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
|
|