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,107 @@
1
+ require 'tempfile'
2
+ require 'chef_zero/server'
3
+ require 'chef_zero/rest_request'
4
+
5
+ module ChefZero
6
+ module RSpec
7
+ def self.server
8
+ @server
9
+ end
10
+ def self.server=(value)
11
+ @server = value
12
+ end
13
+ def self.client_key
14
+ @client_key
15
+ end
16
+ def self.client_key=(value)
17
+ @client_key = value
18
+ end
19
+ def self.request_log
20
+ @request_log ||= []
21
+ end
22
+ def self.clear_request_log
23
+ @request_log = []
24
+ end
25
+
26
+ def when_the_chef_server(description, *tags, &block)
27
+ context "When the Chef server #{description}", *tags do
28
+ before :each do
29
+ unless ChefZero::RSpec.server
30
+ # Set up configuration so that clients will point to the server
31
+ ChefZero::RSpec.server = ChefZero::Server.new(:port => 8889, :signals => false, :log_requests => true)
32
+ ChefZero::RSpec.client_key = Tempfile.new(['chef_zero_client_key', '.pem'])
33
+ ChefZero::RSpec.client_key.write(ChefZero::PRIVATE_KEY)
34
+ ChefZero::RSpec.client_key.close
35
+ # Start the server
36
+ ChefZero::RSpec.server.start_background
37
+ ChefZero::RSpec.server.on_response do |request, response|
38
+ ChefZero::RSpec.request_log << [ request, response ]
39
+ end
40
+ else
41
+ ChefZero::RSpec.server.clear_data
42
+ end
43
+ ChefZero::RSpec.clear_request_log
44
+
45
+ if defined?(Chef::Config)
46
+ @old_chef_server_url = Chef::Config.chef_server_url
47
+ @old_node_name = Chef::Config.node_name
48
+ @old_client_key = Chef::Config.client_key
49
+ Chef::Config.chef_server_url = ChefZero::RSpec.server.url
50
+ Chef::Config.node_name = 'admin'
51
+ Chef::Config.client_key = ChefZero::RSpec.client_key.path
52
+ Chef::Config.http_retry_count = 0
53
+ end
54
+ end
55
+
56
+ if defined?(Chef::Config)
57
+ after :each do
58
+ Chef::Config.chef_server_url = @old_chef_server_url
59
+ Chef::Config.node_name = @old_node_name
60
+ Chef::Config.client_key = @old_client_key
61
+ end
62
+ end
63
+
64
+ def self.client(name, client)
65
+ before(:each) { ChefZero::RSpec.server.load_data({ 'clients' => { name => client }}) }
66
+ end
67
+
68
+ def self.cookbook(name, version, cookbook, options = {})
69
+ before(:each) { ChefZero::RSpec.server.load_data({ 'cookbooks' => { "#{name}-#{version}" => cookbook.merge(options) }}) }
70
+ end
71
+
72
+ def self.data_bag(name, data_bag)
73
+ before(:each) { ChefZero::RSpec.server.load_data({ 'data' => { name => data_bag }}) }
74
+ end
75
+
76
+ def self.environment(name, environment)
77
+ before(:each) { ChefZero::RSpec.server.load_data({ 'environments' => { name => environment }}) }
78
+ end
79
+
80
+ def self.node(name, node)
81
+ before(:each) { ChefZero::RSpec.server.load_data({ 'nodes' => { name => node }}) }
82
+ end
83
+
84
+ def self.role(name, role)
85
+ before(:each) { ChefZero::RSpec.server.load_data({ 'roles' => { name => role }}) }
86
+ end
87
+
88
+ def self.user(name, user)
89
+ before(:each) { ChefZero::RSpec.server.load_data({ 'users' => { name => user }}) }
90
+ end
91
+
92
+ # after :each do
93
+ # if @@ChefZero::RSpec.server
94
+ # @@ChefZero::RSpec.server.stop
95
+ # @@ChefZero::RSpec.server = nil
96
+ # end
97
+ # if @@ChefZero::RSpec.client_key
98
+ # @@ChefZero::RSpec.client_key.unlink
99
+ # @@ChefZero::RSpec.client_key = nil
100
+ # end
101
+ # end
102
+
103
+ instance_eval(&block)
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,309 @@
1
+ #
2
+ # Author:: John Keiser (<jkeiser@opscode.com>)
3
+ # Copyright:: Copyright (c) 2012 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'openssl'
20
+ require 'puma'
21
+ require 'rubygems'
22
+ require 'timeout'
23
+
24
+ require 'chef_zero'
25
+ require 'chef_zero/cookbook_data'
26
+ require 'chef_zero/rest_router'
27
+ require 'chef_zero/data_store/memory_store'
28
+ require 'chef_zero/version'
29
+
30
+ require 'chef_zero/endpoints/authenticate_user_endpoint'
31
+ require 'chef_zero/endpoints/actors_endpoint'
32
+ require 'chef_zero/endpoints/actor_endpoint'
33
+ require 'chef_zero/endpoints/cookbooks_endpoint'
34
+ require 'chef_zero/endpoints/cookbook_endpoint'
35
+ require 'chef_zero/endpoints/cookbook_version_endpoint'
36
+ require 'chef_zero/endpoints/data_bags_endpoint'
37
+ require 'chef_zero/endpoints/data_bag_endpoint'
38
+ require 'chef_zero/endpoints/data_bag_item_endpoint'
39
+ require 'chef_zero/endpoints/rest_list_endpoint'
40
+ require 'chef_zero/endpoints/environment_endpoint'
41
+ require 'chef_zero/endpoints/environment_cookbooks_endpoint'
42
+ require 'chef_zero/endpoints/environment_cookbook_endpoint'
43
+ require 'chef_zero/endpoints/environment_cookbook_versions_endpoint'
44
+ require 'chef_zero/endpoints/environment_nodes_endpoint'
45
+ require 'chef_zero/endpoints/environment_recipes_endpoint'
46
+ require 'chef_zero/endpoints/environment_role_endpoint'
47
+ require 'chef_zero/endpoints/node_endpoint'
48
+ require 'chef_zero/endpoints/principal_endpoint'
49
+ require 'chef_zero/endpoints/role_endpoint'
50
+ require 'chef_zero/endpoints/role_environments_endpoint'
51
+ require 'chef_zero/endpoints/sandboxes_endpoint'
52
+ require 'chef_zero/endpoints/sandbox_endpoint'
53
+ require 'chef_zero/endpoints/searches_endpoint'
54
+ require 'chef_zero/endpoints/search_endpoint'
55
+ require 'chef_zero/endpoints/file_store_file_endpoint'
56
+ require 'chef_zero/endpoints/not_found_endpoint'
57
+
58
+ module ChefZero
59
+ class Server
60
+ DEFAULT_OPTIONS = {
61
+ :host => '127.0.0.1',
62
+ :port => 8889,
63
+ :log_level => :info,
64
+ :generate_real_keys => true
65
+ }.freeze
66
+
67
+ def initialize(options = {})
68
+ options = DEFAULT_OPTIONS.merge(options)
69
+ @url = "http://#{options[:host]}:#{options[:port]}"
70
+ @generate_real_keys = options[:generate_real_keys]
71
+
72
+ ChefZero::Log.level = options[:log_level].to_sym
73
+
74
+ @server = Puma::Server.new(make_app, Puma::Events.new(STDERR, STDOUT))
75
+ @server.add_tcp_listener(options[:host], options[:port])
76
+
77
+ @data_store = options[:data_store] || DataStore::MemoryStore.new
78
+ end
79
+
80
+ attr_reader :server
81
+ attr_reader :data_store
82
+ attr_reader :url
83
+
84
+ include ChefZero::Endpoints
85
+
86
+ def start(options = {})
87
+ if options[:publish]
88
+ puts ">> Starting Chef Zero (v#{ChefZero::VERSION})..."
89
+ puts ">> Puma (v#{Puma::Const::PUMA_VERSION}) is listening at #{url}"
90
+ puts ">> Press CTRL+C to stop"
91
+ end
92
+
93
+ begin
94
+ thread = server.run.join
95
+ rescue Object, Interrupt
96
+ puts "\n>> Stopping Puma..."
97
+ server.stop(true) if running?
98
+ end
99
+ end
100
+
101
+ def start_background(wait = 5)
102
+ @thread = Thread.new {
103
+ begin
104
+ start
105
+ rescue
106
+ @server_error = $!
107
+ ChefZero::Log.error("#{$!.message}\n#{$!.backtrace.join("\n")}")
108
+ end
109
+ }
110
+
111
+ # Wait x seconds to make sure the server actually started
112
+ Timeout::timeout(wait) {
113
+ sleep(0.01) until running? || @server_error
114
+ raise @server_error if @server_error
115
+ }
116
+
117
+ # Give the user the thread, just in case they want it
118
+ @thread
119
+ end
120
+
121
+ def running?
122
+ !!server.running
123
+ end
124
+
125
+ def stop(wait = 5)
126
+ if @thread
127
+ @thread.join(wait)
128
+ else
129
+ server.stop(true)
130
+ end
131
+ rescue
132
+ ChefZero::Log.error "Server did not stop within #{wait} seconds. Killing..."
133
+ @thread.kill if @thread
134
+ ensure
135
+ @thread = nil
136
+ end
137
+
138
+ def gen_key_pair
139
+ if generate_real_keys?
140
+ private_key = OpenSSL::PKey::RSA.new(2048)
141
+ public_key = private_key.public_key.to_s
142
+ public_key.sub!(/^-----BEGIN RSA PUBLIC KEY-----/, '-----BEGIN PUBLIC KEY-----')
143
+ public_key.sub!(/-----END RSA PUBLIC KEY-----(\s+)$/, '-----END PUBLIC KEY-----\1')
144
+ [private_key.to_s, public_key]
145
+ else
146
+ [PRIVATE_KEY, PUBLIC_KEY]
147
+ end
148
+ end
149
+
150
+ def on_request(&block)
151
+ @on_request_proc = block
152
+ end
153
+
154
+ def on_response(&block)
155
+ @on_response_proc = block
156
+ end
157
+
158
+ # Load data in a nice, friendly form:
159
+ # {
160
+ # 'roles' => {
161
+ # 'desert' => '{ "description": "Hot and dry"' },
162
+ # 'rainforest' => { "description" => 'Wet and humid' }
163
+ # },
164
+ # 'cookbooks' => {
165
+ # 'apache2-1.0.1' => {
166
+ # 'templates' => { 'default' => { 'blah.txt' => 'hi' }}
167
+ # 'recipes' => { 'default.rb' => 'template "blah.txt"' }
168
+ # 'metadata.rb' => 'depends "mysql"'
169
+ # },
170
+ # 'apache2-1.2.0' => {
171
+ # 'templates' => { 'default' => { 'blah.txt' => 'lo' }}
172
+ # 'recipes' => { 'default.rb' => 'template "blah.txt"' }
173
+ # 'metadata.rb' => 'depends "mysql"'
174
+ # },
175
+ # 'mysql' => {
176
+ # 'recipes' => { 'default.rb' => 'file { contents "hi" }' },
177
+ # 'metadata.rb' => 'version "1.0.0"'
178
+ # }
179
+ # }
180
+ # }
181
+ def load_data(contents)
182
+ %w(clients environments nodes roles users).each do |data_type|
183
+ if contents[data_type]
184
+ dejsonize_children(contents[data_type]).each_pair do |name, data|
185
+ data_store.set([data_type, name], data, :create)
186
+ end
187
+ end
188
+ end
189
+ if contents['data']
190
+ contents['data'].each_pair do |key, data_bag|
191
+ data_store.create_dir(['data'], key, :recursive)
192
+ dejsonize_children(data_bag).each do |item_name, item|
193
+ data_store.set(['data', key, item_name], item, :create)
194
+ end
195
+ end
196
+ end
197
+ if contents['cookbooks']
198
+ contents['cookbooks'].each_pair do |name_version, cookbook|
199
+ if name_version =~ /(.+)-(\d+\.\d+\.\d+)$/
200
+ cookbook_data = CookbookData.to_hash(cookbook, $1, $2)
201
+ else
202
+ cookbook_data = CookbookData.to_hash(cookbook, name_version)
203
+ end
204
+ raise "No version specified" if !cookbook_data[:version]
205
+ data_store.create_dir(['cookbooks'], cookbook_data[:cookbook_name], :recursive)
206
+ data_store.set(['cookbooks', cookbook_data[:cookbook_name], cookbook_data[:version]], JSON.pretty_generate(cookbook_data), :create)
207
+ cookbook_data.values.each do |files|
208
+ next unless files.is_a? Array
209
+ files.each do |file|
210
+ data_store.set(['file_store', 'checksums', file[:checksum]], get_file(cookbook, file[:path]), :create)
211
+ end
212
+ end
213
+ end
214
+ end
215
+ end
216
+
217
+ def clear_data
218
+ @data_store.clear
219
+ end
220
+
221
+ def request_handler(&block)
222
+ @request_handler = block
223
+ end
224
+
225
+ private
226
+
227
+ def make_app
228
+ router = RestRouter.new([
229
+ [ '/authenticate_user', AuthenticateUserEndpoint.new(self) ],
230
+ [ '/clients', ActorsEndpoint.new(self) ],
231
+ [ '/clients/*', ActorEndpoint.new(self) ],
232
+ [ '/cookbooks', CookbooksEndpoint.new(self) ],
233
+ [ '/cookbooks/*', CookbookEndpoint.new(self) ],
234
+ [ '/cookbooks/*/*', CookbookVersionEndpoint.new(self) ],
235
+ [ '/data', DataBagsEndpoint.new(self) ],
236
+ [ '/data/*', DataBagEndpoint.new(self) ],
237
+ [ '/data/*/*', DataBagItemEndpoint.new(self) ],
238
+ [ '/environments', RestListEndpoint.new(self) ],
239
+ [ '/environments/*', EnvironmentEndpoint.new(self) ],
240
+ [ '/environments/*/cookbooks', EnvironmentCookbooksEndpoint.new(self) ],
241
+ [ '/environments/*/cookbooks/*', EnvironmentCookbookEndpoint.new(self) ],
242
+ [ '/environments/*/cookbook_versions', EnvironmentCookbookVersionsEndpoint.new(self) ],
243
+ [ '/environments/*/nodes', EnvironmentNodesEndpoint.new(self) ],
244
+ [ '/environments/*/recipes', EnvironmentRecipesEndpoint.new(self) ],
245
+ [ '/environments/*/roles/*', EnvironmentRoleEndpoint.new(self) ],
246
+ [ '/nodes', RestListEndpoint.new(self) ],
247
+ [ '/nodes/*', NodeEndpoint.new(self) ],
248
+ [ '/principals/*', PrincipalEndpoint.new(self) ],
249
+ [ '/roles', RestListEndpoint.new(self) ],
250
+ [ '/roles/*', RoleEndpoint.new(self) ],
251
+ [ '/roles/*/environments', RoleEnvironmentsEndpoint.new(self) ],
252
+ [ '/roles/*/environments/*', EnvironmentRoleEndpoint.new(self) ],
253
+ [ '/sandboxes', SandboxesEndpoint.new(self) ],
254
+ [ '/sandboxes/*', SandboxEndpoint.new(self) ],
255
+ [ '/search', SearchesEndpoint.new(self) ],
256
+ [ '/search/*', SearchEndpoint.new(self) ],
257
+ [ '/users', ActorsEndpoint.new(self) ],
258
+ [ '/users/*', ActorEndpoint.new(self) ],
259
+
260
+ [ '/file_store/**', FileStoreFileEndpoint.new(self) ],
261
+ ])
262
+ router.not_found = NotFoundEndpoint.new
263
+
264
+ return proc do |env|
265
+ request = RestRequest.new(env)
266
+ if @on_request_proc
267
+ @on_request_proc.call(request)
268
+ end
269
+ response = nil
270
+ if @request_handler
271
+ response = @request_handler.call(request)
272
+ end
273
+ unless response
274
+ response = router.call(request)
275
+ end
276
+ if @on_response_proc
277
+ @on_response_proc.call(request, response)
278
+ end
279
+
280
+ # Puma expects the response to be an array (chunked responses). Since
281
+ # we are statically generating data, we won't ever have said chunked
282
+ # response, so fake it.
283
+ response[-1] = Array(response[-1])
284
+
285
+ response
286
+ end
287
+ end
288
+
289
+ def dejsonize_children(hash)
290
+ result = {}
291
+ hash.each_pair do |key, value|
292
+ result[key] = value.is_a?(Hash) ? JSON.pretty_generate(value) : value
293
+ end
294
+ result
295
+ end
296
+
297
+ def get_file(directory, path)
298
+ value = directory
299
+ path.split('/').each do |part|
300
+ value = value[part]
301
+ end
302
+ value
303
+ end
304
+
305
+ def generate_real_keys?
306
+ !!@generate_real_keys
307
+ end
308
+ end
309
+ end
@@ -0,0 +1,53 @@
1
+ module ChefZero
2
+ module Solr
3
+ module Query
4
+ class BinaryOperator
5
+ def initialize(left, operator, right)
6
+ @left = left
7
+ @operator = operator
8
+ @right = right
9
+ end
10
+
11
+ def to_s
12
+ "(#{left} #{operator} #{right})"
13
+ end
14
+
15
+ attr_reader :left
16
+ attr_reader :operator
17
+ attr_reader :right
18
+
19
+ def matches_doc?(doc)
20
+ case @operator
21
+ when 'AND'
22
+ left.matches_doc?(doc) && right.matches_doc?(doc)
23
+ when 'OR'
24
+ left.matches_doc?(doc) || right.matches_doc?(doc)
25
+ when '^'
26
+ left.matches_doc?(doc)
27
+ when ':'
28
+ if left.respond_to?(:literal_string) && left.literal_string
29
+ value = doc[left.literal_string]
30
+ right.matches_values?([value])
31
+ else
32
+ values = doc.matching_values { |key| left.matches_values?([key]) }
33
+ right.matches_values?(values)
34
+ end
35
+ end
36
+ end
37
+
38
+ def matches_values?(values)
39
+ case @operator
40
+ when 'AND'
41
+ left.matches_values?(values) && right.matches_values?(values)
42
+ when 'OR'
43
+ left.matches_values?(values) || right.matches_values?(values)
44
+ when '^'
45
+ left.matches_values?(values)
46
+ when ':'
47
+ raise ": does not work inside a : or term"
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end