chef-rundeck 2.0.0 → 2.1.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9ce7ece53bc0c72a4fe221d30e8babef9077dacf
4
- data.tar.gz: 98f8966b2cda5a2eeb852333e7c409af35c6bfbb
3
+ metadata.gz: acd9c147df627c29f79ec208b7ad4796a1933c81
4
+ data.tar.gz: 312f65e10a495d38b4e752fefef49099073b872a
5
5
  SHA512:
6
- metadata.gz: adb94d82eab00bc70ee9c8ae0a9a21c269f4cd27a27cc4620c66e85777760ca39862e5d89b20b59465beca6255ea6a058d727d4845939ab6c18e0ebf68f07943
7
- data.tar.gz: f522ced6479217a1ff953757f80dae46241bc9cfad96b3b923babd4dddb05c14c558ae66a917ab715d9a3f0eb2717a445bc876d5c6a8db2d4c4ff7fb08e5a6fd
6
+ metadata.gz: 6427c173c17c256172eb7b81c67047aa724d91e34004c2ddb3873aade19844977dbeebe38952964ac36cd14047b77cbfc2b4a863bc2aeeb9ebf9f704ba8faeb9
7
+ data.tar.gz: 207ed6aaaf9fad065ec4c06a44e7b9791d766569fcf59c8a8991d4dce4ffd8a3019f6fce8d1021d61ac780d571005ffc4ee9ac63429c39348fa8b5c26a96b654
@@ -75,6 +75,23 @@ class ChefRundeckCLI
75
75
  :long => "--pidfile FILE",
76
76
  :description => "Prefix for our PID file, default: /var/run/chef-rundeck- ('PORT.pid' will be added)",
77
77
  :default => "/var/run/chef-rundeck"
78
+
79
+ option :env,
80
+ :short => "-e ENV",
81
+ :long => "--env ENV",
82
+ :description => "Sets the environment for chef-rundeck to execute under. Use 'development' for more logging.",
83
+ :default => "production"
84
+
85
+ option :cache_timeout,
86
+ :short => "-t CACHE_TIMEOUT",
87
+ :long => "--timeout CACHE_TIMEOUT",
88
+ :description => "Sets the response timeout in seconds for the query data.",
89
+ :default => "30"
90
+
91
+ option :partial_search,
92
+ :long => "--partial-search TRUE/FALSE",
93
+ :description => "Enables partial searching, Chef 11 or greater.",
94
+ :default => "false"
78
95
  end
79
96
 
80
97
  cli = ChefRundeckCLI.new
@@ -86,6 +103,9 @@ ChefRundeck.web_ui_url = cli.config[:web_ui_url]
86
103
  ChefRundeck.api_url = cli.config[:api_url]
87
104
  ChefRundeck.client_key = cli.config[:client_key]
88
105
  ChefRundeck.project_config = cli.config[:project_config]
106
+ ChefRundeck.environment = cli.config[:env].to_sym
107
+ ChefRundeck.cache_timeout = cli.config[:cache_timeout].to_i
108
+ ChefRundeck.partial_search = cli.config[:partial_search] == "true"
89
109
  ChefRundeck.configure
90
110
 
91
111
  begin
@@ -19,6 +19,11 @@ require 'chef'
19
19
  require 'chef/node'
20
20
  require 'chef/mixin/xml_escape'
21
21
  require 'chef/rest'
22
+ require 'chef/role'
23
+ require 'chef/environment'
24
+ require 'chef/data_bag'
25
+ require 'chef/data_bag_item'
26
+ require 'partial_search'
22
27
 
23
28
  REQUIRED_ATTRS = [ :kernel, :fqdn, :platform, :platform_version ]
24
29
 
@@ -40,6 +45,8 @@ class ChefRundeck < Sinatra::Base
40
45
  attr_accessor :api_url
41
46
  attr_accessor :client_key
42
47
  attr_accessor :project_config
