chef-zero 1.0.1 → 1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. data/lib/chef_zero/cookbook_data.rb +58 -24
  2. data/lib/chef_zero/data_normalizer.rb +1 -1
  3. data/lib/chef_zero/data_store/chef_fs_store.rb +96 -0
  4. data/lib/chef_zero/data_store/data_already_exists_error.rb +29 -0
  5. data/lib/chef_zero/data_store/data_error.rb +31 -0
  6. data/lib/chef_zero/data_store/data_not_found_error.rb +29 -0
  7. data/lib/chef_zero/data_store/memory_store.rb +164 -0
  8. data/lib/chef_zero/endpoints/authenticate_user_endpoint.rb +6 -2
  9. data/lib/chef_zero/endpoints/cookbook_endpoint.rb +4 -4
  10. data/lib/chef_zero/endpoints/cookbook_version_endpoint.rb +16 -12
  11. data/lib/chef_zero/endpoints/cookbooks_base.rb +10 -1
  12. data/lib/chef_zero/endpoints/cookbooks_endpoint.rb +1 -1
  13. data/lib/chef_zero/endpoints/data_bag_endpoint.rb +1 -6
  14. data/lib/chef_zero/endpoints/data_bags_endpoint.rb +2 -3
  15. data/lib/chef_zero/endpoints/environment_cookbook_endpoint.rb +2 -2
  16. data/lib/chef_zero/endpoints/environment_cookbook_versions_endpoint.rb +13 -19
  17. data/lib/chef_zero/endpoints/environment_cookbooks_endpoint.rb +1 -1
  18. data/lib/chef_zero/endpoints/environment_nodes_endpoint.rb +2 -2
  19. data/lib/chef_zero/endpoints/environment_recipes_endpoint.rb +2 -2
  20. data/lib/chef_zero/endpoints/environment_role_endpoint.rb +1 -0
  21. data/lib/chef_zero/endpoints/file_store_file_endpoint.rb +1 -1
  22. data/lib/chef_zero/endpoints/principal_endpoint.rb +2 -2
  23. data/lib/chef_zero/endpoints/rest_list_endpoint.rb +2 -5
  24. data/lib/chef_zero/endpoints/rest_object_endpoint.rb +9 -13
  25. data/lib/chef_zero/endpoints/sandbox_endpoint.rb +4 -6
  26. data/lib/chef_zero/endpoints/sandboxes_endpoint.rb +11 -4
  27. data/lib/chef_zero/endpoints/search_endpoint.rb +7 -9
  28. data/lib/chef_zero/endpoints/searches_endpoint.rb +1 -1
  29. data/lib/chef_zero/rest_base.rb +67 -9
  30. data/lib/chef_zero/rest_router.rb +5 -1
  31. data/lib/chef_zero/server.rb +15 -28
  32. data/lib/chef_zero/version.rb +1 -1
  33. metadata +7 -2
@@ -9,8 +9,12 @@ module ChefZero
9
9
  request_json = JSON.parse(request.body, :create_additions => false)
10
10
  name = request_json['name']
11
11
  password = request_json['password']
