chef-solr 0.9.8 → 0.9.10.rc.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -17,10 +17,11 @@
17
17
  # limitations under the License.
18
18
  #
19
19
 
20
+ require 'rubygems'
21
+
20
22
  $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))
21
23
  $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "chef", "lib")))
22
24
 
23
- require 'rubygems'
24
25
  require 'chef/solr/application/indexer'
25
26
 
26
27
  Chef::Solr::Application::Indexer.new.run
data/lib/chef/solr.rb CHANGED
@@ -20,13 +20,6 @@ require 'chef/mixin/xml_escape'
20
20
  require 'chef/log'
21
21
  require 'chef/config'
22
22
  require 'chef/couchdb'
23
- require 'chef/role'
24
- require 'chef/node'
25
- require 'chef/data_bag'
26
- require 'chef/data_bag_item'
27
- require 'chef/api_client'
28
- require 'chef/openid_registration'
29
- require 'chef/webui_user'
30
23
  require 'net/http'
31
24
  require 'libxml'
32
25
  require 'uri'
@@ -55,11 +48,10 @@ class Chef
55
48
  select_url = "/solr/select?#{to_params(options)}"
56
49
  Chef::Log.debug("Sending #{select_url} to Solr")
57
50
  req = Net::HTTP::Get.new(select_url)
58
- res = @http.request(req)
59
- unless res.kind_of?(Net::HTTPSuccess)
60
- Chef::Log.fatal("Search Query to Solr '#{select_url}' failed")
61
- res.error!
62
- end
51
+
52
+ description = "Search Query to Solr '#{solr_url}#{select_url}'"
53
+
54
+ res = http_request_handler(req, description)
63
55
  Chef::Log.debug("Parsing Solr result set:\n#{res.body}")
64
56
  eval(res.body)
65
57
  end
@@ -68,34 +60,37 @@ class Chef
68
60
  Chef::Log.debug("POSTing document to SOLR:\n#{doc}")
69
61
  req = Net::HTTP::Post.new("/solr/update", "Content-Type" => "text/xml")
70
62
  req.body = doc.to_s
71
- res = @http.request(req)
72
- unless res.kind_of?(Net::HTTPSuccess)
73
- res.error!
74
- end
75
- res
63
+
64
+ description = "POST to Solr '#{solr_url}'"
65
+
66
+ http_request_handler(req, description)
76
67
  end
77
68
 
78
- def solr_add(data)
79
- data = [data] unless data.kind_of?(Array)
69
+ START_XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<add><doc>"
70
+ END_XML = "</doc></add>\n"
71
+ FIELD_ATTR = '<field name="'
72
+ FIELD_ATTR_END = '">'
73
+ CLOSE_FIELD = "</field>"
80
74
 
75
+ def solr_add(data)
81
76
  Chef::Log.debug("adding to SOLR: #{data.inspect}")
82
- xml_document = LibXML::XML::Document.new
83
- xml_add = LibXML::XML::Node.new("add")
84
- data.each do |doc|
85
- xml_doc = LibXML::XML::Node.new("doc")
86
- doc.each do |field, values|
87
- values = [values] unless values.kind_of?(Array)
88
- values.each do |v|
89
- xml_field = LibXML::XML::Node.new("field")
90
- xml_field["name"] = field
91
- xml_field.content = xml_escape(v.to_s)
92
- xml_doc << xml_field
93
- end
77
+
78
+ xml = ""
79
+ xml << START_XML
80
+
81
+ data.each do |field, values|
82
+ values.each do |v|
83
+ xml << FIELD_ATTR
84
+ xml << field
85
+ xml << FIELD_ATTR_END
86
+ xml << xml_escape(v)
87
+ xml << CLOSE_FIELD
94
88
  end
95
- xml_add << xml_doc
96
89
  end
97
- xml_document.root = xml_add
98
- post_to_solr(xml_document.to_s(:indent => false))
90
+ xml << END_XML
91
+ xml
92
+
93
+ post_to_solr(xml)
99
94
  end
100
95
 
101
96
  def solr_commit(opts={})
@@ -210,6 +205,30 @@ class Chef
210
205
  '%'+$1.unpack('H2'*$1.size).join('%').upcase
211
206
  }.tr(' ', '+')
212
207
  end
