diff options
23 files changed, 176 insertions, 125 deletions
diff --git a/lib/chef_zero/endpoints/authenticate_user_endpoint.rb b/lib/chef_zero/endpoints/authenticate_user_endpoint.rb index ce044c7..37e56a9 100644 --- a/lib/chef_zero/endpoints/authenticate_user_endpoint.rb +++ b/lib/chef_zero/endpoints/authenticate_user_endpoint.rb @@ -9,8 +9,12 @@ module ChefZero request_json = JSON.parse(request.body, :create_additions => false) name = request_json['name'] password = request_json['password'] - user = data['users'][name] - verified = user && JSON.parse(user, :create_additions => false)['password'] == password + begin + user = data_store.get(['users', name]) + verified = JSON.parse(user, :create_additions => false)['password'] == password + rescue DataStore::DataNotFoundError + verified = false + end json_response(200, { 'name' => name, 'verified' => !!verified diff --git a/lib/chef_zero/endpoints/cookbook_endpoint.rb b/lib/chef_zero/endpoints/cookbook_endpoint.rb index d910e9c..215a21e 100644 --- a/lib/chef_zero/endpoints/cookbook_endpoint.rb +++ b/lib/chef_zero/endpoints/cookbook_endpoint.rb @@ -9,7 +9,7 @@ module ChefZero case filter when '_latest' result = {} - filter_cookbooks(data['cookbooks'], {}, 1) do |name, versions| + filter_cookbooks(all_cookbooks_list, {}, 1) do |name, versions| if versions.size > 0 result[name] = build_uri(request.base_uri, ['cookbooks', name, versions[0]]) end @@ -17,15 +17,15 @@ module ChefZero json_response(200, result) when '_recipes' result = [] - filter_cookbooks(data['cookbooks'], {}, 1) do |name, versions| + filter_cookbooks(all_cookbooks_list, {}, 1) do |name, versions| if versions.size > 0 - cookbook = JSON.parse(data['cookbooks'][name][versions[0]], :create_additions => false) + cookbook = JSON.parse(get_data(['cookbooks', name, versions[0]]), :create_additions => false) result += recipe_names(name, cookbook) end end json_response(200, result.sort) else - cookbook_list = { filter => get_data(request, request.rest_path) } + cookbook_list = { filter => list_data(request, request.rest_path) } json_response(200, format_cookbooks_list(request, cookbook_list)) end end diff --git a/lib/chef_zero/endpoints/cookbook_version_endpoint.rb b/lib/chef_zero/endpoints/cookbook_version_endpoint.rb index 0cde0ec..c9a115a 100644 --- a/lib/chef_zero/endpoints/cookbook_version_endpoint.rb +++ b/lib/chef_zero/endpoints/cookbook_version_endpoint.rb @@ -9,7 +9,7 @@ module ChefZero class CookbookVersionEndpoint < RestObjectEndpoint def get(request) if request.rest_path[2] == "_latest" || request.rest_path[2] == "latest" - request.rest_path[2] = latest_version(get_data(request, request.rest_path[0..1]).keys) + request.rest_path[2] = latest_version(list_data(request, request.rest_path[0..1])) end super(request) end @@ -17,9 +17,8 @@ module ChefZero def put(request) name = request.rest_path[1] version = request.rest_path[2] - data['cookbooks'][name] = {} if !data['cookbooks'][name] - existing_cookbook = data['cookbooks'][name][version] - + existing_cookbook = get_data(request, request.rest_path, :nil) + # Honor frozen if existing_cookbook existing_cookbook_json = JSON.parse(existing_cookbook, :create_additions => false) @@ -37,7 +36,7 @@ module ChefZero end # Set the cookbook - data['cookbooks'][name][version] = request.body + set_data(request, ['cookbooks', name, version], request.body, :create_dir, :create) # If the cookbook was updated, check for deleted files and clean them up if existing_cookbook @@ -47,18 +46,22 @@ module ChefZero end end - already_json_response(existing_cookbook ? 200 : 201, populate_defaults(request, data['cookbooks'][name][version])) + already_json_response(existing_cookbook ? 200 : 201, populate_defaults(request, request.body)) end def delete(request) if request.rest_path[2] == "_latest" || request.rest_path[2] == "latest" - request.rest_path[2] = latest_version(get_data(request, request.rest_path[0..1]).keys) + request.rest_path[2] = latest_version(list_data(request, request.rest_path[0..1])) end - deleted_cookbook = get_data(request, request.rest_path) + deleted_cookbook = get_data(request) response = super(request) cookbook_name = request.rest_path[1] - data['cookbooks'].delete(cookbook_name) if data['cookbooks'][cookbook_name].size == 0 + begin + data_store.delete(['cookbooks', cookbook_name]) if data_store.list(['cookbooks', cookbook_name]).size == 0 + rescue DataStore::DataNotFoundError + # This is just a race. + end # Hoover deleted files, if they exist hoover_unused_checksums(get_checksums(deleted_cookbook)) @@ -79,14 +82,18 @@ module ChefZero result end + private + def hoover_unused_checksums(deleted_checksums) - data['cookbooks'].each_pair do |cookbook_name, versions| - versions.each_pair do |cookbook_version, cookbook| + data_store.list(['cookbooks']).each do |cookbook_name| + data_store.list(['cookbooks', cookbook_name]).each do |version| + cookbook = data_store.get(['cookbooks', cookbook_name, version]) deleted_checksums = deleted_checksums - get_checksums(cookbook) end end deleted_checksums.each do |checksum| - data['file_store'].delete(checksum) + # There can be a race here if multiple cookbooks are uploading. + data_store.delete(['file_store', checksum]) end end diff --git a/lib/chef_zero/endpoints/cookbooks_base.rb b/lib/chef_zero/endpoints/cookbooks_base.rb index 68db3a1..2df1dde 100644 --- a/lib/chef_zero/endpoints/cookbooks_base.rb +++ b/lib/chef_zero/endpoints/cookbooks_base.rb @@ -23,11 +23,20 @@ module ChefZero results end + def all_cookbooks_list + result = {} + # Race conditions exist here (if someone deletes while listing). I don't care. + data_store.list(['cookbooks']).each do |name| + result[name] = data_store.list(['cookbooks', name]) + end + result + end + def filter_cookbooks(cookbooks_list, constraints = {}, num_versions = nil) cookbooks_list.keys.sort.each do |name| constraint = Gem::Requirement.new(constraints[name]) versions = [] - cookbooks_list[name].keys.sort_by { |version| Gem::Version.new(version.dup) }.reverse.each do |version| + cookbooks_list[name].sort_by { |version| Gem::Version.new(version.dup) }.reverse.each do |version| break if num_versions && versions.size >= num_versions if constraint.satisfied_by?(Gem::Version.new(version.dup)) versions << version diff --git a/lib/chef_zero/endpoints/cookbooks_endpoint.rb b/lib/chef_zero/endpoints/cookbooks_endpoint.rb index a595718..587862d 100644 --- a/lib/chef_zero/endpoints/cookbooks_endpoint.rb +++ b/lib/chef_zero/endpoints/cookbooks_endpoint.rb @@ -5,7 +5,7 @@ module ChefZero # /cookbooks class CookbooksEndpoint < CookbooksBase def get(request) - json_response(200, format_cookbooks_list(request, data['cookbooks'])) + json_response(200, format_cookbooks_list(request, all_cookbooks_list)) end end end diff --git a/lib/chef_zero/endpoints/data_bag_endpoint.rb b/lib/chef_zero/endpoints/data_bag_endpoint.rb index 6f3d204..9287d8d 100644 --- a/lib/chef_zero/endpoints/data_bag_endpoint.rb +++ b/lib/chef_zero/endpoints/data_bag_endpoint.rb @@ -32,12 +32,7 @@ module ChefZero def delete(request) key = request.rest_path[1] - container = data['data'] - if !container.has_key?(key) - raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, request.rest_path)}") - end - result = container[key] - container.delete(key) + delete_data(request) json_response(200, { 'chef_type' => 'data_bag', 'json_class' => 'Chef::DataBag', diff --git a/lib/chef_zero/endpoints/data_bags_endpoint.rb b/lib/chef_zero/endpoints/data_bags_endpoint.rb index b400fda..f1bb6a8 100644 --- a/lib/chef_zero/endpoints/data_bags_endpoint.rb +++ b/lib/chef_zero/endpoints/data_bags_endpoint.rb @@ -6,15 +6,14 @@ module ChefZero # /data class DataBagsEndpoint < RestListEndpoint def post(request) - container = get_data(request) contents = request.body name = JSON.parse(contents, :create_additions => false)[identity_key] if name.nil? error(400, "Must specify '#{identity_key}' in JSON") - elsif container[name] + elsif exists_data?(request, ['data', name]) error(409, "Object already exists") else - container[name] = {} + data_store.create_dir(['data'], name, :keep_existing) json_response(201, {"uri" => "#{build_uri(request.base_uri, request.rest_path + [name])}"}) end end diff --git a/lib/chef_zero/endpoints/environment_cookbook_endpoint.rb b/lib/chef_zero/endpoints/environment_cookbook_endpoint.rb index 3360cd5..f9bb6b1 100644 --- a/lib/chef_zero/endpoints/environment_cookbook_endpoint.rb +++ b/lib/chef_zero/endpoints/environment_cookbook_endpoint.rb @@ -9,7 +9,7 @@ module ChefZero cookbook_name = request.rest_path[3] environment = JSON.parse(get_data(request, request.rest_path[0..1]), :create_additions => false) constraints = environment['cookbook_versions'] || {} - cookbook = get_data(request, request.rest_path[2..3]) + cookbook_versions = list_data(request, request.rest_path[2..3]) if request.query_params['num_versions'] == 'all' num_versions = nil elsif request.query_params['num_versions'] @@ -17,7 +17,7 @@ module ChefZero else num_versions = nil end - json_response(200, format_cookbooks_list(request, { cookbook_name => cookbook }, constraints, num_versions)) + json_response(200, format_cookbooks_list(request, { cookbook_name => cookbook_versions }, constraints, num_versions)) end end end diff --git a/lib/chef_zero/endpoints/environment_cookbook_versions_endpoint.rb b/lib/chef_zero/endpoints/environment_cookbook_versions_endpoint.rb index 2874e47..5ce8afb 100644 --- a/lib/chef_zero/endpoints/environment_cookbook_versions_endpoint.rb +++ b/lib/chef_zero/endpoints/environment_cookbook_versions_endpoint.rb @@ -6,27 +6,21 @@ module ChefZero module Endpoints # /environments/NAME/cookbook_versions class EnvironmentCookbookVersionsEndpoint < RestBase - def cookbooks - data['cookbooks'] - end - - def environments - data['environments'] - end - def post(request) + cookbook_names = list_data(request, ['cookbooks']) + # Get the list of cookbooks and versions desired by the runlist desired_versions = {} run_list = JSON.parse(request.body, :create_additions => false)['run_list'] run_list.each do |run_list_entry| if run_list_entry =~ /(.+)(::.+)?\@(.+)/ - raise RestErrorResponse.new(412, "No such cookbook: #{$1}") if !cookbooks[$1] - raise RestErrorResponse.new(412, "No such cookbook version for cookbook #{$1}: #{$2}") if !cookbooks[$1][$2] + raise RestErrorResponse.new(412, "No such cookbook: #{$1}") if !cookbook_names.include?($1) + raise RestErrorResponse.new(412, "No such cookbook version for cookbook #{$1}: #{$3}") if !list_data(request, ['cookbooks', $1]).include?($3) desired_versions[$1] = [ $3 ] else desired_cookbook = run_list_entry.split('::')[0] - raise RestErrorResponse.new(412, "No such cookbook: #{desired_cookbook}") if !cookbooks[desired_cookbook] - desired_versions[desired_cookbook] = cookbooks[desired_cookbook].keys + raise RestErrorResponse.new(412, "No such cookbook: #{desired_cookbook}") if !cookbook_names.include?(desired_cookbook) + desired_versions[desired_cookbook] = list_data(request, ['cookbooks', desired_cookbook]) end end @@ -46,7 +40,7 @@ module ChefZero result = {} solved.each_pair do |name, versions| - cookbook = JSON.parse(data['cookbooks'][name][versions[0]], :create_additions => false) + cookbook = JSON.parse(get_data(request, ['cookbooks', name, versions[0]]), :create_additions => false) result[name] = DataNormalizer.normalize_cookbook(cookbook, name, versions[0], request.base_uri, 'GET') end json_response(200, result) @@ -67,7 +61,7 @@ module ChefZero new_unsolved = unsolved[1..-1] # Pick this cookbook, and add dependencies - cookbook_obj = JSON.parse(cookbooks[solve_for][desired_version], :create_additions => false) + cookbook_obj = JSON.parse(get_data(request, ['cookbooks', solve_for, desired_version]), create_additions => false) cookbook_metadata = cookbook_obj['metadata'] || {} cookbook_dependencies = cookbook_metadata['dependencies'] || {} dep_not_found = false @@ -81,7 +75,7 @@ module ChefZero dep_not_found = true break end - new_desired_versions[dep_name] = cookbooks[dep_name].keys + new_desired_versions[dep_name] = list_data(request, ['cookbooks', dep_name]) new_desired_versions = filter_by_constraint(new_desired_versions, dep_name, environment_constraints[dep_name]) end new_desired_versions = filter_by_constraint(new_desired_versions, dep_name, dep_constraint) diff --git a/lib/chef_zero/endpoints/environment_cookbooks_endpoint.rb b/lib/chef_zero/endpoints/environment_cookbooks_endpoint.rb index 591f43f..1222b94 100644 --- a/lib/chef_zero/endpoints/environment_cookbooks_endpoint.rb +++ b/lib/chef_zero/endpoints/environment_cookbooks_endpoint.rb @@ -15,7 +15,7 @@ module ChefZero else num_versions = 1 end - json_response(200, format_cookbooks_list(request, data['cookbooks'], constraints, num_versions)) + json_response(200, format_cookbooks_list(request, all_cookbooks_list, constraints, num_versions)) end end end diff --git a/lib/chef_zero/endpoints/environment_nodes_endpoint.rb b/lib/chef_zero/endpoints/environment_nodes_endpoint.rb index 31a4044..6d0ca76 100644 --- a/lib/chef_zero/endpoints/environment_nodes_endpoint.rb +++ b/lib/chef_zero/endpoints/environment_nodes_endpoint.rb @@ -10,8 +10,8 @@ module ChefZero get_data(request, request.rest_path[0..1]) result = {} - data['nodes'].each_pair do |name, node| - node_json = JSON.parse(node, :create_additions => false) + list_data(request, ['nodes']).each do |name| + node = JSON.parse(get_data(request, ['nodes', name]), :create_additions => false) if node['chef_environment'] == request.rest_path[1] result[name] = build_uri(request.base_uri, 'nodes', name) end diff --git a/lib/chef_zero/endpoints/environment_recipes_endpoint.rb b/lib/chef_zero/endpoints/environment_recipes_endpoint.rb index 0bbaa8b..67d48f3 100644 --- a/lib/chef_zero/endpoints/environment_recipes_endpoint.rb +++ b/lib/chef_zero/endpoints/environment_recipes_endpoint.rb @@ -9,9 +9,9 @@ module ChefZero environment = JSON.parse(get_data(request, request.rest_path[0..1]), :create_additions => false) constraints = environment['cookbook_versions'] || {} result = [] - filter_cookbooks(data['cookbooks'], constraints, 1) do |name, versions| + filter_cookbooks(all_cookbooks_list, constraints, 1) do |name, versions| if versions.size > 0 - cookbook = JSON.parse(data['cookbooks'][name][versions[0]], :create_additions => false) + cookbook = JSON.parse(get_data(request, ['cookbooks', name, versions[0]]), :create_additions => false) result += recipe_names(name, cookbook) end end diff --git a/lib/chef_zero/endpoints/environment_role_endpoint.rb b/lib/chef_zero/endpoints/environment_role_endpoint.rb index 94be3ab..474376b 100644 --- a/lib/chef_zero/endpoints/environment_role_endpoint.rb +++ b/lib/chef_zero/endpoints/environment_role_endpoint.rb @@ -15,6 +15,7 @@ module ChefZero environment_path = request.rest_path[2..3] role_path = request.rest_path[0..1] end + # Verify that the environment exists get_data(request, environment_path) role = JSON.parse(get_data(request, role_path), :create_additions => false) diff --git a/lib/chef_zero/endpoints/file_store_file_endpoint.rb b/lib/chef_zero/endpoints/file_store_file_endpoint.rb index 98cea4d..fdae771 100644 --- a/lib/chef_zero/endpoints/file_store_file_endpoint.rb +++ b/lib/chef_zero/endpoints/file_store_file_endpoint.rb @@ -14,7 +14,7 @@ module ChefZero end def put(request) - data['file_store'][request.rest_path[1]] = request.body + data_store.set(request.rest_path, request.body, :create) json_response(200, {}) end end diff --git a/lib/chef_zero/endpoints/principal_endpoint.rb b/lib/chef_zero/endpoints/principal_endpoint.rb index 1833592..dde9219 100644 --- a/lib/chef_zero/endpoints/principal_endpoint.rb +++ b/lib/chef_zero/endpoints/principal_endpoint.rb @@ -8,11 +8,11 @@ module ChefZero class PrincipalEndpoint < RestBase def get(request) name = request.rest_path[-1] - json = data['users'][name] + json = get_data(request, [ 'users', name ], :nil) if json type = 'user' else - json = data['clients'][name] + json = get_data(request, [ 'clients', name ], :nil) type = 'client' end if json diff --git a/lib/chef_zero/endpoints/rest_list_endpoint.rb b/lib/chef_zero/endpoints/rest_list_endpoint.rb index 6cdf739..4fa277b 100644 --- a/lib/chef_zero/endpoints/rest_list_endpoint.rb +++ b/lib/chef_zero/endpoints/rest_list_endpoint.rb @@ -15,22 +15,19 @@ module ChefZero def get(request) # Get the result result_hash = {} - get_data(request).keys.sort.each do |name| + list_data(request).sort.each do |name| result_hash[name] = "#{build_uri(request.base_uri, request.rest_path + [name])}" end json_response(200, result_hash) end def post(request) - container = get_data(request) contents = request.body key = get_key(contents) if key.nil? error(400, "Must specify '#{identity_key}' in JSON") - elsif container[key] - error(409, 'Object already exists') else - container[key] = contents + create_data(request, request.rest_path, key, contents) json_response(201, {'uri' => "#{build_uri(request.base_uri, request.rest_path + [key])}"}) end end diff --git a/lib/chef_zero/endpoints/rest_object_endpoint.rb b/lib/chef_zero/endpoints/rest_object_endpoint.rb index bd45afe..f301a99 100644 --- a/lib/chef_zero/endpoints/rest_object_endpoint.rb +++ b/lib/chef_zero/endpoints/rest_object_endpoint.rb @@ -22,33 +22,29 @@ module ChefZero old_body = get_data(request) request_json = JSON.parse(request.body, :create_additions => false) key = request_json[identity_key] || request.rest_path[-1] - container = get_data(request, request.rest_path[0..-2]) # If it's a rename, check for conflict and delete the old value rename = key != request.rest_path[-1] if rename - if container.has_key?(key) + begin + data_store.create(request.rest_path[0..-2] + [key], request.body) + rescue DataStore::DataAlreadyExistsError return error(409, "Cannot rename '#{request.rest_path[-1]}' to '#{key}': '#{key}' already exists") end - container.delete(request.rest_path[-1]) + delete_data(request) + else + set_data(request, request.rest_path, request.body) end - container[key] = request.body already_json_response(200, populate_defaults(request, request.body)) end def delete(request) - key = request.rest_path[-1] - container = get_data(request, request.rest_path[0..-2]) - if !container.has_key?(key) - raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, request.rest_path)}") - end - result = container[key] - container.delete(key) + result = get_data(request) + delete_data(request) already_json_response(200, populate_defaults(request, result)) end def patch_request_body(request) - container = get_data(request, request.rest_path[0..-2]) - existing_value = container[request.rest_path[-1]] + existing_value = get_data(request, nil, :nil) if existing_value request_json = JSON.parse(request.body, :create_additions => false) existing_json = JSON.parse(existing_value, :create_additions => false) diff --git a/lib/chef_zero/endpoints/sandbox_endpoint.rb b/lib/chef_zero/endpoints/sandbox_endpoint.rb index 09cb180..680f0ef 100644 --- a/lib/chef_zero/endpoints/sandbox_endpoint.rb +++ b/lib/chef_zero/endpoints/sandbox_endpoint.rb @@ -5,15 +5,13 @@ module ChefZero # /sandboxes/ID class SandboxEndpoint < RestBase def put(request) - existing_sandbox = get_data(request, request.rest_path) - data['sandboxes'].delete(request.rest_path[1]) - time_str = existing_sandbox[:create_time].strftime('%Y-%m-%dT%H:%M:%S%z') - time_str = "#{time_str[0..21]}:#{time_str[22..23]}" + existing_sandbox = JSON.parse(get_data(request), :create_additions => false) + delete_data(request) json_response(200, { :guid => request.rest_path[1], :name => request.rest_path[1], :checksums => existing_sandbox[:checksums], - :create_time => time_str, + :create_time => existing_sandbox[:create_time], :is_completed => true }) end diff --git a/lib/chef_zero/endpoints/sandboxes_endpoint.rb b/lib/chef_zero/endpoints/sandboxes_endpoint.rb index 698564f..561c94f 100644 --- a/lib/chef_zero/endpoints/sandboxes_endpoint.rb +++ b/lib/chef_zero/endpoints/sandboxes_endpoint.rb @@ -16,7 +16,7 @@ module ChefZero needed_checksums = JSON.parse(request.body, :create_additions => false)['checksums'] result_checksums = {} needed_checksums.keys.each do |needed_checksum| - if data['file_store'].has_key?(needed_checksum) + if list_data(request, ['file_store']).include?(needed_checksum) result_checksums[needed_checksum] = { :needs_upload => false } else result_checksums[needed_checksum] = { @@ -27,13 +27,20 @@ module ChefZero end end + # There is an obvious race condition here. id = @next_id.to_s @next_id+=1 - data['sandboxes'][id] = { :create_time => Time.now.utc, :checksums => sandbox_checksums } + time_str = Time.now.utc.strftime('%Y-%m-%dT%H:%M:%S%z') + time_str = "#{time_str[0..21]}:#{time_str[22..23]}" + + create_data(request, request.rest_path, id, JSON.pretty_generate({ + :create_time => time_str, + :checksums => sandbox_checksums + })) json_response(201, { - :uri => build_uri(request.base_uri, request.rest_path + [id.to_s]), + :uri => build_uri(request.base_uri, request.rest_path + [id]), :checksums => result_checksums, :sandbox_id => id }) diff --git a/lib/chef_zero/endpoints/search_endpoint.rb b/lib/chef_zero/endpoints/search_endpoint.rb index 4440c7a..d698b45 100644 --- a/lib/chef_zero/endpoints/search_endpoint.rb +++ b/lib/chef_zero/endpoints/search_endpoint.rb @@ -48,15 +48,15 @@ module ChefZero def search_container(request, index) case index when 'client' - [ data['clients'], Proc.new { |client, name| DataNormalizer.normalize_client(client, name) }, build_uri(request.base_uri, [ 'clients' ]) ] + [ ['clients'], Proc.new { |client, name| DataNormalizer.normalize_client(client, name) }, build_uri(request.base_uri, [ 'clients' ]) ] when 'node' - [ data['nodes'], Proc.new { |node, name| DataNormalizer.normalize_node(node, name) }, build_uri(request.base_uri, [ 'nodes' ]) ] + [ ['nodes'], Proc.new { |node, name| DataNormalizer.normalize_node(node, name) }, build_uri(request.base_uri, [ 'nodes' ]) ] when 'environment' - [ data['environments'], Proc.new { |environment, name| DataNormalizer.normalize_environment(environment, name) }, build_uri(request.base_uri, [ 'environments' ]) ] + [ ['environments'], Proc.new { |environment, name| DataNormalizer.normalize_environment(environment, name) }, build_uri(request.base_uri, [ 'environments' ]) ] when 'role' - [ data['roles'], Proc.new { |role, name| DataNormalizer.normalize_role(role, name) }, build_uri(request.base_uri, [ 'roles' ]) ] + [ ['roles'], Proc.new { |role, name| DataNormalizer.normalize_role(role, name) }, build_uri(request.base_uri, [ 'roles' ]) ] else - [ data['data'][index], Proc.new { |data_bag_item, id| DataNormalizer.normalize_data_bag_item(data_bag_item, index, id, 'DELETE') }, build_uri(request.base_uri, [ 'data', index ]) ] + [ ['data', index], Proc.new { |data_bag_item, id| DataNormalizer.normalize_data_bag_item(data_bag_item, index, id, 'DELETE') }, build_uri(request.base_uri, [ 'data', index ]) ] end end @@ -101,13 +101,11 @@ module ChefZero # Get the search container container, expander, base_uri = search_container(request, index) - if container.nil? - raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, request.rest_path)}") - end # Search! result = [] - container.each_pair do |name,value| + list_data(request, container).each do |name| + value = get_data(request, container + [name]) expanded = expander.call(JSON.parse(value, :create_additions => false), name) result << [ name, build_uri(base_uri, [name]), expanded, expand_for_indexing(expanded, index, name) ] end diff --git a/lib/chef_zero/endpoints/searches_endpoint.rb b/lib/chef_zero/endpoints/searches_endpoint.rb index d7ab451..dabe164 100644 --- a/lib/chef_zero/endpoints/searches_endpoint.rb +++ b/lib/chef_zero/endpoints/searches_endpoint.rb @@ -7,7 +7,7 @@ module ChefZero def get(request) # Get the result result_hash = {} - indices = (%w(client environment node role) + data['data'].keys).sort + indices = (%w(client environment node role) + data_store.list(['data'])).sort indices.each do |index| result_hash[index] = build_uri(request.base_uri, request.rest_path + [index]) end diff --git a/lib/chef_zero/rest_base.rb b/lib/chef_zero/rest_base.rb index f219ff1..5371a0a 100644 --- a/lib/chef_zero/rest_base.rb +++ b/lib/chef_zero/rest_base.rb @@ -1,5 +1,6 @@ require 'chef_zero/rest_request' require 'chef_zero/rest_error_response' +require 'chef_zero/data_store/data_not_found_error' module ChefZero class RestBase @@ -9,8 +10,8 @@ module ChefZero attr_reader :server - def data - server.data + def data_store + server.data_store end def call(request) @@ -35,17 +36,58 @@ module ChefZero true end - def get_data(request, rest_path=nil) + def get_data(request, rest_path=nil, *options) rest_path ||= request.rest_path - # Grab the value we're looking for - value = data - rest_path.each do |path_part| - if !value.has_key?(path_part) + begin + data_store.get(rest_path) + rescue DataStore::DataNotFoundError + if options.include?(:nil) + nil + else raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, rest_path)}") end - value = value[path_part] end - value + end + + def list_data(request, rest_path=nil) + rest_path ||= request.rest_path + begin + data_store.list(rest_path) + rescue DataStore::DataNotFoundError + raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, rest_path)}") + end + end + + def delete_data(request, rest_path=nil) + rest_path ||= request.rest_path + begin + data_store.delete(rest_path) + rescue DataStore::DataNotFoundError + raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, request.rest_path)}") + end + end + + def set_data(request, rest_path, data, *options) + rest_path ||= request.rest_path + begin + data_store.set(rest_path, request.body, *options) + rescue DataStore::DataNotFoundError + raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, request.rest_path)}") + end + end + + def create_data(request, rest_path, name, data, *options) + rest_path ||= request.rest_path + begin + data_store.create(rest_path, name, data, *options) + rescue DataStore::DataAlreadyExistsError + raise RestErrorResponse.new(409, "Object already exists: #{build_uri(request.base_uri, request.rest_path + [name])}") + end + end + + def exists_data?(request, rest_path=nil) + rest_path ||= request.rest_path + data_store.exists?(rest_path) end def error(response_code, error) diff --git a/lib/chef_zero/server.rb b/lib/chef_zero/server.rb index d8f2c7d..50f016a 100644 --- a/lib/chef_zero/server.rb +++ b/lib/chef_zero/server.rb @@ -24,6 +24,7 @@ require 'timeout' require 'chef_zero' require 'chef_zero/cookbook_data' require 'chef_zero/rest_router' +require 'chef_zero/data_store/memory_store' require 'chef_zero/version' require 'chef_zero/endpoints/authenticate_user_endpoint' @@ -77,7 +78,7 @@ module ChefZero end attr_reader :server - attr_reader :data + attr_reader :data_store attr_reader :url include ChefZero::Endpoints @@ -180,15 +181,18 @@ module ChefZero def load_data(contents) %w(clients environments nodes roles users).each do |data_type| if contents[data_type] - data[data_type].merge!(dejsonize_children(contents[data_type])) + dejsonize_children(contents[data_type]).each_pair do |name, data| + data_store.create([data_type], name, data) + end end end if contents['data'] - new_data = {} contents['data'].each_pair do |key, data_bag| - new_data[key] = dejsonize_children(data_bag) + data_store.create_dir(['data'], key, :keep_existing) + dejsonize_children(data_bag).each do |item_name, item| + data_store.set(['data', key], item_name, item, :create) + end end - data['data'].merge!(new_data) end if contents['cookbooks'] contents['cookbooks'].each_pair do |name_version, cookbook| @@ -198,12 +202,12 @@ module ChefZero cookbook_data = CookbookData.to_hash(cookbook, name_version) end raise "No version specified" if !cookbook_data[:version] - data['cookbooks'][cookbook_data[:cookbook_name]] = {} if !data['cookbooks'][cookbook_data[:cookbook_name]] - data['cookbooks'][cookbook_data[:cookbook_name]][cookbook_data[:version]] = JSON.pretty_generate(cookbook_data) + data_store.create_dir(['cookbooks'], cookbook_data[:cookbook_name], :keep_existing) + data_store.set(['cookbooks', cookbook_data[:cookbook_name], cookbook_data[:version]], JSON.pretty_generate(cookbook_data), :create) cookbook_data.values.each do |files| next unless files.is_a? Array files.each do |file| - data['file_store'][file[:checksum]] = get_file(cookbook, file[:path]) + data_store.set(['file_store', file[:checksum]], get_file(cookbook, file[:path]), :create) end end end @@ -211,24 +215,24 @@ module ChefZero end def clear_data - @data = { - 'clients' => { - 'chef-validator' => '{ "validator": true }', - 'chef-webui' => '{ "admin": true }' - }, - 'cookbooks' => {}, - 'data' => {}, - 'environments' => { - '_default' => '{ "description": "The default Chef environment" }' - }, - 'file_store' => {}, - 'nodes' => {}, - 'roles' => {}, - 'sandboxes' => {}, - 'users' => { - 'admin' => '{ "admin": true }' - } - } + @data_store = DataStore::MemoryStore.new + + # Create containers + data_store.create_dir([], 'clients') + data_store.create_dir([], 'cookbooks') + data_store.create_dir([], 'data') + data_store.create_dir([], 'environments') + data_store.create_dir([], 'file_store') + data_store.create_dir([], 'nodes') + data_store.create_dir([], 'roles') + data_store.create_dir([], 'sandboxes') + data_store.create_dir([], 'users') + + # Set defaults + data_store.create(['clients'], 'chef-validator', '{ "validator": true }') + data_store.create(['clients'], 'chef-webui', '{ "admin": true }') + data_store.create(['environments'], '_default', '{ "description": "The default Chef environment" }') + data_store.create(['users'], 'admin', '{ "admin": true }') end def request_handler(&block) |