48
+ attr_accessor :cache_timeout
49
+ attr_accessor :partial_search
43
50
 
44
51
  def configure
45
52
  Chef::Config.from_file(ChefRundeck.config_file)
@@ -53,6 +60,7 @@ class ChefRundeck < Sinatra::Base
53
60
  ChefRundeck.client_key = Chef::Config[:client_key]
54
61
  end
55
62
 
63
+
56
64
  if (File.exists?(ChefRundeck.project_config)) then
57
65
  Chef::Log.info("Using JSON project file #{ChefRundeck.project_config}")
58
66
  projects = File.open(ChefRundeck.project_config, "r") { |f| JSON.parse(f.read) }
@@ -60,45 +68,99 @@ class ChefRundeck < Sinatra::Base
60
68
  get "/#{project}" do
61
69
  content_type 'text/xml'
62
70
  Chef::Log.info("Loading nodes for /#{project}")
63
- response = build_project projects[project]['pattern'], projects[project]['username'], (projects[project]['hostname'].nil? ? "fqdn" : projects[project]['hostname']), projects[project]['attributes']
64
- response
71
+ send_file build_project project, projects[project]['pattern'], projects[project]['username'], (projects[project]['hostname'].nil? ? "fqdn" : projects[project]['hostname']), projects[project]['attributes']
65
72
  end
73
+ cache_file = "#{Dir.tmpdir}/chef-rundeck-#{project}.xml"
74
+ at_exit { File.delete(cache_file) if File.exist?(cache_file) }
66
75
  end
67
76
  end
68
77
 
69
78
  get '/' do
70
79
  content_type 'text/xml'
71
80
  Chef::Log.info("Loading all nodes for /")
72
- response = build_project
73
- response
81
+ send_file build_project
74
82
  end
83
+
84
+ cache_file = "#{Dir.tmpdir}/chef-rundeck-default.xml"
85
+ at_exit { File.delete(cache_file) if File.exist?(cache_file) }
75
86
  end
76
87
  end
77
88
 
78
- def build_project (pattern="*:*", username=ChefRundeck.username, hostname="fqdn", custom_attributes=nil)
79
- response = '<?xml version="1.0" encoding="UTF-8"?>'
80
- response << '<!DOCTYPE project PUBLIC "-//DTO Labs Inc.//DTD Resources Document 1.0//EN" "project.dtd">'
81
- response << '<project>'
89
+ def build_project (project="default", pattern="*:*", username=ChefRundeck.username, hostname="fqdn", custom_attributes=nil)
90
+ response = nil
91
+ begin
82
92
 
83
- q = Chef::Search::Query.new
84
- q.search("node",pattern) do |node|
85
-
86
- begin
87
- if node_is_valid? node
88
- response << build_node(node, username, hostname, custom_attributes)
89
- else
90
- Chef::Log.warn("invalid node element: #{node.inspect}")
93
+ # file is too new use it again
94
+ if (File.exists?("#{Dir.tmpdir}/chef-rundeck-#{project}.xml") && (Time.now - File.atime("#{Dir.tmpdir}/chef-rundeck-#{project}.xml") < ChefRundeck.cache_timeout)) then
95
+ return "#{Dir.tmpdir}/chef-rundeck-#{project}.xml"
91
96
  end