208
+
209
+ # handles multiple net/http exceptions and no method closed? bug
210
+ def http_request_handler(req, description='HTTP call')
211
+ res = @http.request(req)
212
+ unless res.kind_of?(Net::HTTPSuccess)
213
+ Chef::Log.fatal("#{description} failed (#{res.class} #{res.code} #{res.message})")
214
+ res.error!
215
+ end
216
+ res
217
+ rescue Timeout::Error, Errno::EINVAL, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ETIMEDOUT, NoMethodError => e
218
+ # http://redmine.ruby-lang.org/issues/show/2708
219
+ # http://redmine.ruby-lang.org/issues/show/2758
220
+ if e.to_s =~ /#{Regexp.escape(%q|undefined method 'closed?' for nil:NilClass|)}/
221
+ Chef::Log.fatal("#{description} failed. Chef::Exceptions::SolrConnectionError exception: Errno::ECONNREFUSED (net/http undefined method closed?) attempting to contact #{@solr_url}")
222
+ Chef::Log.debug("rescued error in http connect, treating it as Errno::ECONNREFUSED to hide bug in net/http")
223
+ Chef::Log.debug(e.backtrace.join("\n"))
224
+ raise Chef::Exceptions::SolrConnectionError, "Errno::ECONNREFUSED: Connection refused attempting to contact #{@solr_url}"
225
+ end
226
+
227
+ Chef::Log.fatal("#{description} failed. Chef::Exceptions::SolrConnectionError exception: #{e.class.name}: #{e.to_s} attempting to contact #{@solr_url}")
228
+ Chef::Log.debug(e.backtrace.join("\n"))
229
+
230
+ raise Chef::Exceptions::SolrConnectionError, "#{e.class.name}: #{e.to_s}"
231
+ end
213
232
 
214
233
  end
215
234
  end
@@ -6,9 +6,9 @@
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
7
7
  # you may not use this file except in compliance with the License.
8
8
  # You may obtain a copy of the License at
9
- #
9
+ #
10
10
  # http://www.apache.org/licenses/LICENSE-2.0
11
- #
11
+ #
12
12
  # Unless required by applicable law or agreed to in writing, software
13
13
  # distributed under the License is distributed on an "AS IS" BASIS,
14
14
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -26,12 +26,24 @@ class Chef
26
26
  class Solr
27
27
  class Index < Solr
28
28
 
29
+ UNDERSCORE = '_'
30
+ X = 'X'
31
+
32
+ X_CHEF_id_CHEF_X = 'X_CHEF_id_CHEF_X'
33
+ X_CHEF_database_CHEF_X = 'X_CHEF_database_CHEF_X'
34
+ X_CHEF_type_CHEF_X = 'X_CHEF_type_CHEF_X'
35
+
29
36
  def add(id, database, type, item)
30
- raise ArgumentError, "Object must respond to keys!" unless item.respond_to?(:keys)
37
+ unless item.respond_to?(:keys)
38
+ raise ArgumentError, "#{self.class.name} can only index Hash-like objects. You gave #{item.inspect}"
39
+ end
40
+
31
41
  to_index = flatten_and_expand(item)
32
- to_index["X_CHEF_id_CHEF_X"] = id
33
- to_index["X_CHEF_database_CHEF_X"] = database
34
- to_index["X_CHEF_type_CHEF_X"] = type
42
+
43
+ to_index[X_CHEF_id_CHEF_X] = [id]
44
+ to_index[X_CHEF_database_CHEF_X] = [database]
45
+ to_index[X_CHEF_type_CHEF_X] = [type]
46
+
35
47
  solr_add(to_index)
36
48
  to_index
37
49
  end
@@ -44,117 +56,48 @@ class Chef
44
56
  solr_delete_by_query(query)
45
57
  end
46
58
 
47
- def flatten_and_expand(item, fields=Hash.new, parent=nil)
48
- item.keys.each do |key|
49
- # If we have a parent, we want to add the current key as a value
50
- if parent
51
- # foo_bar = bar
52
- set_field_value(fields, parent, key)
53
- # foo_X = bar, etc.
54
- make_expando_fields(parent).each do |ex_key|
55
- set_field_value(fields, ex_key, key)
56
- end
57
- end
58
- case item[key]
59
- when Hash
60
- parent_key = parent ? "#{parent}_#{key}" : key
61
- flatten_and_expand(item[key], fields, parent_key)
62
- else
63
- parent_key = parent ? "#{parent}_#{key}" : key
64
- set_field_value(fields, key, item[key])
65
- set_field_value(fields, parent_key, item[key]) if parent
66
- make_expando_fields(parent_key).each do |ex_key|
67
- set_field_value(fields, ex_key, item[key])
68
- end
69
- end
70
- end
71
- fields
72
- end
73
-
74
- def make_expando_fields(key)
75
- key = key.to_s
76
- fields = Array.new
77
- parts = key.split("_")
78
- length = parts.length
79
- parts.each_index do |i|
80
- beginning = nil
81
- remainder = nil
82
- if i == 0
83
- beginning = "X"
84
- else
85
- beginning = parts[0..i-1].join("_")
86
- end
87
-
88
- if i == length-1
89
- remainder = "X"
90
- else
91
- remainder = parts[i+1..-1].join("_")
92
- end
59
+ def flatten_and_expand(item)
60
+ @flattened_item = Hash.new {|hash, key| hash[key] = []}
93
61
 