12
- user = data['users'][name]
13
- verified = user && JSON.parse(user, :create_additions => false)['password'] == password
12
+ begin
13
+ user = data_store.get(['users', name])
14
+ verified = JSON.parse(user, :create_additions => false)['password'] == password
15
+ rescue DataStore::DataNotFoundError
16
+ verified = false
17
+ end
14
18
  json_response(200, {
15
19
  'name' => name,
16
20
  'verified' => !!verified
@@ -9,7 +9,7 @@ module ChefZero
9
9
  case filter
10
10
  when '_latest'
11
11
  result = {}
12
- filter_cookbooks(data['cookbooks'], {}, 1) do |name, versions|
12
+ filter_cookbooks(all_cookbooks_list, {}, 1) do |name, versions|
13
13
  if versions.size > 0
14
14
  result[name] = build_uri(request.base_uri, ['cookbooks', name, versions[0]])
15
15
  end
@@ -17,15 +17,15 @@ module ChefZero
17
17
  json_response(200, result)
18
18
  when '_recipes'
19
19
  result = []
20
- filter_cookbooks(data['cookbooks'], {}, 1) do |name, versions|
20
+ filter_cookbooks(all_cookbooks_list, {}, 1) do |name, versions|
21
21
  if versions.size > 0
22
- cookbook = JSON.parse(data['cookbooks'][name][versions[0]], :create_additions => false)
22
+ cookbook = JSON.parse(get_data(request, ['cookbooks', name, versions[0]]), :create_additions => false)
23
23
  result += recipe_names(name, cookbook)
24
24
  end
25
25
  end
26
26
  json_response(200, result.sort)
27
27
  else
28
- cookbook_list = { filter => get_data(request, request.rest_path) }
28
+ cookbook_list = { filter => list_data(request, request.rest_path) }
29
29
  json_response(200, format_cookbooks_list(request, cookbook_list))
30
30
  end
31
31
  end
@@ -9,7 +9,7 @@ module ChefZero
9
9
  class CookbookVersionEndpoint < RestObjectEndpoint
10
10
  def get(request)
11
11
  if request.rest_path[2] == "_latest" || request.rest_path[2] == "latest"
12
- request.rest_path[2] = latest_version(get_data(request, request.rest_path[0..1]).keys)
12
+ request.rest_path[2] = latest_version(list_data(request, request.rest_path[0..1]))
13
13
  end
14
14
  super(request)
15
15
  end
@@ -17,9 +17,8 @@ module ChefZero
17
17
  def put(request)
18
18
  name = request.rest_path[1]
19
19
  version = request.rest_path[2]
20
- data['cookbooks'][name] = {} if !data['cookbooks'][name]
21
- existing_cookbook = data['cookbooks'][name][version]
22
-
20
+ existing_cookbook = get_data(request, request.rest_path, :nil)
21
+
23
22
  # Honor frozen
24
23
  if existing_cookbook
25
24
  existing_cookbook_json = JSON.parse(existing_cookbook, :create_additions => false)
@@ -37,7 +36,7 @@ module ChefZero
37
36
  end
38
37
 
39
38
  # Set the cookbook
40
- data['cookbooks'][name][version] = request.body
39
+ set_data(request, ['cookbooks', name, version], request.body, :create_dir, :create)
41
40
 
42
41
  # If the cookbook was updated, check for deleted files and clean them up
43
42
  if existing_cookbook
@@ -47,18 +46,19 @@ module ChefZero
47
46
  end
48
47
  end
49
48
 
50
- already_json_response(existing_cookbook ? 200 : 201, populate_defaults(request, data['cookbooks'][name][version]))
49
+ already_json_response(existing_cookbook ? 200 : 201, populate_defaults(request, request.body))
51
50
  end
52
51
 
53
52
  def delete(request)
54
53
  if request.rest_path[2] == "_latest" || request.rest_path[2] == "latest"
55
- request.rest_path[2] = latest_version(get_data(request, request.rest_path[0..1]).keys)
54
+ request.rest_path[2] = latest_version(list_data(request, request.rest_path[0..1]))
56
55
  end
57
56
 
58
- deleted_cookbook = get_data(request, request.rest_path)
57
+ deleted_cookbook = get_data(request)
58
+
59
59
  response = super(request)
60
60
  cookbook_name = request.rest_path[1]
61
- data['cookbooks'].delete(cookbook_name) if data['cookbooks'][cookbook_name].size == 0
61
+ delete_data_dir(request, ['cookbooks', cookbook_name]) if list_data(request, ['cookbooks', cookbook_name]).size == 0
62
62
 
63
63
  # Hoover deleted files, if they exist
64
64
  hoover_unused_checksums(get_checksums(deleted_cookbook))
@@ -79,14 +79,18 @@ module ChefZero
79
79
  result
80
80
  end
81
81
 
82
+ private
83
+
82
84
  def hoover_unused_checksums(deleted_checksums)
83
- data['cookbooks'].each_pair do |cookbook_name, versions|
84
- versions.each_pair do |cookbook_version, cookbook|
85
+ data_store.list(['cookbooks']).each do |cookbook_name|
86
+ data_store.list(['cookbooks', cookbook_name]).each do |version|
87
+ cookbook = data_store.get(['cookbooks', cookbook_name, version])
85
88
  deleted_checksums = deleted_checksums - get_checksums(cookbook)
86
89
  end
87
90
  end
88
91
  deleted_checksums.each do |checksum|
89
- data['file_store'].delete(checksum)
92
+ # There can be a race here if multiple cookbooks are uploading.
93
+ data_store.delete(['file_store', 'checksums', checksum])
90
94
  end
91
95
  end
92
96
 
@@ -23,11 +23,20 @@ module ChefZero
23
23
  results
24
24
  end
25
25
 
26
+ def all_cookbooks_list
27
+ result = {}
28
+ # Race conditions exist here (if someone deletes while listing). I don't care.
29
+ data_store.list(['cookbooks']).each do |name|
30
+ result[name] = data_store.list(['cookbooks', name])
31
+ end
32
+ result
33
+ end
34
+
26
35
  def filter_cookbooks(cookbooks_list, constraints = {}, num_versions = nil)
27
36
  cookbooks_list.keys.sort.each do |name|
28
37
  constraint = Gem::Requirement.new(constraints[name])
29
38
  versions = []
30
- cookbooks_list[name].keys.sort_by { |version| Gem::Version.new(version.dup) }.reverse.each do |version|
39
+ cookbooks_list[name].sort_by { |version| Gem::Version.new(version.dup) }.reverse.each do |version|
31
40
  break if num_versions && versions.size >= num_versions
32
41
  if constraint.satisfied_by?(Gem::Version.new(version.dup))
33
42
  versions << version
@@ -5,7 +5,7 @@ module ChefZero
5
5
  # /cookbooks
6
6
  class CookbooksEndpoint < CookbooksBase
7
7
  def get(request)
8
- json_response(200, format_cookbooks_list(request, data['cookbooks']))
8
+ json_response(200, format_cookbooks_list(request, all_cookbooks_list))
9
9
  end
10
10
  end
11
11
  end
@@ -32,12 +32,7 @@ module ChefZero
32
32
 
33
33
  def delete(request)
34
34
  key = request.rest_path[1]
35
- container = data['data']
36
- if !container.has_key?(key)
37
- raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, request.rest_path)}")
38
- end
39
- result = container[key]
40
- container.delete(key)
35
+ delete_data_dir(request, request.rest_path, :recursive)
41
36
  json_response(200, {
42
37
  'chef_type' => 'data_bag',
43
38
  'json_class' => 'Chef::DataBag',
@@ -6,15 +6,14 @@ module ChefZero
6
6
  # /data
7
7
  class DataBagsEndpoint < RestListEndpoint
8
8
  def post(request)
9
- container = get_data(request)
10
9
  contents = request.body
11
10
  name = JSON.parse(contents, :create_additions => false)[identity_key]
12
11
  if name.nil?
13
12
  error(400, "Must specify '#{identity_key}' in JSON")
14
- elsif container[name]
13
+ elsif exists_data_dir?(request, ['data', name])
15
14
  error(409, "Object already exists")
16
15
  else
17
- container[name] = {}
16
+ data_store.create_dir(['data'], name, :keep_existing)
18
17
  json_response(201, {"uri" => "#{build_uri(request.base_uri, request.rest_path + [name])}"})
19
18
  end
20
19
  end
@@ -9,7 +9,7 @@ module ChefZero
9
9
  cookbook_name = request.rest_path[3]
10
10
  environment = JSON.parse(get_data(request, request.rest_path[0..1]), :create_additions => false)
11
11
  constraints = environment['cookbook_versions'] || {}
12
- cookbook = get_data(request, request.rest_path[2..3])
12
+ cookbook_versions = list_data(request, request.rest_path[2..3])
13
13
  if request.query_params['num_versions'] == 'all'
14
14
  num_versions = nil
15
15
  elsif request.query_params['num_versions']
@@ -17,7 +17,7 @@ module ChefZero
17
17
  else
18
18
  num_versions = nil
19
19
  end
20
- json_response(200, format_cookbooks_list(request, { cookbook_name => cookbook }, constraints, num_versions))
20
+ json_response(200, format_cookbooks_list(request, { cookbook_name => cookbook_versions }, constraints, num_versions))
21
21
  end
22
22
  end
23
23
  end
@@ -6,27 +6,21 @@ module ChefZero
6
6
  module Endpoints
7
7
  # /environments/NAME/cookbook_versions
8
8
  class EnvironmentCookbookVersionsEndpoint < RestBase
9
- def cookbooks
10
- data['cookbooks']
11
- end
12
-
13
- def environments
14
- data['environments']
15
- end
16
-
17
9
  def post(request)
10
+ cookbook_names = list_data(request, ['cookbooks'])
11
+
18
12
  # Get the list of cookbooks and versions desired by the runlist
19
13
  desired_versions = {}
20
14
  run_list = JSON.parse(request.body, :create_additions => false)['run_list']
21
15
  run_list.each do |run_list_entry|
22
16
  if run_list_entry =~ /(.+)(::.+)?\@(.+)/
23
- raise RestErrorResponse.new(412, "No such cookbook: #{$1}") if !cookbooks[$1]
24
- raise RestErrorResponse.new(412, "No such cookbook version for cookbook #{$1}: #{$2}") if !cookbooks[$1][$2]
17
+ raise RestErrorResponse.new(412, "No such cookbook: #{$1}") if !cookbook_names.include?($1)
18
+ raise RestErrorResponse.new(412, "No such cookbook version for cookbook #{$1}: #{$3}") if !list_data(request, ['cookbooks', $1]).include?($3)
25
19
  desired_versions[$1] = [ $3 ]
26
20
  else
27
21
  desired_cookbook = run_list_entry.split('::')[0]
28
- raise RestErrorResponse.new(412, "No such cookbook: #{desired_cookbook}") if !cookbooks[desired_cookbook]
29
- desired_versions[desired_cookbook] = cookbooks[desired_cookbook].keys
22
+ raise RestErrorResponse.new(412, "No such cookbook: #{desired_cookbook}") if !cookbook_names.include?(desired_cookbook)
23
+ desired_versions[desired_cookbook] = list_data(request, ['cookbooks', desired_cookbook])
30
24
  end
31
25
  end
32
26
 
@@ -39,20 +33,20 @@ module ChefZero
39
33
  end
40
34
 
41
35
  # Depsolve!
42
- solved = depsolve(desired_versions.keys, desired_versions, environment_constraints)
36
+ solved = depsolve(request, desired_versions.keys, desired_versions, environment_constraints)
43
37
  if !solved
44
38
  return raise RestErrorResponse.new(412, "Unsolvable versions!")
45
39
  end
46
40
 
47
41
  result = {}
48
42
  solved.each_pair do |name, versions|
49
- cookbook = JSON.parse(data['cookbooks'][name][versions[0]], :create_additions => false)
43
+ cookbook = JSON.parse(get_data(request, ['cookbooks', name, versions[0]]), :create_additions => false)
50
44
  result[name] = DataNormalizer.normalize_cookbook(cookbook, name, versions[0], request.base_uri, 'GET')
51
45
  end
52
46
  json_response(200, result)
53
47
  end
54
48
 
55
- def depsolve(unsolved, desired_versions, environment_constraints)
49
+ def depsolve(request, unsolved, desired_versions, environment_constraints)
56
50
  return nil if desired_versions.values.any? { |versions| versions.empty? }
57
51
 
58
52
  # If everything is already
@@ -67,7 +61,7 @@ module ChefZero
67
61
  new_unsolved = unsolved[1..-1]
68
62
 
69
63
  # Pick this cookbook, and add dependencies
70
- cookbook_obj = JSON.parse(cookbooks[solve_for][desired_version], :create_additions => false)
64
+ cookbook_obj = JSON.parse(get_data(request, ['cookbooks', solve_for, desired_version]), :create_additions => false)
71
65
  cookbook_metadata = cookbook_obj['metadata'] || {}
72
66
  cookbook_dependencies = cookbook_metadata['dependencies'] || {}
73
67
  dep_not_found = false
@@ -77,11 +71,11 @@ module ChefZero
77
71
  if !new_desired_versions.has_key?(dep_name)
78
72
  new_unsolved = new_unsolved + [dep_name]
79
73
  # If the dep is missing, we will try other versions of the cookbook that might not have the bad dep.
80
- if !cookbooks[dep_name]
74
+ if !exists_data_dir?(request, ['cookbooks', dep_name])
81
75
  dep_not_found = true
82
76
  break
83
77
  end
84
- new_desired_versions[dep_name] = cookbooks[dep_name].keys
78
+ new_desired_versions[dep_name] = list_data(request, ['cookbooks', dep_name])
85
79
  new_desired_versions = filter_by_constraint(new_desired_versions, dep_name, environment_constraints[dep_name])
86
80
  end
87
81
  new_desired_versions = filter_by_constraint(new_desired_versions, dep_name, dep_constraint)
@@ -90,7 +84,7 @@ module ChefZero
90
84
  next if dep_not_found
91
85
 
92
86
  # Depsolve children with this desired version! First solution wins.
93
- result = depsolve(new_unsolved, new_desired_versions, environment_constraints)
87
+ result = depsolve(request, new_unsolved, new_desired_versions, environment_constraints)
94
88
  return result if result
95
89
  end
96
90
  return nil
@@ -15,7 +15,7 @@ module ChefZero
15
15
  else
16
16
  num_versions = 1
17
17
  end
18
- json_response(200, format_cookbooks_list(request, data['cookbooks'], constraints, num_versions))
18
+ json_response(200, format_cookbooks_list(request, all_cookbooks_list, constraints, num_versions))
19
19
  end
20
20
  end
21
21
  end
@@ -10,8 +10,8 @@ module ChefZero
10
10
  get_data(request, request.rest_path[0..1])
11
11
 
12
12
  result = {}
13
- data['nodes'].each_pair do |name, node|
14
- node_json = JSON.parse(node, :create_additions => false)
13
+ list_data(request, ['nodes']).each do |name|
14
+ node = JSON.parse(get_data(request, ['nodes', name]), :create_additions => false)
15
15
  if node['chef_environment'] == request.rest_path[1]
16
16
  result[name] = build_uri(request.base_uri, 'nodes', name)
17
17
  end
@@ -9,9 +9,9 @@ module ChefZero
9
9
  environment = JSON.parse(get_data(request, request.rest_path[0..1]), :create_additions => false)
10
10
  constraints = environment['cookbook_versions'] || {}
11
11
  result = []
12
- filter_cookbooks(data['cookbooks'], constraints, 1) do |name, versions|
12
+ filter_cookbooks(all_cookbooks_list, constraints, 1) do |name, versions|
13
13
  if versions.size > 0
14
- cookbook = JSON.parse(data['cookbooks'][name][versions[0]], :create_additions => false)
14
+ cookbook = JSON.parse(get_data(request, ['cookbooks', name, versions[0]]), :create_additions => false)
15
15
  result += recipe_names(name, cookbook)
16
16
  end
17
17
  end
@@ -15,6 +15,7 @@ module ChefZero
15
15
  environment_path = request.rest_path[2..3]
16
16
  role_path = request.rest_path[0..1]
17
17
  end
18
+ # Verify that the environment exists
18
19
  get_data(request, environment_path)
19
20
 
20
21
  role = JSON.parse(get_data(request, role_path), :create_additions => false)
@@ -14,7 +14,7 @@ module ChefZero
14
14
  end
15
15
 
16
16
  def put(request)
17
- data['file_store'][request.rest_path[1]] = request.body
17
+ data_store.set(request.rest_path, request.body, :create, :create_dir)
18
18
  json_response(200, {})
19
19
  end
20
20
  end
@@ -8,11 +8,11 @@ module ChefZero
8
8
  class PrincipalEndpoint < RestBase
9
9
  def get(request)
10
10
  name = request.rest_path[-1]
11
- json = data['users'][name]
11
+ json = get_data(request, [ 'users', name ], :nil)
12
12
  if json
13
13
  type = 'user'
14
14
  else
15
- json = data['clients'][name]
15
+ json = get_data(request, [ 'clients', name ], :nil)
16
16
  type = 'client'
17
17
  end
18
18
  if json
@@ -15,22 +15,19 @@ module ChefZero
15
15
  def get(request)
16
16
  # Get the result
17
17
  result_hash = {}
18
- get_data(request).keys.sort.each do |name|
18
+ list_data(request).sort.each do |name|
19
19
  result_hash[name] = "#{build_uri(request.base_uri, request.rest_path + [name])}"
20
20
  end
21
21
  json_response(200, result_hash)
22
22
  end
23
23
 
24
24
  def post(request)
25
- container = get_data(request)
26
25
  contents = request.body
27
26
  key = get_key(contents)
28
27
  if key.nil?
29
28
  error(400, "Must specify '#{identity_key}' in JSON")
30
- elsif container[key]
31
- error(409, 'Object already exists')
32
29
  else
33
- container[key] = contents
30
+ create_data(request, request.rest_path, key, contents)
34
31
  json_response(201, {'uri' => "#{build_uri(request.base_uri, request.rest_path + [key])}"})
35
32
  end
36
33
  end
@@ -22,33 +22,29 @@ module ChefZero
22
22
  old_body = get_data(request)
23
23
  request_json = JSON.parse(request.body, :create_additions => false)
24
24
  key = request_json[identity_key] || request.rest_path[-1]
25
- container = get_data(request, request.rest_path[0..-2])
26
25
  # If it's a rename, check for conflict and delete the old value
27
26
  rename = key != request.rest_path[-1]
28
27
  if rename
29
- if container.has_key?(key)
28
+ begin
29
+ data_store.create(request.rest_path[0..-2], key, request.body)
30
+ rescue DataStore::DataAlreadyExistsError
30
31
  return error(409, "Cannot rename '#{request.rest_path[-1]}' to '#{key}': '#{key}' already exists")
31
32
  end
32
- container.delete(request.rest_path[-1])
33
+ delete_data(request)
34
+ else
35
+ set_data(request, request.rest_path, request.body)
33
36
  end
34
- container[key] = request.body
35
37
  already_json_response(200, populate_defaults(request, request.body))
36
38
  end
37
39
 
38
40
  def delete(request)
39
- key = request.rest_path[-1]
40
- container = get_data(request, request.rest_path[0..-2])
41
- if !container.has_key?(key)
42
- raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, request.rest_path)}")
43
- end
44
- result = container[key]
45
- container.delete(key)
41
+ result = get_data(request)
42
+ delete_data(request)
46
43
  already_json_response(200, populate_defaults(request, result))
