foreman_salt 0.0.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.
- checksums.yaml +7 -0
- data/LICENSE +619 -0
- data/README.md +29 -0
- data/Rakefile +40 -0
- data/app/controllers/foreman_salt/concerns/hostgroups_controller_extensions.rb +19 -0
- data/app/controllers/foreman_salt/concerns/hosts_controller_extensions.rb +69 -0
- data/app/controllers/foreman_salt/concerns/smart_proxy_auth_extensions.rb +24 -0
- data/app/controllers/foreman_salt/concerns/unattended_controller_extensions.rb +18 -0
- data/app/controllers/foreman_salt/salt_autosign_controller.rb +43 -0
- data/app/controllers/foreman_salt/salt_keys_controller.rb +59 -0
- data/app/controllers/foreman_salt/salt_modules_controller.rb +45 -0
- data/app/helpers/concerns/foreman_salt/hosts_helper_extensions.rb +28 -0
- data/app/helpers/concerns/foreman_salt/smart_proxies_helper_extensions.rb +22 -0
- data/app/helpers/foreman_salt/salt_keys_helper.rb +9 -0
- data/app/lib/proxy_api/salt.rb +60 -0
- data/app/models/concerns/foreman_salt/host_managed_extensions.rb +64 -0
- data/app/models/concerns/foreman_salt/hostgroup_extensions.rb +49 -0
- data/app/models/concerns/foreman_salt/orchestration/salt.rb +72 -0
- data/app/models/foreman_salt/fact_name.rb +4 -0
- data/app/models/foreman_salt/salt_module.rb +22 -0
- data/app/overrides/foreman/salt_modules/_host_tab.html.erb +2 -0
- data/app/overrides/foreman/salt_modules/_host_tab_pane.html.erb +22 -0
- data/app/overrides/salt_modules_selector.rb +19 -0
- data/app/overrides/salt_proxy_selector.rb +13 -0
- data/app/services/foreman_salt/fact_importer.rb +108 -0
- data/app/services/foreman_salt/smart_proxies/salt_keys.rb +68 -0
- data/app/views/foreman_salt/salt_autosign/_form.html.erb +8 -0
- data/app/views/foreman_salt/salt_autosign/index.html.erb +21 -0
- data/app/views/foreman_salt/salt_autosign/new.html.erb +3 -0
- data/app/views/foreman_salt/salt_keys/index.erb +34 -0
- data/app/views/foreman_salt/salt_modules/_form.html.erb +15 -0
- data/app/views/foreman_salt/salt_modules/edit.html.erb +4 -0
- data/app/views/foreman_salt/salt_modules/index.html.erb +23 -0
- data/app/views/foreman_salt/salt_modules/new.html.erb +4 -0
- data/config/routes.rb +32 -0
- data/db/migrate/20140813081913_add_salt_proxy_to_host_and_host_group.rb +12 -0
- data/db/migrate/20140817210214_create_salt_modules.rb +20 -0
- data/db/migrate/20140829210214_add_salt_modules_to_hostgroups.rb +12 -0
- data/db/seeds.d/75-salt-seeds.rb +31 -0
- data/lib/foreman_salt/engine.rb +90 -0
- data/lib/foreman_salt/version.rb +3 -0
- data/lib/foreman_salt.rb +4 -0
- data/lib/tasks/foreman_salt_tasks.rake +31 -0
- data/test/factories/foreman_salt_factories.rb +32 -0
- data/test/functional/hosts_controller_test.rb +17 -0
- data/test/integration/salt_autosign_test.rb +29 -0
- data/test/integration/salt_keys_test.rb +45 -0
- data/test/integration/salt_module_test.rb +27 -0
- data/test/test_plugin_helper.rb +14 -0
- data/test/unit/grains_centos.json +108 -0
- data/test/unit/grains_importer_test.rb +38 -0
- data/test/unit/host_extensions_test.rb +32 -0
- data/test/unit/hostgroup_extensions_test.rb +40 -0
- data/test/unit/salt_keys_test.rb +46 -0
- metadata +122 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
<% @inherited_salt_modules = [] if @inherited_salt_modules.blank? %>
|
2
|
+
<div class="tab-pane" id="salt_modules">
|
3
|
+
<div class="row">
|
4
|
+
<div class="col-md-4">
|
5
|
+
<h3>Inherited Modules</h3>
|
6
|
+
<ul>
|
7
|
+
<% @inherited_salt_modules.sort.each do |salt_module| -%>
|
8
|
+
<li title="Inherited from hostgroup"><%= salt_module.name -%></li>
|
9
|
+
<% end -%>
|
10
|
+
</ul>
|
11
|
+
</div>
|
12
|
+
<div class="col-md-8">
|
13
|
+
<h3>Salt Modules</h3>
|
14
|
+
<% if @inherited_salt_modules.blank? -%>
|
15
|
+
<%= multiple_selects f, :salt_module, ::ForemanSalt::SaltModule, @salt_modules.try(:map, &:id), :label => "" %>
|
16
|
+
<% else -%>
|
17
|
+
<%= multiple_selects f, :salt_module, ::ForemanSalt::SaltModule.where('id NOT IN (?)', @inherited_salt_modules.map(&:id)), @salt_modules.try(:map, &:id), :label => "" %>
|
18
|
+
<% end -%>
|
19
|
+
</div>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Deface::Override.new(:virtual_path => "hosts/_form",
|
2
|
+
:name => "add_salt_modules_tab_to_host",
|
3
|
+
:insert_after => 'li.active',
|
4
|
+
:partial => '../overrides/foreman/salt_modules/host_tab')
|
5
|
+
|
6
|
+
Deface::Override.new(:virtual_path => "hosts/_form",
|
7
|
+
:name => "add_salt_modules_tab_pane_to_host",
|
8
|
+
:insert_before => 'div#puppet_klasses',
|
9
|
+
:partial => '../overrides/foreman/salt_modules/host_tab_pane')
|
10
|
+
|
11
|
+
Deface::Override.new(:virtual_path => "hostgroups/_form",
|
12
|
+
:name => "add_salt_modules_tab_to_hg",
|
13
|
+
:insert_after => 'li.active',
|
14
|
+
:partial => '../overrides/foreman/salt_modules/host_tab')
|
15
|
+
|
16
|
+
Deface::Override.new(:virtual_path => "hostgroups/_form",
|
17
|
+
:name => "add_salt_modules_tab_pane_to_hg",
|
18
|
+
:insert_before => 'div#puppet_klasses',
|
19
|
+
:partial => '../overrides/foreman/salt_modules/host_tab_pane')
|
@@ -0,0 +1,13 @@
|
|
1
|
+
selector_text = "<%= select_f f, :salt_proxy_id, SmartProxy.unscoped.with_features('Salt').with_taxonomy_scope(@location,@organization, :path_ids), :id, :name,
|
2
|
+
{ :include_blank => blank_or_inherit_f(f, :salt_proxy) },
|
3
|
+
{ :label => _('Salt Master') } %>"
|
4
|
+
|
5
|
+
Deface::Override.new(:virtual_path => "hosts/_form",
|
6
|
+
:name => "add_salt_proxy_to_host",
|
7
|
+
:insert_bottom => 'div#primary',
|
8
|
+
:text => selector_text)
|
9
|
+
|
10
|
+
Deface::Override.new(:virtual_path => "hostgroups/_form",
|
11
|
+
:name => "add_salt_proxy_to_hostgroup",
|
12
|
+
:insert_bottom => 'div#primary',
|
13
|
+
:text => selector_text)
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module ForemanSalt
|
2
|
+
class FactImporter < ::FactImporter
|
3
|
+
def fact_name_class
|
4
|
+
ForemanSalt::FactName
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.support_background
|
8
|
+
true
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
attr_accessor :original_facts
|
14
|
+
|
15
|
+
def add_new_facts
|
16
|
+
@counters[:added] = 0
|
17
|
+
add_missing_facts(unsparse(original_facts))
|
18
|
+
logger.debug("Merging facts for '#{host}': added #{@counters[:added]} facts")
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_missing_facts(tree_hash, parent = nil, prefix = '')
|
22
|
+
tree_hash.each do |name, value|
|
23
|
+
name_with_prefix = prefix.empty? ? name : prefix + FactName::SEPARATOR + name
|
24
|
+
|
25
|
+
compose = value.is_a?(Hash)
|
26
|
+
if fact_names[name_with_prefix].present?
|
27
|
+
fact_name_id = fact_names[name_with_prefix]
|
28
|
+
else
|
29
|
+
fact_name_id = fact_name_class.create!(:name => name_with_prefix,
|
30
|
+
:parent_id => parent,
|
31
|
+
:compose => compose).id
|
32
|
+
end
|
33
|
+
|
34
|
+
if compose
|
35
|
+
add_fact(name_with_prefix, nil, fact_name_id)
|
36
|
+
add_missing_facts(value, fact_name_id, name_with_prefix)
|
37
|
+
else
|
38
|
+
add_fact(name_with_prefix, value, fact_name_id)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_fact(name, value, fact_name_id)
|
44
|
+
if facts_to_create.include?(name)
|
45
|
+
host.fact_values.send(method,
|
46
|
+
:value => value, :fact_name_id => fact_name_id)
|
47
|
+
@counters[:added] += 1
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def facts_to_create
|
52
|
+
@facts_to_create ||= facts.keys - db_facts.keys
|
53
|
+
end
|
54
|
+
|
55
|
+
def fact_names
|
56
|
+
@fact_names ||= fact_name_class.maximum(:id, :group => 'name')
|
57
|
+
end
|
58
|
+
|
59
|
+
# if the host does not exists yet, we don't have an host_id to use the fact_values table.
|
60
|
+
def method
|
61
|
+
@method ||= host.new_record? ? :build : :create!
|
62
|
+
end
|
63
|
+
|
64
|
+
def normalize(facts)
|
65
|
+
@original_facts = super(facts)
|
66
|
+
@facts = completify(@original_facts)
|
67
|
+
end
|
68
|
+
|
69
|
+
def completify(hash)
|
70
|
+
new_facts = hash.dup
|
71
|
+
hash.each do |fact_name, value|
|
72
|
+
name_parts = fact_name.split(FactName::SEPARATOR)
|
73
|
+
|
74
|
+
name_parts.inject([]) do |memo, name|
|
75
|
+
memo = memo + [name]
|
76
|
+
key = memo.join(FactName::SEPARATOR)
|
77
|
+
new_facts[key] ||= name_parts == memo ? value : nil
|
78
|
+
memo
|
79
|
+
end
|
80
|
+
end
|
81
|
+
new_facts
|
82
|
+
end
|
83
|
+
|
84
|
+
def sort_by_key(hash)
|
85
|
+
hash.sort_by { |k, v| k.to_s }
|
86
|
+
end
|
87
|
+
|
88
|
+
def sparse(hash, options={})
|
89
|
+
hash.map do |k, v|
|
90
|
+
prefix = (options.fetch(:prefix, [])+[k])
|
91
|
+
next Sparsify::sparse(v, options.merge(:prefix => prefix)) if v.is_a? Hash
|
92
|
+
{ prefix.join(options.fetch(:separator, FactName::SEPARATOR)) => v }
|
93
|
+
end.reduce(:merge) || Hash.new
|
94
|
+
end
|
95
|
+
|
96
|
+
def unsparse(hash, options={})
|
97
|
+
ret = Hash.new
|
98
|
+
sparse(hash).each do |k, v|
|
99
|
+
current = ret
|
100
|
+
key = k.to_s.split(options.fetch(:separator, FactName::SEPARATOR))
|
101
|
+
current = (current[key.shift] ||= Hash.new) until (key.size<=1)
|
102
|
+
current[key.first] = v
|
103
|
+
end
|
104
|
+
return ret
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module ForemanSalt
|
2
|
+
class SmartProxies::SaltKeys
|
3
|
+
|
4
|
+
attr_reader :name, :state, :fingerprint, :smart_proxy_id
|
5
|
+
|
6
|
+
def initialize opts
|
7
|
+
@name, @state, @fingerprint, @smart_proxy_id = opts.flatten
|
8
|
+
end
|
9
|
+
|
10
|
+
class << self
|
11
|
+
|
12
|
+
def all(proxy)
|
13
|
+
raise ::Foreman::Exception.new(N_("Must specify a Smart Proxy to use")) if proxy.nil?
|
14
|
+
|
15
|
+
unless (keys = Rails.cache.read("saltkeys_#{proxy.id}"))
|
16
|
+
api = ProxyAPI::Salt.new({:url => proxy.url})
|
17
|
+
keys = api.key_list.map do |name, properties|
|
18
|
+
new([name.strip, properties['state'], properties['fingerprint'], proxy.id])
|
19
|
+
end.compact
|
20
|
+
|
21
|
+
Rails.cache.write("saltkeys_#{proxy.id}", keys, {:expires_in => 1.minute }) if Rails.env.production?
|
22
|
+
end
|
23
|
+
keys
|
24
|
+
end
|
25
|
+
|
26
|
+
def find(proxy, name)
|
27
|
+
all(proxy).select{|c| c.name == name}.first
|
28
|
+
end
|
29
|
+
|
30
|
+
def find_by_state(proxy, state)
|
31
|
+
all(proxy).select{|c| c.state == state}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def accept
|
36
|
+
raise ::Foreman::Exception.new(N_("unable to re-accept an accepted key")) unless state == "unaccepted"
|
37
|
+
proxy = SmartProxy.find(smart_proxy_id)
|
38
|
+
Rails.cache.delete("saltkeys_#{proxy.id}") if Rails.env.production?
|
39
|
+
ProxyAPI::Salt.new({:url => proxy.url}).key_accept name
|
40
|
+
end
|
41
|
+
|
42
|
+
def reject
|
43
|
+
raise ::Foreman::Exception.new(N_("unable to reject an accepted key")) unless state == "unaccepted"
|
44
|
+
proxy = SmartProxy.find(smart_proxy_id)
|
45
|
+
Rails.cache.delete("saltkeys_#{proxy.id}") if Rails.env.production?
|
46
|
+
ProxyAPI::Salt.new({:url => proxy.url}).key_reject name
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
def delete
|
51
|
+
proxy = SmartProxy.find(smart_proxy_id)
|
52
|
+
Rails.cache.delete("saltkeys_#{proxy.id}") if Rails.env.production?
|
53
|
+
ProxyAPI::Salt.new({:url => proxy.url}).key_delete name
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_param
|
57
|
+
name
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_s
|
61
|
+
name
|
62
|
+
end
|
63
|
+
|
64
|
+
def <=> other
|
65
|
+
self.name <=> other.name
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<%= form_tag url_for(:controller => 'foreman_salt/salt_autosign', :smart_proxy_id => @proxy, :action => 'index') do |f| %>
|
2
|
+
<div class="clearfix">
|
3
|
+
<div class="input">
|
4
|
+
<%= label_tag(_("Name")) %> <%= text_field_tag(:id, "", :size => 60) %> <%= submit_tag _('Save'), :class => "btn btn-success" %>
|
5
|
+
</div>
|
6
|
+
</div>
|
7
|
+
<% end %>
|
8
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<% title _("Autosign entries for %s") % @proxy %>
|
2
|
+
|
3
|
+
<% title_actions display_link_if_authorized(_("Keys"), hash_for_smart_proxy_salt_keys_path), display_link_if_authorized(_("New"), {:controller => 'foreman_salt/salt_autosign', :action => 'new', :smart_proxy_id => @proxy, :auth_object => @proxy, :permission => 'create_smart_proxies_salt_keys'}) %>
|
4
|
+
|
5
|
+
<table class='table table-bordered table-striped table-condensed'>
|
6
|
+
<tr>
|
7
|
+
<th><%= _("Name") %></th>
|
8
|
+
<th></th>
|
9
|
+
</tr>
|
10
|
+
<% @autosign.each do |key| %>
|
11
|
+
<tr>
|
12
|
+
<td><%= key%> </td>
|
13
|
+
<td>
|
14
|
+
<%= action_buttons(display_delete_if_authorized(hash_for_smart_proxy_salt_autosign_path.merge(:smart_proxy_id => @proxy, :id => key), :class => 'delete')) %>
|
15
|
+
</td>
|
16
|
+
</tr>
|
17
|
+
<% end %>
|
18
|
+
</table>
|
19
|
+
|
20
|
+
<%= page_entries_info @autosign %>
|
21
|
+
<%= will_paginate @autosign %>
|
@@ -0,0 +1,34 @@
|
|
1
|
+
<% title _("Salt Keys on %s") % @proxy.name %>
|
2
|
+
<% search_bar _("Filter %s") % salt_keys_state_filter %>
|
3
|
+
|
4
|
+
<% title_actions display_link_if_authorized(_("Autosign"), hash_for_smart_proxy_salt_autosign_index_path) %>
|
5
|
+
|
6
|
+
<table class='table table-bordered table-striped table-condensed'>
|
7
|
+
<tr>
|
8
|
+
<th><%= _("Key Name") %></th>
|
9
|
+
<th><%= _("State") %></th>
|
10
|
+
<th><%= _("Fingerprint") %></th>
|
11
|
+
<th></th>
|
12
|
+
</tr>
|
13
|
+
<% @keys.each do |key| %>
|
14
|
+
<tr>
|
15
|
+
<td><%= key.name %> </td>
|
16
|
+
<td><%= _(key.state.humanize) %></td>
|
17
|
+
<td><%= key.fingerprint %></td>
|
18
|
+
<td>
|
19
|
+
<%= action_buttons(
|
20
|
+
if key.state == "unaccepted"
|
21
|
+
[display_link_if_authorized(_("Accept"), hash_for_smart_proxy_salt_key_accept_path.merge(:state => params[:state], :salt_key_id => key)),
|
22
|
+
display_link_if_authorized(_("Reject"), hash_for_smart_proxy_salt_key_reject_path.merge(:state => params[:state], :salt_key_id => key))]
|
23
|
+
end,
|
24
|
+
display_delete_if_authorized(hash_for_smart_proxy_salt_key_path(:smart_proxy_id => @proxy, :id => key, :state => params[:state], :class => 'delete'))
|
25
|
+
)
|
26
|
+
%>
|
27
|
+
</td>
|
28
|
+
</tr>
|
29
|
+
<% end %>
|
30
|
+
</table>
|
31
|
+
|
32
|
+
<%= page_entries_info @keys %>
|
33
|
+
<%= will_paginate @keys %>
|
34
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<%= form_for @salt_module, :url => (@salt_module.new_record? ? salt_modules_path : salt_module_path(:id => @salt_module.id)) do |f| %>
|
2
|
+
<%= base_errors_for @salt_module %>
|
3
|
+
<ul class="nav nav-tabs" data-tabs="tabs">
|
4
|
+
<li class="active"><a href="#primary" data-toggle="tab"><%= _('Salt Modules') %></a></li>
|
5
|
+
</ul>
|
6
|
+
|
7
|
+
<div class="tab-content">
|
8
|
+
<div class="tab-pane active" id="primary">
|
9
|
+
<%= text_f f, :name %>
|
10
|
+
</div>
|
11
|
+
</div>
|
12
|
+
|
13
|
+
<%= submit_or_cancel f %>
|
14
|
+
<% end %>
|
15
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<% title _("Salt modules") %>
|
2
|
+
|
3
|
+
<% title_actions button_group(display_link_if_authorized(_("New Salt module"), hash_for_new_salt_module_path)) %>
|
4
|
+
|
5
|
+
<table class="table table-bordered table-striped">
|
6
|
+
<tr>
|
7
|
+
<th><%= sort :name, :as => s_("SaltModule|Name") %></th>
|
8
|
+
<th></th>
|
9
|
+
</tr>
|
10
|
+
<% for salt_module in @salt_modules %>
|
11
|
+
<tr>
|
12
|
+
<td><%=link_to_if_authorized h(salt_module.name), hash_for_edit_salt_module_path(:id => salt_module).merge(:auth_object => salt_module, :authorizer => authorizer) %></td>
|
13
|
+
<td>
|
14
|
+
<%= action_buttons(display_delete_if_authorized hash_for_salt_module_path(:id => salt_module).merge(:auth_object => salt_module, :authorizer => authorizer),
|
15
|
+
:confirm => _("Delete %s?") % salt_module.name) %>
|
16
|
+
</td>
|
17
|
+
</tr>
|
18
|
+
<% end %>
|
19
|
+
</table>
|
20
|
+
|
21
|
+
<%= page_entries_info @salt_modules %>
|
22
|
+
<%= will_paginate @salt_modules %>
|
23
|
+
|
data/config/routes.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
Rails.application.routes.draw do
|
2
|
+
|
3
|
+
scope :salt, :path => '/salt' do
|
4
|
+
match "/node/:name" => 'hosts#salt_external_node', :constraints => { :name => /[^\.][\w\.-]+/ }
|
5
|
+
|
6
|
+
resources :salt_modules, :controller => 'foreman_salt/salt_modules' do
|
7
|
+
collection do
|
8
|
+
get 'auto_complete_search'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
constraints(:smart_proxy_id => /[^\/]+/) do
|
14
|
+
resources :smart_proxies, :except => [:show] do
|
15
|
+
constraints(:id => /[^\/]+/) do
|
16
|
+
resources :salt_autosign, :only => [:index, :destroy, :create, :new], :controller => 'foreman_salt/salt_autosign'
|
17
|
+
resources :salt_keys, :only => [:index, :destroy], :controller => 'foreman_salt/salt_keys' do
|
18
|
+
get :accept
|
19
|
+
get :reject
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
constraints(:id => /[^\/]+/) do
|
26
|
+
resources :hosts do
|
27
|
+
member do
|
28
|
+
get :saltrun
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class AddSaltProxyToHostAndHostGroup < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
add_column :hosts, :salt_proxy_id, :integer
|
4
|
+
add_column :hostgroups, :salt_proxy_id, :integer
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.down
|
8
|
+
remove_column :hosts, :salt_proxy_id
|
9
|
+
remove_column :hostgroups, :salt_proxy_id
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class CreateSaltModules < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :salt_modules do |t|
|
4
|
+
t.string :name, :default => "", :null => false
|
5
|
+
t.timestamps
|
6
|
+
end
|
7
|
+
|
8
|
+
create_table 'hosts_salt_modules', :id => false do |t|
|
9
|
+
t.column :host_id, :integer
|
10
|
+
t.column :salt_module_id, :integer
|
11
|
+
end
|
12
|
+
|
13
|
+
add_index :salt_modules, :name, :unique => true
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.down
|
17
|
+
drop_table :salt_modules
|
18
|
+
drop_table :hosts_salt_modules
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class AddSaltModulesToHostgroups < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table 'hostgroups_salt_modules', :id => false do |t|
|
4
|
+
t.column :hostgroup_id, :integer
|
5
|
+
t.column :salt_module_id, :integer
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.down
|
10
|
+
drop_table :hostgroups_salt_modules
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Create feature for Smart Proxy
|
2
|
+
Feature.find_or_create_by_name("Salt")
|
3
|
+
|
4
|
+
# Seed our permissions
|
5
|
+
permissions = [
|
6
|
+
%w(Host saltrun_hosts),
|
7
|
+
%w(Host view_hosts),
|
8
|
+
%w(ForemanSalt::SaltModule create_salt_modules),
|
9
|
+
%w(ForemanSalt::SaltModule view_salt_modules),
|
10
|
+
%w(ForemanSalt::SaltModule edit_salt_modules),
|
11
|
+
%w(ForemanSalt::SaltModule destroy_salt_modules),
|
12
|
+
%w(SmartProxy view_smart_proxies_salt_keys),
|
13
|
+
%w(SmartProxy destroy_smart_proxies_salt_keys),
|
14
|
+
%w(SmartProxy edit_smart_proxies_salt_keys),
|
15
|
+
%w(SmartProxy destroy_smart_proxies_salt_autosign),
|
16
|
+
%w(SmartProxy create_smart_proxies_salt_autosign),
|
17
|
+
%w(SmartProxy view_smart_proxies_salt_autosign),
|
18
|
+
]
|
19
|
+
|
20
|
+
permissions.each do |resource, permission|
|
21
|
+
Permission.find_or_create_by_resource_type_and_name resource, permission
|
22
|
+
end
|
23
|
+
|
24
|
+
# Add new viewing permissions to Viewer role
|
25
|
+
viewer = Role.find_by_name('Viewer')
|
26
|
+
|
27
|
+
if viewer
|
28
|
+
[:view_smart_proxies_salt_keys, :view_smart_proxies_salt_autosign, :view_salt_modules].each do |permission|
|
29
|
+
viewer.add_permissions!([permission]) unless viewer.permissions.include? Permission.find_by_name(permission)
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'deface'
|
2
|
+
|
3
|
+
module ForemanSalt
|
4
|
+
class Engine < ::Rails::Engine
|
5
|
+
|
6
|
+
config.autoload_paths += Dir["#{config.root}/app/controllers/concerns"]
|
7
|
+
config.autoload_paths += Dir["#{config.root}/app/helpers/concerns"]
|
8
|
+
config.autoload_paths += Dir["#{config.root}/app/models/concerns"]
|
9
|
+
config.autoload_paths += Dir["#{config.root}/app/overrides"]
|
10
|
+
config.autoload_paths += Dir["#{config.root}/app/services"]
|
11
|
+
config.autoload_paths += Dir["#{config.root}/app/lib"]
|
12
|
+
|
13
|
+
# Add any db migrations
|
14
|
+
initializer "foreman_salt.load_app_instance_data" do |app|
|
15
|
+
app.config.paths['db/migrate'] += ForemanSalt::Engine.paths['db/migrate'].existent
|
16
|
+
end
|
17
|
+
|
18
|
+
initializer 'foreman_salt.register_plugin', :after=> :finisher_hook do |app|
|
19
|
+
Foreman::Plugin.register :foreman_salt do
|
20
|
+
requires_foreman '>= 1.6'
|
21
|
+
|
22
|
+
menu :top_menu, :salt,
|
23
|
+
:url_hash => {:controller => :'foreman_salt/salt_modules', :action => :index },
|
24
|
+
:caption => 'Modules',
|
25
|
+
:parent => :configure_menu,
|
26
|
+
:after => :common_parameters
|
27
|
+
|
28
|
+
divider :top_menu, :parent => :configure_menu,
|
29
|
+
:caption => "Salt",
|
30
|
+
:after => :common_parameters
|
31
|
+
|
32
|
+
security_block :hosts do |map|
|
33
|
+
permission :saltrun_hosts, {:hosts => [:saltrun]}, :resource_type => 'Host'
|
34
|
+
permission :view_hosts, {:hosts => [:salt_external_node]}, :resource_type => 'Host'
|
35
|
+
end
|
36
|
+
|
37
|
+
security_block :salt_modules do |map|
|
38
|
+
permission :create_salt_modules, {:'foreman_salt/salt_modules' => [:new, :create]}, :resource_type => "ForemanSalt::SaltModule"
|
39
|
+
permission :view_salt_modules, {:'foreman_salt/salt_modules' => [:index, :show, :auto_complete_search]}, :resource_type => "ForemanSalt::SaltModule"
|
40
|
+
permission :edit_salt_modules, {:'foreman_salt/salt_modules' => [:update, :edit]},:resource_type => "ForemanSalt::SaltModule"
|
41
|
+
permission :destroy_salt_modules, {:'foreman_salt/salt_modules' => [:destroy]}, :resource_type => "ForemanSalt::SaltModule"
|
42
|
+
end
|
43
|
+
|
44
|
+
security_block :salt_keys do |map|
|
45
|
+
permission :view_smart_proxies_salt_keys, {:'foreman_salt/salt_keys' => [:index]}, :resource_type => "SmartProxy"
|
46
|
+
permission :destroy_smart_proxies_salt_keys, {:'foreman_salt/salt_keys' => [:destroy]},:resource_type => "SmartProxy"
|
47
|
+
permission :edit_smart_proxies_salt_keys, {:'foreman_salt/salt_keys' => [:accept, :reject]}, :resource_type => "SmartProxy"
|
48
|
+
end
|
49
|
+
|
50
|
+
security_block :salt_autosign do |map|
|
51
|
+
permission :destroy_smart_proxies_salt_autosign, {:'foreman_salt/salt_autosign' => [:destroy]}, :resource_type => "SmartProxy"
|
52
|
+
permission :create_smart_proxies_salt_autosign, {:'foreman_salt/salt_autosign' => [:new, :create]}, :resource_type => "SmartProxy"
|
53
|
+
permission :view_smart_proxies_salt_autosign, {:'foreman_salt/salt_autosign' => [:index]}, :resource_type => "SmartProxy"
|
54
|
+
end
|
55
|
+
|
56
|
+
role "Salt admin", [:saltrun_hosts, :create_salt_modules, :view_salt_modules, :edit_salt_modules, :destroy_salt_modules,
|
57
|
+
:view_smart_proxies_salt_keys, :destroy_smart_proxies_salt_keys, :edit_smart_proxies_salt_keys,
|
58
|
+
:create_smart_proxies_salt_autosign, :view_smart_proxies_salt_autosign, :destroy_smart_proxies_salt_autosign]
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
config.to_prepare do
|
64
|
+
begin
|
65
|
+
::FactImporter.register_fact_importer(:foreman_salt, ForemanSalt::FactImporter)
|
66
|
+
|
67
|
+
# Helper Extensions
|
68
|
+
::HostsHelper.send :include, ForemanSalt::HostsHelperExtensions
|
69
|
+
::SmartProxiesHelper.send :include, ForemanSalt::SmartProxiesHelperExtensions
|
70
|
+
|
71
|
+
# Model Extensions
|
72
|
+
::Host::Managed.send :include, ForemanSalt::Concerns::HostManagedExtensions
|
73
|
+
::Host::Managed.send :include, ForemanSalt::Orchestration::Salt
|
74
|
+
::Hostgroup.send :include, ForemanSalt::Concerns::HostgroupExtensions
|
75
|
+
|
76
|
+
# Controller Extensions
|
77
|
+
::UnattendedController.send :include, ForemanSalt::Concerns::UnattendedControllerExtensions
|
78
|
+
::HostsController.send :include, ForemanSalt::Concerns::HostsControllerExtensions
|
79
|
+
::HostsController.send :include, ForemanSalt::Concerns::SmartProxyAuthExtensions
|
80
|
+
::HostgroupsController.send :include, ForemanSalt::Concerns::HostgroupsControllerExtensions
|
81
|
+
|
82
|
+
# API Extensions
|
83
|
+
::Api::V2::HostsController.send :include, ForemanSalt::Concerns::SmartProxyAuthExtensions
|
84
|
+
::Api::V2::ReportsController.send :include, ForemanSalt::Concerns::SmartProxyAuthExtensions
|
85
|
+
rescue => e
|
86
|
+
puts "ForemanSalt: skipping engine hook (#{e.to_s})"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/foreman_salt.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Tasks
|
2
|
+
namespace :foreman_salt do
|
3
|
+
namespace :example do
|
4
|
+
desc 'Example Task'
|
5
|
+
task :task => :environment do
|
6
|
+
# Task goes here
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# Tests
|
12
|
+
namespace :test do
|
13
|
+
desc "Test ForemanSalt"
|
14
|
+
Rake::TestTask.new(:foreman_salt) do |t|
|
15
|
+
test_dir = File.join(File.dirname(__FILE__), '../..', 'test')
|
16
|
+
t.libs << ["test",test_dir]
|
17
|
+
t.pattern = "#{test_dir}/**/*_test.rb"
|
18
|
+
t.verbose = true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
Rake::Task[:test].enhance do
|
23
|
+
Rake::Task['test:foreman_salt'].invoke
|
24
|
+
end
|
25
|
+
|
26
|
+
load 'tasks/jenkins.rake'
|
27
|
+
if Rake::Task.task_defined?(:'jenkins:setup')
|
28
|
+
Rake::Task["jenkins:unit"].enhance do
|
29
|
+
Rake::Task['test:foreman_salt'].invoke
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
FactoryGirl.define do
|
2
|
+
factory :smart_proxy, :class => "::SmartProxy" do
|
3
|
+
sequence(:name) { |n| "proxy#{n}" }
|
4
|
+
sequence(:url) { |n| "http://proxy#{n}.example.com:9090" }
|
5
|
+
|
6
|
+
trait :with_salt_feature do
|
7
|
+
features { [::Feature.find_or_create_by_name('Salt')] }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
factory :salt_module, :class => "ForemanSalt::SaltModule" do
|
12
|
+
sequence(:name) { |n| "module#{n}" }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
FactoryGirl.modify do
|
17
|
+
factory :host do
|
18
|
+
trait :with_salt_proxy do
|
19
|
+
salt_proxy { FactoryGirl.create :smart_proxy, :with_salt_feature }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
factory :hostgroup do
|
24
|
+
trait :with_salt_proxy do
|
25
|
+
salt_proxy { FactoryGirl.create :smart_proxy, :with_salt_feature }
|
26
|
+
end
|
27
|
+
|
28
|
+
trait :with_salt_modules do
|
29
|
+
salt_modules { FactoryGirl.create_list :salt_module, 10 }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'test_plugin_helper'
|
2
|
+
|
3
|
+
class HostsControllerTest < ActionController::TestCase
|
4
|
+
test 'salt smart proxy should get salt external node' do
|
5
|
+
User.current = nil
|
6
|
+
Setting[:restrict_registered_puppetmasters] = true
|
7
|
+
Setting[:require_ssl_puppetmasters] = false
|
8
|
+
|
9
|
+
|
10
|
+
proxy = FactoryGirl.create :smart_proxy, :with_salt_feature
|
11
|
+
Resolv.any_instance.stubs(:getnames).returns([proxy.to_s])
|
12
|
+
|
13
|
+
host = FactoryGirl.create :host
|
14
|
+
get :salt_external_node, {:name => host.name, :format => "yml"}
|
15
|
+
assert_response :success
|
16
|
+
end
|
17
|
+
end
|