94
- if beginning == "X" || remainder == "X"
95
- unless beginning == "X" && remainder == "X"
96
- fields << "#{beginning}_#{remainder}"
97
- end
98
- else
99
- fields << "#{beginning}_X_#{remainder}"
100
- end
62
+ item.each do |key, value|
63
+ flatten_each([key.to_s], value)
101
64
  end
102
- fields
103
- end
104
65
 
105
- def set_field_value(fields, key, value)
106
- key = key.to_s
107
- if fields.has_key?(key)
108
- convert_field_to_array(fields, key, value) unless fields[key].kind_of?(Array)
109
- add_value_to_field_array(fields, key, value)
110
- else
111
- check_value(value)
112
- if value.kind_of?(Array)
113
- fields[key] = Array.new
114
- value.each do |v|
115
- if v.kind_of?(Hash)
116
- flatten_and_expand(v, fields, key)
117
- else
118
- fields[key] << v.to_s
119
- end
120
- end
121
- else
122
- fields[key] = value.to_s
123
- end
124
- end
125
- fields
66
+ @flattened_item.each_value { |values| values.uniq! }
67
+ @flattened_item
126
68
  end
127
69
 
128
- def add_value_to_field_array(fields, key, value)
129
- check_value(value)
130
- if value.kind_of?(Array)
131
- value.each do |v|
132
- if v.kind_of?(Hash)
133
- flatten_and_expand(v, fields, key)
134
- else
135
- fields[key] << v.to_s unless fields[key].include?(v.to_s)
136
- end
70
+ def flatten_each(keys, values)
71
+ case values
72
+ when Hash
73
+ values.each do |child_key, child_value|
74
+ add_field_value(keys, child_key)
75
+ flatten_each(keys + [child_key.to_s], child_value)
137
76
  end
77
+ when Array
78
+ values.each { |child_value| flatten_each(keys, child_value) }
138
79
  else
139
- fields[key] << value.to_s unless fields[key].include?(value.to_s)
80
+ add_field_value(keys, values)
140
81
  end
141
- fields
142
82
  end
143
83
 
144
- def convert_field_to_array(fields, key, value)
145
- if fields[key] != value
146
- safe = fields[key]
147
- fields[key] = [ safe ]
148
- end
149
- fields
84
+ def add_field_value(keys, value)
85
+ value = value.to_s
86
+ each_expando_field(keys) { |expando_field| @flattened_item[expando_field] << value }
87
+ @flattened_item[keys.join(UNDERSCORE)] << value
88
+ @flattened_item[keys.last] << value
150
89
  end
151
90
 
152
- def check_value(value)
153
- raise ArgumentError, "Value must not be a type of hash!" if value.kind_of?(Hash)
154
- value
91
+ def each_expando_field(keys)
92
+ return if keys.size == 1
93
+ 0.upto(keys.size - 1) do |index|
94
+ original = keys[index]
95
+ keys[index] = X
96
+ yield keys.join(UNDERSCORE)
97
+ keys[index] = original
98
+ end
155
99
  end
156
100
 
157
101
  end
158
102
  end
159
103
  end
160
-
@@ -20,12 +20,6 @@ require 'chef/log'
20
20
  require 'chef/config'
21
21
  require 'chef/solr'
22
22
  require 'chef/solr/index'
23
- require 'chef/node'
24
- require 'chef/role'
25
- require 'chef/rest'
26
- require 'chef/data_bag'
27
- require 'chef/data_bag_item'
28
- require 'chef/api_client'
29
23
  require 'chef/couchdb'
30
24
  require 'chef/index_queue'
31
25
 
@@ -41,7 +35,10 @@ class Chef
41
35
  Chef::Log.debug("Dequeued item for indexing: #{payload.inspect}")
