chef-zero 1.0.1 → 1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/chef_zero/cookbook_data.rb +58 -24
- data/lib/chef_zero/data_normalizer.rb +1 -1
- data/lib/chef_zero/data_store/chef_fs_store.rb +96 -0
- data/lib/chef_zero/data_store/data_already_exists_error.rb +29 -0
- data/lib/chef_zero/data_store/data_error.rb +31 -0
- data/lib/chef_zero/data_store/data_not_found_error.rb +29 -0
- data/lib/chef_zero/data_store/memory_store.rb +164 -0
- data/lib/chef_zero/endpoints/authenticate_user_endpoint.rb +6 -2
- data/lib/chef_zero/endpoints/cookbook_endpoint.rb +4 -4
- data/lib/chef_zero/endpoints/cookbook_version_endpoint.rb +16 -12
- data/lib/chef_zero/endpoints/cookbooks_base.rb +10 -1
- data/lib/chef_zero/endpoints/cookbooks_endpoint.rb +1 -1
- data/lib/chef_zero/endpoints/data_bag_endpoint.rb +1 -6
- data/lib/chef_zero/endpoints/data_bags_endpoint.rb +2 -3
- data/lib/chef_zero/endpoints/environment_cookbook_endpoint.rb +2 -2
- data/lib/chef_zero/endpoints/environment_cookbook_versions_endpoint.rb +13 -19
- data/lib/chef_zero/endpoints/environment_cookbooks_endpoint.rb +1 -1
- data/lib/chef_zero/endpoints/environment_nodes_endpoint.rb +2 -2
- data/lib/chef_zero/endpoints/environment_recipes_endpoint.rb +2 -2
- data/lib/chef_zero/endpoints/environment_role_endpoint.rb +1 -0
- data/lib/chef_zero/endpoints/file_store_file_endpoint.rb +1 -1
- data/lib/chef_zero/endpoints/principal_endpoint.rb +2 -2
- data/lib/chef_zero/endpoints/rest_list_endpoint.rb +2 -5
- data/lib/chef_zero/endpoints/rest_object_endpoint.rb +9 -13
- data/lib/chef_zero/endpoints/sandbox_endpoint.rb +4 -6
- data/lib/chef_zero/endpoints/sandboxes_endpoint.rb +11 -4
- data/lib/chef_zero/endpoints/search_endpoint.rb +7 -9
- data/lib/chef_zero/endpoints/searches_endpoint.rb +1 -1
- data/lib/chef_zero/rest_base.rb +67 -9
- data/lib/chef_zero/rest_router.rb +5 -1
- data/lib/chef_zero/server.rb +15 -28
- data/lib/chef_zero/version.rb +1 -1
- 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
|
-
|
13
|
-
|
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(
|
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(
|
20
|
+
filter_cookbooks(all_cookbooks_list, {}, 1) do |name, versions|
|
21
21
|
if versions.size > 0
|
22
|
-
cookbook = JSON.parse(
|
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 =>
|
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(
|
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
|
-
|
21
|
-
|
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
|
-
|
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,
|
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(
|
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
|
57
|
+
deleted_cookbook = get_data(request)
|
58
|
+
|
59
59
|
response = super(request)
|
60
60
|
cookbook_name = request.rest_path[1]
|
61
|
-
|
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
|
-
|
84
|
-
|
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
|
-
|
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].
|
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
|
@@ -32,12 +32,7 @@ module ChefZero
|
|
32
32
|
|
33
33
|
def delete(request)
|
34
34
|
key = request.rest_path[1]
|
35
|
-
|
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
|
13
|
+
elsif exists_data_dir?(request, ['data', name])
|
15
14
|
error(409, "Object already exists")
|
16
15
|
else
|
17
|
-
|
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
|
-
|
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 =>
|
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 !
|
24
|
-
raise RestErrorResponse.new(412, "No such cookbook version for cookbook #{$1}: #{$
|
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 !
|
29
|
-
desired_versions[desired_cookbook] = cookbooks
|
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(
|
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
|
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
|
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
|
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,
|
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
|
-
|
14
|
-
|
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(
|
12
|
+
filter_cookbooks(all_cookbooks_list, constraints, 1) do |name, versions|
|
13
13
|
if versions.size > 0
|
14
|
-
cookbook = JSON.parse(
|
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)
|
@@ -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 =
|
11
|
+
json = get_data(request, [ 'users', name ], :nil)
|
12
12
|
if json
|
13
13
|
type = 'user'
|
14
14
|
else
|
15
|
-
json =
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
40
|
-
|
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
|
-
|
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,
|
9
|
-
|
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[
|
16
|
-
:create_time =>
|
13
|
+
:checksums => existing_sandbox['checksums'],
|
14
|
+
:create_time => existing_sandbox['create_time'],
|
17
15
|
:is_completed => true
|
18
16
|
})
|
19
17
|
end
|