foreman_deployments 0.0.1
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.
- checksums.yaml +15 -0
- data/LICENSE +619 -0
- data/README.md +52 -0
- data/Rakefile +30 -0
- data/app/controllers/foreman_deployments/api/v2/base_controller.rb +25 -0
- data/app/controllers/foreman_deployments/api/v2/deployments_controller.rb +102 -0
- data/app/controllers/foreman_deployments/api/v2/stacks_controller.rb +52 -0
- data/app/controllers/foreman_deployments/create_resources_controller.rb +62 -0
- data/app/controllers/foreman_deployments/deployments_controller.rb +5 -0
- data/app/controllers/foreman_deployments/stacks_controller.rb +5 -0
- data/app/lib/foreman_deployments/base_dereference_visitor.rb +55 -0
- data/app/lib/foreman_deployments/config.rb +16 -0
- data/app/lib/foreman_deployments/config/array.rb +91 -0
- data/app/lib/foreman_deployments/config/configurator.rb +23 -0
- data/app/lib/foreman_deployments/config/hash.rb +81 -0
- data/app/lib/foreman_deployments/config/load_visitor.rb +23 -0
- data/app/lib/foreman_deployments/config/merge_visitor.rb +23 -0
- data/app/lib/foreman_deployments/config/save_visitor.rb +23 -0
- data/app/lib/foreman_deployments/inputs/base_input_definition.rb +39 -0
- data/app/lib/foreman_deployments/inputs/value.rb +25 -0
- data/app/lib/foreman_deployments/planner_visitor.rb +26 -0
- data/app/lib/foreman_deployments/registry.rb +67 -0
- data/app/lib/foreman_deployments/stack_definition.rb +37 -0
- data/app/lib/foreman_deployments/stack_parser.rb +121 -0
- data/app/lib/foreman_deployments/task_reference.rb +48 -0
- data/app/lib/foreman_deployments/tasks/base_action.rb +6 -0
- data/app/lib/foreman_deployments/tasks/base_definition.rb +72 -0
- data/app/lib/foreman_deployments/tasks/creation_task_definition.rb +68 -0
- data/app/lib/foreman_deployments/tasks/host_creation_task_definition.rb +44 -0
- data/app/lib/foreman_deployments/tasks/search_task_definition.rb +55 -0
- data/app/lib/foreman_deployments/tasks/stack_deploy_action.rb +10 -0
- data/app/lib/foreman_deployments/tasks/wait_until_built_task_definition.rb +65 -0
- data/app/lib/foreman_deployments/validation/dereference_visitor.rb +18 -0
- data/app/lib/foreman_deployments/validation/remove_ids_visitor.rb +59 -0
- data/app/lib/foreman_deployments/validation/validation_error.rb +12 -0
- data/app/lib/foreman_deployments/validation/validation_result.rb +26 -0
- data/app/lib/foreman_deployments/validation/validation_visitor.rb +20 -0
- data/app/lib/foreman_deployments/validation/validator.rb +29 -0
- data/app/models/foreman_deployments/concerns/belongs_to_single_taxonomy.rb +42 -0
- data/app/models/foreman_deployments/concerns/belongs_to_stack_taxonomy.rb +24 -0
- data/app/models/foreman_deployments/configuration.rb +26 -0
- data/app/models/foreman_deployments/deployment.rb +57 -0
- data/app/models/foreman_deployments/resource_models/create_resource.rb +18 -0
- data/app/models/foreman_deployments/stack.rb +20 -0
- data/app/views/foreman_deployments/api/v2/deployments/base.json.rabl +3 -0
- data/app/views/foreman_deployments/api/v2/deployments/create.json.rabl +3 -0
- data/app/views/foreman_deployments/api/v2/deployments/index.json.rabl +3 -0
- data/app/views/foreman_deployments/api/v2/deployments/main.json.rabl +5 -0
- data/app/views/foreman_deployments/api/v2/deployments/merge_configuration.json.rabl +3 -0
- data/app/views/foreman_deployments/api/v2/deployments/replace_configuration.json.rabl +3 -0
- data/app/views/foreman_deployments/api/v2/deployments/run.json.rabl +3 -0
- data/app/views/foreman_deployments/api/v2/deployments/show.json.rabl +11 -0
- data/app/views/foreman_deployments/api/v2/deployments/update.json.rabl +3 -0
- data/app/views/foreman_deployments/api/v2/stacks/base.json.rabl +3 -0
- data/app/views/foreman_deployments/api/v2/stacks/create.json.rabl +3 -0
- data/app/views/foreman_deployments/api/v2/stacks/index.json.rabl +3 -0
- data/app/views/foreman_deployments/api/v2/stacks/main.json.rabl +3 -0
- data/app/views/foreman_deployments/api/v2/stacks/show.json.rabl +7 -0
- data/app/views/foreman_deployments/api/v2/stacks/update.json.rabl +3 -0
- data/app/views/foreman_deployments/create_resources/new.html.erb +6 -0
- data/config/routes.rb +41 -0
- data/db/migrate/20150623140612_create_stacks.rb +10 -0
- data/db/migrate/20150814092932_create_deployment.rb +20 -0
- data/db/migrate/20150916133305_add_task_to_deployments.rb +5 -0
- data/db/migrate/20150917130618_add_taxonomy_to_deployments.rb +8 -0
- data/db/seeds.d/03-permissions.rb +14 -0
- data/doc/deployment_process.md +112 -0
- data/doc/design/capsule_stack.puml +51 -0
- data/doc/design/complete_stack.puml +17 -0
- data/doc/design/config_resource_overview.puml +15 -0
- data/doc/design/design.md +230 -0
- data/doc/design/diagrams/capsule_stack.png +0 -0
- data/doc/design/diagrams/capsule_stack.svg +1 -0
- data/doc/design/diagrams/complete_stack.png +0 -0
- data/doc/design/diagrams/complete_stack.svg +1 -0
- data/doc/design/diagrams/config_resource_overview.png +0 -0
- data/doc/design/diagrams/config_resource_overview.svg +1 -0
- data/doc/design/diagrams/ordered_resource_overview.png +0 -0
- data/doc/design/diagrams/ordered_resource_overview.svg +1 -0
- data/doc/design/diagrams/overview.png +0 -0
- data/doc/design/diagrams/overview.svg +1 -0
- data/doc/design/diagrams/overview_class.png +0 -0
- data/doc/design/diagrams/overview_class.svg +1 -0
- data/doc/design/diagrams/sat_stack.png +0 -0
- data/doc/design/diagrams/sat_stack.svg +1 -0
- data/doc/design/diagrams/solr_usecase.png +0 -0
- data/doc/design/diagrams/solr_usecase.svg +1 -0
- data/doc/design/examples.md +192 -0
- data/doc/design/generate-diagrams.sh +7 -0
- data/doc/design/implementation.md +128 -0
- data/doc/design/ordered_resource_overview.puml +15 -0
- data/doc/design/overview.puml +42 -0
- data/doc/design/overview_class.puml +64 -0
- data/doc/design/resources.md +134 -0
- data/doc/design/sat_stack.puml +37 -0
- data/doc/design/shared.puml +171 -0
- data/doc/design/solr_usecase.puml +189 -0
- data/doc/design/tasks.md +74 -0
- data/doc/design/user_interfaces.md +189 -0
- data/doc/introduction.md +84 -0
- data/doc/writing_stacks.md +102 -0
- data/lib/foreman_deployments.rb +7 -0
- data/lib/foreman_deployments/engine.rb +94 -0
- data/lib/foreman_deployments/monkey_patches.rb +9 -0
- data/lib/foreman_deployments/version.rb +3 -0
- data/lib/tasks/foreman_deployments_tasks.rake +22 -0
- data/locale/Makefile +62 -0
- data/test/factories/foreman_deployments.rb +70 -0
- data/test/functional/api/v2/deployments_controller_test.rb +318 -0
- data/test/functional/api/v2/stacks_controller_test.rb +140 -0
- data/test/test_plugin_helper.rb +14 -0
- data/test/unit/lib/config/array_test.rb +217 -0
- data/test/unit/lib/config/configurator_test.rb +66 -0
- data/test/unit/lib/config/hash_test.rb +178 -0
- data/test/unit/lib/inputs/value_test.rb +47 -0
- data/test/unit/lib/registry_test.rb +117 -0
- data/test/unit/lib/stack_definition_test.rb +54 -0
- data/test/unit/lib/stack_parser_test.rb +129 -0
- data/test/unit/lib/task_reference_test.rb +63 -0
- data/test/unit/lib/tasks/base_definition_test.rb +137 -0
- data/test/unit/lib/tasks/creation_task_definition_test.rb +57 -0
- data/test/unit/lib/tasks/host_creation_task_definition_test.rb +10 -0
- data/test/unit/lib/tasks/search_task_definition_test.rb +49 -0
- data/test/unit/lib/tasks/stack_deploy_action_test.rb +83 -0
- data/test/unit/lib/tasks/wait_until_built_task_definition_test.rb +71 -0
- data/test/unit/lib/validation/dereference_visitor_test.rb +48 -0
- data/test/unit/lib/validation/remove_ids_visitor_test.rb +90 -0
- data/test/unit/lib/validation/validation_result_test.rb +40 -0
- data/test/unit/lib/validation/validation_visitor_test.rb +67 -0
- data/test/unit/model/configuration_test.rb +88 -0
- data/test/unit/model/deployment_test.rb +159 -0
- data/test/unit/model/stack_test.rb +33 -0
- metadata +241 -0
data/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Foreman Multi-Host Deployments
|
|
2
|
+
|
|
3
|
+
A plugin adding Foreman Multi-Host Deployment support.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Documentation
|
|
7
|
+
|
|
8
|
+
* [Introduction](doc/introduction.md)
|
|
9
|
+
* [How to write stacks](doc/writing_stacks.md)
|
|
10
|
+
* [Create, configure and deploy a stack](doc/deployment_process.md)
|
|
11
|
+
* Other media:
|
|
12
|
+
* [Development preview demo](https://www.youtube.com/watch?v=U5qtriQfu6A)
|
|
13
|
+
|
|
14
|
+
## Design documentation
|
|
15
|
+
|
|
16
|
+
* [Design](doc/design/design.md)
|
|
17
|
+
* [List of deployment tasks](doc/design/tasks.md)
|
|
18
|
+
* [Examples and usecases](doc/design/examples.md)
|
|
19
|
+
* [User interfaces](doc/design/user_interfaces.md)
|
|
20
|
+
* Other sources:
|
|
21
|
+
* [Redmine tracker](http://projects.theforeman.org/issues/10092)
|
|
22
|
+
* [Deep dive](https://www.youtube.com/watch?v=ISQdESgmOqo) about the task-centric approach ([presentation](https://tstrachota.fedorapeople.org/slides/mhd_presentation/))
|
|
23
|
+
* The original design:
|
|
24
|
+
* Foreman [Deep dive](https://www.youtube.com/watch?v=CFPLGfA6-jU) ([presentation](https://drive.google.com/file/d/0B4ZecfmLdabga0c2SGNqN1ZDQzg/view?usp=sharing))
|
|
25
|
+
* Cfgmgmtcamp [presentation](http://blog.pitr.ch/presentations/2015/cfgmgmtcamp/)
|
|
26
|
+
* Code walk-through [session](https://bluejeans.com/s/83Q9/)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
Add `gem 'foreman_deployments', path: '../foreman_deployments'` to your `Gemfile.local.rb` file.
|
|
32
|
+
For more information see
|
|
33
|
+
[How to Install a Plugin](http://projects.theforeman.org/projects/foreman/wiki/How_to_Install_a_Plugin).
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## Copyright
|
|
37
|
+
|
|
38
|
+
Copyright (c) 2014-2015 Red Hat Inc
|
|
39
|
+
|
|
40
|
+
This program is free software: you can redistribute it and/or modify
|
|
41
|
+
it under the terms of the GNU General Public License as published by
|
|
42
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
43
|
+
(at your option) any later version.
|
|
44
|
+
|
|
45
|
+
This program is distributed in the hope that it will be useful,
|
|
46
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
47
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
48
|
+
GNU General Public License for more details.
|
|
49
|
+
|
|
50
|
+
You should have received a copy of the GNU General Public License
|
|
51
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
52
|
+
|
data/Rakefile
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env rake
|
|
2
|
+
begin
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
rescue LoadError
|
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
Bundler::GemHelper.install_tasks
|
|
9
|
+
|
|
10
|
+
require 'rake/testtask'
|
|
11
|
+
|
|
12
|
+
Rake::TestTask.new(:test) do |t|
|
|
13
|
+
t.libs << 'lib'
|
|
14
|
+
t.libs << 'test'
|
|
15
|
+
t.pattern = 'test/**/*_test.rb'
|
|
16
|
+
t.verbose = false
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
task default: :test
|
|
20
|
+
|
|
21
|
+
begin
|
|
22
|
+
require 'rubocop/rake_task'
|
|
23
|
+
RuboCop::RakeTask.new
|
|
24
|
+
rescue LoadError
|
|
25
|
+
puts 'Rubocop not loaded.'
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
task :default do
|
|
29
|
+
Rake::Task['rubocop'].execute
|
|
30
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require_dependency 'foreman_deployments/stack_parser'
|
|
2
|
+
|
|
3
|
+
module ForemanDeployments
|
|
4
|
+
module Api
|
|
5
|
+
module V2
|
|
6
|
+
class BaseController < ::Api::V2::BaseController
|
|
7
|
+
rescue_from StackParseException, :with => :unprocessable_entity_error
|
|
8
|
+
|
|
9
|
+
resource_description do
|
|
10
|
+
api_version 'v2'
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def unprocessable_entity_error(exception)
|
|
14
|
+
render_error 'standard_error', :locals => { :exception => exception }, :status => :unprocessable_entity
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def resource_class_for(resource)
|
|
18
|
+
return "ForemanDeployments::#{resource.classify}".constantize
|
|
19
|
+
rescue NameError
|
|
20
|
+
super
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
module ForemanDeployments
|
|
2
|
+
module Api
|
|
3
|
+
module V2
|
|
4
|
+
class DeploymentsController < BaseController
|
|
5
|
+
include ::Api::TaxonomyScope
|
|
6
|
+
|
|
7
|
+
before_filter :find_resource, :only => [:show, :replace_configuration, :merge_configuration, :run]
|
|
8
|
+
|
|
9
|
+
rescue_from ForemanDeployments::Config::InvalidValueException, :with => :unprocessable_entity_error
|
|
10
|
+
rescue_from ForemanDeployments::Validation::ValidationError, :with => :unprocessable_entity_error
|
|
11
|
+
|
|
12
|
+
def_param_group :deployment do
|
|
13
|
+
param :deployment, Hash, :required => true, :action_aware => true do
|
|
14
|
+
param :name, String, :required => true, :desc => N_('Name for the deployment')
|
|
15
|
+
param :stack_id, :identifier, :required => true, :desc => N_('Id of the stack to deploy')
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
api :POST, '/deployments/', N_('Create a deployment')
|
|
20
|
+
param_group :deployment, :as => :create
|
|
21
|
+
def create
|
|
22
|
+
deployment_params = params[:deployment]
|
|
23
|
+
|
|
24
|
+
if deployment_params[:stack_id]
|
|
25
|
+
stack = ForemanDeployments::Stack.authorized(:view_stacks).find(deployment_params.delete(:stack_id))
|
|
26
|
+
end
|
|
27
|
+
deployment_params[:configuration] = ForemanDeployments::Configuration.new(:stack => stack)
|
|
28
|
+
|
|
29
|
+
@deployment = ForemanDeployments::Deployment.new(deployment_params)
|
|
30
|
+
process_response @deployment.save
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
api :GET, '/deployments/', N_('List deployments')
|
|
34
|
+
api :GET, '/locations/:location_id/deployments/', N_('List of deployments per location')
|
|
35
|
+
api :GET, '/organizations/:organization_id/deployments/', N_('List of deployments per organization')
|
|
36
|
+
param_group :taxonomy_scope, ::Api::V2::BaseController
|
|
37
|
+
param_group :search_and_pagination, ::Api::V2::BaseController
|
|
38
|
+
def index
|
|
39
|
+
@deployments = resource_scope_for_index
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
api :POST, '/deployments/:id/configuration/', N_('Merge a configuration update with current values')
|
|
43
|
+
param :id, :identifier, :required => true
|
|
44
|
+
param :values, Hash,
|
|
45
|
+
:required => true,
|
|
46
|
+
:desc => N_('Hash with configuration update for tasks (Format: task ID => configuration values).')
|
|
47
|
+
def merge_configuration
|
|
48
|
+
configuration_update = ForemanDeployments::Configuration.new(:values => params[:values])
|
|
49
|
+
|
|
50
|
+
config = @deployment.configurator
|
|
51
|
+
config.merge(@deployment.configuration, configuration_update)
|
|
52
|
+
config.dump(@deployment.configuration)
|
|
53
|
+
|
|
54
|
+
@deployment.configuration.save!
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
api :PUT, '/deployments/:id/configuration/', N_('Configure a deployment')
|
|
58
|
+
param :id, :identifier, :required => true
|
|
59
|
+
param :values, Hash,
|
|
60
|
+
:required => true,
|
|
61
|
+
:desc => N_('Hash with configuration for tasks (Format: task ID => configuration values).')
|
|
62
|
+
def replace_configuration
|
|
63
|
+
new_configuration = ForemanDeployments::Configuration.new(:values => params[:values])
|
|
64
|
+
|
|
65
|
+
config = @deployment.configurator
|
|
66
|
+
config.configure(new_configuration)
|
|
67
|
+
config.dump(@deployment.configuration)
|
|
68
|
+
|
|
69
|
+
@deployment.configuration.save!
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
api :GET, '/deployments/:id/', N_('Get information about a deployment')
|
|
73
|
+
param :id, :identifier, :required => true
|
|
74
|
+
def show
|
|
75
|
+
config = @deployment.configurator
|
|
76
|
+
config.configure(@deployment.configuration)
|
|
77
|
+
|
|
78
|
+
@validation_result = @deployment.parsed_stack.validate
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
api :POST, '/deployments/:id/run/', N_('Start a deployment')
|
|
82
|
+
param :id, :identifier, :required => true
|
|
83
|
+
def run
|
|
84
|
+
@deployment.run
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
def action_permission
|
|
90
|
+
case params[:action]
|
|
91
|
+
when 'merge_configuration', 'replace_configuration'
|
|
92
|
+
'create'
|
|
93
|
+
when 'run'
|
|
94
|
+
'run'
|
|
95
|
+
else
|
|
96
|
+
super
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module ForemanDeployments
|
|
2
|
+
module Api
|
|
3
|
+
module V2
|
|
4
|
+
class StacksController < BaseController
|
|
5
|
+
include ::Api::TaxonomyScope
|
|
6
|
+
|
|
7
|
+
before_filter :find_resource, :only => [:show, :update]
|
|
8
|
+
|
|
9
|
+
def_param_group :stack do
|
|
10
|
+
param :stack, Hash, :required => true, :action_aware => true do
|
|
11
|
+
param :name, String, :required => true, :desc => N_('Name for the stack')
|
|
12
|
+
param :definition, String, :required => true, :desc => N_('Stack definition in YAML format')
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
api :POST, '/stacks/', N_('Import a stack')
|
|
17
|
+
param_group :stack, :as => :create
|
|
18
|
+
def create
|
|
19
|
+
@stack = Stack.new(params[:stack])
|
|
20
|
+
# parse the stack to see if it's valid
|
|
21
|
+
ForemanDeployments::StackParser.parse(@stack.definition)
|
|
22
|
+
process_response @stack.save
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
api :PUT, '/stacks/:id/', N_('Update imported stack')
|
|
26
|
+
param_group :stack, :as => :update
|
|
27
|
+
param :id, :identifier, :required => true
|
|
28
|
+
def update
|
|
29
|
+
if !@stack.configurations.empty? && !params[:stack][:definition].nil?
|
|
30
|
+
render :json => { :error => _("Can't update stack that has been configured") }, :status => :unprocessable_entity
|
|
31
|
+
else
|
|
32
|
+
ForemanDeployments::StackParser.parse(params[:stack][:definition]) if params[:stack][:definition]
|
|
33
|
+
process_response @stack.update_attributes(params[:stack])
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
api :GET, '/stacks/', N_('List saved stacks')
|
|
38
|
+
api :GET, '/locations/:location_id/stacks/', N_('List of stacks per location')
|
|
39
|
+
api :GET, '/organizations/:organization_id/stacks/', N_('List of stacks per organization')
|
|
40
|
+
def index
|
|
41
|
+
@stacks = resource_scope_for_index
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
api :GET, '/stacks/:id/', N_('Get information about a stack')
|
|
45
|
+
param :id, :identifier, :required => true
|
|
46
|
+
def show
|
|
47
|
+
@parsed_stack = ForemanDeployments::StackParser.parse(@stack.definition)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module ForemanDeployments
|
|
2
|
+
class CreateResourcesController < ApplicationController
|
|
3
|
+
def index
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def new
|
|
7
|
+
@resource = ResourceModels::CreateResource.new
|
|
8
|
+
@resource.resource_type = User
|
|
9
|
+
@resource.definition_hash = {
|
|
10
|
+
login: 'my_user1',
|
|
11
|
+
mail: 'test@foreman.org'
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@user = @resource.new_resource
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# rubocop:disable Metrics/MethodLength
|
|
18
|
+
def create
|
|
19
|
+
params[:user].delete :admin if params[:user]
|
|
20
|
+
|
|
21
|
+
# stack definition
|
|
22
|
+
# will be selected from DB as Stack object
|
|
23
|
+
stack = [
|
|
24
|
+
'user1: !task:SearchResource',
|
|
25
|
+
' class: User',
|
|
26
|
+
' search_term: "1"',
|
|
27
|
+
'usergroup1: !task:CreateResource',
|
|
28
|
+
' class: Usergroup',
|
|
29
|
+
' params:',
|
|
30
|
+
' name: zzz',
|
|
31
|
+
' user_ids:',
|
|
32
|
+
' !reference',
|
|
33
|
+
' object: user1',
|
|
34
|
+
' field: result.ids'
|
|
35
|
+
].join("\n")
|
|
36
|
+
|
|
37
|
+
# parse the stack
|
|
38
|
+
stack_definition = ForemanDeployments::StackParser.parse(stack)
|
|
39
|
+
|
|
40
|
+
# configure with user input
|
|
41
|
+
# stack_definition.tasks['user1'].configure('params' => params[:user])
|
|
42
|
+
stack_definition.tasks['usergroup1'].configure({})
|
|
43
|
+
|
|
44
|
+
# validate
|
|
45
|
+
result = ForemanDeployments::Validation::Validator.validate(stack_definition)
|
|
46
|
+
fail "Validation failed:\n #{result.messages}" unless result.valid?
|
|
47
|
+
|
|
48
|
+
ForemanTasks.sync_task(Tasks::StackDeployAction, stack_definition)
|
|
49
|
+
|
|
50
|
+
redirect_to foreman_tasks_tasks_path
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def edit
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def update
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def destroy
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module ForemanDeployments
|
|
2
|
+
class BaseDereferenceVisitor
|
|
3
|
+
def initialize
|
|
4
|
+
@task_cache = {}
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def visit(subject)
|
|
8
|
+
if subject.is_a? ForemanDeployments::Tasks::BaseDefinition
|
|
9
|
+
visit_task_definition(subject)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
protected
|
|
14
|
+
|
|
15
|
+
def visit_task_definition(definition)
|
|
16
|
+
definition.parameters = dereference(definition.parameters)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def dereference(value)
|
|
20
|
+
case value
|
|
21
|
+
when ForemanDeployments::TaskReference
|
|
22
|
+
return get_dereference(value)
|
|
23
|
+
when Config::Array
|
|
24
|
+
return value.transform! { |obj| dereference(obj) }
|
|
25
|
+
when Config::Hash
|
|
26
|
+
return value.transform! { |key, obj| [key, dereference(obj)] }
|
|
27
|
+
else
|
|
28
|
+
return value
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def get_dereference(_ref)
|
|
33
|
+
fail NotImplementedError, "Method 'get_dereference' needs to be implemented"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def cached(task)
|
|
37
|
+
key = cache_key(task)
|
|
38
|
+
if cached?(task)
|
|
39
|
+
@task_cache[key]
|
|
40
|
+
else
|
|
41
|
+
@task_cache[key] = yield
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def cached?(task)
|
|
46
|
+
@task_cache.key?(cache_key(task))
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def cache_key(task)
|
|
52
|
+
task.task_id.to_s
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module ForemanDeployments
|
|
2
|
+
module Config
|
|
3
|
+
class InvalidValueException < ::Foreman::Exception; end
|
|
4
|
+
|
|
5
|
+
def self.cast_to_configuration(value)
|
|
6
|
+
case value
|
|
7
|
+
when ::Hash
|
|
8
|
+
ForemanDeployments::Config::Hash[value.map { |key, obj| [key, cast_to_configuration(obj)] }]
|
|
9
|
+
when ::Array
|
|
10
|
+
ForemanDeployments::Config::Array[*value.map { |obj| cast_to_configuration(obj) }]
|
|
11
|
+
else
|
|
12
|
+
value
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
module ForemanDeployments
|
|
2
|
+
module Config
|
|
3
|
+
class Array < ::Array
|
|
4
|
+
def merge_configuration(values)
|
|
5
|
+
values = ensure_hash(values)
|
|
6
|
+
values.map do |index, value|
|
|
7
|
+
configure_index(index, value, :merge_configuration)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def configure(values)
|
|
12
|
+
values = ensure_hash(values)
|
|
13
|
+
configuration_storage.clear
|
|
14
|
+
values.map do |index, value|
|
|
15
|
+
configure_index(index, value, :configure)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def configured
|
|
20
|
+
map.with_index do |item, index|
|
|
21
|
+
if item.respond_to?(:configured)
|
|
22
|
+
item.configured
|
|
23
|
+
elsif item.nil?
|
|
24
|
+
configuration_storage[index]
|
|
25
|
+
else
|
|
26
|
+
item
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def configuration
|
|
32
|
+
result = configuration_storage.deep_clone
|
|
33
|
+
each_with_index do |item, index|
|
|
34
|
+
if item.respond_to?(:configuration)
|
|
35
|
+
result[index] = item.configuration unless item.configuration.nil?
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
result.empty? ? nil : result
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def transform!(&block)
|
|
42
|
+
self.map! do |item|
|
|
43
|
+
block.call(item)
|
|
44
|
+
end
|
|
45
|
+
self
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
protected
|
|
49
|
+
|
|
50
|
+
def configuration_storage
|
|
51
|
+
@configuration_storage ||= {}
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def ensure_hash(values)
|
|
55
|
+
if values.is_a? ::Hash
|
|
56
|
+
::Hash[values.map { |index, value| sanitize_pair(index, value) }]
|
|
57
|
+
elsif values.is_a? ::Array
|
|
58
|
+
::Hash[values.map.with_index { |value, index| sanitize_pair(index, value) }]
|
|
59
|
+
else
|
|
60
|
+
fail(InvalidValueException, _("Unexpected type '%s', only hash or array is accepted") % values.class)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def sanitize_pair(index, value)
|
|
65
|
+
index = Integer(index)
|
|
66
|
+
if (index >= size) || (index < 0)
|
|
67
|
+
fail(InvalidValueException, _('Can\'t configure items outside the range'))
|
|
68
|
+
end
|
|
69
|
+
[index, value]
|
|
70
|
+
rescue ArgumentError
|
|
71
|
+
raise(InvalidValueException, _("Key '%s' isn't numeric value") % index)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def configure_index(index, value, method)
|
|
75
|
+
preconfigured = self[index]
|
|
76
|
+
|
|
77
|
+
if preconfigured.nil?
|
|
78
|
+
if value.nil?
|
|
79
|
+
configuration_storage.delete(index)
|
|
80
|
+
else
|
|
81
|
+
configuration_storage[index] = value
|
|
82
|
+
end
|
|
83
|
+
elsif preconfigured.respond_to?(method)
|
|
84
|
+
preconfigured.send(method, value)
|
|
85
|
+
else
|
|
86
|
+
fail(InvalidValueException, _("You can't override values hardcoded in the stack definition"))
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|