42
36
 
43
37
  begin
44
- pitem = payload["item"].to_hash
38
+ # older producers will send the raw item, and we no longer inflate it
39
+ # to an object.
40
+ pitem = payload["item"].to_hash
41
+ pitem.delete("json_class")
45
42
  response = generate_response { index.add(payload["id"], payload["database"], payload["type"], pitem) }
46
43
  rescue NoMethodError
47
44
  response = generate_response() { raise ArgumentError, "Payload item does not respond to :keys or :to_hash, cannot index!" }
@@ -1,5 +1,5 @@
1
1
  class Chef
2
2
  class Solr
3
- VERSION = '0.9.8'
3
+ VERSION = '0.9.10.rc.0'
4
4
  end
5
5
  end
@@ -1,5 +1,7 @@
1
1
  require File.expand_path(File.join("#{File.dirname(__FILE__)}", '..', '..', 'spec_helper'))
2
2
 
3
+
4
+
3
5
  describe Chef::Solr::Index do
4
6
  before(:each) do
5
7
  @index = Chef::Solr::Index.new
@@ -25,37 +27,36 @@ describe Chef::Solr::Index do
25
27
 
26
28
  it "should index the object as a single flat hash, with only strings or arrays as values" do
27
29
  validate = {
28
- "X_CHEF_id_CHEF_X" => 1,
29
- "X_CHEF_database_CHEF_X" => "monkey",
30
- "X_CHEF_type_CHEF_X" => "snakes",
31
- "foo" => "bar",
30
+ "X_CHEF_id_CHEF_X" => [1],
31
+ "X_CHEF_database_CHEF_X" => ["monkey"],
32
+ "X_CHEF_type_CHEF_X" => ["snakes"],
33
+ "foo" => ["bar"],
32
34
  "battles" => [ "often", "but", "for" ],
33
- "battles_often" => "sings like smurfs",
34
- "often" => "sings like smurfs",
35
- "battles_but" => "still has good records",
36
- "but" => "still has good records",
35
+ "battles_often" => ["sings like smurfs"],
36
+ "often" => ["sings like smurfs"],
37
+ "battles_but" => ["still has good records"],
38
+ "but" => ["still has good records"],
37
39
  "battles_for" => [ "all", "of", "that" ],
38
40
  "for" => [ "all", "of", "that" ],
39
- "snoopy" => "sits_in_a_barn",
41
+ "snoopy" => ["sits-in-a-barn"],
40
42
  "battles_X" => [ "sings like smurfs", "still has good records", "all", "of", "that" ],
41
- "X_often" => "sings like smurfs",
42
- "X_but" => "still has good records",
43
+ "X_often" =>[ "sings like smurfs"],
44
+ "X_but" => ["still has good records"],
43
45
  "X_for" => [ "all", "of", "that" ]
44
- }
45
- to_index = @index.add(1, "monkey", "snakes", {
46
+ }
47
+ to_index = @index.add(1, "monkey", "snakes", {
46
48
  "foo" => :bar,
47
- "battles" => {
49
+ "battles" => {
48
50
  "often" => "sings like smurfs",
49
51
  "but" => "still has good records",
50
52
  "for" => [ "all", "of", "that" ]
51
53
  },
52
- "snoopy" => "sits_in_a_barn"
54
+ "snoopy" => "sits-in-a-barn"
53
55
  })
56
+
54
57
  validate.each do |k, v|
55
58
  if v.kind_of?(Array)
56
- # Every entry in to_index[k] should be in v
57
- r = to_index[k] & v
58
- r.length.should == to_index[k].length
59
+ to_index[k].sort.should == v.sort
59
60
  else
60
61
  to_index[k].should == v
61
62
  end
@@ -88,101 +89,99 @@ describe Chef::Solr::Index do
88
89
  end
89
90
 
90
91
  it "should set a value for the parent as key, with the key as the value" do
91
- @index.flatten_and_expand({ "one" => "woot" }, @fields, "omerta")
92
- @fields["omerta"].should == "one"
92
+ @fields = @index.flatten_and_expand("omerta" => { "one" => "woot" })
93
+ @fields["omerta"].should == ["one"]
93
94
  end
94
95
 
95
96
  it "should call itself recursively for values that are hashes" do
96
- @index.flatten_and_expand({ "one" => { "two" => "three", "four" => { "five" => "six" } }}, @fields)
97
- {
98
- "one" => [ "two", "four" ],
99
- "one_two" => "three",
100
- "X_two" => "three",
101
- "two" => "three",
102
- "one_four" => "five",
103
- "X_four" => "five",
104
- "one_X" => [ "three", "five" ],
105
- "one_four_five" => "six",
106
- "X_four_five" => "six",
107
- "one_X_five" => "six",
108
- "one_four_X" => "six",
109
- "five" => "six"
110
- }.each do |k, v|
97
+ @fields = @index.flatten_and_expand({ "one" => { "two" => "three", "four" => { "five" => "six" } }})
98
+ expected = {"one" => [ "two", "four" ],
99
+ "one_two" => ["three"],
100
+ "X_two" => ["three"],
101
+ "two" => ["three"],
102
+ "one_four" => ["five"],
103
+ "X_four" => ["five"],
104
+ "one_X" => [ "three", "five" ],
105
+ "one_four_five" => ["six"],
106
+ "X_four_five" => ["six"],
107
+ "one_X_five" => ["six"],
108
+ "one_four_X" => ["six"],
109
+ "five" => ["six"]}
110
+ expected.each do |k, v|
111
111
  @fields[k].should == v
112
112
  end
113
113
  end
114
114
 
115
115
  it "should call itself recursively for hashes nested in arrays" do
116
- @index.flatten_and_expand({ :one => [ { :two => "three" }, { :four => { :five => "six" } } ] }, @fields)
117
- {
118
- "one_X_five" => "six",
119
- "one_four" => "five",
120
- "one_X" => [ "three", "five" ],
121
- "two" => "three",
122
- "one_four_X" => "six",
123
- "X_four" => "five",
124
- "X_four_five" => "six",
125
- "one" => [ "two", "four" ],
126
- "one_four_five" => "six",
127
- "five" => "six",
128
- "X_two" => "three",
129
- "one_two" => "three"
130
- }.each do |k, v|
131
- @fields[k].should == v
116
+ @fields = @index.flatten_and_expand({ :one => [ { :two => "three" }, { :four => { :five => "six" } } ] })
117
+ expected = {"one_X_five" => ["six"],
118
+ "one_four" => ["five"],
119
+ "one_X" => [ "three", "five" ],
120
+ "two" => ["three"],
121
+ "one_four_X" => ["six"],
122
+ "X_four" => ["five"],
123
+ "X_four_five" => ["six"],
124
+ "one" => [ "two", "four" ],
125
+ "one_four_five" => ["six"],
126
+ "five" => ["six"],
127
+ "X_two" => ["three"],
128
+ "one_two" => ["three"]}
129
+
130
+ expected.each do |key, expected_value|
131
+ @fields[key].should == expected_value
132
132
  end
133
133
  end
134
134
 
135
- end
136
-
137
- describe "set_field_value" do
138
- before(:each) do
139
- @fields = Hash.new
140
- end
141
-
142
- it "should set a value in the fields hash" do
143
- @index.set_field_value(@fields, "one", "two")
144
- @fields["one"].should eql("two")
145
- end
146
-
147
- it "should create an array of all values, if a field is set twice" do
148
- @index.set_field_value(@fields, "one", "two")
149
- @index.set_field_value(@fields, "one", "three")
150
- @fields["one"].should eql([ "two", "three" ])
151
- end
152
-
153
- it "should not add duplicate values to a field when there is one string entry" do
154
- @index.set_field_value(@fields, "one", "two")
155
- @index.set_field_value(@fields, "one", "two")
156
- @fields["one"].should eql("two")
157
- end
158
-
159
- it "should not add duplicate values to a field when it is an array" do
160
- @index.set_field_value(@fields, "one", "two")
161
- @index.set_field_value(@fields, "one", "three")
162
- @index.set_field_value(@fields, "one", "two")
163
- @fields["one"].should eql([ "two", "three" ])
164
- end
165
-
166
- it "should accept arrays as values" do
167
- @index.set_field_value(@fields, "one", [ "two", "three" ])
168
- @fields["one"].should eql([ "two", "three" ])
169
- end
170
-
171
- it "should not duplicate values when a field has been set with multiple arrays" do
172
- @index.set_field_value(@fields, "one", [ "two", "three" ])
173
- @index.set_field_value(@fields, "one", [ "two", "four" ])
174
- @fields["one"].should eql([ "two", "three", "four" ])
135
+ it "generates unlimited levels of expando fields when expanding" do
136
+ expected_keys = ["one",
137
+ "one_two",
138
+ "X_two",
139
+ "one_X",
140
+ "one_two_three",
141
+ "X_two_three",
142
+ "one_X_three",
143
+ "one_two_X",
144
+ "one_two_three_four",
145
+ "X_two_three_four",
146
+ "one_X_three_four",
147
+ "one_two_X_four",
148
+ "one_two_three_X",
149
+ "one_two_three_four_five",
150
+ "X_two_three_four_five",
151
+ "one_X_three_four_five",
152
+ "one_two_X_four_five",
153
+ "one_two_three_X_five",
154
+ "one_two_three_four_X",
155
+ "six",
156
+ "one_two_three_four_five_six",
157
+ "X_two_three_four_five_six",
158
+ "one_X_three_four_five_six",
159
+ "one_two_X_four_five_six",
160
+ "one_two_three_X_five_six",
161
+ "one_two_three_four_X_six",
162
+ "one_two_three_four_five_X"].sort
163
+
164
+ nested = {:one => {:two => {:three => {:four => {:five => {:six => :end}}}}}}
165
+ @fields = @index.flatten_and_expand(nested)
166
+
167
+ @fields.keys.sort.should include(*expected_keys)
175
168
  end
176
169
 
170
+ end
177
171
 
178
- it "should allow you to set a value in the fields hash to an array" do
179
- @index.set_field_value(@fields, "one", [ "foo", "bar", "baz" ])
172
+ describe "creating expando fields" do
173
+ def make_expando_fields(parts)
174
+ expando_fields = []
175
+ @index.each_expando_field(parts) { |ex| expando_fields << ex }
176
+ expando_fields
180
177
  end
181
178
 
182
- it "should not allow you to set a value in the fields hash to a hash" do
183
- lambda {
184
- @index.set_field_value(@fields, "one", { "two" => "three" })
185
- }.should raise_error(ArgumentError)
179
+ it "joins the fields with a big X" do
180
+ make_expando_fields(%w{foo bar baz qux}).should == ["X_bar_baz_qux", "foo_X_baz_qux", "foo_bar_X_qux", "foo_bar_baz_X"]
181
+ make_expando_fields(%w{foo bar baz}).should == ["X_bar_baz", "foo_X_baz", "foo_bar_X"]
182
+ make_expando_fields(%w{foo bar}).should == ["X_bar", "foo_X"]
183
+ make_expando_fields(%w{foo}).should == []
186
184
  end
187
185
  end
186
+
188
187
  end
@@ -1,4 +1,5 @@
1
1
  require File.expand_path(File.join("#{File.dirname(__FILE__)}", '..', 'spec_helper'))
2
+ require 'net/http'
2
3
 
3
4
  describe Chef::Solr do
4
5
  before(:each) do
@@ -27,33 +28,55 @@ describe Chef::Solr do
27
28
  @solr.http = @http
28
29
  end
29
30
 
30
- it "should call get to /solr/select with the escaped query" do
31
- Net::HTTP::Get.should_receive(:new).with(%r(q=hostname%3Alatte))
32
- @solr.solr_select("chef_opscode", "node", :q => "hostname:latte")
33
- end
31
+ describe "when the HTTP call is successful" do
32
+ it "should call get to /solr/select with the escaped query" do
33
+ Net::HTTP::Get.should_receive(:new).with(%r(q=hostname%3Alatte))
34
+ @solr.solr_select("chef_opscode", "node", :q => "hostname:latte")
35
+ end
34
36
 
35
- it "should call get to /solr/select with wt=ruby" do
36
- Net::HTTP::Get.should_receive(:new).with(%r(wt=ruby))
37
- @solr.solr_select("chef_opscode", "node", :q => "hostname:latte")
38
- end
37
+ it "should call get to /solr/select with wt=ruby" do
38
+ Net::HTTP::Get.should_receive(:new).with(%r(wt=ruby))
39
+ @solr.solr_select("chef_opscode", "node", :q => "hostname:latte")
40
+ end
39
41
 
40
- it "should call get to /solr/select with indent=off" do
41
- Net::HTTP::Get.should_receive(:new).with(%r(indent=off))
42
- @solr.solr_select("chef_opscode", "node", :q => "hostname:latte")
43
- end
44
-
45
- it "should call get to /solr/select with filter query" do
46
- Net::HTTP::Get.should_receive(:new).with(/fq=%2BX_CHEF_database_CHEF_X%3Achef_opscode\+%2BX_CHEF_type_CHEF_X%3Anode/)
47
- @solr.solr_select("chef_opscode", "node", :q => "hostname:latte")
42
+ it "should call get to /solr/select with indent=off" do
43
+ Net::HTTP::Get.should_receive(:new).with(%r(indent=off))
44
+ @solr.solr_select("chef_opscode", "node", :q => "hostname:latte")
45
+ end
46
+
47
+ it "should call get to /solr/select with filter query" do
48
+ Net::HTTP::Get.should_receive(:new).with(/fq=%2BX_CHEF_database_CHEF_X%3Achef_opscode\+%2BX_CHEF_type_CHEF_X%3Anode/)
49
+ @solr.solr_select("chef_opscode", "node", :q => "hostname:latte")
50
+ end
51
+
52
+ it "should return the evaluated response body" do
53
+ res = @solr.solr_select("chef_opscode", "node", :q => "hostname:latte")
54
+ res.should == { :some => :hash }
55
+ end
48
56
  end
49
57
 
50
- it "should return the evaluated response body" do
51
- res = @solr.solr_select("chef_opscode", "node", :q => "hostname:latte")
52
- res.should == { :some => :hash }
58
+ describe "when the HTTP call is unsuccessful" do
59
+ [Timeout::Error, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EINVAL].each do |exception|
60
+ it "should rescue, log an error message, and raise a SolrConnectionError encountering exception #{exception}" do
61
+ lambda {
62
+ @http.should_receive(:request).with(instance_of(Net::HTTP::Get)).and_raise(exception)
63
+ Chef::Log.should_receive(:fatal).with(/Search Query to Solr '(.+?)' failed. Chef::Exceptions::SolrConnectionError exception: #{exception}:.+/)
64
+ @solr.solr_select("chef_opscode", "node", :q => "hostname:latte")
65
+ }.should raise_error(Chef::Exceptions::SolrConnectionError)
66
+ end
67
+ end
68
+
69
+ it "should rescue, log an error message, and raise a SolrConnectionError when encountering exception NoMethodError and net/http closed? bug" do
70
+ lambda {
71
+ @no_method_error = NoMethodError.new("undefined method 'closed\?' for nil:NilClass")
72
+ @http.should_receive(:request).with(instance_of(Net::HTTP::Get)).and_raise(@no_method_error)
73
+ Chef::Log.should_receive(:fatal).with(/Search Query to Solr '(.+?)' failed. Chef::Exceptions::SolrConnectionError exception: Errno::ECONNREFUSED.+net\/http undefined method closed.+/)
74
+ @solr.solr_select("chef_opscode", "node", :q => "hostname:latte")
75
+ }.should raise_error(Chef::Exceptions::SolrConnectionError)
76
+ end
53
77
  end
54
78
  end
55
79
 
56
-
57
80
  describe "post_to_solr" do
58
81
  before(:each) do
59
82
  @http_response = mock(
@@ -70,27 +93,60 @@ describe Chef::Solr do
70
93
  Net::HTTP::Post.stub!(:new).and_return(@http_request)
71
94
  @doc = { "foo" => "bar" }
72
95
  end
73
-
74
- it "should post to /solr/update" do
75
- Net::HTTP::Post.should_receive(:new).with("/solr/update", "Content-Type" => "text/xml").and_return(@http_request)
76
- @solr.post_to_solr(@doc)
77
- end
78
96
 
79
- it "should set the body of the request to the stringified doc" do
80
- @http_request.should_receive(:body=).with("foo")
81
- @solr.post_to_solr(:foo)
97
+ describe 'when the HTTP call is successful' do
98
+ it "should post to /solr/update" do
99
+ Net::HTTP::Post.should_receive(:new).with("/solr/update", "Content-Type" => "text/xml").and_return(@http_request)
100
+ @solr.post_to_solr(@doc)
101
+ end
102
+
103
+ it "should set the body of the request to the stringified doc" do
104
+ @http_request.should_receive(:body=).with("foo")
105
+ @solr.post_to_solr(:foo)
106
+ end
107
+
108
+ it "should send the request to solr" do
109
+ @http.should_receive(:request).with(@http_request).and_return(@http_response)
110
+ @solr.post_to_solr(:foo).should
111
+ end
82
112
  end
83
113
 
84
- it "should send the request to solr" do
85
- @http.should_receive(:request).with(@http_request).and_return(@http_response)
86
- @solr.post_to_solr(:foo)
114
+ describe "when the HTTP call is unsuccessful due to an exception" do
115
+ it "should post to /solr/update" do
116
+ Net::HTTP::Post.should_receive(:new).with("/solr/update", "Content-Type" => "text/xml").and_return(@http_request)
117
+ @solr.post_to_solr(@doc)
118
+ end
119
+
120
+ it "should set the body of the request to the stringified doc" do
121
+ @http_request.should_receive(:body=).with("foo")
122
+ @solr.post_to_solr(:foo)
123
+ end
124
+
125
+ [Timeout::Error, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EINVAL].each do |exception|
126
+ it "should rescue and log an error message when encountering exception #{exception} and then re-raise it" do
127
+ lambda {
128
+ @http.should_receive(:request).with(@http_request).and_raise(exception)
129
+ Chef::Log.should_receive(:fatal).with(/POST to Solr '(.+?)' failed. Chef::Exceptions::SolrConnectionError exception: #{exception}:.+/)
130
+ @solr.post_to_solr(:foo)
131
+ }.should raise_error(Chef::Exceptions::SolrConnectionError)
132
+ end
133
+ end
134
+
135
+ it "should rescue and log an error message when encountering exception NoMethodError and net/http closed? bug" do
136
+ lambda {
137
+ @no_method_error = NoMethodError.new("undefined method 'closed\?' for nil:NilClass")
138
+ @http.should_receive(:request).with(@http_request).and_raise(@no_method_error)
139
+ Chef::Log.should_receive(:fatal).with(/POST to Solr '(.+?)' failed. Chef::Exceptions::SolrConnectionError exception: Errno::ECONNREFUSED.+net\/http undefined method closed.+/)
140
+ @solr.post_to_solr(:foo)
141
+ }.should raise_error(Chef::Exceptions::SolrConnectionError)
142
+ end
87
143
  end
88
144
  end
89
145
 
90
146
  describe "solr_add" do
91
147
  before(:each) do
92
148
  @solr.stub!(:post_to_solr).and_return(true)
93
- @data = { "foo" => "bar" }
149
+ @data = { "foo" => ["bar"] }
94
150
  end
95
151
 
96
152
  it "should send valid XML to solr" do
@@ -99,7 +155,7 @@ describe Chef::Solr do
99
155
  end
100
156
 
101
157
  it "XML escapes content before sending to SOLR" do
102
- @data["foo"] = "<&>"
158
+ @data["foo"] = ["<&>"]
103
159
  @solr.should_receive(:post_to_solr).with("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<add><doc><field name=\"foo\">&lt;&amp;&gt;</field></doc></add>\n")
104
160
 
105
161
  @solr.solr_add(@data)
@@ -234,5 +290,4 @@ describe Chef::Solr do
234
290
  @solr.rebuild_index["Chef::DataBag"].should == "success"
235
291
  end
236
292
  end
237
-
238
293
  end
metadata CHANGED
@@ -1,12 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chef-solr
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
4
+ prerelease: true
5
5
  segments:
6
6
  - 0
7
7
  - 9
8
- - 8
9
- version: 0.9.8
8
+ - 10
9
+ - rc
10
+ - 0
11
+ version: 0.9.10.rc.0
10
12
  platform: ruby
11
13
  authors:
12
14
  - Adam Jacob
@@ -14,7 +16,7 @@ autorequire:
14
16
  bindir: bin
15
17
  cert_chain: []
16
18
 
17
- date: 2010-08-05 00:00:00 -07:00
19
+ date: 2010-10-07 00:00:00 -07:00
18
20
  default_executable:
19
21
  dependencies:
20
22
  - !ruby/object:Gem::Dependency
@@ -58,8 +60,10 @@ dependencies:
58
60
  segments:
59
61
  - 0
60
62
  - 9
61
- - 8
62
- version: 0.9.8
63
+ - 10
64
+ - rc
65
+ - 0
66
+ version: 0.9.10.rc.0
63
67
  type: :runtime
64
68
  version_requirements: *id003
65
69
  description:
@@ -114,11 +118,13 @@ required_ruby_version: !ruby/object:Gem::Requirement
114
118
  required_rubygems_version: !ruby/object:Gem::Requirement
115
119
  none: false
116
120
  requirements:
117
- - - ">="
121
+ - - ">"
118
122
  - !ruby/object:Gem::Version
119
123
  segments:
120
- - 0
121
- version: "0"
124
+ - 1
125
+ - 3
126
+ - 1
127
+ version: 1.3.1
122
128
  requirements: []
123
129
 
124
130
  rubyforge_project: