chef-zero 1.4.0.alpha-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/LICENSE +201 -0
  2. data/README.md +145 -0
  3. data/Rakefile +11 -0
  4. data/bin/chef-zero +43 -0
  5. data/lib/chef_zero.rb +7 -0
  6. data/lib/chef_zero/cookbook_data.rb +223 -0
  7. data/lib/chef_zero/data_normalizer.rb +142 -0
  8. data/lib/chef_zero/data_store/data_already_exists_error.rb +29 -0
  9. data/lib/chef_zero/data_store/data_error.rb +31 -0
  10. data/lib/chef_zero/data_store/data_not_found_error.rb +29 -0
  11. data/lib/chef_zero/data_store/memory_store.rb +167 -0
  12. data/lib/chef_zero/endpoints/actor_endpoint.rb +68 -0
  13. data/lib/chef_zero/endpoints/actors_endpoint.rb +32 -0
  14. data/lib/chef_zero/endpoints/authenticate_user_endpoint.rb +25 -0
  15. data/lib/chef_zero/endpoints/cookbook_endpoint.rb +39 -0
  16. data/lib/chef_zero/endpoints/cookbook_version_endpoint.rb +110 -0
  17. data/lib/chef_zero/endpoints/cookbooks_base.rb +65 -0
  18. data/lib/chef_zero/endpoints/cookbooks_endpoint.rb +19 -0
  19. data/lib/chef_zero/endpoints/data_bag_endpoint.rb +45 -0
  20. data/lib/chef_zero/endpoints/data_bag_item_endpoint.rb +25 -0
  21. data/lib/chef_zero/endpoints/data_bags_endpoint.rb +22 -0
  22. data/lib/chef_zero/endpoints/environment_cookbook_endpoint.rb +24 -0
  23. data/lib/chef_zero/endpoints/environment_cookbook_versions_endpoint.rb +109 -0
  24. data/lib/chef_zero/endpoints/environment_cookbooks_endpoint.rb +22 -0
  25. data/lib/chef_zero/endpoints/environment_endpoint.rb +33 -0
  26. data/lib/chef_zero/endpoints/environment_nodes_endpoint.rb +23 -0
  27. data/lib/chef_zero/endpoints/environment_recipes_endpoint.rb +22 -0
  28. data/lib/chef_zero/endpoints/environment_role_endpoint.rb +36 -0
  29. data/lib/chef_zero/endpoints/file_store_file_endpoint.rb +22 -0
  30. data/lib/chef_zero/endpoints/node_endpoint.rb +17 -0
  31. data/lib/chef_zero/endpoints/not_found_endpoint.rb +11 -0
  32. data/lib/chef_zero/endpoints/principal_endpoint.rb +30 -0
  33. data/lib/chef_zero/endpoints/rest_list_endpoint.rb +40 -0
  34. data/lib/chef_zero/endpoints/rest_object_endpoint.rb +61 -0
  35. data/lib/chef_zero/endpoints/role_endpoint.rb +16 -0
  36. data/lib/chef_zero/endpoints/role_environments_endpoint.rb +14 -0
  37. data/lib/chef_zero/endpoints/sandbox_endpoint.rb +27 -0
  38. data/lib/chef_zero/endpoints/sandboxes_endpoint.rb +51 -0
  39. data/lib/chef_zero/endpoints/search_endpoint.rb +188 -0
  40. data/lib/chef_zero/endpoints/searches_endpoint.rb +18 -0
  41. data/lib/chef_zero/log.rb +7 -0
  42. data/lib/chef_zero/rest_base.rb +133 -0
  43. data/lib/chef_zero/rest_error_response.rb +11 -0
  44. data/lib/chef_zero/rest_request.rb +56 -0
  45. data/lib/chef_zero/rest_router.rb +44 -0
  46. data/lib/chef_zero/rspec.rb +107 -0
  47. data/lib/chef_zero/server.rb +309 -0
  48. data/lib/chef_zero/solr/query/binary_operator.rb +53 -0
  49. data/lib/chef_zero/solr/query/phrase.rb +23 -0
  50. data/lib/chef_zero/solr/query/range_query.rb +34 -0
  51. data/lib/chef_zero/solr/query/regexpable_query.rb +29 -0
  52. data/lib/chef_zero/solr/query/subquery.rb +35 -0
  53. data/lib/chef_zero/solr/query/term.rb +45 -0
  54. data/lib/chef_zero/solr/query/unary_operator.rb +43 -0
  55. data/lib/chef_zero/solr/solr_doc.rb +62 -0
  56. data/lib/chef_zero/solr/solr_parser.rb +193 -0
  57. data/lib/chef_zero/version.rb +3 -0
  58. data/spec/run.rb +25 -0
  59. data/spec/support/pedant.rb +117 -0
  60. data/spec/support/stickywicket.pem +27 -0
  61. metadata +204 -0
@@ -0,0 +1,23 @@
1
+ require 'chef_zero/solr/query/regexpable_query'
2
+
3
+ module ChefZero
4
+ module Solr
5
+ module Query
6
+ class Phrase < RegexpableQuery
7
+ def initialize(terms)
8
+ # Phrase is terms separated by whitespace
9
+ if terms.size == 0 && terms[0].literal_string
10
+ literal_string = terms[0].literal_string
11
+ else
12
+ literal_string = nil
13
+ end
14
+ super(terms.map { |term| term.regexp_string }.join("#{NON_WORD_CHARACTER}+"), literal_string)
15
+ end
16
+
17
+ def to_s
18
+ "Phrase(\"#{@regexp_string}\")"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,34 @@
1
+ module ChefZero
2
+ module Solr
3
+ module Query
4
+ class RangeQuery
5
+ def initialize(from, to, from_inclusive, to_inclusive)
6
+ @from = from
7
+ @to = to
8
+ @from_inclusive = from_inclusive
9
+ @to_inclusive = to_inclusive
10
+ end
11
+
12
+ def to_s
13
+ "#{@from_inclusive ? '[' : '{'}#{@from} TO #{@to}#{@to_inclusive ? '[' : '{'}"
14
+ end
15
+
16
+ def matches?(key, value)
17
+ case @from <=> value
18
+ when -1
19
+ return false
20
+ when 0
21
+ return false if !@from_inclusive
22
+ end
23
+ case @to <=> value
24
+ when 1
25
+ return false
26
+ when 0
27
+ return false if !@to_inclusive
28
+ end
29
+ return true
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,29 @@
1
+ module ChefZero
2
+ module Solr
3
+ module Query
4
+ class RegexpableQuery
5
+ def initialize(regexp_string, literal_string)
6
+ @regexp_string = regexp_string
7
+ # Surround the regexp with word boundaries
8
+ @regexp = Regexp.new("(^|#{NON_WORD_CHARACTER})#{regexp_string}($|#{NON_WORD_CHARACTER})", true)
9
+ @literal_string = literal_string
10
+ end
11
+
12
+ attr_reader :literal_string
13
+ attr_reader :regexp_string
14
+ attr_reader :regexp
15
+
16
+ def matches_doc?(doc)
17
+ value = doc[DEFAULT_FIELD]
18
+ return value ? matches_values?([value]) : false
19
+ end
20
+ def matches_values?(values)
21
+ values.any? { |value| !@regexp.match(value).nil? }
22
+ end
23
+
24
+ WORD_CHARACTER = "[A-Za-z0-9@._':]"
25
+ NON_WORD_CHARACTER = "[^A-Za-z0-9@._':]"
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,35 @@
1
+ module ChefZero
2
+ module Solr
3
+ module Query
4
+ class Subquery
5
+ def initialize(subquery)
6
+ @subquery = subquery
7
+ end
8
+
9
+ def to_s
10
+ "(#{@subquery})"
11
+ end
12
+
13
+ def literal_string
14
+ subquery.literal_string
15
+ end
16
+
17
+ def regexp
18
+ subquery.regexp
19
+ end
20
+
21
+ def regexp_string
22
+ subquery.regexp_string
23
+ end
24
+
25
+ def matches_doc?(doc)
26
+ subquery.matches_doc?(doc)
27
+ end
28
+
29
+ def matches_values?(values)
30
+ subquery.matches_values?(values)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,45 @@
1
+ require 'chef_zero/solr/query/regexpable_query'
2
+
3
+ module ChefZero
4
+ module Solr
5
+ module Query
6
+ class Term < RegexpableQuery
7
+ def initialize(term)
8
+ # Get rid of escape characters, turn * and ? into .* and . for regex, and
9
+ # escape everything that needs escaping
10
+ literal_string = ""
11
+ regexp_string = ""
12
+ index = 0
13
+ while index < term.length
14
+ if term[index] == '*'
15
+ regexp_string << "#{WORD_CHARACTER}*"
16
+ literal_string = nil
17
+ index += 1
18
+ elsif term[index] == '?'
19
+ regexp_string << WORD_CHARACTER
20
+ literal_string = nil
21
+ index += 1
22
+ elsif term[index] == '~'
23
+ raise "~ unsupported"
24
+ else
25
+ if term[index] == '\\'
26
+ index = index+1
27
+ if index >= term.length
28
+ raise "Backslash at end of string '#{term}'"
29
+ end
30
+ end
31
+ literal_string << term[index] if literal_string
32
+ regexp_string << Regexp.escape(term[index])
33
+ index += 1
34
+ end
35
+ end
36
+ super(regexp_string, literal_string)
37
+ end
38
+
39
+ def to_s
40
+ "Term(#{regexp_string})"
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,43 @@
1
+ module ChefZero
2
+ module Solr
3
+ module Query
4
+ class UnaryOperator
5
+ def initialize(operator, operand)
6
+ @operator = operator
7
+ @operand = operand
8
+ end
9
+
10
+ def to_s
11
+ "#{operator} #{operand}"
12
+ end
13
+
14
+ attr_reader :operator
15
+ attr_reader :operand
16
+
17
+ def matches_doc?(doc)
18
+ case @operator
19
+ when '-'
20
+ when 'NOT'
21
+ !operand.matches_doc?(doc)
22
+ when '+'
23
+ # TODO This operator uses relevance to eliminate other, unrelated
24
+ # expressions. +a OR b means "if it has b but not a, don't return it"
25
+ raise "+ not supported yet, because it is hard."
26
+ end
27
+ end
28
+
29
+ def matches_values?(values)
30
+ case @operator
31
+ when '-'
32
+ when 'NOT'
33
+ !operand.matches_values?(values)
34
+ when '+'
35
+ # TODO This operator uses relevance to eliminate other, unrelated
36
+ # expressions. +a OR b means "if it has b but not a, don't return it"
37
+ raise "+ not supported yet, because it is hard."
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,62 @@
1
+ module ChefZero
2
+ module Solr
3
+ # This does what expander does, flattening the json doc into keys and values
4
+ # so that solr can search them.
5
+ class SolrDoc
6
+ def initialize(json, id)
7
+ @json = json
8
+ @id = id
9
+ end
10
+
11
+ def [](key)
12
+ values = matching_values { |match_key| match_key == key }
13
+ values[0]
14
+ end
15
+
16
+ def matching_values(&block)
17
+ result = {}
18
+ key_values(nil, @json) do |key, value|
19
+ if block.call(key)
20
+ if result.has_key?(key)
21
+ result[key] << value.to_s
22
+ else
23
+ result[key] = value.to_s.clone
24
+ end
25
+ end
26
+ end
27
+ # Handle manufactured value(s)
28
+ if block.call('X_CHEF_id_CHEF_X')
29
+ if result.has_key?('X_CHEF_id_CHEF_X')
30
+ result['X_CHEF_id_CHEF_X'] << @id.to_s
31
+ else
32
+ result['X_CHEF_id_CHEF_X'] = @id.to_s.clone
33
+ end
34
+ end
35
+
36
+ result.values
37
+ end
38
+
39
+ private
40
+
41
+ def key_values(key_so_far, value, &block)
42
+ if value.is_a?(Hash)
43
+ value.each_pair do |child_key, child_value|
44
+ block.call(child_key, child_value.to_s)
45
+ if key_so_far
46
+ new_key = "#{key_so_far}_#{child_key}"
47
+ key_values(new_key, child_value, &block)
48
+ else
49
+ key_values(child_key, child_value, &block) if child_value.is_a?(Hash) || child_value.is_a?(Array)
50
+ end
51
+ end
52
+ elsif value.is_a?(Array)
53
+ value.each do |child_value|
54
+ key_values(key_so_far, child_value, &block)
55
+ end
56
+ else
57
+ block.call(key_so_far || 'text', value.to_s)
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,193 @@
1
+ require 'chef_zero/solr/query/binary_operator'
2
+ require 'chef_zero/solr/query/unary_operator'
3
+ require 'chef_zero/solr/query/term'
4
+ require 'chef_zero/solr/query/phrase'
5
+ require 'chef_zero/solr/query/range_query'
6
+ require 'chef_zero/solr/query/subquery'
7
+
8
+ module ChefZero
9
+ module Solr
10
+ class SolrParser
11
+ def initialize(query_string)
12
+ @query_string = query_string
13
+ @index = 0
14
+ end
15
+
16
+ def parse
17
+ read_expression
18
+ end
19
+
20
+ #
21
+ # Tokenization
22
+ #
23
+ def peek_token
24
+ @next_token ||= parse_token
25
+ end
26
+
27
+ def next_token
28
+ result = peek_token
29
+ @next_token = nil
30
+ result
31
+ end
32
+
33
+ def parse_token
34
+ # Skip whitespace
35
+ skip_whitespace
36
+ return nil if eof?
37
+
38
+ # Operators
39
+ operator = peek_operator_token
40
+ if operator
41
+ @index+=operator.length
42
+ operator
43
+ else
44
+ # Everything that isn't whitespace or an operator, is part of a term
45
+ # (characters plus backslashed escaped characters)
46
+ start_index = @index
47
+ begin
48
+ if @query_string[@index] == '\\'
49
+ @index+=1
50
+ end
51
+ @index+=1 if !eof?
52
+ end until eof? || @query_string[@index] =~ /\s/ || peek_operator_token
53
+ @query_string[start_index..@index-1]
54
+ end
55
+ end
56
+
57
+ def skip_whitespace
58
+ if @query_string[@index] =~ /\s/
59
+ whitespace = /\s+/.match(@query_string, @index)
60
+ @index += whitespace[0].length
61
+ end
62
+ end
63
+
64
+ def peek_operator_token
65
+ if ['"', '+', '-', '!', '(', ')', '{', '}', '[', ']', '^', ':'].include?(@query_string[@index])
66
+ return @query_string[@index]
67
+ else
68
+ result = @query_string[@index..@index+1]
69
+ if ['&&', '||'].include?(result)
70
+ return result
71
+ end
72
+ end
73
+ nil
74
+ end
75
+
76
+ def eof?
77
+ !@next_token && @index >= @query_string.length
78
+ end
79
+
80
+ # Parse tree creation
81
+ def read_expression
82
+ result = read_single_expression
83
+ # Expression is over when we hit a close paren or eof
84
+ # (peek_token has the side effect of skipping whitespace for us, so we
85
+ # really know if we're at eof or not)
86
+ until peek_token == ')' || eof?
87
+ operator = peek_token
88
+ if binary_operator?(operator)
89
+ next_token
90
+ else
91
+ # If 2 terms are next to each other, the default operator is OR
92
+ operator = 'OR'
93
+ end
94
+ next_expression = read_single_expression
95
+
96
+ # Build the operator, taking precedence into account
97
+ if result.is_a?(Query::BinaryOperator) &&
98
+ binary_operator_precedence(operator) > binary_operator_precedence(result.operator)
99
+ # a+b*c -> a+(b*c)
100
+ new_right = Query::BinaryOperator.new(result.right, operator, next_expression)
101
+ result = Query::BinaryOperator.new(result.left, result.operator, new_right)
102
+ else
103
+ # a*b+c -> (a*b)+c
104
+ result = Query::BinaryOperator.new(result, operator, next_expression)
105
+ end
106
+ end
107
+ result
108
+ end
109
+
110
+ def parse_error(token, str)
111
+ error = "Error on token '#{token}' at #{@index} of '#{@query_string}': #{str}"
112
+ raise error
113
+ end
114
+
115
+ def read_single_expression
116
+ token = next_token
117
+ # If EOF, we have a problem Houston
118
+ if !token
119
+ parse_error(nil, "Expected expression!")
120
+
121
+ # If it's an unary operand, build that
122
+ elsif unary_operator?(token)
123
+ operand = read_single_expression
124
+ # TODO We rely on all unary operators having higher precedence than all
125
+ # binary operators. Check if this is the case.
126
+ Query::UnaryOperator.new(token, operand)
127
+
128
+ # If it's the start of a phrase, read the terms in the phrase
129
+ elsif token == '"'
130
+ # Read terms until close "
131
+ phrase_terms = []
132
+ until (term = next_token) == '"'
133
+ phrase_terms << Query::Term.new(term)
134
+ end
135
+ Query::Phrase.new(phrase_terms)
136
+
137
+ # If it's the start of a range query, build that
138
+ elsif token == '{' || token == '['
139
+ left = next_token
140
+ parse_error(left, "Expected left term in range query") if !left
141
+ to = next_token
142
+ parse_error(left, "Expected TO in range query") if to != "TO"
143
+ right = next_token
144
+ parse_error(right, "Expected left term in range query") if !right
145
+ end_range = next_token
146
+ parse_error(right, "Expected end range '#{expected_end_range}") if !['{', '['].include?(end_range)
147
+ Query::RangeQuery.new(left, right, token == '[', end_range == ']')
148
+
149
+ elsif token == '('
150
+ subquery = read_expression
151
+ close_paren = next_token
152
+ parse_error(close_paren, "Expected ')'") if close_paren != ')'
153
+ Query::Subquery.new(subquery)
154
+
155
+ # If it's the end of a closure, raise an exception
156
+ elsif ['}',']',')'].include?(token)
157
+ parse_error(token, "Unexpected end paren")
158
+
159
+ # If it's a binary operator, raise an exception
160
+ elsif binary_operator?(token)
161
+ parse_error(token, "Unexpected binary operator")
162
+
163
+ # Otherwise it's a term.
164
+ else
165
+ Query::Term.new(token)
166
+ end
167
+ end
168
+
169
+ def unary_operator?(token)
170
+ [ 'NOT', '+', '-' ].include?(token)
171
+ end
172
+
173
+ def binary_operator?(token)
174
+ [ 'AND', 'OR', '^', ':'].include?(token)
175
+ end
176
+
177
+ def binary_operator_precedence(token)
178
+ case token
179
+ when '^'
180
+ 4
181
+ when ':'
182
+ 3
183
+ when 'AND'
184
+ 2
185
+ when 'OR'
186
+ 1
187
+ end
188
+ end
189
+
190
+ DEFAULT_FIELD = 'text'
191
+ end
192
+ end
193
+ end