chef-zero 1.0.1 → 1.1

Sign up to get free protection for your applications and to get access to all the features.
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