chef-rundeck 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/chef-rundeck +20 -0
- data/lib/chef-rundeck.rb +214 -52
- data/lib/partial_search.rb +80 -0
- data/spec/chef-rundeck_spec.rb +33 -0
- metadata +17 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: acd9c147df627c29f79ec208b7ad4796a1933c81
|
4
|
+
data.tar.gz: 312f65e10a495d38b4e752fefef49099073b872a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6427c173c17c256172eb7b81c67047aa724d91e34004c2ddb3873aade19844977dbeebe38952964ac36cd14047b77cbfc2b4a863bc2aeeb9ebf9f704ba8faeb9
|
7
|
+
data.tar.gz: 207ed6aaaf9fad065ec4c06a44e7b9791d766569fcf59c8a8991d4dce4ffd8a3019f6fce8d1021d61ac780d571005ffc4ee9ac63429c39348fa8b5c26a96b654
|
data/bin/chef-rundeck
CHANGED
@@ -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
|
data/lib/chef-rundeck.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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 =
|
80
|
-
|
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
|
-
|
84
|
-
|
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
|
-
|
93
|
-
|
94
|
-
|
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[
|
111
|
-
nodeexec = node[
|
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[
|
175
|
+
<node name="#{xml_escape(node['fqdn'])}" #{nodeexec}
|
114
176
|
type="Node"
|
115
|
-
description="#{xml_escape(node
|
116
|
-
osArch="#{xml_escape(node[
|
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[
|
119
|
-
osVersion="#{xml_escape(node[
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
environment="#{xml_escape(node
|
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
|
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
|
-
|
131
|
-
|
132
|
-
|
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
|
-
|
136
|
-
|
137
|
-
|
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[
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
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
|
data/spec/chef-rundeck_spec.rb
CHANGED
@@ -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.
|
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
|