chef-zero 4.3.0 → 4.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +201 -201
  3. data/README.md +155 -155
  4. data/Rakefile +31 -31
  5. data/bin/chef-zero +100 -100
  6. data/lib/chef_zero.rb +10 -10
  7. data/lib/chef_zero/chef_data/acl_path.rb +139 -139
  8. data/lib/chef_zero/chef_data/cookbook_data.rb +240 -240
  9. data/lib/chef_zero/chef_data/data_normalizer.rb +208 -207
  10. data/lib/chef_zero/chef_data/default_creator.rb +446 -446
  11. data/lib/chef_zero/data_store/data_already_exists_error.rb +29 -29
  12. data/lib/chef_zero/data_store/data_error.rb +31 -31
  13. data/lib/chef_zero/data_store/data_not_found_error.rb +28 -28
  14. data/lib/chef_zero/data_store/default_facade.rb +149 -149
  15. data/lib/chef_zero/data_store/interface_v1.rb +67 -67
  16. data/lib/chef_zero/data_store/interface_v2.rb +18 -18
  17. data/lib/chef_zero/data_store/memory_store.rb +33 -33
  18. data/lib/chef_zero/data_store/memory_store_v2.rb +155 -155
  19. data/lib/chef_zero/data_store/raw_file_store.rb +147 -147
  20. data/lib/chef_zero/data_store/v1_to_v2_adapter.rb +142 -142
  21. data/lib/chef_zero/data_store/v2_to_v1_adapter.rb +107 -107
  22. data/lib/chef_zero/endpoints/acl_endpoint.rb +38 -38
  23. data/lib/chef_zero/endpoints/acls_endpoint.rb +29 -29
  24. data/lib/chef_zero/endpoints/actor_endpoint.rb +94 -94
  25. data/lib/chef_zero/endpoints/actors_endpoint.rb +64 -64
  26. data/lib/chef_zero/endpoints/authenticate_user_endpoint.rb +31 -31
  27. data/lib/chef_zero/endpoints/container_endpoint.rb +22 -22
  28. data/lib/chef_zero/endpoints/containers_endpoint.rb +13 -13
  29. data/lib/chef_zero/endpoints/cookbook_endpoint.rb +39 -39
  30. data/lib/chef_zero/endpoints/cookbook_version_endpoint.rb +119 -119
  31. data/lib/chef_zero/endpoints/cookbooks_base.rb +65 -65
  32. data/lib/chef_zero/endpoints/cookbooks_endpoint.rb +19 -19
  33. data/lib/chef_zero/endpoints/data_bag_endpoint.rb +45 -45
  34. data/lib/chef_zero/endpoints/data_bag_item_endpoint.rb +25 -25
  35. data/lib/chef_zero/endpoints/data_bags_endpoint.rb +23 -23
  36. data/lib/chef_zero/endpoints/environment_cookbook_endpoint.rb +24 -24
  37. data/lib/chef_zero/endpoints/environment_cookbook_versions_endpoint.rb +123 -123
  38. data/lib/chef_zero/endpoints/environment_cookbooks_endpoint.rb +22 -22
  39. data/lib/chef_zero/endpoints/environment_endpoint.rb +33 -33
  40. data/lib/chef_zero/endpoints/environment_nodes_endpoint.rb +23 -23
  41. data/lib/chef_zero/endpoints/environment_recipes_endpoint.rb +22 -22
  42. data/lib/chef_zero/endpoints/environment_role_endpoint.rb +36 -36
  43. data/lib/chef_zero/endpoints/file_store_file_endpoint.rb +22 -22
  44. data/lib/chef_zero/endpoints/group_endpoint.rb +20 -20
  45. data/lib/chef_zero/endpoints/groups_endpoint.rb +13 -13
  46. data/lib/chef_zero/endpoints/license_endpoint.rb +25 -25
  47. data/lib/chef_zero/endpoints/node_endpoint.rb +17 -17
  48. data/lib/chef_zero/endpoints/node_identifiers_endpoint.rb +22 -22
  49. data/lib/chef_zero/endpoints/not_found_endpoint.rb +11 -11
  50. data/lib/chef_zero/endpoints/organization_association_request_endpoint.rb +22 -22
  51. data/lib/chef_zero/endpoints/organization_association_requests_endpoint.rb +30 -30
  52. data/lib/chef_zero/endpoints/organization_authenticate_user_endpoint.rb +26 -26
  53. data/lib/chef_zero/endpoints/organization_endpoint.rb +46 -46
  54. data/lib/chef_zero/endpoints/organization_user_base.rb +15 -15
  55. data/lib/chef_zero/endpoints/organization_user_endpoint.rb +26 -26
  56. data/lib/chef_zero/endpoints/organization_users_endpoint.rb +43 -43
  57. data/lib/chef_zero/endpoints/organization_validator_key_endpoint.rb +20 -20
  58. data/lib/chef_zero/endpoints/organizations_endpoint.rb +62 -62
  59. data/lib/chef_zero/endpoints/policies_endpoint.rb +151 -151
  60. data/lib/chef_zero/endpoints/principal_endpoint.rb +42 -42
  61. data/lib/chef_zero/endpoints/rest_list_endpoint.rb +42 -42
  62. data/lib/chef_zero/endpoints/rest_object_endpoint.rb +63 -63
  63. data/lib/chef_zero/endpoints/role_endpoint.rb +16 -16
  64. data/lib/chef_zero/endpoints/role_environments_endpoint.rb +14 -14
  65. data/lib/chef_zero/endpoints/sandbox_endpoint.rb +27 -27
  66. data/lib/chef_zero/endpoints/sandboxes_endpoint.rb +50 -50
  67. data/lib/chef_zero/endpoints/search_endpoint.rb +194 -194
  68. data/lib/chef_zero/endpoints/searches_endpoint.rb +18 -18
  69. data/lib/chef_zero/endpoints/server_api_version_endpoint.rb +14 -14
  70. data/lib/chef_zero/endpoints/system_recovery_endpoint.rb +30 -30
  71. data/lib/chef_zero/endpoints/user_association_request_endpoint.rb +40 -40
  72. data/lib/chef_zero/endpoints/user_association_requests_count_endpoint.rb +19 -19
  73. data/lib/chef_zero/endpoints/user_association_requests_endpoint.rb +19 -19
  74. data/lib/chef_zero/endpoints/user_organizations_endpoint.rb +22 -22
  75. data/lib/chef_zero/endpoints/version_endpoint.rb +12 -12
  76. data/lib/chef_zero/log.rb +7 -7
  77. data/lib/chef_zero/rest_base.rb +242 -242
  78. data/lib/chef_zero/rest_error_response.rb +11 -11
  79. data/lib/chef_zero/rest_request.rb +69 -69
  80. data/lib/chef_zero/rest_router.rb +45 -45
  81. data/lib/chef_zero/rspec.rb +308 -308
  82. data/lib/chef_zero/server.rb +642 -642
  83. data/lib/chef_zero/socketless_server_map.rb +92 -92
  84. data/lib/chef_zero/solr/query/binary_operator.rb +52 -52
  85. data/lib/chef_zero/solr/query/phrase.rb +23 -23
  86. data/lib/chef_zero/solr/query/range_query.rb +46 -46
  87. data/lib/chef_zero/solr/query/regexpable_query.rb +29 -29
  88. data/lib/chef_zero/solr/query/subquery.rb +37 -37
  89. data/lib/chef_zero/solr/query/term.rb +45 -45
  90. data/lib/chef_zero/solr/query/unary_operator.rb +43 -43
  91. data/lib/chef_zero/solr/solr_doc.rb +53 -53
  92. data/lib/chef_zero/solr/solr_parser.rb +203 -203
  93. data/lib/chef_zero/version.rb +3 -3
  94. data/spec/run_oc_pedant.rb +63 -63
  95. data/spec/search_spec.rb +32 -32
  96. data/spec/server_spec.rb +92 -92
  97. data/spec/socketless_server_map_spec.rb +76 -76
  98. data/spec/support/oc_pedant.rb +132 -132
  99. data/spec/support/stickywicket.pem +27 -27
  100. metadata +3 -3
