Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Full Chef 11 compatibility update. #18

Merged
merged 4 commits into from
Feb 20, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 30 additions & 96 deletions libraries/search.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#
# Authors:
# Markus Korn <markus.korn@edelight.de>
# Seth Chisamore <schisamo@opscode.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand All @@ -19,109 +20,42 @@

if Chef::Config[:solo]

if (defined? require_relative).nil?
# definition of 'require_relative' for ruby < 1.9, found on stackoverflow.com
def require_relative(relative_feature)
c = caller.first
fail "Can't parse #{c}" unless c.rindex(/:\d+(:in `.*')?$/)
file = $`
if /\A\((.*)\)/ =~ file # eval, etc.
raise LoadError, "require_relative is called in #{$1}"
end
absolute = File.expand_path(relative_feature, File.dirname(file))
require absolute
end
end

require_relative 'parser.rb'
# add currrent dir to load path
$: << File.dirname(__FILE__)

class Chef
module Mixin
module Language

# Overwrite the search method of recipes to operate locally by using
# data found in data_bags.
# Only very basic lucene syntax is supported and also sorting the result
# is not implemented, if this search method does not support a given query
# an exception is raised.
# This search() method returns a block iterator or an Array, depending
# on how this method is called.
def search(obj, query=nil, sort=nil, start=0, rows=1000, &block)
if !sort.nil?
raise "Sorting search results is not supported"
end
_query = Query.parse(query)
if _query.nil?
raise "Query #{query} is not supported"
end
_result = []
# All chef/solr_query/* classes were removed in Chef 11; Load vendored copy
# that ships with this cookbook
$: << File.expand_path("vendor", File.dirname(__FILE__)) if Chef::VERSION.to_i >= 11

case obj
when :node
nodes = search_nodes(_query, start, rows, &block)
_result += nodes
when :role
roles = search_roles(_query, start, rows, &block)
_result += roles
else
bags = search_data_bag(_query, obj, start, rows, &block)
_result += bags
end


if block_given?
pos = 0
while (pos >= start and pos < (start + rows) and pos < _result.size)
yield _result[pos]
pos += 1
end
else
return _result.slice(start, rows)
end
end
# Ensure the treetop gem is installed and available
begin
require 'treetop'
rescue LoadError
run_context = Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
Chef::Resource::ChefGem.new("treetop", run_context).run_action(:install)
end

def search_nodes(_query, start, rows, &block)
_result = []
Dir.glob(File.join(Chef::Config[:data_bag_path], "node", "*.json")).map do |f|
# parse and hashify the node
node = JSON.parse(IO.read(f))
if _query.match(node.to_hash)
_result << node
end
end
return _result
end
require 'search/overrides'
require 'search/parser'

def search_roles(_query, start, rows, &block)
_result = []
Dir.glob(File.join(Chef::Config[:role_path], "*.json")).map do |f|
# parse and hashify the role
role = JSON.parse(IO.read(f))
if _query.match(role.to_hash)
_result << role
end
end
return _result
end
module Search; class Helper; end; end

def search_data_bag(_query, bag_name, start, rows, &block)
_result = []
data_bag(bag_name.to_s).each do |bag_item_id|
bag_item = data_bag_item(bag_name.to_s, bag_item_id)
if _query.match(bag_item)
_result << bag_item
end
end
return _result
end
# The search and data_bag related methods moved form `Chef::Mixin::Language`
# to `Chef::DSL::DataQuery` in Chef 11.
if Chef::VERSION.to_i >= 11
module Chef::DSL::DataQuery
def self.included(base)
base.send(:include, Search::Overrides)
end
end
end

class QuerySearchHelper
class << self
include Chef::Mixin::Language
Search::Helper.send(:include, Chef::DSL::DataQuery)
else
module Chef::Mixin::Language
def self.included(base)
base.send(:include, Search::Overrides)
end
end
Search::Helper.send(:include, Chef::Mixin::Language)
end

class Chef
Expand All @@ -130,7 +64,7 @@ class Query
def initialize(*args)
end
def search(*args, &block)
QuerySearchHelper.search(*args, &block)
::Search::Helper.new.search(*args, &block)
end
end
end
Expand Down
99 changes: 99 additions & 0 deletions libraries/search/overrides.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#
# Copyright 2011, edelight GmbH
#
# Authors:
# Markus Korn <markus.korn@edelight.de>
# Seth Chisamore <schisamo@opscode.com>
#
# 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.
#

module Search
module Overrides
# Overwrite the search method of recipes to operate locally by using
# data found in data_bags.
# Only very basic lucene syntax is supported and also sorting the result
# is not implemented, if this search method does not support a given query
# an exception is raised.
# This search() method returns a block iterator or an Array, depending
# on how this method is called.
def search(obj, query=nil, sort=nil, start=0, rows=1000, &block)
if !sort.nil?
raise "Sorting search results is not supported"
end
_query = Query.parse(query)
if _query.nil?
raise "Query #{query} is not supported"
end
_result = []

case obj
when :node
nodes = search_nodes(_query, start, rows, &block)
_result += nodes
when :role
roles = search_roles(_query, start, rows, &block)
_result += roles
else
bags = search_data_bag(_query, obj, start, rows, &block)
_result += bags
end


if block_given?
pos = 0
while (pos >= start and pos < (start + rows) and pos < _result.size)
yield _result[pos]
pos += 1
end
else
return _result.slice(start, rows)
end
end

def search_nodes(_query, start, rows, &block)
_result = []
Dir.glob(File.join(Chef::Config[:data_bag_path], "node", "*.json")).map do |f|
# parse and hashify the node
node = Chef::Node.json_create(JSON.parse(IO.read(f)))
if _query.match(node.to_hash)
_result << node
end
end
return _result
end

def search_roles(_query, start, rows, &block)
_result = []
Dir.glob(File.join(Chef::Config[:role_path], "*.json")).map do |f|
# parse and hashify the role
role = Chef::Role.json_create(JSON.parse(IO.read(f)))
if _query.match(role.to_hash)
_result << role
end
end
return _result
end

def search_data_bag(_query, bag_name, start, rows, &block)
_result = []
data_bag(bag_name.to_s).each do |bag_item_id|
bag_item = data_bag_item(bag_name.to_s, bag_item_id)
if _query.match(bag_item)
_result << bag_item
end
end
return _result
end
end
end
37 changes: 18 additions & 19 deletions libraries/parser.rb → libraries/search/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
# limitations under the License.
#

require 'treetop'
require 'chef/solr_query/query_transform'

# mock QueryTransform such that we can access the location of the lucene grammar
Expand Down Expand Up @@ -70,13 +69,13 @@ def match( item )
end
end
end

# we don't support range matches
# range of integers would be easy to implement
# but string ranges are hard
class FiledRange < Treetop::Runtime::SyntaxNode
end

# we handle '[* TO *]' as a special case since it is common in
# cookbooks for matching the existence of keys
class InclFieldRange
Expand All @@ -91,13 +90,13 @@ def match(item)
end
end
end

class ExclFieldRange < FieldRange
end

class RangeValue < Treetop::Runtime::SyntaxNode
end

class FieldName < Treetop::Runtime::SyntaxNode
def match( item )
if self.text_value.count("_") > 0
Expand All @@ -121,13 +120,13 @@ def match( item )
self.elements[0].match( item )
end
end

class Group < Treetop::Runtime::SyntaxNode
def match( item )
self.elements[0].match(item)
end
end

class BinaryOp < Treetop::Runtime::SyntaxNode
def match( item )
self.elements[1].match(
Expand All @@ -136,49 +135,49 @@ def match( item )
)
end
end

class OrOperator < Treetop::Runtime::SyntaxNode
def match( cond1, cond2 )
cond1 or cond2
end
end

class AndOperator < Treetop::Runtime::SyntaxNode
def match( cond1, cond2 )
cond1 and cond2
end
end

# we don't support fuzzy string matching
class FuzzyOp < Treetop::Runtime::SyntaxNode
end

class BoostOp < Treetop::Runtime::SyntaxNode
end

class FuzzyParam < Treetop::Runtime::SyntaxNode
end

class UnaryOp < Treetop::Runtime::SyntaxNode
def match( item )
self.elements[0].match(
self.elements[1].match(item)
)
end
end

class NotOperator < Treetop::Runtime::SyntaxNode
def match( cond )
not cond
end
end

class RequiredOperator < Treetop::Runtime::SyntaxNode
end

class ProhibitedOperator < Treetop::Runtime::SyntaxNode
end

class Phrase < Treetop::Runtime::SyntaxNode
# a quoted ::Term
def match( value )
Expand Down Expand Up @@ -207,7 +206,7 @@ def self.parse(data)
self.clean_tree(tree)
tree
end

private

def self.clean_tree(root_node)
Expand Down
Loading