92
- rescue Exception => e
93
- Chef::Log.error("=== could not generate xml for Node: #{node.name} - #{e.message}")
94
- Chef::Log.debug(e.backtrace.join('\n'))
97
+
98
+ results = []
99
+ if ChefRundeck.partial_search then
100
+ keys = { 'name' => ['name'],
101
+ 'kernel_machine' => [ 'kernel', 'machine' ],
102
+ 'kernel_os' => [ 'kernel', 'os' ],
103
+ 'fqdn' => [ 'fqdn' ],
104
+ 'run_list' => [ 'run_list' ],
105
+ 'roles' => [ 'roles' ],
106
+ 'recipes' => [ 'recipes' ],
107
+ 'chef_environment' => [ 'chef_environment' ],
108
+ 'platform' => [ 'platform'],
109
+ 'platform_version' => [ 'platform_version' ],
110
+ 'hostname' => [hostname]
111
+ }
112
+ if !custom_attributes.nil? then
113
+ custom_attributes.each do |attr|
114
+ attr_name = attr.gsub('.', '_')
115
+ attr_value = attr.split('.')
116
+ keys[attr_name] = attr_value
117
+ end
118
+ end
119
+ # do search
120
+ Chef::Log.info("partial search started (project: '#{project}')")
121
+ results = partial_search(:node,pattern, :keys => keys)
122
+ Chef::Log.info("partial search finshed (project: '#{project}', count: #{results.length})")
123
+ else
124
+ q = Chef::Search::Query.new
125
+ Chef::Log.info("search started (project: '#{project}')")
126
+ results = q.search("node",pattern)[0]
127
+ Chef::Log.info("search finshed (project: '#{project}', count: #{results.length})")
128
+ results = convert_results(results, hostname, custom_attributes)
95
129
  end
130
+
131
+ response = File.open("#{Dir.tmpdir}/chef-rundeck-#{project}.xml", 'w')
132
+ response.write '<?xml version="1.0" encoding="UTF-8"?>'
133
+ response.write '<!DOCTYPE project PUBLIC "-//DTO Labs Inc.//DTD Resources Document 1.0//EN" "project.dtd">'
134
+ response.write '<project>'
135
+
136
+ Chef::Log.info("building nodes (project: '#{project}')")
137
+ failed = 0
138
+ results.each do |node|
139
+ begin
140
+ # validate the node
141
+ begin
142
+ node_is_valid? node
143
+ rescue ArgumentError => ae
144
+ Chef::Log.warn("invalid node element: #{ae}")
145
+ failed = failed +1
146
+ next
147
+ end
148
+
149
+ #write the node to the project
150
+ response.write build_node(node, username, hostname, custom_attributes)
151
+ rescue Exception => e
152
+ Chef::Log.error("=== could not generate xml for #{node}: #{e.message}")
153
+ Chef::Log.debug(e.backtrace.join('\n'))
154
+ end
155
+ end
156
+ Chef::Log.info("nodes complete (project: '#{project}', total: #{results.length - failed}, failed: #{failed})")
157
+
158
+ response.write "</project>"
159
+ Chef::Log.debug(response)
160
+ ensure
161
+ response.close unless response == nil
96
162
  end
97
-
98
- response << "</project>"
99
- Chef::Log.debug(response)
100
-
101
- return response
163
+ return response.path
102
164
  end
103
165
  end
104
166
 
@@ -107,41 +169,41 @@ def build_node (node, username, hostname, custom_attributes)
107
169
  # Certain features in Rundeck require the osFamily value to be set to 'unix' to work appropriately. - SRK
108
170
  #++
109
171
  data = ''
110
- os_family = node[:kernel][:os] =~ /winnt|windows/i ? 'winnt' : 'unix'
111
- nodeexec = node[:kernel][:os] =~ /winnt|windows/i ? "node-executor=\"overthere-winrm\"" : ''
172
+ os_family = node['kernel_os'] =~ /winnt|windows/i ? 'winnt' : 'unix'
173
+ nodeexec = node['kernel_os'] =~ /winnt|windows/i ? "node-executor=\"overthere-winrm\"" : ''
112
174
  data << <<-EOH
113
- <node name="#{xml_escape(node[:fqdn])}" #{nodeexec}
175
+ <node name="#{xml_escape(node['fqdn'])}" #{nodeexec}
114
176
  type="Node"
115
- description="#{xml_escape(node.name)}"
116
- osArch="#{xml_escape(node[:kernel][:machine])}"
177
+ description="#{xml_escape(node['name'])}"
178
+ osArch="#{xml_escape(node['kernel_machine'])}"
117
179
  osFamily="#{xml_escape(os_family)}"
