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,129 @@
|
|
1
|
+
require 'chef_zero'
|
2
|
+
require 'chef_zero/rest_base'
|
3
|
+
|
4
|
+
module ChefZero
|
5
|
+
class DataNormalizer
|
6
|
+
def self.normalize_client(client, name)
|
7
|
+
client['name'] ||= name
|
8
|
+
client['admin'] ||= false
|
9
|
+
client['public_key'] ||= PUBLIC_KEY
|
10
|
+
client['validator'] ||= false
|
11
|
+
client['json_class'] ||= "Chef::ApiClient"
|
12
|
+
client['chef_type'] ||= "client"
|
13
|
+
client
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.normalize_user(user, name)
|
17
|
+
user['name'] ||= name
|
18
|
+
user['admin'] ||= false
|
19
|
+
user['public_key'] ||= PUBLIC_KEY
|
20
|
+
user
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.normalize_data_bag_item(data_bag_item, data_bag_name, id, method)
|
24
|
+
if method == 'DELETE'
|
25
|
+
# TODO SERIOUSLY, WHO DOES THIS MANY EXCEPTIONS IN THEIR INTERFACE
|
26
|
+
if !(data_bag_item['json_class'] == 'Chef::DataBagItem' && data_bag_item['raw_data'])
|
27
|
+
data_bag_item['id'] ||= id
|
28
|
+
data_bag_item = { 'raw_data' => data_bag_item }
|
29
|
+
data_bag_item['chef_type'] ||= 'data_bag_item'
|
30
|
+
data_bag_item['json_class'] ||= 'Chef::DataBagItem'
|
31
|
+
data_bag_item['data_bag'] ||= data_bag_name
|
32
|
+
data_bag_item['name'] ||= "data_bag_item_#{data_bag_name}_#{id}"
|
33
|
+
end
|
34
|
+
else
|
35
|
+
# If it's not already wrapped with raw_data, wrap it.
|
36
|
+
if data_bag_item['json_class'] == 'Chef::DataBagItem' && data_bag_item['raw_data']
|
37
|
+
data_bag_item = data_bag_item['raw_data']
|
38
|
+
end
|
39
|
+
# Argh. We don't do this on GET, but we do on PUT and POST????
|
40
|
+
if %w(PUT POST).include?(method)
|
41
|
+
data_bag_item['chef_type'] ||= 'data_bag_item'
|
42
|
+
data_bag_item['data_bag'] ||= data_bag_name
|
43
|
+
end
|
44
|
+
data_bag_item['id'] ||= id
|
45
|
+
end
|
46
|
+
data_bag_item
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.normalize_environment(environment, name)
|
50
|
+
environment['name'] ||= name
|
51
|
+
environment['description'] ||= ''
|
52
|
+
environment['cookbook_versions'] ||= {}
|
53
|
+
environment['json_class'] ||= "Chef::Environment"
|
54
|
+
environment['chef_type'] ||= "environment"
|
55
|
+
environment['default_attributes'] ||= {}
|
56
|
+
environment['override_attributes'] ||= {}
|
57
|
+
environment
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.normalize_cookbook(cookbook, name, version, base_uri, method)
|
61
|
+
# TODO I feel dirty
|
62
|
+
if method != 'PUT'
|
63
|
+
cookbook.each_pair do |key, value|
|
64
|
+
if value.is_a?(Array)
|
65
|
+
value.each do |file|
|
66
|
+
if file.is_a?(Hash) && file.has_key?('checksum')
|
67
|
+
file['url'] ||= RestBase::build_uri(base_uri, ['file_store', file['checksum']])
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
cookbook['name'] ||= "#{name}-#{version}"
|
74
|
+
# TODO this feels wrong, but the real chef server doesn't expand this default
|
75
|
+
# cookbook['version'] ||= version
|
76
|
+
cookbook['cookbook_name'] ||= name
|
77
|
+
cookbook['json_class'] ||= 'Chef::CookbookVersion'
|
78
|
+
cookbook['chef_type'] ||= 'cookbook_version'
|
79
|
+
cookbook['frozen?'] ||= false
|
80
|
+
cookbook['metadata'] ||= {}
|
81
|
+
cookbook['metadata']['version'] ||= version
|
82
|
+
cookbook['metadata']['name'] ||= name
|
83
|
+
cookbook
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.normalize_node(node, name)
|
87
|
+
node['name'] ||= name
|
88
|
+
node['json_class'] ||= 'Chef::Node'
|
89
|
+
node['chef_type'] ||= 'node'
|
90
|
+
node['chef_environment'] ||= '_default'
|
91
|
+
node['override'] ||= {}
|
92
|
+
node['normal'] ||= {}
|
93
|
+
node['default'] ||= {}
|
94
|
+
node['automatic'] ||= {}
|
95
|
+
node['run_list'] ||= []
|
96
|
+
node['run_list'] = normalize_run_list(node['run_list'])
|
97
|
+
node
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.normalize_role(role, name)
|
101
|
+
role['name'] ||= name
|
102
|
+
role['description'] ||= ''
|
103
|
+
role['json_class'] ||= 'Chef::Role'
|
104
|
+
role['chef_type'] ||= 'role'
|
105
|
+
role['default_attributes'] ||= {}
|
106
|
+
role['override_attributes'] ||= {}
|
107
|
+
role['run_list'] ||= []
|
108
|
+
role['run_list'] = normalize_run_list(role['run_list'])
|
109
|
+
role['env_run_lists'] ||= {}
|
110
|
+
role['env_run_lists'].each_pair do |env, run_list|
|
111
|
+
role['env_run_lists'][env] = normalize_run_list(run_list)
|
112
|
+
end
|
113
|
+
role
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.normalize_run_list(run_list)
|
117
|
+
run_list.map{|item|
|
118
|
+
case item
|
119
|
+
when /^recipe\[.*\]$/
|
120
|
+
item # explicit recipe
|
121
|
+
when /^role\[.*\]$/
|
122
|
+
item # explicit role
|
123
|
+
else
|
124
|
+
"recipe[#{item}]"
|
125
|
+
end
|
126
|
+
}.uniq
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,68 @@
|
|
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
|
+
# /clients/* and /users/*
|
8
|
+
class ActorEndpoint < RestObjectEndpoint
|
9
|
+
def put(request)
|
10
|
+
# Find out if we're updating the public key.
|
11
|
+
request_body = JSON.parse(request.body, :create_additions => false)
|
12
|
+
if request_body['public_key'].nil?
|
13
|
+
# If public_key is null, then don't overwrite it. Weird patchiness.
|
14
|
+
body_modified = true
|
15
|
+
request_body.delete('public_key')
|
16
|
+
else
|
17
|
+
updating_public_key = true
|
18
|
+
end
|
19
|
+
|
20
|
+
# Generate private_key if requested.
|
21
|
+
if request_body.has_key?('private_key')
|
22
|
+
body_modified = true
|
23
|
+
if request_body['private_key']
|
24
|
+
private_key, public_key = server.gen_key_pair
|
25
|
+
updating_public_key = true
|
26
|
+
request_body['public_key'] = public_key
|
27
|
+
end
|
28
|
+
request_body.delete('private_key')
|
29
|
+
end
|
30
|
+
|
31
|
+
# Save request
|
32
|
+
request.body = JSON.pretty_generate(request_body) if body_modified
|
33
|
+
|
34
|
+
# PUT /clients is patchy
|
35
|
+
request.body = patch_request_body(request)
|
36
|
+
|
37
|
+
result = super(request)
|
38
|
+
|
39
|
+
# Inject private_key into response, delete public_key/password if applicable
|
40
|
+
if result[0] == 200
|
41
|
+
response = JSON.parse(result[2], :create_additions => false)
|
42
|
+
response['private_key'] = private_key if private_key
|
43
|
+
response.delete('public_key') if !updating_public_key && request.rest_path[0] == 'users'
|
44
|
+
response.delete('password')
|
45
|
+
# For PUT /clients, a rename returns 201.
|
46
|
+
if request_body['name'] && request.rest_path[1] != request_body['name']
|
47
|
+
json_response(201, response)
|
48
|
+
else
|
49
|
+
json_response(200, response)
|
50
|
+
end
|
51
|
+
else
|
52
|
+
result
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def populate_defaults(request, response_json)
|
57
|
+
response = JSON.parse(response_json, :create_additions => false)
|
58
|
+
if request.rest_path[0] == 'clients'
|
59
|
+
response = DataNormalizer.normalize_client(response, request.rest_path[1])
|
60
|
+
else
|
61
|
+
response = DataNormalizer.normalize_user(response, request.rest_path[1])
|
62
|
+
end
|
63
|
+
JSON.pretty_generate(response)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'chef_zero/endpoints/rest_list_endpoint'
|
3
|
+
|
4
|
+
module ChefZero
|
5
|
+
module Endpoints
|
6
|
+
# /clients or /users
|
7
|
+
class ActorsEndpoint < RestListEndpoint
|
8
|
+
def post(request)
|
9
|
+
# First, find out if the user actually posted a public key. If not, make
|
10
|
+
# one.
|
11
|
+
request_body = JSON.parse(request.body, :create_additions => false)
|
12
|
+
public_key = request_body['public_key']
|
13
|
+
if !public_key
|
14
|
+
private_key, public_key = server.gen_key_pair
|
15
|
+
request_body['public_key'] = public_key
|
16
|
+
request.body = JSON.pretty_generate(request_body)
|
17
|
+
end
|
18
|
+
|
19
|
+
result = super(request)
|
20
|
+
if result[0] == 201
|
21
|
+
# If we generated a key, stuff it in the response.
|
22
|
+
response = JSON.parse(result[2], :create_additions => false)
|
23
|
+
response['private_key'] = private_key if private_key
|
24
|
+
response['public_key'] = public_key
|
25
|
+
json_response(201, response)
|
26
|
+
else
|
27
|
+
result
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'chef_zero/rest_base'
|
3
|
+
|
4
|
+
module ChefZero
|
5
|
+
module Endpoints
|
6
|
+
# /authenticate_user
|
7
|
+
class AuthenticateUserEndpoint < RestBase
|
8
|
+
def post(request)
|
9
|
+
request_json = JSON.parse(request.body, :create_additions => false)
|
10
|
+
name = request_json['name']
|
11
|
+
password = request_json['password']
|
12
|
+
user = data['users'][name]
|
13
|
+
verified = user && JSON.parse(user, :create_additions => false)['password'] == password
|
14
|
+
json_response(200, {
|
15
|
+
'name' => name,
|
16
|
+
'verified' => !!verified
|
17
|
+
})
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'chef_zero/endpoints/cookbooks_base'
|
2
|
+
|
3
|
+
module ChefZero
|
4
|
+
module Endpoints
|
5
|
+
# /cookbooks/NAME
|
6
|
+
class CookbookEndpoint < CookbooksBase
|
7
|
+
def get(request)
|
8
|
+
filter = request.rest_path[1]
|
9
|
+
case filter
|
10
|
+
when '_latest'
|
11
|
+
result = {}
|
12
|
+
filter_cookbooks(data['cookbooks'], {}, 1) do |name, versions|
|
13
|
+
if versions.size > 0
|
14
|
+
result[name] = build_uri(request.base_uri, ['cookbooks', name, versions[0]])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
json_response(200, result)
|
18
|
+
when '_recipes'
|
19
|
+
result = []
|
20
|
+
filter_cookbooks(data['cookbooks'], {}, 1) do |name, versions|
|
21
|
+
if versions.size > 0
|
22
|
+
cookbook = JSON.parse(data['cookbooks'][name][versions[0]], :create_additions => false)
|
23
|
+
result += recipe_names(name, cookbook)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
json_response(200, result.sort)
|
27
|
+
else
|
28
|
+
cookbook_list = { filter => get_data(request, request.rest_path) }
|
29
|
+
json_response(200, format_cookbooks_list(request, cookbook_list))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def latest_version(versions)
|
34
|
+
sorted = versions.sort_by { |version| Chef::Version.new(version) }
|
35
|
+
sorted[-1]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'chef_zero/endpoints/rest_object_endpoint'
|
3
|
+
require 'chef_zero/rest_error_response'
|
4
|
+
require 'chef_zero/data_normalizer'
|
5
|
+
|
6
|
+
module ChefZero
|
7
|
+
module Endpoints
|
8
|
+
# /cookbooks/NAME/VERSION
|
9
|
+
class CookbookVersionEndpoint < RestObjectEndpoint
|
10
|
+
def get(request)
|
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)
|
13
|
+
end
|
14
|
+
super(request)
|
15
|
+
end
|
16
|
+
|
17
|
+
def put(request)
|
18
|
+
name = request.rest_path[1]
|
19
|
+
version = request.rest_path[2]
|
20
|
+
data['cookbooks'][name] = {} if !data['cookbooks'][name]
|
21
|
+
existing_cookbook = data['cookbooks'][name][version]
|
22
|
+
|
23
|
+
# Honor frozen
|
24
|
+
if existing_cookbook
|
25
|
+
existing_cookbook_json = JSON.parse(existing_cookbook, :create_additions => false)
|
26
|
+
if existing_cookbook_json['frozen?']
|
27
|
+
if request.query_params['force'] != "true"
|
28
|
+
raise RestErrorResponse.new(409, "The cookbook #{name} at version #{version} is frozen. Use the 'force' option to override.")
|
29
|
+
end
|
30
|
+
# For some reason, you are forever unable to modify "frozen?" on a frozen cookbook.
|
31
|
+
request_body = JSON.parse(request.body, :create_additions => false)
|
32
|
+
if !request_body['frozen?']
|
33
|
+
request_body['frozen?'] = true
|
34
|
+
request.body = JSON.pretty_generate(request_body)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Set the cookbook
|
40
|
+
data['cookbooks'][name][version] = request.body
|
41
|
+
|
42
|
+
# If the cookbook was updated, check for deleted files and clean them up
|
43
|
+
if existing_cookbook
|
44
|
+
missing_checksums = get_checksums(existing_cookbook) - get_checksums(request.body)
|
45
|
+
if missing_checksums.size > 0
|
46
|
+
hoover_unused_checksums(missing_checksums)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
already_json_response(existing_cookbook ? 200 : 201, populate_defaults(request, data['cookbooks'][name][version]))
|
51
|
+
end
|
52
|
+
|
53
|
+
def delete(request)
|
54
|
+
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)
|
56
|
+
end
|
57
|
+
|
58
|
+
deleted_cookbook = get_data(request, request.rest_path)
|
59
|
+
response = super(request)
|
60
|
+
cookbook_name = request.rest_path[1]
|
61
|
+
data['cookbooks'].delete(cookbook_name) if data['cookbooks'][cookbook_name].size == 0
|
62
|
+
|
63
|
+
# Hoover deleted files, if they exist
|
64
|
+
hoover_unused_checksums(get_checksums(deleted_cookbook))
|
65
|
+
response
|
66
|
+
end
|
67
|
+
|
68
|
+
def get_checksums(cookbook)
|
69
|
+
result = []
|
70
|
+
JSON.parse(cookbook, :create_additions => false).each_pair do |key, value|
|
71
|
+
if value.is_a?(Array)
|
72
|
+
value.each do |file|
|
73
|
+
if file.is_a?(Hash) && file.has_key?('checksum')
|
74
|
+
result << file['checksum']
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
result
|
80
|
+
end
|
81
|
+
|
82
|
+
def hoover_unused_checksums(deleted_checksums)
|
83
|
+
data['cookbooks'].each_pair do |cookbook_name, versions|
|
84
|
+
versions.each_pair do |cookbook_version, cookbook|
|
85
|
+
deleted_checksums = deleted_checksums - get_checksums(cookbook)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
deleted_checksums.each do |checksum|
|
89
|
+
data['file_store'].delete(checksum)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def populate_defaults(request, response_json)
|
94
|
+
# Inject URIs into each cookbook file
|
95
|
+
cookbook = JSON.parse(response_json, :create_additions => false)
|
96
|
+
cookbook = DataNormalizer.normalize_cookbook(cookbook, request.rest_path[1], request.rest_path[2], request.base_uri, request.method)
|
97
|
+
JSON.pretty_generate(cookbook)
|
98
|
+
end
|
99
|
+
|
100
|
+
def latest_version(versions)
|
101
|
+
sorted = versions.sort_by { |version| Chef::Version.new(version) }
|
102
|
+
sorted[-1]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,59 @@
|
|
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/data_normalizer'
|
7
|
+
|
8
|
+
module ChefZero
|
9
|
+
module Endpoints
|
10
|
+
# Common code for endpoints that return cookbook lists
|
11
|
+
class CookbooksBase < RestBase
|
12
|
+
def format_cookbooks_list(request, cookbooks_list, constraints = {}, num_versions = nil)
|
13
|
+
results = {}
|
14
|
+
filter_cookbooks(cookbooks_list, constraints, num_versions) do |name, versions|
|
15
|
+
versions_list = versions.map do |version|
|
16
|
+
{
|
17
|
+
'url' => build_uri(request.base_uri, ['cookbooks', name, version]),
|
18
|
+
'version' => version
|
19
|
+
}
|
20
|
+
end
|
21
|
+
results[name] = {
|
22
|
+
'url' => build_uri(request.base_uri, ['cookbooks', name]),
|
23
|
+
'versions' => versions_list
|
24
|
+
}
|
25
|
+
end
|
26
|
+
results
|
27
|
+
end
|
28
|
+
|
29
|
+
def filter_cookbooks(cookbooks_list, constraints = {}, num_versions = nil)
|
30
|
+
cookbooks_list.keys.sort.each do |name|
|
31
|
+
constraint = Chef::VersionConstraint.new(constraints[name])
|
32
|
+
versions = []
|
33
|
+
cookbooks_list[name].keys.sort_by { |version| Chef::Version.new(version) }.reverse.each do |version|
|
34
|
+
break if num_versions && versions.size >= num_versions
|
35
|
+
if constraint.include?(version)
|
36
|
+
versions << version
|
37
|
+
end
|
38
|
+
end
|
39
|
+
yield [name, versions]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def recipe_names(cookbook_name, cookbook)
|
44
|
+
result = []
|
45
|
+
if cookbook['recipes']
|
46
|
+
cookbook['recipes'].each do |recipe|
|
47
|
+
if recipe['path'] == "recipes/#{recipe['name']}" && recipe['name'][-3..-1] == '.rb'
|
48
|
+
if recipe['name'] == 'default.rb'
|
49
|
+
result << cookbook_name
|
50
|
+
end
|
51
|
+
result << "#{cookbook_name}::#{recipe['name'][0..-4]}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
result
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|