Skip to content

Commit

Permalink
Merge pull request #18 from schisamo/chef-11-compat
Browse files Browse the repository at this point in the history
Full Chef 11 compatibility update.
  • Loading branch information
tobami committed Feb 20, 2013
2 parents d84105a + 7d158e9 commit da3cdbd
Show file tree
Hide file tree
Showing 6 changed files with 647 additions and 115 deletions.
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

0 comments on commit da3cdbd

Please sign in to comment.