118
- osName="#{xml_escape(node[:platform])}"
119
- osVersion="#{xml_escape(node[:platform_version])}"
120
- tags="#{xml_escape(node.run_list.roles.concat(node.run_list.recipes).join(',') + ',' + node.chef_environment)}"
121
- roles="#{xml_escape(node.run_list.roles.join(','))}"
122
- recipes="#{xml_escape(node.run_list.recipes.join(','))}"
123
- environment="#{xml_escape(node.chef_environment)}"
180
+ osName="#{xml_escape(node['platform'])}"
181
+ osVersion="#{xml_escape(node['platform_version'])}"
182
+ roles="#{xml_escape(node['roles'].join(','))}"
183
+ recipes="#{xml_escape(node['recipes'].join(','))}"
184
+ tags="#{xml_escape(node['roles'].concat(node['recipes']).join(',') + ',' + node['chef_environment'])}"
185
+ environment="#{xml_escape(node['chef_environment'])}"
124
186
  username="#{xml_escape(username)}"
125
- hostname="#{xml_escape(node[hostname])}"
126
- editUrl="#{xml_escape(ChefRundeck.web_ui_url)}/nodes/#{xml_escape(node.name)}/edit" #{custom_attributes.nil? ? '/': ''}>
187
+ hostname="#{xml_escape(node['hostname'])}"
188
+ editUrl="#{xml_escape(ChefRundeck.web_ui_url)}/nodes/#{xml_escape(node['name'])}/edit" #{custom_attributes.nil? ? '/': ''}>
127
189
  EOH
128
190
  if !custom_attributes.nil? then
129
191
  custom_attributes.each do |attr|