@@ -1,65 +1,65 @@
1
- require 'ffi_yajl'
2
- require 'chef_zero/rest_base'
3
- require 'chef_zero/chef_data/data_normalizer'
4
-
5
- module ChefZero
6
- module Endpoints
7
- # Common code for endpoints that return cookbook lists
8
- class CookbooksBase < RestBase
9
- def format_cookbooks_list(request, cookbooks_list, constraints = {}, num_versions = nil)
10
- results = {}
11
- filter_cookbooks(cookbooks_list, constraints, num_versions) do |name, versions|
12
- versions_list = versions.map do |version|
13
- {
14
- 'url' => build_uri(request.base_uri, request.rest_path[0..1] + ['cookbooks', name, version]),
15
- 'version' => version
16
- }
17
- end
18
- results[name] = {
19
- 'url' => build_uri(request.base_uri, request.rest_path[0..1] + ['cookbooks', name]),
20
- 'versions' => versions_list
21
- }
22
- end
23
- results
24
- end
25
-
26
- def all_cookbooks_list(request)
27
- result = {}
28
- # Race conditions exist here (if someone deletes while listing). I don't care.
29
- data_store.list(request.rest_path[0..1] + ['cookbooks']).each do |name|
30
- result[name] = data_store.list(request.rest_path[0..1] + ['cookbooks', name])
31
- end
32
- result
33
- end
34
-
35
- def filter_cookbooks(cookbooks_list, constraints = {}, num_versions = nil)
36
- cookbooks_list.keys.sort.each do |name|
37
- constraint = Gem::Requirement.new(constraints[name])
38
- versions = []
39
- cookbooks_list[name].sort_by { |version| Gem::Version.new(version.dup) }.reverse.each do |version|
40
- break if num_versions && versions.size >= num_versions
41
- if constraint.satisfied_by?(Gem::Version.new(version.dup))
42
- versions << version
43
- end
44
- end
45
- yield [name, versions]
46
- end
47
- end
48
-
49
- def recipe_names(cookbook_name, cookbook)
50
- result = []
51
- if cookbook['recipes']
52
- cookbook['recipes'].each do |recipe|
53
- if recipe['path'] == "recipes/#{recipe['name']}" && recipe['name'][-3..-1] == '.rb'
54
- if recipe['name'] == 'default.rb'
55
- result << cookbook_name
56
- end
57
- result << "#{cookbook_name}::#{recipe['name'][0..-4]}"
58
- end
59
- end
60
- end
61
- result
62
- end
63
- end
64
- end
65
- end
1
+ require 'ffi_yajl'
2
+ require 'chef_zero/rest_base'
3
+ require 'chef_zero/chef_data/data_normalizer'
4
+
5
+ module ChefZero
6
+ module Endpoints
7
+ # Common code for endpoints that return cookbook lists
8
+ class CookbooksBase < RestBase
9
+ def format_cookbooks_list(request, cookbooks_list, constraints = {}, num_versions = nil)
10
+ results = {}
11
+ filter_cookbooks(cookbooks_list, constraints, num_versions) do |name, versions|
12
+ versions_list = versions.map do |version|
13
+ {
14
+ 'url' => build_uri(request.base_uri, request.rest_path[0..1] + ['cookbooks', name, version]),
15
+ 'version' => version
16
+ }
17
+ end
18
+ results[name] = {
19
+ 'url' => build_uri(request.base_uri, request.rest_path[0..1] + ['cookbooks', name]),
20
+ 'versions' => versions_list
21
+ }
22
+ end
23
+ results
24
+ end
25
+
26
+ def all_cookbooks_list(request)
27
+ result = {}
28
+ # Race conditions exist here (if someone deletes while listing). I don't care.
29
+ data_store.list(request.rest_path[0..1] + ['cookbooks']).each do |name|
30
+ result[name] = data_store.list(request.rest_path[0..1] + ['cookbooks', name])
31
+ end
32
+ result
33
+ end
34
+
35
+ def filter_cookbooks(cookbooks_list, constraints = {}, num_versions = nil)
36
+ cookbooks_list.keys.sort.each do |name|
37
+ constraint = Gem::Requirement.new(constraints[name])
38
+ versions = []
39
+ cookbooks_list[name].sort_by { |version| Gem::Version.new(version.dup) }.reverse.each do |version|
40
+ break if num_versions && versions.size >= num_versions
41
+ if constraint.satisfied_by?(Gem::Version.new(version.dup))
42
+ versions << version
43
+ end
44
+ end
45
+ yield [name, versions]
46
+ end
47
+ end
48
+
49
+ def recipe_names(cookbook_name, cookbook)
50
+ result = []
51
+ if cookbook['recipes']
52
+ cookbook['recipes'].each do |recipe|
53
+ if recipe['path'] == "recipes/#{recipe['name']}" && recipe['name'][-3..-1] == '.rb'
54
+ if recipe['name'] == 'default.rb'
55
+ result << cookbook_name
56
+ end
57
+ result << "#{cookbook_name}::#{recipe['name'][0..-4]}"
58
+ end
59
+ end
60
+ end
61
+ result
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,19 +1,19 @@
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
- if request.query_params['num_versions'] == 'all'
9
- num_versions = nil
10
- elsif request.query_params['num_versions']
11
- num_versions = request.query_params['num_versions'].to_i
12
- else
13
- num_versions = 1
14
- end
15
- json_response(200, format_cookbooks_list(request, all_cookbooks_list(request), {}, num_versions))
16
- end
17
- end
18
- end
19
- end
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
+ if request.query_params['num_versions'] == 'all'
9
+ num_versions = nil
10
+ elsif request.query_params['num_versions']
11
+ num_versions = request.query_params['num_versions'].to_i
12
+ else
13
+ num_versions = 1
14
+ end
15
+ json_response(200, format_cookbooks_list(request, all_cookbooks_list(request), {}, num_versions))
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,45 +1,45 @@
1
- require 'ffi_yajl'
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
- json = FFI_Yajl::Parser.parse(request.body, :create_additions => false)
16
- key = identity_keys.map { |k| json[k] }.select { |v| v }.first
17
- response = super(request)
18
- if response[0] == 201
19
- already_json_response(201, DataBagItemEndpoint::populate_defaults(request, request.body, request.rest_path[3], key))
20
- else
21
- response
22
- end
23
- end
24
-
25
- def get_key(contents)
26
- data_bag_item = FFI_Yajl::Parser.parse(contents, :create_additions => false)
27
- if data_bag_item['json_class'] == 'Chef::DataBagItem' && data_bag_item['raw_data']
28
- data_bag_item['raw_data']['id']
29
- else
30
- data_bag_item['id']
31
- end
32
- end
33
-
34
- def delete(request)
35
- key = request.rest_path[3]
36
- delete_data_dir(request, request.rest_path, :recursive)
37
- json_response(200, {
38
- 'chef_type' => 'data_bag',
39
- 'json_class' => 'Chef::DataBag',
40
- 'name' => key
41
- })
42
- end
43
- end
44
- end
45
- end
1
+ require 'ffi_yajl'
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
+ json = FFI_Yajl::Parser.parse(request.body, :create_additions => false)
16
+ key = identity_keys.map { |k| json[k] }.select { |v| v }.first
17
+ response = super(request)
18
+ if response[0] == 201
19
+ already_json_response(201, DataBagItemEndpoint::populate_defaults(request, request.body, request.rest_path[3], key))
20
+ else
21
+ response
22
+ end
23
+ end
24
+
25
+ def get_key(contents)
26
+ data_bag_item = FFI_Yajl::Parser.parse(contents, :create_additions => false)
27
+ if data_bag_item['json_class'] == 'Chef::DataBagItem' && data_bag_item['raw_data']
28
+ data_bag_item['raw_data']['id']
29
+ else
30
+ data_bag_item['id']
31
+ end
32
+ end
33
+
34
+ def delete(request)
35
+ key = request.rest_path[3]
36
+ delete_data_dir(request, request.rest_path, :recursive)
37
+ json_response(200, {
38
+ 'chef_type' => 'data_bag',
39
+ 'json_class' => 'Chef::DataBag',
40
+ 'name' => key
41
+ })
42
+ end
43
+ end
44
+ end
45
+ end
@@ -1,25 +1,25 @@
1
- require 'ffi_yajl'
2
- require 'chef_zero/endpoints/rest_object_endpoint'
3
- require 'chef_zero/endpoints/data_bag_item_endpoint'
4
- require 'chef_zero/chef_data/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[3], request.rest_path[4])
16
- end
17
-
18
- def self.populate_defaults(request, response_json, data_bag, data_bag_item)
19
- response = FFI_Yajl::Parser.parse(response_json, :create_additions => false)
20
- response = ChefData::DataNormalizer.normalize_data_bag_item(response, data_bag, data_bag_item, request.method)
21
- FFI_Yajl::Encoder.encode(response, :pretty => true)
22
- end
23
- end
24
- end
25
- end
1
+ require 'ffi_yajl'
2
+ require 'chef_zero/endpoints/rest_object_endpoint'
3
+ require 'chef_zero/endpoints/data_bag_item_endpoint'
4
+ require 'chef_zero/chef_data/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[3], request.rest_path[4])
16
+ end
17
+
18
+ def self.populate_defaults(request, response_json, data_bag, data_bag_item)
19
+ response = FFI_Yajl::Parser.parse(response_json, :create_additions => false)
20
+ response = ChefData::DataNormalizer.normalize_data_bag_item(response, data_bag, data_bag_item, request.method)
21
+ FFI_Yajl::Encoder.encode(response, :pretty => true)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,23 +1,23 @@
1
- require 'ffi_yajl'
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
- contents = request.body
10
- json = FFI_Yajl::Parser.parse(contents, :create_additions => false)
11
- name = identity_keys.map { |k| json[k] }.select { |v| v }.first
12
- if name.nil?
13
- error(400, "Must specify #{identity_keys.map { |k| k.inspect }.join(' or ')} in JSON")
14
- elsif exists_data_dir?(request, request.rest_path[0..1] + ['data', name])
15
- error(409, "Object already exists")
16
- else
17
- create_data_dir(request, request.rest_path[0..1] + ['data'], name, :recursive)
18
- json_response(201, {"uri" => "#{build_uri(request.base_uri, request.rest_path + [name])}"})
19
- end
20
- end
21
- end
22
- end
23
- end
1
+ require 'ffi_yajl'
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
+ contents = request.body
10
+ json = FFI_Yajl::Parser.parse(contents, :create_additions => false)
11
+ name = identity_keys.map { |k| json[k] }.select { |v| v }.first
12
+ if name.nil?
13
+ error(400, "Must specify #{identity_keys.map { |k| k.inspect }.join(' or ')} in JSON")
14
+ elsif exists_data_dir?(request, request.rest_path[0..1] + ['data', name])
15
+ error(409, "Object already exists")
16
+ else
17
+ create_data_dir(request, request.rest_path[0..1] + ['data'], name, :recursive)
18
+ json_response(201, {"uri" => "#{build_uri(request.base_uri, request.rest_path + [name])}"})
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,24 +1,24 @@
1
- require 'ffi_yajl'
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[5]
10
- environment = FFI_Yajl::Parser.parse(get_data(request, request.rest_path[0..3]), :create_additions => false)
11
- constraints = environment['cookbook_versions'] || {}
12
- cookbook_versions = list_data(request, request.rest_path[0..1] + request.rest_path[4..5])
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_versions }, constraints, num_versions))
21
- end
22
- end
23
- end
24
- end
1
+ require 'ffi_yajl'
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[5]
10
+ environment = FFI_Yajl::Parser.parse(get_data(request, request.rest_path[0..3]), :create_additions => false)
11
+ constraints = environment['cookbook_versions'] || {}
12
+ cookbook_versions = list_data(request, request.rest_path[0..1] + request.rest_path[4..5])
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_versions }, constraints, num_versions))
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,123 +1,123 @@
1
- require 'ffi_yajl'
2
- require 'chef_zero/rest_base'
3
- require 'chef_zero/rest_error_response'
4
-
5
- module ChefZero
6
- module Endpoints
7
- # /environments/NAME/cookbook_versions
8
- class EnvironmentCookbookVersionsEndpoint < RestBase
9
-
10
- def post(request)
11
- cookbook_names = list_data(request, request.rest_path[0..1] + ['cookbooks'])
12
-
13
- # Get the list of cookbooks and versions desired by the runlist
14
- desired_versions = {}
15
- run_list = FFI_Yajl::Parser.parse(request.body, :create_additions => false)['run_list']
16
- run_list.each do |run_list_entry|
17
- if run_list_entry =~ /(.+)::.+\@(.+)/ || run_list_entry =~ /(.+)\@(.+)/
18
- raise RestErrorResponse.new(412, "No such cookbook: #{$1}") if !cookbook_names.include?($1)
19
- raise RestErrorResponse.new(412, "No such cookbook version for cookbook #{$1}: #{$2}") if !list_data(request, request.rest_path[0..1] + ['cookbooks', $1]).include?($2)
20
- desired_versions[$1] = [ $2 ]
21
- else
22
- desired_cookbook = run_list_entry.split('::')[0]
23
- raise RestErrorResponse.new(412, "No such cookbook: #{desired_cookbook}") if !cookbook_names.include?(desired_cookbook)
24
- desired_versions[desired_cookbook] = list_data(request, request.rest_path[0..1] + ['cookbooks', desired_cookbook])
25
- end
26
- end
27
-
28
- # Filter by environment constraints
29
- environment = FFI_Yajl::Parser.parse(get_data(request, request.rest_path[0..3]), :create_additions => false)
30
- environment_constraints = environment['cookbook_versions'] || {}
31
-
32
- desired_versions.each_key do |name|
33
- desired_versions = filter_by_constraint(desired_versions, name, environment_constraints[name])
34
- end
35
-
36
- # Depsolve!
37
- solved = depsolve(request, desired_versions.keys, desired_versions, environment_constraints)
38
- if !solved
39
- if @last_missing_dep && !cookbook_names.include?(@last_missing_dep)
40
- return raise RestErrorResponse.new(412, "No such cookbook: #{@last_missing_dep}")
41
- elsif @last_constraint_failure
42
- return raise RestErrorResponse.new(412, "Could not satisfy version constraints for: #{@last_constraint_failure}")
43
- else
44
-
45
- return raise RestErrorResponse.new(412, "Unsolvable versions!")
46
- end
47
- end
48
-
49
- result = {}
50
- solved.each_pair do |name, versions|
51
- cookbook = FFI_Yajl::Parser.parse(get_data(request, request.rest_path[0..1] + ['cookbooks', name, versions[0]]), :create_additions => false)
52
- result[name] = ChefData::DataNormalizer.normalize_cookbook(self, request.rest_path[0..1], cookbook, name, versions[0], request.base_uri, 'MIN')
53
- end
54
- json_response(200, result)
55
- end
56
-
57
- def depsolve(request, unsolved, desired_versions, environment_constraints)
58
- desired_versions.each do |cb, ver|
59
- if ver.empty?
60
- @last_constraint_failure = cb
61
- return nil
62
- end
63
- end
64
-
65
- # If everything is already
66
- solve_for = unsolved[0]
67
- return desired_versions if !solve_for
68
-
69
- # Go through each desired version of this cookbook, starting with the latest,
70
- # until we find one we can solve successfully with
71
- sort_versions(desired_versions[solve_for]).each do |desired_version|
72
- new_desired_versions = desired_versions.clone
73
- new_desired_versions[solve_for] = [ desired_version ]
74
- new_unsolved = unsolved[1..-1]
75
-
76
- # Pick this cookbook, and add dependencies
77
- cookbook_obj = FFI_Yajl::Parser.parse(get_data(request, request.rest_path[0..1] + ['cookbooks', solve_for, desired_version]), :create_additions => false)
78
- cookbook_metadata = cookbook_obj['metadata'] || {}
79
- cookbook_dependencies = cookbook_metadata['dependencies'] || {}
80
- dep_not_found = false
81
- cookbook_dependencies.each_pair do |dep_name, dep_constraint|
82
- # If the dep is not already in the list, add it to the list to solve
83
- # and bring in all environment-allowed cookbook versions to desired_versions
84
- if !new_desired_versions.has_key?(dep_name)
85
- new_unsolved = new_unsolved + [dep_name]
86
- # If the dep is missing, we will try other versions of the cookbook that might not have the bad dep.
87
- if !exists_data_dir?(request, request.rest_path[0..1] + ['cookbooks', dep_name])
88
- @last_missing_dep = dep_name.to_s
89
- dep_not_found = true
90
- break
91
- end
92
- new_desired_versions[dep_name] = list_data(request, request.rest_path[0..1] + ['cookbooks', dep_name])
93
- new_desired_versions = filter_by_constraint(new_desired_versions, dep_name, environment_constraints[dep_name])
94
- end
95
- new_desired_versions = filter_by_constraint(new_desired_versions, dep_name, dep_constraint)
96
- end
97
-
98
- next if dep_not_found
99
-
100
- # Depsolve children with this desired version! First solution wins.
101
- result = depsolve(request, new_unsolved, new_desired_versions, environment_constraints)
102
- return result if result
103
- end
104
- return nil
105
- end
106
-
107
- def sort_versions(versions)
108
- result = versions.sort_by { |version| Gem::Version.new(version.dup) }
109
- result.reverse
110
- end
111
-
112
- def filter_by_constraint(versions, cookbook_name, constraint)
113
- return versions if !constraint
114
- constraint = Gem::Requirement.new(constraint)
115
- new_versions = versions[cookbook_name]
116
- new_versions = new_versions.select { |version| constraint.satisfied_by?(Gem::Version.new(version.dup)) }
117
- result = versions.clone
118
- result[cookbook_name] = new_versions
119
- result
120
- end
121
- end
122
- end
123
- end
1
+ require 'ffi_yajl'
2
+ require 'chef_zero/rest_base'
3
+ require 'chef_zero/rest_error_response'
4
+
5
+ module ChefZero
6
+ module Endpoints
7
+ # /environments/NAME/cookbook_versions
8
+ class EnvironmentCookbookVersionsEndpoint < RestBase
9
+
10
+ def post(request)
11
+ cookbook_names = list_data(request, request.rest_path[0..1] + ['cookbooks'])
12
+
13
+ # Get the list of cookbooks and versions desired by the runlist
14
+ desired_versions = {}
15
+ run_list = FFI_Yajl::Parser.parse(request.body, :create_additions => false)['run_list']
16
+ run_list.each do |run_list_entry|
17
+ if run_list_entry =~ /(.+)::.+\@(.+)/ || run_list_entry =~ /(.+)\@(.+)/
18
+ raise RestErrorResponse.new(412, "No such cookbook: #{$1}") if !cookbook_names.include?($1)
19
+ raise RestErrorResponse.new(412, "No such cookbook version for cookbook #{$1}: #{$2}") if !list_data(request, request.rest_path[0..1] + ['cookbooks', $1]).include?($2)
20
+ desired_versions[$1] = [ $2 ]
21
+ else
22
+ desired_cookbook = run_list_entry.split('::')[0]
23
+ raise RestErrorResponse.new(412, "No such cookbook: #{desired_cookbook}") if !cookbook_names.include?(desired_cookbook)
24
+ desired_versions[desired_cookbook] = list_data(request, request.rest_path[0..1] + ['cookbooks', desired_cookbook])
25
+ end
26
+ end
27
+
28
+ # Filter by environment constraints
29
+ environment = FFI_Yajl::Parser.parse(get_data(request, request.rest_path[0..3]), :create_additions => false)
30
+ environment_constraints = environment['cookbook_versions'] || {}
31
+
32
+ desired_versions.each_key do |name|
33
+ desired_versions = filter_by_constraint(desired_versions, name, environment_constraints[name])
34
+ end
35
+
36
+ # Depsolve!
37
+ solved = depsolve(request, desired_versions.keys, desired_versions, environment_constraints)
38
+ if !solved
39
+ if @last_missing_dep && !cookbook_names.include?(@last_missing_dep)
40
+ return raise RestErrorResponse.new(412, "No such cookbook: #{@last_missing_dep}")
41
+ elsif @last_constraint_failure
42
+ return raise RestErrorResponse.new(412, "Could not satisfy version constraints for: #{@last_constraint_failure}")
43
+ else
44
+
45
+ return raise RestErrorResponse.new(412, "Unsolvable versions!")
46
+ end
47
+ end
48
+
49
+ result = {}
50
+ solved.each_pair do |name, versions|
51
+ cookbook = FFI_Yajl::Parser.parse(get_data(request, request.rest_path[0..1] + ['cookbooks', name, versions[0]]), :create_additions => false)
52
+ result[name] = ChefData::DataNormalizer.normalize_cookbook(self, request.rest_path[0..1], cookbook, name, versions[0], request.base_uri, 'MIN')
53
+ end
54
+ json_response(200, result)
55
+ end
56
+
57
+ def depsolve(request, unsolved, desired_versions, environment_constraints)
58
+ desired_versions.each do |cb, ver|
59
+ if ver.empty?
60
+ @last_constraint_failure = cb
61
+ return nil
62
+ end
63
+ end
64
+
65
+ # If everything is already
66
+ solve_for = unsolved[0]
67
+ return desired_versions if !solve_for
68
+
69
+ # Go through each desired version of this cookbook, starting with the latest,
70
+ # until we find one we can solve successfully with
71
+ sort_versions(desired_versions[solve_for]).each do |desired_version|
72
+ new_desired_versions = desired_versions.clone
73
+ new_desired_versions[solve_for] = [ desired_version ]
74
+ new_unsolved = unsolved[1..-1]
75
+
76
+ # Pick this cookbook, and add dependencies
77
+ cookbook_obj = FFI_Yajl::Parser.parse(get_data(request, request.rest_path[0..1] + ['cookbooks', solve_for, desired_version]), :create_additions => false)
78
+ cookbook_metadata = cookbook_obj['metadata'] || {}
79
+ cookbook_dependencies = cookbook_metadata['dependencies'] || {}
80
+ dep_not_found = false
81
+ cookbook_dependencies.each_pair do |dep_name, dep_constraint|
82
+ # If the dep is not already in the list, add it to the list to solve
83
+ # and bring in all environment-allowed cookbook versions to desired_versions
84
+ if !new_desired_versions.has_key?(dep_name)
85
+ new_unsolved = new_unsolved + [dep_name]
86
+ # If the dep is missing, we will try other versions of the cookbook that might not have the bad dep.
87
+ if !exists_data_dir?(request, request.rest_path[0..1] + ['cookbooks', dep_name])
88
+ @last_missing_dep = dep_name.to_s
89
+ dep_not_found = true
90
+ break
91
+ end
92
+ new_desired_versions[dep_name] = list_data(request, request.rest_path[0..1] + ['cookbooks', dep_name])
93
+ new_desired_versions = filter_by_constraint(new_desired_versions, dep_name, environment_constraints[dep_name])
94
+ end
95
+ new_desired_versions = filter_by_constraint(new_desired_versions, dep_name, dep_constraint)
96
+ end
97
+
98
+ next if dep_not_found
99
+
100
+ # Depsolve children with this desired version! First solution wins.
101
+ result = depsolve(request, new_unsolved, new_desired_versions, environment_constraints)
102
+ return result if result
103
+ end
104
+ return nil
105
+ end
106
+
107
+ def sort_versions(versions)
108
+ result = versions.sort_by { |version| Gem::Version.new(version.dup) }
109
+ result.reverse
110
+ end
111
+
112
+ def filter_by_constraint(versions, cookbook_name, constraint)
113
+ return versions if !constraint
114
+ constraint = Gem::Requirement.new(constraint)
115
+ new_versions = versions[cookbook_name]
116
+ new_versions = new_versions.select { |version| constraint.satisfied_by?(Gem::Version.new(version.dup)) }
117
+ result = versions.clone
118
+ result[cookbook_name] = new_versions
119
+ result
120
+ end
121
+ end
122
+ end
123
+ end