foreman_salt 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|