chef-zero 0.9
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.
- data/LICENSE +201 -0
- data/README.rdoc +79 -0
- data/Rakefile +19 -0
- data/bin/chef-zero +40 -0
- data/lib/chef_zero.rb +5 -0
- data/lib/chef_zero/cookbook_data.rb +110 -0
- data/lib/chef_zero/data_normalizer.rb +129 -0
- data/lib/chef_zero/endpoints/actor_endpoint.rb +68 -0
- data/lib/chef_zero/endpoints/actors_endpoint.rb +32 -0
- data/lib/chef_zero/endpoints/authenticate_user_endpoint.rb +21 -0
- data/lib/chef_zero/endpoints/cookbook_endpoint.rb +39 -0
- data/lib/chef_zero/endpoints/cookbook_version_endpoint.rb +106 -0
- data/lib/chef_zero/endpoints/cookbooks_base.rb +59 -0
- data/lib/chef_zero/endpoints/cookbooks_endpoint.rb +12 -0
- data/lib/chef_zero/endpoints/data_bag_endpoint.rb +50 -0
- data/lib/chef_zero/endpoints/data_bag_item_endpoint.rb +25 -0
- data/lib/chef_zero/endpoints/data_bags_endpoint.rb +21 -0
- data/lib/chef_zero/endpoints/environment_cookbook_endpoint.rb +24 -0
- data/lib/chef_zero/endpoints/environment_cookbook_versions_endpoint.rb +114 -0
- data/lib/chef_zero/endpoints/environment_cookbooks_endpoint.rb +22 -0
- data/lib/chef_zero/endpoints/environment_endpoint.rb +33 -0
- data/lib/chef_zero/endpoints/environment_nodes_endpoint.rb +23 -0
- data/lib/chef_zero/endpoints/environment_recipes_endpoint.rb +22 -0
- data/lib/chef_zero/endpoints/environment_role_endpoint.rb +35 -0
- data/lib/chef_zero/endpoints/file_store_file_endpoint.rb +22 -0
- data/lib/chef_zero/endpoints/node_endpoint.rb +17 -0
- data/lib/chef_zero/endpoints/not_found_endpoint.rb +9 -0
- data/lib/chef_zero/endpoints/principal_endpoint.rb +30 -0
- data/lib/chef_zero/endpoints/rest_list_endpoint.rb +41 -0
- data/lib/chef_zero/endpoints/rest_object_endpoint.rb +65 -0
- data/lib/chef_zero/endpoints/role_endpoint.rb +16 -0
- data/lib/chef_zero/endpoints/role_environments_endpoint.rb +14 -0
- data/lib/chef_zero/endpoints/sandbox_endpoint.rb +22 -0
- data/lib/chef_zero/endpoints/sandboxes_endpoint.rb +44 -0
- data/lib/chef_zero/endpoints/search_endpoint.rb +139 -0
- data/lib/chef_zero/endpoints/searches_endpoint.rb +18 -0
- data/lib/chef_zero/rest_base.rb +82 -0
- data/lib/chef_zero/rest_error_response.rb +11 -0
- data/lib/chef_zero/rest_request.rb +42 -0
- data/lib/chef_zero/router.rb +26 -0
- data/lib/chef_zero/server.rb +255 -0
- data/lib/chef_zero/solr/query/binary_operator.rb +53 -0
- data/lib/chef_zero/solr/query/phrase.rb +23 -0
- data/lib/chef_zero/solr/query/range_query.rb +34 -0
- data/lib/chef_zero/solr/query/regexpable_query.rb +29 -0
- data/lib/chef_zero/solr/query/subquery.rb +35 -0
- data/lib/chef_zero/solr/query/term.rb +45 -0
- data/lib/chef_zero/solr/query/unary_operator.rb +43 -0
- data/lib/chef_zero/solr/solr_doc.rb +62 -0
- data/lib/chef_zero/solr/solr_parser.rb +194 -0
- data/lib/chef_zero/version.rb +3 -0
- metadata +132 -0
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'chef_zero/endpoints/cookbooks_base'
|
2
|
+
|
3
|
+
module ChefZero
|
4
|
+
module Endpoints
|
5
|
+
# /cookbooks
|
6
|
+
class CookbooksEndpoint < CookbooksBase
|
7
|
+
def get(request)
|
8
|
+
json_response(200, format_cookbooks_list(request, data['cookbooks']))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'chef_zero/endpoints/rest_list_endpoint'
|
3
|
+
require 'chef_zero/endpoints/data_bag_item_endpoint'
|
4
|
+
require 'chef_zero/rest_error_response'
|
5
|
+
|
6
|
+
module ChefZero
|
7
|
+
module Endpoints
|
8
|
+
# /data/NAME
|
9
|
+
class DataBagEndpoint < RestListEndpoint
|
10
|
+
def initialize(server)
|
11
|
+
super(server, 'id')
|
12
|
+
end
|
13
|
+
|
14
|
+
def post(request)
|
15
|
+
key = JSON.parse(request.body, :create_additions => false)[identity_key]
|
16
|
+
response = super(request)
|
17
|
+
if response[0] == 201
|
18
|
+
already_json_response(201, DataBagItemEndpoint::populate_defaults(request, request.body, request.rest_path[1], key))
|
19
|
+
else
|
20
|
+
response
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_key(contents)
|
25
|
+
data_bag_item = JSON.parse(contents, :create_additions => false)
|
26
|
+
if data_bag_item['json_class'] == 'Chef::DataBagItem' && data_bag_item['raw_data']
|
27
|
+
data_bag_item['raw_data']['id']
|
28
|
+
else
|
29
|
+
data_bag_item['id']
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def delete(request)
|
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)
|
41
|
+
json_response(200, {
|
42
|
+
'chef_type' => 'data_bag',
|
43
|
+
'json_class' => 'Chef::DataBag',
|
44
|
+
'name' => key
|
45
|
+
})
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'chef_zero/endpoints/rest_object_endpoint'
|
3
|
+
require 'chef_zero/endpoints/data_bag_item_endpoint'
|
4
|
+
require 'chef_zero/data_normalizer'
|
5
|
+
|
6
|
+
module ChefZero
|
7
|
+
module Endpoints
|
8
|
+
# /data/NAME/NAME
|
9
|
+
class DataBagItemEndpoint < RestObjectEndpoint
|
10
|
+
def initialize(server)
|
11
|
+
super(server, 'id')
|
12
|
+
end
|
13
|
+
|
14
|
+
def populate_defaults(request, response_json)
|
15
|
+
DataBagItemEndpoint::populate_defaults(request, response_json, request.rest_path[1], request.rest_path[2])
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.populate_defaults(request, response_json, data_bag, data_bag_item)
|
19
|
+
response = JSON.parse(response_json, :create_additions => false)
|
20
|
+
response = DataNormalizer.normalize_data_bag_item(response, data_bag, data_bag_item, request.method)
|
21
|
+
JSON.pretty_generate(response)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'chef_zero/endpoints/rest_list_endpoint'
|
3
|
+
|
4
|
+
module ChefZero
|
5
|
+
module Endpoints
|
6
|
+
# /data
|
7
|
+
class DataBagsEndpoint < RestListEndpoint
|
8
|
+
def post(request)
|
9
|
+
container = get_data(request)
|
10
|
+
contents = request.body
|
11
|
+
name = JSON.parse(contents, :create_additions => false)[identity_key]
|
12
|
+
if container[name]
|
13
|
+
error(409, "Object already exists")
|
14
|
+
else
|
15
|
+
container[name] = {}
|
16
|
+
json_response(201, {"uri" => "#{build_uri(request.base_uri, request.rest_path + [name])}"})
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'chef_zero/endpoints/cookbooks_base'
|
3
|
+
|
4
|
+
module ChefZero
|
5
|
+
module Endpoints
|
6
|
+
# /environments/NAME/cookbooks/NAME
|
7
|
+
class EnvironmentCookbookEndpoint < CookbooksBase
|
8
|
+
def get(request)
|
9
|
+
cookbook_name = request.rest_path[3]
|
10
|
+
environment = JSON.parse(get_data(request, request.rest_path[0..1]), :create_additions => false)
|
11
|
+
constraints = environment['cookbook_versions'] || {}
|
12
|
+
cookbook = get_data(request, request.rest_path[2..3])
|
13
|
+
if request.query_params['num_versions'] == 'all'
|
14
|
+
num_versions = nil
|
15
|
+
elsif request.query_params['num_versions']
|
16
|
+
num_versions = request.query_params['num_versions'].to_i
|
17
|
+
else
|
18
|
+
num_versions = nil
|
19
|
+
end
|
20
|
+
json_response(200, format_cookbooks_list(request, { cookbook_name => cookbook }, constraints, num_versions))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'chef/exceptions' # Needed so Chef::Version/VersionConstraint load
|
3
|
+
require 'chef/version_class'
|
4
|
+
require 'chef/version_constraint'
|
5
|
+
require 'chef_zero/rest_base'
|
6
|
+
require 'chef_zero/rest_error_response'
|
7
|
+
|
8
|
+
module ChefZero
|
9
|
+
module Endpoints
|
10
|
+
# /environments/NAME/cookbook_versions
|
11
|
+
class EnvironmentCookbookVersionsEndpoint < RestBase
|
12
|
+
def cookbooks
|
13
|
+
data['cookbooks']
|
14
|
+
end
|
15
|
+
|
16
|
+
def environments
|
17
|
+
data['environments']
|
18
|
+
end
|
19
|
+
|
20
|
+
def post(request)
|
21
|
+
# Get the list of cookbooks and versions desired by the runlist
|
22
|
+
desired_versions = {}
|
23
|
+
run_list = JSON.parse(request.body, :create_additions => false)['run_list']
|
24
|
+
run_list.each do |run_list_entry|
|
25
|
+
if run_list_entry =~ /(.+)\@(.+)/
|
26
|
+
raise RestErrorResponse.new(412, "No such cookbook: #{$1}") if !cookbooks[$1]
|
27
|
+
raise RestErrorResponse.new(412, "No such cookbook version for cookbook #{$1}: #{$2}") if !cookbooks[$1][$2]
|
28
|
+
desired_versions[$1] = [ $2 ]
|
29
|
+
else
|
30
|
+
raise RestErrorResponse.new(412, "No such cookbook: #{run_list_entry}") if !cookbooks[run_list_entry]
|
31
|
+
desired_versions[run_list_entry] = cookbooks[run_list_entry].keys
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Filter by environment constraints
|
36
|
+
environment = JSON.parse(get_data(request, request.rest_path[0..1]), :create_additions => false)
|
37
|
+
environment_constraints = environment['cookbook_versions']
|
38
|
+
|
39
|
+
desired_versions.each_key do |name|
|
40
|
+
desired_versions = filter_by_constraint(desired_versions, name, environment_constraints[name])
|
41
|
+
end
|
42
|
+
|
43
|
+
# Depsolve!
|
44
|
+
solved = depsolve(desired_versions.keys, desired_versions, environment_constraints)
|
45
|
+
if !solved
|
46
|
+
return raise RestErrorResponse.new(412, "Unsolvable versions!")
|
47
|
+
end
|
48
|
+
|
49
|
+
result = {}
|
50
|
+
solved.each_pair do |name, versions|
|
51
|
+
result[name] = JSON.parse(data['cookbooks'][name][versions[0]], :create_additions => false)
|
52
|
+
end
|
53
|
+
json_response(200, result)
|
54
|
+
end
|
55
|
+
|
56
|
+
def depsolve(unsolved, desired_versions, environment_constraints)
|
57
|
+
return nil if desired_versions.values.any? { |versions| versions.empty? }
|
58
|
+
|
59
|
+
# If everything is already
|
60
|
+
solve_for = unsolved[0]
|
61
|
+
return desired_versions if !solve_for
|
62
|
+
|
63
|
+
# Go through each desired version of this cookbook, starting with the latest,
|
64
|
+
# until we find one we can solve successfully with
|
65
|
+
sort_versions(desired_versions[solve_for]).each do |desired_version|
|
66
|
+
new_desired_versions = desired_versions.clone
|
67
|
+
new_desired_versions[solve_for] = [ desired_version ]
|
68
|
+
new_unsolved = unsolved[1..-1]
|
69
|
+
|
70
|
+
# Pick this cookbook, and add dependencies
|
71
|
+
cookbook_obj = JSON.parse(cookbooks[solve_for][desired_version], :create_additions => false)
|
72
|
+
dep_not_found = false
|
73
|
+
cookbook_obj['metadata']['dependencies'].each_pair do |dep_name, dep_constraint|
|
74
|
+
# If the dep is not already in the list, add it to the list to solve
|
75
|
+
# and bring in all environment-allowed cookbook versions to desired_versions
|
76
|
+
if !new_desired_versions.has_key?(dep_name)
|
77
|
+
new_unsolved = new_unsolved + [dep_name]
|
78
|
+
# If the dep is missing, we will try other versions of the cookbook that might not have the bad dep.
|
79
|
+
if !cookbooks[dep_name]
|
80
|
+
dep_not_found = true
|
81
|
+
break
|
82
|
+
end
|
83
|
+
new_desired_versions[dep_name] = cookbooks[dep_name].keys
|
84
|
+
new_desired_versions = filter_by_constraint(new_desired_versions, dep_name, environment_constraints[dep_name])
|
85
|
+
end
|
86
|
+
new_desired_versions = filter_by_constraint(new_desired_versions, dep_name, dep_constraint)
|
87
|
+
end
|
88
|
+
|
89
|
+
next if dep_not_found
|
90
|
+
|
91
|
+
# Depsolve children with this desired version! First solution wins.
|
92
|
+
result = depsolve(new_unsolved, new_desired_versions, environment_constraints)
|
93
|
+
return result if result
|
94
|
+
end
|
95
|
+
return nil
|
96
|
+
end
|
97
|
+
|
98
|
+
def sort_versions(versions)
|
99
|
+
result = versions.sort_by { |version| Chef::Version.new(version) }
|
100
|
+
result.reverse
|
101
|
+
end
|
102
|
+
|
103
|
+
def filter_by_constraint(versions, cookbook_name, constraint)
|
104
|
+
return versions if !constraint
|
105
|
+
constraint = Chef::VersionConstraint.new(constraint)
|
106
|
+
new_versions = versions[cookbook_name]
|
107
|
+
new_versions = new_versions.select { |version| constraint.include?(version) }
|
108
|
+
result = versions.clone
|
109
|
+
result[cookbook_name] = new_versions
|
110
|
+
result
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'chef_zero/endpoints/cookbooks_base'
|
3
|
+
|
4
|
+
module ChefZero
|
5
|
+
module Endpoints
|
6
|
+
# /environments/NAME/cookbooks
|
7
|
+
class EnvironmentCookbooksEndpoint < CookbooksBase
|
8
|
+
def get(request)
|
9
|
+
environment = JSON.parse(get_data(request, request.rest_path[0..1]), :create_additions => false)
|
10
|
+
constraints = environment['cookbook_versions'] || {}
|
11
|
+
if request.query_params['num_versions'] == 'all'
|
12
|
+
num_versions = nil
|
13
|
+
elsif request.query_params['num_versions']
|
14
|
+
num_versions = request.query_params['num_versions'].to_i
|
15
|
+
else
|
16
|
+
num_versions = 1
|
17
|
+
end
|
18
|
+
json_response(200, format_cookbooks_list(request, data['cookbooks'], constraints, num_versions))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'chef_zero/endpoints/rest_object_endpoint'
|
3
|
+
require 'chef_zero/data_normalizer'
|
4
|
+
|
5
|
+
module ChefZero
|
6
|
+
module Endpoints
|
7
|
+
# /environments/NAME
|
8
|
+
class EnvironmentEndpoint < RestObjectEndpoint
|
9
|
+
def delete(request)
|
10
|
+
if request.rest_path[1] == "_default"
|
11
|
+
# 405, really?
|
12
|
+
error(405, "The '_default' environment cannot be modified.")
|
13
|
+
else
|
14
|
+
super(request)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def put(request)
|
19
|
+
if request.rest_path[1] == "_default"
|
20
|
+
error(405, "The '_default' environment cannot be modified.")
|
21
|
+
else
|
22
|
+
super(request)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def populate_defaults(request, response_json)
|
27
|
+
response = JSON.parse(response_json, :create_additions => false)
|
28
|
+
response = DataNormalizer.normalize_environment(response, request.rest_path[1])
|
29
|
+
JSON.pretty_generate(response)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'chef_zero/rest_base'
|
3
|
+
|
4
|
+
module ChefZero
|
5
|
+
module Endpoints
|
6
|
+
# /environment/NAME/nodes
|
7
|
+
class EnvironmentNodesEndpoint < RestBase
|
8
|
+
def get(request)
|
9
|
+
# 404 if environment does not exist
|
10
|
+
get_data(request, request.rest_path[0..1])
|
11
|
+
|
12
|
+
result = {}
|
13
|
+
data['nodes'].each_pair do |name, node|
|
14
|
+
node_json = JSON.parse(node, :create_additions => false)
|
15
|
+
if node['chef_environment'] == request.rest_path[1]
|
16
|
+
result[name] = build_uri(request.base_uri, 'nodes', name)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
json_response(200, result)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'chef_zero/endpoints/cookbooks_base'
|
3
|
+
|
4
|
+
module ChefZero
|
5
|
+
module Endpoints
|
6
|
+
# /environment/NAME/recipes
|
7
|
+
class EnvironmentRecipesEndpoint < CookbooksBase
|
8
|
+
def get(request)
|
9
|
+
environment = JSON.parse(get_data(request, request.rest_path[0..1]), :create_additions => false)
|
10
|
+
constraints = environment['cookbook_versions'] || {}
|
11
|
+
result = []
|
12
|
+
filter_cookbooks(data['cookbooks'], constraints, 1) do |name, versions|
|
13
|
+
if versions.size > 0
|
14
|
+
cookbook = JSON.parse(data['cookbooks'][name][versions[0]], :create_additions => false)
|
15
|
+
result += recipe_names(name, cookbook)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
json_response(200, result.sort)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'chef_zero/endpoints/cookbooks_base'
|
3
|
+
|
4
|
+
module ChefZero
|
5
|
+
module Endpoints
|
6
|
+
# /environments/NAME/roles/NAME
|
7
|
+
# /roles/NAME/environments/NAME
|
8
|
+
class EnvironmentRoleEndpoint < CookbooksBase
|
9
|
+
def get(request)
|
10
|
+
# 404 if environment does not exist
|
11
|
+
if request.rest_path[0] == 'environments'
|
12
|
+
environment_path = request.rest_path[0..1]
|
13
|
+
role_path = request.rest_path[2..3]
|
14
|
+
else
|
15
|
+
environment_path = request.rest_path[2..3]
|
16
|
+
role_path = request.rest_path[0..1]
|
17
|
+
end
|
18
|
+
get_data(request, environment_path)
|
19
|
+
|
20
|
+
role = JSON.parse(get_data(request, role_path), :create_additions => false)
|
21
|
+
environment_name = environment_path[1]
|
22
|
+
if environment_name == '_default'
|
23
|
+
run_list = role['run_list']
|
24
|
+
else
|
25
|
+
if role['env_run_lists']
|
26
|
+
run_list = role['env_run_lists'][environment_name]
|
27
|
+
else
|
28
|
+
run_list = nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
json_response(200, { 'run_list' => run_list })
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'chef_zero/rest_base'
|
2
|
+
|
3
|
+
module ChefZero
|
4
|
+
module Endpoints
|
5
|
+
# The minimum amount of S3 necessary to support cookbook upload/download
|
6
|
+
# /file_store/FILE
|
7
|
+
class FileStoreFileEndpoint < RestBase
|
8
|
+
def json_only
|
9
|
+
false
|
10
|
+
end
|
11
|
+
|
12
|
+
def get(request)
|
13
|
+
[200, {"Content-Type" => 'application/x-binary'}, get_data(request) ]
|
14
|
+
end
|
15
|
+
|
16
|
+
def put(request)
|
17
|
+
data['file_store'][request.rest_path[1]] = request.body
|
18
|
+
json_response(200, {})
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'chef_zero/endpoints/rest_object_endpoint'
|
3
|
+
require 'chef_zero/data_normalizer'
|
4
|
+
|
5
|
+
module ChefZero
|
6
|
+
module Endpoints
|
7
|
+
# /nodes/ID
|
8
|
+
class NodeEndpoint < RestObjectEndpoint
|
9
|
+
def populate_defaults(request, response_json)
|
10
|
+
node = JSON.parse(response_json, :create_additions => false)
|
11
|
+
node = DataNormalizer.normalize_node(node, request.rest_path[1])
|
12
|
+
JSON.pretty_generate(node)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|