summaryrefslogtreecommitdiff
path: root/chef
diff options
context:
space:
mode:
authorSeth Chisamore <schisamo@opscode.com>2012-10-29 15:33:14 -0400
committerSeth Chisamore <schisamo@opscode.com>2012-10-30 09:47:37 -0400
commit862d971eed1c11e6990b1c08f00cac67a10a2e79 (patch)
tree88a383894926c0c56a25d7a31ec37e821b7e7b97 /chef
parentf9d960515e7aa736c439cb914cb0498a415e5c5a (diff)
downloadchef-862d971eed1c11e6990b1c08f00cac67a10a2e79.tar.gz
[OC-3564] remove Chef::SolrQuery
This class was only used by Chef server components and has been succeeded by Erchef.
Diffstat (limited to 'chef')
-rw-r--r--chef/chef.gemspec1
-rw-r--r--chef/lib/chef/config.rb8
-rw-r--r--chef/lib/chef/solr_query.rb187
-rw-r--r--chef/lib/chef/solr_query/lucene.treetop150
-rw-r--r--chef/lib/chef/solr_query/lucene_nodes.rb285
-rw-r--r--chef/lib/chef/solr_query/query_transform.rb65
-rw-r--r--chef/lib/chef/solr_query/solr_http_request.rb132
-rw-r--r--chef/spec/unit/solr_query/query_transform_spec.rb454
-rw-r--r--chef/spec/unit/solr_query/solr_http_request_spec.rb244
-rw-r--r--chef/spec/unit/solr_query_spec.rb203
10 files changed, 0 insertions, 1729 deletions
diff --git a/chef/chef.gemspec b/chef/chef.gemspec
index 732601cc79..e40a0cf8ee 100644
--- a/chef/chef.gemspec
+++ b/chef/chef.gemspec
@@ -24,7 +24,6 @@ Gem::Specification.new do |s|
s.add_dependency "bunny", ">= 0.6.0", "< 0.8.0"
s.add_dependency "json", ">= 1.4.4", "<= 1.6.1"
s.add_dependency "yajl-ruby", "~> 1.1"
- s.add_dependency "treetop", "~> 1.4.9"
s.add_dependency "net-ssh", "~> 2.2.2"
s.add_dependency "net-ssh-multi", "~> 1.1.0"
# CHEF-3027: The knife-cloud plugins require newer features from highline, core chef should not.
diff --git a/chef/lib/chef/config.rb b/chef/lib/chef/config.rb
index 924a20ab3b..5fd50d3bda 100644
--- a/chef/lib/chef/config.rb
+++ b/chef/lib/chef/config.rb
@@ -173,7 +173,6 @@ class Chef
## Daemonization Settings ##
# What user should Chef run as?
user nil
- # What group should the chef-server, -solr, -solr-indexer run as
group nil
umask 0022
@@ -230,13 +229,6 @@ class Chef
# Where should chef-solo download recipes from?
recipe_url nil
- solr_url "http://localhost:8983/solr"
- solr_jetty_path "/var/chef/solr-jetty"
- solr_data_path "/var/chef/solr/data"
- solr_home_path "/var/chef/solr"
- solr_heap_size "256M"
- solr_java_opts nil
-
# Parameters for connecting to RabbitMQ
amqp_host '0.0.0.0'
amqp_port '5672'
diff --git a/chef/lib/chef/solr_query.rb b/chef/lib/chef/solr_query.rb
deleted file mode 100644
index 3360ecc24d..0000000000
--- a/chef/lib/chef/solr_query.rb
+++ /dev/null
@@ -1,187 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@opscode.com>)
-# Author:: Daniel DeLeo (<dan@opscode.com>)
-# Author:: Seth Falcon (<seth@opscode.com>)
-# Copyright:: Copyright (c) 2009-2011 Opscode, Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require 'chef/mixin/xml_escape'
-require 'chef/log'
-require 'chef/config'
-require 'chef/couchdb'
-require 'chef/solr_query/solr_http_request'
-require 'chef/solr_query/query_transform'
-
-class Chef
- class SolrQuery
-
- ID_KEY = "X_CHEF_id_CHEF_X"
- DEFAULT_PARAMS = Mash.new(:start => 0, :rows => 1000, :sort => "#{ID_KEY} asc", :wt => 'json', :indent => 'off').freeze
- FILTER_PARAM_MAP = {:database => 'X_CHEF_database_CHEF_X', :type => "X_CHEF_type_CHEF_X", :data_bag => 'data_bag'}
- VALID_PARAMS = [:start,:rows,:sort,:q,:type]
- BUILTIN_SEARCH_TYPES = ["role","node","client","environment"]
- DATA_BAG_ITEM = 'data_bag_item'
-
- include Chef::Mixin::XMLEscape
-
- attr_accessor :query
- attr_accessor :params
-
- # Create a new Query object - takes the solr_url and optional
- # Chef::CouchDB object to inflate objects into.
- def initialize(couchdb = nil)
- @filter_query = {}
- @params = {}
-
- if couchdb.nil?
- @database = Chef::Config[:couchdb_database]
- @couchdb = Chef::CouchDB.new(nil, Chef::Config[:couchdb_database])
- else
- unless couchdb.respond_to?(:couchdb_database)
- Chef::Log.warn("Passing the database name to Chef::Solr::Query initialization is deprecated. Please pass in the Chef::CouchDB object instead.")
- @database = couchdb
- @couchdb = Chef::CouchDB.new(nil, couchdb)
- else
- @database = couchdb.couchdb_database
- @couchdb = couchdb
- end
- end
- end
-
- def self.from_params(params, couchdb=nil)
- query = new(couchdb)
- query.params = VALID_PARAMS.inject({}) do |p, param_name|
- p[param_name] = params[param_name] if params.key?(param_name)
- p
- end
- query.update_filter_query_from_params
- query.update_query_from_params
- query
- end
-
- def filter_by(filter_query_params)
- filter_query_params.each do |key, value|
- @filter_query[FILTER_PARAM_MAP[key]] = value
- end
- end
-
- def filter_query
- @filter_query.map { |param, value| "+#{param}:#{value}" }.join(' ')
- end
-
- def filter_by_type(type)
- case type
- when *BUILTIN_SEARCH_TYPES
- filter_by(:type => type)
- else
- filter_by(:type => DATA_BAG_ITEM, :data_bag => type)
- end
- end
-
- def update_filter_query_from_params
- filter_by(:database => @database)
- filter_by_type(params.delete(:type))
- end
-
- def update_query_from_params
- original_query = URI.decode(params.delete(:q) || "*:*")
- @query = Chef::SolrQuery::QueryTransform.transform(original_query)
- end
-
- def objects
- if !object_ids.empty?
- @bulk_objects ||= @couchdb.bulk_get(object_ids)
- Chef::Log.debug { "Bulk get of objects: #{@bulk_objects.inspect}" }
- @bulk_objects
- else
- []
- end
- end
-
- def object_ids
- @object_ids ||= results["response"]["docs"].map { |d| d[ID_KEY] }
- end
-
- def results
- @results ||= SolrHTTPRequest.select(self.to_hash)
- end
-
- # Search Solr for objects of a given type, for a given query.
- def search
- { "rows" => objects, "start" => results["response"]["start"],
- "total" => results["response"]["numFound"] }
- end
-
- def to_hash
- options = DEFAULT_PARAMS.merge(params)
- options[:fq] = filter_query
- options[:q] = @query
- options
- end
-
- START_XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n".freeze
- START_DELETE_BY_QUERY = "<delete><query>".freeze
- END_DELETE_BY_QUERY = "</query></delete>\n".freeze
- COMMIT = "<commit/>\n".freeze
-
- def commit(opts={})
- SolrHTTPRequest.update("#{START_XML}#{COMMIT}")
- end
-
- def delete_database(db)
- query_data = xml_escape("X_CHEF_database_CHEF_X:#{db}")
- xml = "#{START_XML}#{START_DELETE_BY_QUERY}#{query_data}#{END_DELETE_BY_QUERY}"
- SolrHTTPRequest.update(xml)
- commit
- end
-
- def rebuild_index(db=Chef::Config[:couchdb_database])
- delete_database(db)
-
- results = {}
- [Chef::ApiClient, Chef::Node, Chef::Role, Chef::Environment].each do |klass|
- results[klass.name] = reindex_all(klass) ? "success" : "failed"
- end
- databags = Chef::DataBag.cdb_list(true)
- Chef::Log.info("Reloading #{databags.size.to_s} #{Chef::DataBag} objects into the indexer")
- databags.each { |i| i.add_to_index; i.list(true).each { |x| x.add_to_index } }
- results[Chef::DataBag.name] = "success"
- results
- end
-
- def reindex_all(klass, metadata={})
- begin
- items = klass.cdb_list(true)
- Chef::Log.info("Reloading #{items.size.to_s} #{klass.name} objects into the indexer")
- items.each { |i| i.add_to_index }
- rescue Net::HTTPServerException => e
- # 404s are okay, there might not be any of that kind of object...
- if e.message =~ /Not Found/
- Chef::Log.warn("Could not load #{klass.name} objects from couch for re-indexing (this is ok if you don't have any of these)")
- return false
- else
- raise e
- end
- rescue Exception => e
- Chef::Log.fatal("Chef encountered an error while attempting to load #{klass.name} objects back into the index")
- raise e
- end
- true
- end
-
-
- end
-end
diff --git a/chef/lib/chef/solr_query/lucene.treetop b/chef/lib/chef/solr_query/lucene.treetop
deleted file mode 100644
index 99d3e1f709..0000000000
--- a/chef/lib/chef/solr_query/lucene.treetop
+++ /dev/null
@@ -1,150 +0,0 @@
-#
-# Author:: Seth Falcon (<seth@opscode.com>)
-# Copyright:: Copyright (c) 2010-2011 Opscode, Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-grammar Lucene
-
- rule body
- (expression / space)* <Body>
- end
-
- rule expression
- operation / group / field / field_range / term / string
- end
-
- rule term
- keyword valid_letter+ <Term> / !keyword !"?" valid_letter <Term>
- end
-
- rule field
- field_name ":" (term/string/group) <Field>
- end
-
- rule field_range
- field_name ":" "[" range_value " TO " range_value "]" <InclFieldRange>
- /
- field_name ":" "{" range_value " TO " range_value "}" <ExclFieldRange>
- end
-
- rule field_name
- !keyword valid_letter+ <FieldName>
- end
-
- rule range_value
- valid_letter+ <RangeValue> / "*" <RangeValue>
- end
-
- rule group
- space? '(' body ')' space? <Group>
- end
-
- rule operation
- binary_op / unary_op / fuzzy_op / boost_op
- end
-
- rule unary_op
- not_op / required_op / prohibited_op
- end
-
- rule binary_op
- (group / field / field_range / term) space? boolean_operator space+ body <BinaryOp>
- end
-
- rule boolean_operator
- and_operator / or_operator
- end
-
- rule and_operator
- 'AND' <AndOperator> / '&&' <AndOperator>
- end
-
- rule or_operator
- 'OR' <OrOperator> / '||' <OrOperator>
- end
-
- rule not_op
- not_operator space (group / field / field_range / term / string) <UnaryOp>
- /
- bang_operator space? (group / field / field_range / term / string) <UnaryOp>
- end
-
- rule not_operator
- 'NOT' <NotOperator>
- end
-
- rule bang_operator
- '!' <NotOperator>
- end
-
- rule required_op
- !valid_letter required_operator (term/string) <UnaryOp>
- /
- required_operator (term/string) <UnaryOp>
- end
-
- rule required_operator
- '+' <RequiredOperator>
- end
-
- rule prohibited_op
- !valid_letter prohibited_operator (field/field_range/term/string) <UnaryOp>
- end
-
- rule prohibited_operator
- '-' <ProhibitedOperator>
- end
-
- rule boost_op
- (term/string) '^' fuzzy_param <BoostOp>
- end
-
- rule fuzzy_op
- (term/string) '~' fuzzy_param? (space / !valid_letter) <FuzzyOp>
- end
-
- rule fuzzy_param
- [0-9] '.'? [0-9] <FuzzyParam> / [0-9]+ <FuzzyParam>
- end
-
- rule string
- '"' term (space term)* '"' <Phrase>
- end
-
- rule keyword
- 'AND' / 'OR' / 'NOT'
- end
-
- rule valid_letter
- start_letter+ ([a-zA-Z0-9@*?_.-] / '\\' special_char)*
- end
-
- rule start_letter
- [a-zA-Z0-9@._*] / '\\' special_char
- end
-
- rule end_letter
- [a-zA-Z0-9*?_.] / '\\' special_char
- end
-
- rule special_char
- [-+&|!(){}\[\]^"~*?:\\]
- end
-
- rule space
- [\s]+
- end
-end
diff --git a/chef/lib/chef/solr_query/lucene_nodes.rb b/chef/lib/chef/solr_query/lucene_nodes.rb
deleted file mode 100644
index c2d7777365..0000000000
--- a/chef/lib/chef/solr_query/lucene_nodes.rb
+++ /dev/null
@@ -1,285 +0,0 @@
-#
-# Author:: Seth Falcon (<seth@opscode.com>)
-# Copyright:: Copyright (c) 2010-2011 Opscode, Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require 'treetop'
-
-module Lucene
- SEP = "__=__"
-
- class Term < Treetop::Runtime::SyntaxNode
- def to_array
- "T:#{self.text_value}"
- end
-
- def transform
- self.text_value
- end
- end
-
- class Field < Treetop::Runtime::SyntaxNode
- def to_array
- field = self.elements[0].text_value
- term = self.elements[1].to_array
- "(F:#{field} #{term})"
- end
-
- def transform
- field = self.elements[0].text_value
- term = self.elements[1]
- if term.is_a? Phrase
- str = term.transform
- # remove quotes
- str = str[1 ... (str.length - 1)]
- "content:\"#{field}#{SEP}#{str}\""
- else
- "content:#{field}#{SEP}#{term.transform}"
- end
- end
- end
-
- class FieldRange < Treetop::Runtime::SyntaxNode
-
- def to_array
- field = self.elements[0].text_value
- range_start = self.elements[1].to_array
- range_end = self.elements[2].to_array
- "(FR:#{field} #{left}#{range_start}#{right} #{left}#{range_end}#{right})"
- end
-
- def transform
- field = self.elements[0].text_value
- range_start = self.elements[1].transform
- range_end = self.elements[2].transform
- # FIXME: handle special cases for missing start/end
- if ("*" == range_start && "*" == range_end)
- "content:#{field}#{SEP}*"
- elsif "*" == range_end
- "content:#{left}#{field}#{SEP}#{range_start} TO #{field}#{SEP}\\ufff0#{right}"
- elsif "*" == range_start
- "content:#{left}#{field}#{SEP} TO #{field}#{SEP}#{range_end}#{right}"
- else
- "content:#{left}#{field}#{SEP}#{range_start} TO #{field}#{SEP}#{range_end}#{right}"
- end
- end
-
- end
-
- class InclFieldRange < FieldRange
- def left
- "["
- end
- def right
- "]"
- end
- end
-
- class ExclFieldRange < FieldRange
- def left
- "{"
- end
- def right
- "}"
- end
- end
-
- class RangeValue < Treetop::Runtime::SyntaxNode
- def to_array
- self.text_value
- end
-
- def transform
- to_array
- end
- end
-
- class FieldName < Treetop::Runtime::SyntaxNode
- def to_array
- self.text_value
- end
-
- def transform
- to_array
- end
- end
-
-
- class Body < Treetop::Runtime::SyntaxNode
- def to_array
- self.elements.map { |x| x.to_array }.join(" ")
- end
-
- def transform
- self.elements.map { |x| x.transform }.join(" ")
- end
- end
-
- class Group < Treetop::Runtime::SyntaxNode
- def to_array
- "(" + self.elements[0].to_array + ")"
- end
-
- def transform
- "(" + self.elements[0].transform + ")"
- end
- end
-
- class BinaryOp < Treetop::Runtime::SyntaxNode
- def to_array
- op = self.elements[1].to_array
- a = self.elements[0].to_array
- b = self.elements[2].to_array
- "(#{op} #{a} #{b})"
- end
-
- def transform
- op = self.elements[1].transform
- a = self.elements[0].transform
- b = self.elements[2].transform
- "#{a} #{op} #{b}"
- end
- end
-
- class AndOperator < Treetop::Runtime::SyntaxNode
- def to_array
- "OP:AND"
- end
-
- def transform
- "AND"
- end
- end
-
- class OrOperator < Treetop::Runtime::SyntaxNode
- def to_array
- "OP:OR"
- end
-
- def transform
- "OR"
- end
- end
-
- class FuzzyOp < Treetop::Runtime::SyntaxNode
- def to_array
- a = self.elements[0].to_array
- param = self.elements[1]
- if param
- "(OP:~ #{a} #{param.to_array})"
- else
- "(OP:~ #{a})"
- end
- end
-
- def transform
- a = self.elements[0].transform
- param = self.elements[1]
- if param
- "#{a}~#{param.transform}"
- else
- "#{a}~"
- end
- end
- end
-
- class BoostOp < Treetop::Runtime::SyntaxNode
- def to_array
- a = self.elements[0].to_array
- param = self.elements[1]
- "(OP:^ #{a} #{param.to_array})"
- end
-
- def transform
- a = self.elements[0].transform
- param = self.elements[1]
- "#{a}^#{param.transform}"
- end
- end
-
- class FuzzyParam < Treetop::Runtime::SyntaxNode
- def to_array
- self.text_value
- end
-
- def transform
- self.text_value
- end
- end
-
- class UnaryOp < Treetop::Runtime::SyntaxNode
- def to_array
- op = self.elements[0].to_array
- a = self.elements[1].to_array
- "(#{op} #{a})"
- end
-
- def transform
- op = self.elements[0].transform
- a = self.elements[1].transform
- spc = case op
- when "+", "-"
- ""
- else
- " "
- end
- "#{op}#{spc}#{a}"
- end
-
- end
-
- class NotOperator < Treetop::Runtime::SyntaxNode
- def to_array
- "OP:NOT"
- end
-
- def transform
- "NOT"
- end
-
- end
-
- class RequiredOperator < Treetop::Runtime::SyntaxNode
- def to_array
- "OP:+"
- end
-
- def transform
- "+"
- end
-
- end
-
- class ProhibitedOperator < Treetop::Runtime::SyntaxNode
- def to_array
- "OP:-"
- end
-
- def transform
- "-"
- end
- end
-
- class Phrase < Treetop::Runtime::SyntaxNode
- def to_array
- "STR:#{self.text_value}"
- end
-
- def transform
- "#{self.text_value}"
- end
- end
-end
diff --git a/chef/lib/chef/solr_query/query_transform.rb b/chef/lib/chef/solr_query/query_transform.rb
deleted file mode 100644
index 529f9de482..0000000000
--- a/chef/lib/chef/solr_query/query_transform.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-#
-# Author:: Seth Falcon (<seth@opscode.com>)
-# Copyright:: Copyright (c) 2010-2011 Opscode, Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-require 'treetop'
-require 'chef/solr_query/lucene_nodes'
-
-class Chef
- class Exceptions
- class QueryParseError < StandardError
- end
- end
-end
-
-class Chef
- class SolrQuery
- class QueryTransform
- @@base_path = File.expand_path(File.dirname(__FILE__))
- Treetop.load(File.join(@@base_path, 'lucene.treetop'))
- @@parser = LuceneParser.new
-
- def self.parse(data)
- tree = @@parser.parse(data)
- msg = "Parse error at offset: #{@@parser.index}\n"
- msg += "Reason: #{@@parser.failure_reason}"
- raise Chef::Exceptions::QueryParseError, msg if tree.nil?
- self.clean_tree(tree)
- tree.to_array
- end
-
- def self.transform(data)
- return "*:*" if data == "*:*"
- tree = @@parser.parse(data)
- msg = "Parse error at offset: #{@@parser.index}\n"
- msg += "Reason: #{@@parser.failure_reason}"
- raise Chef::Exceptions::QueryParseError, msg if tree.nil?
- self.clean_tree(tree)
- tree.transform
- end
-
- private
-
- def self.clean_tree(root_node)
- return if root_node.elements.nil?
- root_node.elements.delete_if do |node|
- node.class.name == "Treetop::Runtime::SyntaxNode"
- end
- root_node.elements.each { |node| self.clean_tree(node) }
- end
- end
- end
-end
diff --git a/chef/lib/chef/solr_query/solr_http_request.rb b/chef/lib/chef/solr_query/solr_http_request.rb
deleted file mode 100644
index e7ce1d5675..0000000000
--- a/chef/lib/chef/solr_query/solr_http_request.rb
+++ /dev/null
@@ -1,132 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@opscode.com>)
-# Author:: Daniel DeLeo (<dan@opscode.com>)
-# Copyright:: Copyright (c) 2009-2011 Opscode, Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require 'net/http'
-require 'uri'
-require 'chef/json_compat'
-require 'chef/config'
-
-class Chef
- class SolrQuery
- class SolrHTTPRequest
- CLASS_FOR_METHOD = {:GET => Net::HTTP::Get, :POST => Net::HTTP::Post}
-
- TEXT_XML = {"Content-Type" => "text/xml"}
-
- def self.solr_url=(solr_url)
- @solr_url = solr_url
- @http_client = nil
- @url_prefix = nil
- end
-
- def self.solr_url
- @solr_url || Chef::Config[:solr_url]
- end
-
- def self.http_client
- @http_client ||= begin
- uri = URI.parse(solr_url)
- http_client = Net::HTTP.new(uri.host, uri.port)
- http_client.use_ssl = true if uri.port == 443
- http_client
- end
- end
-
- def self.url_prefix
- @url_prefix ||= begin
- uri = URI.parse(solr_url)
- if uri.path == ""
- "/solr"
- else
- uri.path.gsub(%r{/$}, '')
- end
- end
- end
-
- def self.select(params={})
- url = "#{url_prefix}/select?#{url_join(params)}"
- Chef::Log.debug("Sending #{url} to Solr")
- request = new(:GET, url)
- json_response = request.run("Search Query to Solr '#{solr_url}#{url}'")
- Chef::JSONCompat.from_json(json_response)
- end
-
- def self.update(doc)
- url = "#{url_prefix}/update"
- Chef::Log.debug("POSTing document to SOLR:\n#{doc}")
- request = new(:POST, url, TEXT_XML) { |req| req.body = doc.to_s }
- request.run("POST to Solr '#{solr_url}#{url}', data: #{doc}")
- end
-
- def self.url_join(params_hash={})
- params = params_hash.inject("") do |param_str, params|
- param_str << "#{params[0]}=#{escape(params[1])}&"
- end
- params.chop! # trailing &
- params
- end
-
- def self.escape(s)
- s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
- '%'+$1.unpack('H2'*$1.size).join('%').upcase
- }.tr(' ', '+')
- end
-
- def initialize(method, url, headers=nil)
- args = headers ? [url, headers] : url
- @request = CLASS_FOR_METHOD[method].new(*args)
- yield @request if block_given?
- end
-
- def http_client
- self.class.http_client
- end
-
- def solr_url
- self.class.solr_url
- end
-
- def run(description="HTTP Request to Solr")
- response = http_client.request(@request)
- request_failed!(response, description) unless response.kind_of?(Net::HTTPSuccess)
- response.body
- rescue NoMethodError => e
- # http://redmine.ruby-lang.org/issues/show/2708
- # http://redmine.ruby-lang.org/issues/show/2758
- if e.to_s =~ /#{Regexp.escape(%q|undefined method 'closed?' for nil:NilClass|)}/
- Chef::Log.fatal("#{description} failed. Chef::Exceptions::SolrConnectionError exception: Errno::ECONNREFUSED (net/http undefined method closed?) attempting to contact #{solr_url}")
- Chef::Log.debug("Rescued error in http connect, treating it as Errno::ECONNREFUSED to hide bug in net/http")
- Chef::Log.debug(e.backtrace.join("\n"))
- raise Chef::Exceptions::SolrConnectionError, "Errno::ECONNREFUSED: Connection refused attempting to contact #{solr_url}"
- else
- raise
- end
- end
-
- def request_failed!(response, description='HTTP call')
- Chef::Log.fatal("#{description} failed (#{response.class} #{response.code} #{response.message})")
- response.error!
- rescue Timeout::Error, Errno::EINVAL, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ETIMEDOUT => e
- Chef::Log.debug(e.backtrace.join("\n"))
- raise Chef::Exceptions::SolrConnectionError, "#{e.class.name}: #{e.to_s}"
- end
-
- end
- end
-end
diff --git a/chef/spec/unit/solr_query/query_transform_spec.rb b/chef/spec/unit/solr_query/query_transform_spec.rb
deleted file mode 100644
index f3fc746746..0000000000
--- a/chef/spec/unit/solr_query/query_transform_spec.rb
+++ /dev/null
@@ -1,454 +0,0 @@
-#
-# Author:: Seth Falcon (<seth@opscode.com>)
-# Copyright:: Copyright (c) 2010-2011 Opscode, Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require 'spec_helper'
-require 'chef/solr_query/query_transform'
-
-describe "Chef::SolrQuery::QueryTransform" do
- before(:each) do
- @parser = Chef::SolrQuery::QueryTransform
- @parseError = Chef::Exceptions::QueryParseError
- end
-
- describe "single term queries" do
- basic_terms = %w(a ab 123 a1 2b foo_bar baz-baz)
- basic_terms << " leading"
- basic_terms << "trailing "
- basic_terms += %w(XAND ANDX XOR ORX XNOT NOTX)
- basic_terms.each do |term|
- expect = "T:#{term.strip}"
- it "'#{term}' => #{expect}" do
- @parser.parse(term).should == expect
- end
- end
- describe "invalid" do
- %w(AND OR NOT :).each do |t|
- it "'#{t}' => ParseError" do
- lambda { @parser.parse(t) }.should raise_error(@parseError)
- end
- end
- end
-
- describe "wildcards in terms" do
- it "allows * as a wildcard" do
- @parser.parse("foo*bar").should == "T:foo*bar"
- end
-
- it "allows a single ? as a wildcard" do
- @parser.parse("foo?bar").should == "T:foo?bar"
- end
-
- it "allows multiple ? as fixed length wildcards" do
- @parser.parse("foo???bar").should == "T:foo???bar"
- end
-
- it "allows a leading wildcard with *" do
- # NOTE: This is not valid lucene query syntax. However, our
- # index format and query transformation can allow it because
- # the transformed query ends up with the '*' not in leading
- # position. We decided that allowing it makes sense because
- # queries like ec2:* are useful and many users expect this
- # behavior to work.
- @parser.parse("*foobar").should == "T:*foobar"
- end
-
- it "does not allow a leading wildcard with ?" do
- lambda { @parser.parse("?foobar") }.should raise_error(@parseError)
- end
-
- it "does not allow a leading wildcard with ?" do
- lambda { @parser.parse("afield:?foobar") }.should raise_error(@parseError)
- end
-
- end
-
- describe "escaped special characters in terms" do
- special_chars = ["!", "(", ")", "{", "}", "[", "]", "^", "\"",
- "~", "*", "?", ":", "\\", "&", "|", "+", "-"]
- example_fmts = ['foo%sbar', '%sb', 'a%s']
- special_chars.each do |char|
- example_fmts.each do |fmt|
- input = fmt % ("\\" + char)
- expect = "T:#{input}"
- it "'#{input}' => #{expect}" do
- @parser.parse(input).should == expect
- end
- end
- end
- end
-
- describe "special characters in terms are not allowed" do
- # NOTE: '*' is not a valid start letter for a lucene search
- # term, however, we can support it because of our index
- # structure and query transformation. We decided to keep this
- # flexibility because queries like ec2:* are common and useful.
- prefix_ok = ["!", "+", "-", "*"]
- suffix_ok = ["*", "?", "~", "-"]
- # FIXME: ideally, '!' would not be allowed in the middle of a
- # term. Currently we parse foo!bar the same as foo !bar.
- # Also '+' might be nice to disallow
- embed_ok = ["*", "?", ":", "-", "!", "+"]
- special_chars = ["!", "(", ")", "{", "}", "[", "]", "^", "\"",
- "~", "*", "?", ":", "\\", "&", "|", "+", "-"]
- example_fmts = {
- :prefix => '%sb',
- :middle => 'foo%sbar',
- :suffix => 'a%s'
- }
- special_chars.each do |char|
- example_fmts.keys.each do |key|
- fmt = example_fmts[key]
- if key == :prefix && prefix_ok.include?(char)
- :pass
- elsif key == :middle && embed_ok.include?(char)
- :pass
- elsif key == :suffix && suffix_ok.include?(char)
- :pass
- else
- input = fmt % char
- it "disallows: '#{input}'" do
- lambda { @parser.parse(input) }.should raise_error(@parseError)
- end
- end
- end
- end
- end
-
- end
-
- describe "multiple terms" do
- it "should allow multiple terms" do
- @parser.parse("a b cdefg").should == "T:a T:b T:cdefg"
- end
- end
-
- describe "boolean queries" do
- describe "two term basic and/or" do
- binary_operators = [['AND', 'AND'], ['&&', 'AND'], ['OR', 'OR'], ['||', 'OR']]
- binary_operators.each do |op, op_name|
- expect = "(OP:#{op_name} T:t1 T:t2)"
- it "should parse 't1 #{op} t2' => #{expect}" do
- @parser.parse("t1 #{op} t2").should == expect
- end
- end
- end
-
- it "should allow a string of terms with ands and ors" do
- expect = "(OP:AND T:t1 (OP:OR T:t2 (OP:AND T:t3 T:t4)))"
- @parser.parse("t1 AND t2 OR t3 AND t4").should == expect
- end
- end
-
- describe "grouping with parens" do
- it "should create a single group for (aterm)" do
- @parser.parse("(aterm)").should == "(T:aterm)"
- end
-
- describe "and booleans" do
-
- %w(AND &&).each do |op|
- expect = "((OP:AND T:a T:b))"
- input = "(a #{op} b)"
- it "parses #{input} => #{expect}" do
- @parser.parse(input).should == expect
- end
- end
-
- %w(OR ||).each do |op|
- expect = "((OP:OR T:a T:b))"
- input = "(a #{op} b)"
- it "parses #{input} => #{expect}" do
- @parser.parse(input).should == expect
- end
- end
-
- it "should handle a LHS group" do
- expect = "(OP:OR ((OP:AND T:a T:b)) T:c)"
- @parser.parse("(a && b) OR c").should == expect
- @parser.parse("(a && b) || c").should == expect
- end
-
- it "should handle a RHS group" do
- expect = "(OP:OR T:c ((OP:AND T:a T:b)))"
- @parser.parse("c OR (a && b)").should == expect
- @parser.parse("c OR (a AND b)").should == expect
- end
-
- it "should handle both sides as groups" do
- expect = "(OP:OR ((OP:AND T:c T:d)) ((OP:AND T:a T:b)))"
- @parser.parse("(c AND d) OR (a && b)").should == expect
- end
- end
- end
-
- describe "NOT queries" do
- # input, output
- [
- ["a NOT b", "T:a (OP:NOT T:b)"],
- ["a ! b", "T:a (OP:NOT T:b)"],
- ["a !b", "T:a (OP:NOT T:b)"],
- ["a NOT (b || c)", "T:a (OP:NOT ((OP:OR T:b T:c)))"],
- ["a ! (b || c)", "T:a (OP:NOT ((OP:OR T:b T:c)))"],
- ["a !(b || c)", "T:a (OP:NOT ((OP:OR T:b T:c)))"]
- ].each do |input, expected|
- it "should parse '#{input}' => #{expected.inspect}" do
- @parser.parse(input).should == expected
- end
- end
-
- ["NOT", "a NOT", "(NOT)"].each do |d|
- it "should raise a ParseError on '#{d}'" do
- lambda { @parser.parse(d) }.should raise_error(@parseError)
- end
- end
- end
-
- describe 'required and prohibited prefixes (+/-)' do
- ["+", "-"].each do |kind|
- [
- ["#{kind}foo", "(OP:#{kind} T:foo)"],
- ["bar #{kind}foo", "T:bar (OP:#{kind} T:foo)"],
- ["(#{kind}oneA twoA) b", "((OP:#{kind} T:oneA) T:twoA) T:b"]
- ].each do |input, expect|
- it "should parse '#{input} => #{expect.inspect}" do
- @parser.parse(input).should == expect
- end
- end
- end
-
- # it 'ignores + embedded in a term' do
- # @parser.parse("one+two").should == "T:one+two"
- # end
-
- it 'ignores - embedded in a term' do
- @parser.parse("one-two").should == "T:one-two"
- end
-
- it "allows a trailing dash" do
- @parser.parse("one-").should == "T:one-"
- end
-
- end
-
- describe "phrases (strings)" do
- phrases = [['"single"', 'STR:"single"'],
- ['"two term"', 'STR:"two term"'],
- ['"has \"escaped\" quote\"s"', 'STR:"has \"escaped\" quote\"s"']
- ]
- phrases.each do |phrase, expect|
- it "'#{phrase}' => #{expect}" do
- @parser.parse(phrase).should == expect
- end
- end
-
- describe "invalid" do
- bad = ['""', '":not:a:term"', '"a :bad:']
- bad.each do |t|
- it "'#{t}' => ParseError" do
- lambda { @parser.parse(t) }.should raise_error(@parseError)
- end
- end
- end
-
- it "allows phrases to be required with '+'" do
- @parser.parse('+"a b c"').should == '(OP:+ STR:"a b c")'
- end
-
- it "allows phrases to be prohibited with '-'" do
- @parser.parse('-"a b c"').should == '(OP:- STR:"a b c")'
- end
-
- it "allows phrases to be excluded with NOT" do
- @parser.parse('a NOT "b c"').should == 'T:a (OP:NOT STR:"b c")'
- end
-
- end
-
- describe "fields" do
- it "parses a term annotated with a field" do
- @parser.parse("afield:aterm").should == "(F:afield T:aterm)"
- end
-
- it "allows underscore in a field name" do
- @parser.parse("a_field:aterm").should == "(F:a_field T:aterm)"
- end
-
- it "parses a group annotated with a field" do
- @parser.parse("afield:(a b c)").should == "(F:afield (T:a T:b T:c))"
- end
-
- it "parses a phrase annotated with a field" do
- @parser.parse('afield:"a b c"').should == '(F:afield STR:"a b c")'
- end
-
- it "allows @ in a term" do
- @parser.parse('afield:foo@acme.org').should == '(F:afield T:foo@acme.org)'
- end
-
- describe "and binary operators" do
- examples = [
- ['term1 AND afield:term2', "(OP:AND T:term1 (F:afield T:term2))"],
- ['afield:term1 AND term2', "(OP:AND (F:afield T:term1) T:term2)"],
- ['afield:term1 AND bfield:term2',
- "(OP:AND (F:afield T:term1) (F:bfield T:term2))"]]
- examples.each do |input, want|
- it "'#{input}' => '#{want}'" do
- @parser.parse(input).should == want
- end
- end
- end
-
- describe "and unary operators" do
- examples = [
- ['term1 AND NOT afield:term2',
- "(OP:AND T:term1 (OP:NOT (F:afield T:term2)))"],
- ['term1 AND ! afield:term2',
- "(OP:AND T:term1 (OP:NOT (F:afield T:term2)))"],
- ['term1 AND !afield:term2',
- "(OP:AND T:term1 (OP:NOT (F:afield T:term2)))"],
- ['term1 AND -afield:term2',
- "(OP:AND T:term1 (OP:- (F:afield T:term2)))"],
- ['-afield:[* TO *]',
- "(OP:- (FR:afield [*] [*]))"]
- ]
- examples.each do |input, want|
- it "#{input} => #{want}" do
- @parser.parse(input).should == want
- end
- end
- end
- end
-
- describe "range queries" do
- before(:each) do
- @kinds = {
- "inclusive" => {:left => "[", :right => "]"},
- "exclusive" => {:left => "{", :right => "}"}
- }
- end
-
- def make_expect(kind, field, s, e)
- expect_fmt = "(FR:%s %s%s%s %s%s%s)"
- left = @kinds[kind][:left]
- right = @kinds[kind][:right]
- expect_fmt % [field, left, s, right, left, e, right]
- end
-
- def make_query(kind, field, s, e)
- query_fmt = "%s:%s%s TO %s%s"
- left = @kinds[kind][:left]
- right = @kinds[kind][:right]
- query_fmt % [field, left, s, e, right]
- end
-
- ["inclusive", "exclusive"].each do |kind|
- tests = [["afield", "start", "end"],
- ["afield", "start", "*"],
- ["afield", "*", "end"],
- ["afield", "*", "*"]
- ]
- tests.each do |field, s, e|
- it "parses an #{kind} range query #{s} TO #{e}" do
- expect = make_expect(kind, field, s, e)
- query = make_query(kind, field, s, e)
- @parser.parse(query).should == expect
- end
- end
- end
-
- describe "and binary operators" do
- [["afield:[start TO end] AND term",
- "(OP:AND (FR:afield [start] [end]) T:term)"],
- ["term OR afield:[start TO end]",
- "(OP:OR T:term (FR:afield [start] [end]))"],
- ["f1:[s1 TO e1] OR f2:[s2 TO e2]",
- "(OP:OR (FR:f1 [s1] [e1]) (FR:f2 [s2] [e2]))"]
- ].each do |q, want|
- it "parses '#{q}'" do
- @parser.parse(q).should == want
- end
- end
- end
-
- describe "and unary operators" do
- [["t1 NOT afield:[start TO end]",
- "T:t1 (OP:NOT (FR:afield [start] [end]))"]
- ].each do |input, want|
- it "#{input} => #{want}" do
- @parser.parse(input).should == want
- end
- end
- end
- end
-
- describe "proximity query" do
- [
- ['"one two"~10', '(OP:~ STR:"one two" 10)'],
- ['word~', '(OP:~ T:word)'],
- ['word~0.5', '(OP:~ T:word 0.5)']
- ].each do |input, expect|
- it "'#{input}' => #{expect}" do
- @parser.parse(input).should == expect
- end
- end
- end
-
- describe "term boosting" do
- [
- ['"one two"^10', '(OP:^ STR:"one two" 10)'],
- ['word^0.5', '(OP:^ T:word 0.5)']
- ].each do |input, expect|
- it "'#{input}' => #{expect}" do
- @parser.parse(input).should == expect
- end
- end
-
- it "should fail to parse if no boosting argument is given" do
- lambda { @parser.parse("foo^")}.should raise_error(@parseError)
- end
- end
-
- describe "examples" do
- examples = [['tags:apples*.for.eating.com', "(F:tags T:apples*.for.eating.com)"],
- ['ohai_time:[1234.567 TO *]', "(FR:ohai_time [1234.567] [*])"],
- ['ohai_time:[* TO 1234.567]', "(FR:ohai_time [*] [1234.567])"],
- ['ohai_time:[* TO *]', "(FR:ohai_time [*] [*])"]]
- # ['aterm AND afield:aterm', "((OP:AND T:aterm ((F:afield T:aterm))))"],
- # ['role:prod AND aterm', "blah"],
- # ['role:prod AND xy:true', "blah"]]
- examples.each do |input, want|
- it "'#{input}' => '#{want}'" do
- @parser.parse(input).should == want
- end
- end
- end
-
- describe "transform queries for solr schema" do
- testcase_file = "#{CHEF_SPEC_DATA}/search_queries_to_transform.txt"
- lines = File.readlines(testcase_file).map { |line| line.strip }
- lines = lines.select { |line| !line.empty? }
- testcases = Hash[*(lines)]
- testcases.keys.sort.each do |input|
- expected = testcases[input]
- it "from> #{input}\n to> #{expected}\n" do
- @parser.transform(input).should == expected
- end
- end
- end
-
-end
diff --git a/chef/spec/unit/solr_query/solr_http_request_spec.rb b/chef/spec/unit/solr_query/solr_http_request_spec.rb
deleted file mode 100644
index c70c347a14..0000000000
--- a/chef/spec/unit/solr_query/solr_http_request_spec.rb
+++ /dev/null
@@ -1,244 +0,0 @@
-# Author:: Daniel DeLeo (<dan@opscode.com>)
-# Copyright:: Copyright (c) 2011 Opscode, inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require 'spec_helper'
-
-require 'chef/solr_query'
-require 'net/http'
-
-describe Chef::SolrQuery::SolrHTTPRequest do
- before do
- Chef::Config[:solr_url] = "http://example.com:8983"
- Chef::SolrQuery::SolrHTTPRequest.instance_variable_set(:@solr_url, nil)
- Chef::SolrQuery::SolrHTTPRequest.instance_variable_set(:@url_prefix, nil)
-
- @request = Chef::SolrQuery::SolrHTTPRequest.new(:GET, '/solr/select')
- end
-
- it "defaults to using the configured solr_url" do
- Chef::SolrQuery::SolrHTTPRequest.solr_url.should == "http://example.com:8983"
- end
-
- it "supports solr_url with a path" do
- Chef::Config[:solr_url] = "http://example.com:8983/test"
- Chef::SolrQuery::SolrHTTPRequest.instance_variable_set(:@solr_url, nil)
-
- Chef::SolrQuery::SolrHTTPRequest.solr_url.should == "http://example.com:8983/test"
- end
-
- it "updates the Solr URL as you like" do
- Chef::SolrQuery::SolrHTTPRequest.solr_url = "http://chunkybacon.org:1234"
- Chef::SolrQuery::SolrHTTPRequest.solr_url.should == "http://chunkybacon.org:1234"
- end
-
- it "updates the URL prefix with a path" do
- Chef::SolrQuery::SolrHTTPRequest.solr_url = "http://chunkybacon.org:1234/something"
- Chef::SolrQuery::SolrHTTPRequest.url_prefix.should == "/something"
- end
-
- it "removes extra / at the end of solr_url" do
- Chef::SolrQuery::SolrHTTPRequest.solr_url = "http://chunkybacon.org:1234/extra/"
- Chef::SolrQuery::SolrHTTPRequest.url_prefix.should == "/extra"
- end
-
- it "creates a Net::HTTP client for the base Solr URL" do
- Chef::SolrQuery::SolrHTTPRequest.solr_url = "http://chunkybacon.org:1234"
- http_client = Chef::SolrQuery::SolrHTTPRequest.http_client
- http_client.address.should == "chunkybacon.org"
- http_client.port.should == 1234
- end
-
- it "creates a Net::HTTP client for the base Solr URL ignoring the path" do
- Chef::SolrQuery::SolrHTTPRequest.solr_url = "http://chunkybacon.org:1234/test"
- http_client = Chef::SolrQuery::SolrHTTPRequest.http_client
- http_client.address.should == "chunkybacon.org"
- http_client.port.should == 1234
- end
-
- it "defaults url_prefix to /solr if the configured solr_url has no path" do
- Chef::SolrQuery::SolrHTTPRequest.solr_url = "http://chunkybacon.org:1234"
- Chef::SolrQuery::SolrHTTPRequest.url_prefix.should == "/solr"
- end
-
- it "defaults url_prefix to the path from the configured solr_url" do
- Chef::SolrQuery::SolrHTTPRequest.solr_url = "http://chunkybacon.org:1234/test"
- Chef::SolrQuery::SolrHTTPRequest.url_prefix.should == "/test"
- end
-
- describe "when configured with the Solr URL" do
- before do
- @http_response = mock(
- "Net::HTTP::Response",
- :kind_of? => Net::HTTPSuccess,
- :body => "{ :some => :hash }"
- )
- @http_request = mock(
- "Net::HTTP::Request",
- :body= => true
- )
- @http = mock("Net::HTTP", :request => @http_response)
- Chef::SolrQuery::SolrHTTPRequest.stub!(:http_client).and_return(@http)
- end
-
- describe "when executing a select query" do
- before(:each) do
- @http_response = mock(
- "Net::HTTP::Response",
- :kind_of? => Net::HTTPSuccess,
- :body => '{"some": "hash" }'
- )
- @solr = Chef::SolrQuery.from_params(:type => 'node',
- :q => "hostname:latte")
- @params = @solr.to_hash
- @http = mock("Net::HTTP", :request => @http_response)
- Chef::SolrQuery::SolrHTTPRequest.stub!(:http_client).and_return(@http)
- end
-
- describe "when the HTTP call is successful" do
- it "should call get to /solr/select with the escaped query" do
- txfm_query = "q=content%3Ahostname__%3D__latte"
- Net::HTTP::Get.should_receive(:new).with(%r(/solr/select?.+#{txfm_query}))
- Chef::SolrQuery::SolrHTTPRequest.select(@params)
- end
-
- it "uses Solr's JSON response format" do
- Net::HTTP::Get.should_receive(:new).with(%r(wt=json))
- Chef::SolrQuery::SolrHTTPRequest.select(@params)
- end
-
- it "uses indent=off to get a compact response" do
- Net::HTTP::Get.should_receive(:new).with(%r(indent=off))
- Chef::SolrQuery::SolrHTTPRequest.select(@params)
- end
-
- it "uses the filter query to restrict the result set" do
- filter_query =@solr.filter_query.gsub('+', '%2B').gsub(':', "%3A").gsub(' ', '+')
- Net::HTTP::Get.should_receive(:new).with(/fq=#{Regexp.escape(filter_query)}/)
- Chef::SolrQuery::SolrHTTPRequest.select(@params)
- end
-
- it "returns the evaluated response body" do
- res = Chef::SolrQuery::SolrHTTPRequest.select(@params)
- res.should == {"some" => "hash" }
- end
- end
- end
-
- describe "when updating" do
- before do
- Net::HTTP::Post.stub!(:new).and_return(@http_request)
- end
-
- it "should post to /solr/update" do
- @doc = "<xml is the old tldr>"
- Net::HTTP::Post.should_receive(:new).with("/solr/update", "Content-Type" => "text/xml").and_return(@http_request)
- Chef::SolrQuery::SolrHTTPRequest.update(@doc)
- end
-
- it "should set the body of the request to the stringified doc" do
- @http_request.should_receive(:body=).with("foo")
- Chef::SolrQuery::SolrHTTPRequest.update(:foo)
- end
-
- it "should send the request to solr" do
- @http.should_receive(:request).with(@http_request).and_return(@http_response)
- Chef::SolrQuery::SolrHTTPRequest.update(:foo)
- end
-
- end
-
- describe "when the HTTP call is unsuccessful" do
- [Timeout::Error, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EINVAL].each do |exception|
- it "should rescue, log an error message, and raise a SolrConnectionError encountering exception #{exception}" do
- response = mock("Net:HTTPResponse test double", :code => 500, :message => "oops", :class => exception)
- @http.should_receive(:request).with(instance_of(Net::HTTP::Get)).and_return(response)
- response.should_receive(:error!).and_raise(exception)
- Chef::Log.should_receive(:fatal).with("Search Query to Solr failed (#{exception} 500 oops)")
-
- lambda {@request.run('Search Query to Solr')}.should raise_error(Chef::Exceptions::SolrConnectionError)
- end
- end
-
- it "should rescue, log an error message, and raise a SolrConnectionError when encountering exception NoMethodError and net/http closed? bug" do
- @no_method_error = NoMethodError.new("undefined method 'closed\?' for nil:NilClass")
- @http.should_receive(:request).with(instance_of(Net::HTTP::Get)).and_raise(@no_method_error)
- Chef::Log.should_receive(:fatal).with("HTTP Request to Solr failed. Chef::Exceptions::SolrConnectionError exception: Errno::ECONNREFUSED (net/http undefined method closed?) attempting to contact http://example.com:8983")
- lambda {
- @request.run
- }.should raise_error(Chef::Exceptions::SolrConnectionError)
- end
- end
-
- end
-
- describe "when configured with the Solr URL with a path" do
- before do
- Chef::Config[:solr_url] = "http://example.com:8983/test"
- Chef::SolrQuery::SolrHTTPRequest.instance_variable_set(:@solr_url, nil)
- Chef::SolrQuery::SolrHTTPRequest.instance_variable_set(:@url_prefix, nil)
-
- @request = Chef::SolrQuery::SolrHTTPRequest.new(:GET, '/solr/select')
-
- @http_response = mock(
- "Net::HTTP::Response",
- :kind_of? => Net::HTTPSuccess,
- :body => "{ :some => :hash }"
- )
- @http_request = mock(
- "Net::HTTP::Request",
- :body= => true
- )
- @http = mock("Net::HTTP", :request => @http_response)
- Chef::SolrQuery::SolrHTTPRequest.stub!(:http_client).and_return(@http)
- end
-
- describe "when executing a select query" do
- before(:each) do
- @http_response = mock(
- "Net::HTTP::Response",
- :kind_of? => Net::HTTPSuccess,
- :body => '{"some": "hash" }'
- )
- @solr = Chef::SolrQuery.from_params(:type => 'node',
- :q => "hostname:latte")
- @params = @solr.to_hash
- @http = mock("Net::HTTP", :request => @http_response)
- Chef::SolrQuery::SolrHTTPRequest.stub!(:http_client).and_return(@http)
- end
-
- describe "when the HTTP call is successful" do
- it "should call get to /test/select with the escaped query" do
- txfm_query = "q=content%3Ahostname__%3D__latte"
- Net::HTTP::Get.should_receive(:new).with(%r(/test/select?.+#{txfm_query}))
- Chef::SolrQuery::SolrHTTPRequest.select(@params)
- end
- end
- end
-
- describe "when updating" do
- before do
- Net::HTTP::Post.stub!(:new).and_return(@http_request)
- end
-
- it "should post to /test/update" do
- @doc = "<xml is the old tldr>"
- Net::HTTP::Post.should_receive(:new).with("/test/update", "Content-Type" => "text/xml").and_return(@http_request)
- Chef::SolrQuery::SolrHTTPRequest.update(@doc)
- end
- end
- end
-end
diff --git a/chef/spec/unit/solr_query_spec.rb b/chef/spec/unit/solr_query_spec.rb
deleted file mode 100644
index 8b48011713..0000000000
--- a/chef/spec/unit/solr_query_spec.rb
+++ /dev/null
@@ -1,203 +0,0 @@
-# Author:: Daniel DeLeo (<dan@opscode.com>)
-# Copyright:: Copyright (c) 2010, 2011 Opscode, inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require 'spec_helper'
-
-require 'chef/solr_query'
-require 'net/http'
-
-#require 'rspec/mocks'
-
-describe Chef::SolrQuery do
- before do
- Chef::SolrQuery::SolrHTTPRequest.solr_url = "http://example.com:8983"
-
- @http_response = mock(
- "Net::HTTP::Response",
- :kind_of? => Net::HTTPSuccess,
- :body => "{ :some => :hash }"
- )
- @http_request = mock(
- "Net::HTTP::Request",
- :body= => true
- )
- @http = mock("Net::HTTP", :request => @http_response)
- Chef::SolrQuery::SolrHTTPRequest.stub!(:http_client).and_return(@http)
- Net::HTTP::Post.stub!(:new).and_return(@http_request)
- Net::HTTP::Get.stub!(:new).and_return(@http_request)
- @doc = { "foo" => "bar" }
- end
-
- before(:each) do
- @solr = Chef::SolrQuery.new
- end
-
- it "sets filter query params" do
- @solr.filter_by(:database => 'chef')
- @solr.filter_query.should == "+X_CHEF_database_CHEF_X:chef"
- end
-
- it "filters by type when querying for a builtin type" do
- @solr.filter_by_type("node")
- @solr.filter_query.should == "+X_CHEF_type_CHEF_X:node"
- end
-
- it "filters by type for data bag items" do
- @solr.filter_by_type("users")
- @solr.filter_query.split(" ").sort.should == ['+X_CHEF_type_CHEF_X:data_bag_item', '+data_bag:users']
- end
-
- it "stores the main query" do
- @solr.query = "role:prod AND tags:chef-server"
- @solr.query.should == "role:prod AND tags:chef-server"
- end
-
- describe "when generating query params for select" do
- before(:each) do
- @solr = Chef::SolrQuery.from_params(:type => 'node', :q => "hostname:latte")
- @params = @solr.to_hash
- end
-
- it "includes the query as q" do
- @params[:q].should == "content:hostname__=__latte"
- end
-
- it "sets the response format to json" do
- @params[:wt].should == "json"
- end
-
- it "uses indent=off to get a compact response" do
- @params[:indent].should == "off"
- end
-
- it "includes the filter query to restrict the result set" do
- @params[:fq].should == @solr.filter_query
- end
-
- it "defaults to returning 1000 rows" do
- @params[:rows].should == 1000
- end
-
- it "returns the number of rows requested" do
- @solr.params[:rows] = 500
- @solr.to_hash[:rows].should == 500
- end
-
- it "offsets the row selection if requested" do
- @solr.params[:start] = 500
- @solr.to_hash[:start].should == 500
- end
-
- end
-
- describe "when querying solr" do
- before do
- @couchdb = mock("CouchDB Test Double", :couchdb_database => "chunky_bacon")
- @couchdb.stub!(:kind_of?).with(Chef::CouchDB).and_return(true) #ugh.
- @solr = Chef::SolrQuery.from_params({:type => 'node', :q => "hostname:latte", :start => 10, :rows => 5}, @couchdb)
- @docs = [1,2,3,4,5].map { |doc_id| {'X_CHEF_id_CHEF_X' => doc_id} }
- @solr_response = {"response" => {"docs" => @docs, "start" => 10, "results" => 123}}
- Chef::SolrQuery::SolrHTTPRequest.should_receive(:select).with(@solr.to_hash).and_return(@solr_response)
- end
-
- it "it collects the document ids from the response" do
- @solr.object_ids.should == [1,2,3,4,5]
- end
-
- it "does a bulk get of the objects from CouchDB" do
- @couchdb.should_receive(:bulk_get).with([1,2,3,4,5]).and_return(%w{obj1 obj2 obj3 obj4 obj5})
- @solr.objects.should == %w{obj1 obj2 obj3 obj4 obj5}
- end
-
- end
-
- describe "when forcing a Solr commit" do
- it "sends valid commit xml to solr" do
- Chef::SolrQuery::SolrHTTPRequest.should_receive(:update).with("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<commit/>\n")
- @solr.commit
- end
- end
-
- describe "when deleting a database from Solr" do
- it "sends a valid delete query to solr and forces a commit" do
- Chef::SolrQuery::SolrHTTPRequest.should_receive(:update).with("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<delete><query>X_CHEF_database_CHEF_X:chef</query></delete>\n")
- @solr.should_receive(:commit)
- @solr.delete_database("chef")
- end
- end
-
- describe "rebuilding the index" do
- before do
- Chef::Config[:couchdb_database] = "chunky_bacon"
- end
-
- it "deletes the index and commits" do
- @solr.should_receive(:delete_database).with("chunky_bacon")
- @solr.stub!(:reindex_all)
- Chef::DataBag.stub!(:cdb_list).and_return([])
- @solr.rebuild_index
- end
-
- it "reindexes Chef::ApiClient, Chef::Node, and Chef::Role objects, reporting the results as a hash" do
- @solr.should_receive(:delete_database).with("chunky_bacon")
- @solr.should_receive(:reindex_all).with(Chef::ApiClient).and_return(true)
- @solr.should_receive(:reindex_all).with(Chef::Environment).and_return(true)
- @solr.should_receive(:reindex_all).with(Chef::Node).and_return(true)
- @solr.should_receive(:reindex_all).with(Chef::Role).and_return(true)
- Chef::DataBag.stub!(:cdb_list).and_return([])
-
- result = @solr.rebuild_index
- result["Chef::ApiClient"].should == "success"
- result["Chef::Node"].should == "success"
- result["Chef::Role"].should == "success"
- end
-
- it "does not reindex Chef::OpenIDRegistration or Chef::WebUIUser objects" do
- # hi there. the reason we're specifying this behavior is because these objects
- # are not properly indexed in the first place and trying to reindex them
- # tickles a bug in our CamelCase to snake_case code. See CHEF-1009.
- @solr.should_receive(:delete_database).with("chunky_bacon")
- @solr.stub!(:reindex_all).with(Chef::ApiClient)
- @solr.stub!(:reindex_all).with(Chef::Node)
- @solr.stub!(:reindex_all).with(Chef::Role)
- @solr.should_not_receive(:reindex_all).with(Chef::OpenIDRegistration)
- @solr.should_not_receive(:reindex_all).with(Chef::WebUIUser)
- Chef::DataBag.stub!(:cdb_list).and_return([])
-
- @solr.rebuild_index
- end
-
- it "reindexes databags" do
- one_data_item = Chef::DataBagItem.new
- one_data_item.raw_data = {"maybe"=>"snakes actually are evil", "id" => "just_sayin"}
- two_data_item = Chef::DataBagItem.new
- two_data_item.raw_data = {"tone_depth"=>"rumble_fish", "id" => "eff_yes"}
- data_bag = Chef::DataBag.new
- data_bag.stub!(:list).and_return([one_data_item, two_data_item])
-
- @solr.should_receive(:delete_database).with("chunky_bacon")
- @solr.stub!(:reindex_all)
- Chef::DataBag.stub!(:cdb_list).and_return([data_bag])
-
- data_bag.should_receive(:add_to_index)
- one_data_item.should_receive(:add_to_index)
- two_data_item.should_receive(:add_to_index)
-
- @solr.rebuild_index["Chef::DataBag"].should == "success"
- end
- end
-end