130
- attr_name = attr
131
- attr_value = get_custom_attr(node, attr.split('.'))
132
- data << <<-EOH
192
+ attr_name = attr
193
+ attr_value = node[attr.gsub('.','_')]
194
+ data << <<-EOH
133
195
  <attribute name="#{attr_name}"><![CDATA[#{attr_value}]]></attribute>
134
196
  EOH
135
- end
136
- data << "</node>"
137
- end
197
+ end
198
+ data << "</node>"
199
+ end
138
200
 
139
201
  return data
140
202
  end
141
203
 
142
204
  def get_custom_attr (obj, params)
143
205
  value = obj
144
- Chef::Log.debug("loading custom attributes for node: #{obj} with #{params}")
206
+ Chef::Log.debug("loading custom attributes for node: #{obj['name']} with #{params}")
145
207
  params.each do |p|
146
208
  value = value[p.to_sym]
147
209
  if value.nil? then
@@ -151,13 +213,113 @@ def get_custom_attr (obj, params)
151
213
  return value.nil? ? "" : value.to_s
152
214
  end
153
215
 
216
+ # Convert results to be compatiable with Chef 11 format
217
+ def convert_results(results, hostname, custom_attributes)
218
+ new_results = []
219
+ results.each do |node|
220
+ n = {}
221
+ n['name'] = node.name
222
+ n['chef_environment'] = node.chef_environment
223
+ n['run_list'] = node.run_list
224
+ n['recipes'] = !node.run_list.nil? ? node.run_list.recipes : nil
225
+ n['roles'] = !node.run_list.nil? ? node.run_list.roles : nil
226
+ n['fqdn'] = node['fqdn']
227
+ n['hostname'] = node[hostname.to_sym]
228
+ n['kernel_machine'] = !node['kernel'].nil? ? node['kernel']['machine'] : nil
229
+ n['kernel_os'] = !node['kernel'].nil? ? node['kernel']['os'] : nil
230
+ n['platform'] = node['platform']
231
+ n['platform_version'] = node['platform_version']
232
+
233
+ if !custom_attributes.nil? then
234
+ custom_attributes.each do |attr|
235
+ ps_name = attr.gsub('.','_')
236
+ n[ps_name] = get_custom_attr(node, attr.split('.'))
237
+ end
238
+ end
239
+ new_results << n
240
+ end
241
+ return new_results
242
+ end
243
+
244
+
245
+ # Helper def to validate the node
154
246
  def node_is_valid?(node)
155
- node[:fqdn] and
156
- node.name and
157
- node[:kernel] and
158
- node[:kernel][:machine] and
159
- node[:kernel][:os] and
160
- node[:platform] and
161
- node[:platform_version] and
162
- node.chef_environment
247
+ raise ArgumentError, "#{node} missing 'name'" if !node['name']
248
+ raise ArgumentError, "#{node} missing 'chef_environment'" if !node['chef_environment']
249
+ raise ArgumentError, "#{node} missing 'run_list'" if !node['run_list']
250
+ raise ArgumentError, "#{node} missing 'recipes'" if !node['recipes']
251
+ raise ArgumentError, "#{node} missing 'roles'" if !node['roles']
252
+ raise ArgumentError, "#{node} missing 'fqdn'" if !node['fqdn']
253
+ raise ArgumentError, "#{node} missing 'hostname'" if !node['hostname']
254
+ raise ArgumentError, "#{node} missing 'kernel.machine'" if !node['kernel_machine']
255
+ raise ArgumentError, "#{node} missing 'kernel.os'" if !node['kernel_os']
256
+ raise ArgumentError, "#{node} missing 'platform'" if !node['platform']
257
+ raise ArgumentError, "#{node} missing 'platform_version'" if !node['platform_version']
258
+ end
259
+
260
+
261
+ # partial_search(type, query, options, &block)
262
+ #
263
+ # Searches for nodes, roles, etc. and returns the results. This method may
264
+ # perform more than one search request, if there are a large number of results.
265
+ #
266
+ # ==== Parameters
267
+ # * +type+: index type (:role, :node, :client, :environment, data bag name)
268
+ # * +query+: SOLR query. "*:*", "role:blah", "not role:blah", etc. Defaults to '*:*'
269
+ # * +options+: hash with options:
270
+ # ** +:start+: First row to return (:start => 50, :rows => 100 means "return the
271
+ # 50th through 150th result")
272
+ # ** +:rows+: Number of rows to return. Defaults to 1000.
273
+ # ** +:sort+: a SOLR sort specification. Defaults to 'X_CHEF_id_CHEF_X asc'.
274
+ # ** +:keys+: partial search keys. If this is not specified, the search will
275
+ # not be partial.
276
+ #
277
+ # ==== Returns
278
+ #
279
+ # This method returns an array of search results. Partial search results will
280
+ # be JSON hashes with the structure specified in the +keys+ option. Other
281
+ # results include +Chef::Node+, +Chef::Role+, +Chef::Client+, +Chef::Environment+,
282
+ # +Chef::DataBag+ and +Chef::DataBagItem+ objects, depending on the search type.
283
+ #
284
+ # If a block is specified, the block will be called with each result instead of
285
+ # returning an array. This method will not block if it returns
286
+ #
287
+ # If start or row is specified, and no block is given, the result will be a
288
+ # triple containing the list, the start and total:
289
+ #
290
+ # [ [ row1, row2, ... ], start, total ]
291
+ #
292
+ # ==== Example
293
+ #
294
+ # partial_search(:node, 'role:webserver',
295
+ # keys: {
296
+ # name: [ 'name' ],
297
+ # ip: [ 'amazon', 'ip', 'public' ]
298
+ # }
299
+ # ).each do |node|
300
+ # puts "#{node[:name]}: #{node[:ip]}"
301
+ # end
302
+ #
303
+ def partial_search(type, query='*:*', *args, &block)
304
+ # Support both the old (positional args) and new (hash args) styles of calling
305
+ if args.length == 1 && args[0].is_a?(Hash)
306
+ args_hash = args[0]
307
+ else
308
+ args_hash = {}
309
+ args_hash[:sort] = args[0] if args.length >= 1
310
+ args_hash[:start] = args[1] if args.length >= 2
311
+ args_hash[:rows] = args[2] if args.length >= 3
312
+ end
313
+ # If you pass a block, or have the start or rows arguments, do raw result parsing
314
+ if Kernel.block_given? || args_hash[:start] || args_hash[:rows]
315
+ PartialSearch.new.search(type, query, args_hash, &block)
316
+
317
+ # Otherwise, do the iteration for the end user
318
+ else
319
+ results = Array.new
320
+ PartialSearch.new.search(type, query, args_hash) do |o|
321
+ results << o
322
+ end
323
+ results
324
+ end
163
325
  end
@@ -0,0 +1,80 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Author:: John Keiser (<jkeiser@opscode.com>)
4
+ # Copyright:: Copyright (c) 2012 Opscode, Inc.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require 'chef/config'
21
+ require 'uri'
22
+ require 'chef/rest'
23
+ # These are needed so that JSON can inflate search results
24
+ require 'chef/node'
25
+ require 'chef/role'
26
+ require 'chef/environment'
27
+ require 'chef/data_bag'
28
+ require 'chef/data_bag_item'
29
+
30
+ class PartialSearch
31
+
32
+ attr_accessor :rest
33
+
34
+ def initialize(url=nil)
35
+ @rest = ::Chef::REST.new(url || ::Chef::Config[:chef_server_url])
36
+ end
37
+
38
+ # Search Solr for objects of a given type, for a given query. If you give
39
+ # it a block, it will handle the paging for you dynamically.
40
+ def search(type, query='*:*', args={}, &block)
41
+ raise ArgumentError, "Type must be a string or a symbol!" unless (type.kind_of?(String) || type.kind_of?(Symbol))
42
+
43
+ sort = args.include?(:sort) ? args[:sort] : 'X_CHEF_id_CHEF_X asc'
44
+ start = args.include?(:start) ? args[:start] : 0
45
+ rows = args.include?(:rows) ? args[:rows] : 1000
46
+ query_string = "search/#{type}?q=#{escape(query)}&sort=#{escape(sort)}&start=#{escape(start)}&rows=#{escape(rows)}"
47
+ if args[:keys]
48
+ response = @rest.post_rest(query_string, args[:keys])
49
+ response_rows = response['rows'].map { |row| row['data'] }
50
+ else
51
+ response = @rest.get_rest(query_string)
52
+ response_rows = response['rows']
53
+ end
54
+ if block
55
+ response_rows.each { |o| block.call(o) unless o.nil?}
56
+ unless (response["start"] + response_rows.length) >= response["total"]
57
+ nstart = response["start"] + rows
58
+ args_hash = {
59
+ :keys => args[:keys],
60
+ :sort => sort,
61
+ :start => nstart,
62
+ :rows => rows
63
+ }
64
+ search(type, query, args_hash, &block)
65
+ end
66
+ true
67
+ else
68
+ [ response_rows, response["start"], response["total"] ]
69
+ end
70
+ end
71
+
72
+ def list_indexes
73
+ response = @rest.get_rest("search")
74
+ end
75
+
76
+ private
77
+ def escape(s)
78
+ s && URI.escape(s.to_s)
79
+ end
80
+ end
@@ -8,6 +8,8 @@ describe 'ChefRundeck' do
8
8
  ChefRundeck.username = ENV['USER']
9
9
  ChefRundeck.web_ui_url = 'https://manage.opscode.com'
10
10
  ChefRundeck.project_config = "#{ENV['TRAVIS_BUILD_DIR']}/spec/support/chef-rundeck.json"
11
+ ChefRundeck.cache_timeout = 0
12
+ ChefRundeck.environment = :development
11
13
  ChefRundeck.configure
12
14
  end
13
15
  it 'fetch to root should return 200' do
@@ -24,6 +26,16 @@ describe 'ChefRundeck' do
24
26
  Nokogiri::XML(last_response.body).xpath("//project/node[@name='node1.chefrundeck.local']").length().should == 1
25
27
  Nokogiri::XML(last_response.body).xpath("//project/node[@name='node2.chefrundeck.local']").length().should == 0
26
28
  end
29
+ it 'fetched document should be node1 only verify hostname override' do
30
+ get '/node1_systems'
31
+ last_response.should be_ok
32
+ Nokogiri::XML(last_response.body).xpath("//project/node[@name='node1.chefrundeck.local']/@hostname").text().should == "10.0.0.1"
33
+ end
34
+ it 'fetched document should be node2 only verify hostname' do
35
+ get '/node2_systems'
36
+ last_response.should be_ok
37
+ Nokogiri::XML(last_response.body).xpath("//project/node[@name='node2.chefrundeck.local']/@hostname").text().should == "node2.chefrundeck.local"
38
+ end
27
39
  it 'fetched document should be node2 only' do
28
40
  get '/node2_systems'
29
41
  last_response.should be_ok
@@ -33,5 +45,26 @@ describe 'ChefRundeck' do
33
45
  it 'check custom attributes on node2 only' do
34
46
  get '/node2_systems'
35
47
  Nokogiri::XML(last_response.body).xpath("//project/node[@name='node2.chefrundeck.local']/attribute").length().should == 2
48
+ Nokogiri::XML(last_response.body).xpath("//project/node[@name='node2.chefrundeck.local']/attribute")[0].text.should == "linux"
49
+ Nokogiri::XML(last_response.body).xpath("//project/node[@name='node2.chefrundeck.local']/attribute")[1].text.should == "centos"
50
+ end
51
+ it 'check partial search' do
52
+ ChefRundeck.partial_search = true
53
+ get '/node2_systems'
54
+ Nokogiri::XML(last_response.body).xpath("//project/node[@name='node2.chefrundeck.local']/attribute").length().should == 2
55
+ Nokogiri::XML(last_response.body).xpath("//project/node[@name='node2.chefrundeck.local']/attribute")[0].text.should == "linux"
56
+ Nokogiri::XML(last_response.body).xpath("//project/node[@name='node2.chefrundeck.local']/attribute")[1].text.should == "centos"
57
+ end
58
+ it 'partial search: fetched document should be node1 only verify hostname override' do
59
+ ChefRundeck.partial_search = true
60
+ get '/node1_systems'
61
+ last_response.should be_ok
62
+ Nokogiri::XML(last_response.body).xpath("//project/node[@name='node1.chefrundeck.local']/@hostname").text().should == "10.0.0.1"
63
+ end
64
+ it 'partial search: fetched document should be node2 only verify hostname' do
65
+ ChefRundeck.partial_search = true
66
+ get '/node2_systems'
67
+ last_response.should be_ok
68
+ Nokogiri::XML(last_response.body).xpath("//project/node[@name='node2.chefrundeck.local']/@hostname").text().should == "node2.chefrundeck.local"
36
69
  end
37
70
  end
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chef-rundeck
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Jacob
8
8
  - Brian Scott
9
9
  - Steven Wagner
10
+ - Peter Crossley
10
11
  autorequire:
11
12
  bindir: bin
12
13
  cert_chain: []
@@ -16,98 +17,98 @@ dependencies:
16
17
  name: sinatra
17
18
  requirement: !ruby/object:Gem::Requirement
18
19
  requirements:
19
- - - ~>
20
+ - - '>='
20
21
  - !ruby/object:Gem::Version
21
22
  version: '0'
22
23
  type: :runtime
23
24
  prerelease: false
24
25
  version_requirements: !ruby/object:Gem::Requirement
25
26
  requirements:
26
- - - ~>
27
+ - - '>='
27
28
  - !ruby/object:Gem::Version
28
29
  version: '0'
29
30
  - !ruby/object:Gem::Dependency
30
31
  name: chef
31
32
  requirement: !ruby/object:Gem::Requirement
32
33
  requirements:
33
- - - ~>
34
+ - - '>='
34
35
  - !ruby/object:Gem::Version
35
36
  version: '0'
36
37
  type: :runtime
37
38
  prerelease: false
38
39
  version_requirements: !ruby/object:Gem::Requirement
39
40
  requirements:
40
- - - ~>
41
+ - - '>='
41
42
  - !ruby/object:Gem::Version
42
43
  version: '0'
43
44
  - !ruby/object:Gem::Dependency
44
45
  name: mixlib-cli
45
46
  requirement: !ruby/object:Gem::Requirement
46
47
  requirements:
47
- - - ~>
48
+ - - '>='
48
49
  - !ruby/object:Gem::Version
49
50
  version: '0'
50
51
  type: :runtime
51
52
  prerelease: false
52
53
  version_requirements: !ruby/object:Gem::Requirement
53
54
  requirements:
54
- - - ~>
55
+ - - '>='
55
56
  - !ruby/object:Gem::Version
56
57
  version: '0'
57
58
  - !ruby/object:Gem::Dependency
58
59
  name: rspec
59
60
  requirement: !ruby/object:Gem::Requirement
60
61
  requirements:
61
- - - ~>
62
+ - - '>='
62
63
  - !ruby/object:Gem::Version
63
64
  version: 1.2.9
64
65
  type: :development
65
66
  prerelease: false
66
67
  version_requirements: !ruby/object:Gem::Requirement
67
68
  requirements:
68
- - - ~>
69
+ - - '>='
69
70
  - !ruby/object:Gem::Version
70
71
  version: 1.2.9
71
72
  - !ruby/object:Gem::Dependency
72
73
  name: yard
73
74
  requirement: !ruby/object:Gem::Requirement
74
75
  requirements:
75
- - - ~>
76
+ - - '>='
76
77
  - !ruby/object:Gem::Version
77
78
  version: '0'
78
79
  type: :development
79
80
  prerelease: false
80
81
  version_requirements: !ruby/object:Gem::Requirement
81
82
  requirements:
82
- - - ~>
83
+ - - '>='
83
84
  - !ruby/object:Gem::Version
84
85
  version: '0'
85
86
  - !ruby/object:Gem::Dependency
86
87
  name: rack-test
87
88
  requirement: !ruby/object:Gem::Requirement
88
89
  requirements:
89
- - - ~>
90
+ - - '>='
90
91
  - !ruby/object:Gem::Version
91
92
  version: '0'
92
93
  type: :development
93
94
  prerelease: false
94
95
  version_requirements: !ruby/object:Gem::Requirement
95
96
  requirements:
96
- - - ~>
97
+ - - '>='
97
98
  - !ruby/object:Gem::Version
98
99
  version: '0'
99
100
  - !ruby/object:Gem::Dependency
100
101
  name: nokogiri
101
102
  requirement: !ruby/object:Gem::Requirement
102
103
  requirements:
103
- - - ~>
104
+ - - '>='
104
105
  - !ruby/object:Gem::Version
105
106
  version: '0'
106
107
  type: :development
107
108
  prerelease: false
108
109
  version_requirements: !ruby/object:Gem::Requirement
109
110
  requirements:
110
- - - ~>
111
+ - - '>='
111
112
  - !ruby/object:Gem::Version
112
113
  version: '0'
113
114
  description: Provides a resource endpoint for RunDeck from a Chef Server
@@ -126,6 +127,7 @@ files:
126
127
  - Rakefile
127
128
  - bin/chef-rundeck
128
129
  - lib/chef-rundeck.rb
130
+ - lib/partial_search.rb
129
131
  - spec/chef-rundeck_spec.rb
130
132
  - spec/spec.opts
131
133
  - spec/spec_helper.rb