47
44
  end
48
45
 
49
46
  def patch_request_body(request)
50
- container = get_data(request, request.rest_path[0..-2])
51
- existing_value = container[request.rest_path[-1]]
47
+ existing_value = get_data(request, nil, :nil)
52
48
  if existing_value
53
49
  request_json = JSON.parse(request.body, :create_additions => false)
54
50
  existing_json = JSON.parse(existing_value, :create_additions => false)
@@ -5,15 +5,13 @@ module ChefZero
5
5
  # /sandboxes/ID
6
6
  class SandboxEndpoint < RestBase
7
7
  def put(request)
8
- existing_sandbox = get_data(request, request.rest_path)
9
- data['sandboxes'].delete(request.rest_path[1])
10
- time_str = existing_sandbox[:create_time].strftime('%Y-%m-%dT%H:%M:%S%z')
11
- time_str = "#{time_str[0..21]}:#{time_str[22..23]}"
8
+ existing_sandbox = JSON.parse(get_data(request), :create_additions => false)
9
+ delete_data(request)
12
10
  json_response(200, {
13
11
  :guid => request.rest_path[1],
14
12
  :name => request.rest_path[1],
15
- :checksums => existing_sandbox[:checksums],
16
- :create_time => time_str,
13
+ :checksums => existing_sandbox['checksums'],
14
+ :create_time => existing_sandbox['create_time'],
17
15
  :is_completed => true
18
16
  })
19
17
  end