chef-zero 4.3.0 → 4.3.1

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.
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,64 +1,64 @@
1
- require 'ffi_yajl'
2
- require 'chef_zero/endpoints/rest_list_endpoint'
3
-
4
- module ChefZero
5
- module Endpoints
6
- # /users, /organizations/ORG/clients or /organizations/ORG/users
7
- class ActorsEndpoint < RestListEndpoint
8
- def get(request)
9
- response = super(request)
10
-
11
- if request.query_params['email']
12
- results = FFI_Yajl::Parser.parse(response[2], :create_additions => false)
13
- new_results = {}
14
- results.each do |name, url|
15
- record = get_data(request, request.rest_path + [ name ], :nil)
16
- if record
17
- record = FFI_Yajl::Parser.parse(record, :create_additions => false)
18
- new_results[name] = url if record['email'] == request.query_params['email']
19
- end
20
- end
21
- response[2] = FFI_Yajl::Encoder.encode(new_results, :pretty => true)
22
- end
23
-
24
- if request.query_params['verbose']
25
- results = FFI_Yajl::Parser.parse(response[2], :create_additions => false)
26
- results.each do |name, url|
27
- record = get_data(request, request.rest_path + [ name ], :nil)
28
- if record
29
- record = FFI_Yajl::Parser.parse(record, :create_additions => false)
30
- record = ChefData::DataNormalizer.normalize_user(record, name, identity_keys, server.options[:osc_compat])
31
- results[name] = record
32
- end
33
- end
34
- response[2] = FFI_Yajl::Encoder.encode(results, :pretty => true)
35
- end
36
- response
37
- end
38
-
39
- def post(request)
40
- # First, find out if the user actually posted a public key. If not, make
41
- # one.
42
- request_body = FFI_Yajl::Parser.parse(request.body, :create_additions => false)
43
- public_key = request_body['public_key']
44
- if !public_key
45
- private_key, public_key = server.gen_key_pair
46
- request_body['public_key'] = public_key
47
- request.body = FFI_Yajl::Encoder.encode(request_body, :pretty => true)
48
- end
49
-
50
- result = super(request)
51
-
52
- if result[0] == 201
53
- # If we generated a key, stuff it in the response.
54
- response = FFI_Yajl::Parser.parse(result[2], :create_additions => false)
55
- response['private_key'] = private_key if private_key
56
- response['public_key'] = public_key unless request.rest_path[0] == 'users'
57
- json_response(201, response)
58
- else
59
- result
60
- end
61
- end
62
- end
63
- end
64
- end
1
+ require 'ffi_yajl'
2
+ require 'chef_zero/endpoints/rest_list_endpoint'
3
+
4
+ module ChefZero
5
+ module Endpoints
6
+ # /users, /organizations/ORG/clients or /organizations/ORG/users
7
+ class ActorsEndpoint < RestListEndpoint
8
+ def get(request)
9
+ response = super(request)
10
+
11
+ if request.query_params['email']
12
+ results = FFI_Yajl::Parser.parse(response[2], :create_additions => false)
13
+ new_results = {}
14
+ results.each do |name, url|
15
+ record = get_data(request, request.rest_path + [ name ], :nil)
16
+ if record
17
+ record = FFI_Yajl::Parser.parse(record, :create_additions => false)
18
+ new_results[name] = url if record['email'] == request.query_params['email']
19
+ end
20
+ end
21
+ response[2] = FFI_Yajl::Encoder.encode(new_results, :pretty => true)
22
+ end
23
+
24
+ if request.query_params['verbose']
25
+ results = FFI_Yajl::Parser.parse(response[2], :create_additions => false)
26
+ results.each do |name, url|
27
+ record = get_data(request, request.rest_path + [ name ], :nil)
28
+ if record
29
+ record = FFI_Yajl::Parser.parse(record, :create_additions => false)
30
+ record = ChefData::DataNormalizer.normalize_user(record, name, identity_keys, server.options[:osc_compat])
31
+ results[name] = record
32
+ end
33
+ end
34
+ response[2] = FFI_Yajl::Encoder.encode(results, :pretty => true)
35
+ end
36
+ response
37
+ end
38
+
39
+ def post(request)
40
+ # First, find out if the user actually posted a public key. If not, make
41
+ # one.
42
+ request_body = FFI_Yajl::Parser.parse(request.body, :create_additions => false)
43
+ public_key = request_body['public_key']
44
+ if !public_key
45
+ private_key, public_key = server.gen_key_pair
46
+ request_body['public_key'] = public_key
47
+ request.body = FFI_Yajl::Encoder.encode(request_body, :pretty => true)
48
+ end
49
+
50
+ result = super(request)
51
+
52
+ if result[0] == 201
53
+ # If we generated a key, stuff it in the response.
54
+ response = FFI_Yajl::Parser.parse(result[2], :create_additions => false)
55
+ response['private_key'] = private_key if private_key
56
+ response['public_key'] = public_key unless request.rest_path[0] == 'users'
57
+ json_response(201, response)
58
+ else
59
+ result
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -1,31 +1,31 @@
1
- require 'ffi_yajl'
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 = FFI_Yajl::Parser.parse(request.body, :create_additions => false)
10
- name = request_json['username']
11
- password = request_json['password']
12
- begin
13
- user = data_store.get(['users', name])
14
- rescue ChefZero::DataStore::DataNotFoundError
15
- raise RestErrorResponse.new(401, "Bad username or password")
16
- end
17
- user = FFI_Yajl::Parser.parse(user, :create_additions => false)
18
- user = ChefData::DataNormalizer.normalize_user(user, name, [ 'username' ], server.options[:osc_compat])
19
- if user['password'] != password
20
- raise RestErrorResponse.new(401, "Bad username or password")
21
- end
22
- # Include only particular user data in the response
23
- user.keep_if { |key,value| %w(first_name last_name display_name email username).include?(key) }
24
- json_response(200, {
25
- 'status' => 'linked',
26
- 'user' => user
27
- })
28
- end
29
- end
30
- end
31
- end
1
+ require 'ffi_yajl'
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 = FFI_Yajl::Parser.parse(request.body, :create_additions => false)
10
+ name = request_json['username']
11
+ password = request_json['password']
12
+ begin
13
+ user = data_store.get(['users', name])
14
+ rescue ChefZero::DataStore::DataNotFoundError
15
+ raise RestErrorResponse.new(401, "Bad username or password")
16
+ end
17
+ user = FFI_Yajl::Parser.parse(user, :create_additions => false)
18
+ user = ChefData::DataNormalizer.normalize_user(user, name, [ 'username' ], server.options[:osc_compat])
19
+ if user['password'] != password
20
+ raise RestErrorResponse.new(401, "Bad username or password")
21
+ end
22
+ # Include only particular user data in the response
23
+ user.keep_if { |key,value| %w(first_name last_name display_name email username).include?(key) }
24
+ json_response(200, {
25
+ 'status' => 'linked',
26
+ 'user' => user
27
+ })
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,22 +1,22 @@
1
- require 'ffi_yajl'
2
- require 'chef_zero/endpoints/rest_object_endpoint'
3
- require 'chef_zero/chef_data/data_normalizer'
4
-
5
- module ChefZero
6
- module Endpoints
7
- # /organizations/ORG/containers/NAME
8
- class ContainerEndpoint < RestObjectEndpoint
9
- def initialize(server)
10
- super(server, %w(id containername))
11
- end
12
-
13
- undef_method(:put)
14
-
15
- def populate_defaults(request, response_json)
16
- container = FFI_Yajl::Parser.parse(response_json, :create_additions => false)
17
- container = ChefData::DataNormalizer.normalize_container(container, request.rest_path[3])
18
- FFI_Yajl::Encoder.encode(container, :pretty => true)
19
- end
20
- end
21
- end
22
- end
1
+ require 'ffi_yajl'
2
+ require 'chef_zero/endpoints/rest_object_endpoint'
3
+ require 'chef_zero/chef_data/data_normalizer'
4
+
5
+ module ChefZero
6
+ module Endpoints
7
+ # /organizations/ORG/containers/NAME
8
+ class ContainerEndpoint < RestObjectEndpoint
9
+ def initialize(server)
10
+ super(server, %w(id containername))
11
+ end
12
+
13
+ undef_method(:put)
14
+
15
+ def populate_defaults(request, response_json)
16
+ container = FFI_Yajl::Parser.parse(response_json, :create_additions => false)
17
+ container = ChefData::DataNormalizer.normalize_container(container, request.rest_path[3])
18
+ FFI_Yajl::Encoder.encode(container, :pretty => true)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,13 +1,13 @@
1
- require 'ffi_yajl'
2
- require 'chef_zero/endpoints/rest_list_endpoint'
3
-
4
- module ChefZero
5
- module Endpoints
6
- # /organizations/ORG/containers
7
- class ContainersEndpoint < RestListEndpoint
8
- def initialize(server)
9
- super(server, %w(id containername))
10
- end
11
- end
12
- end
13
- end
1
+ require 'ffi_yajl'
2
+ require 'chef_zero/endpoints/rest_list_endpoint'
3
+
4
+ module ChefZero
5
+ module Endpoints
6
+ # /organizations/ORG/containers
7
+ class ContainersEndpoint < RestListEndpoint
8
+ def initialize(server)
9
+ super(server, %w(id containername))
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,39 +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[3]
9
- case filter
10
- when '_latest'
11
- result = {}
12
- filter_cookbooks(all_cookbooks_list(request), {}, 1) do |name, versions|
13
- if versions.size > 0
14
- result[name] = build_uri(request.base_uri, request.rest_path[0..1] + ['cookbooks', name, versions[0]])
15
- end
16
- end
17
- json_response(200, result)
18
- when '_recipes'
19
- result = []
20
- filter_cookbooks(all_cookbooks_list(request), {}, 1) do |name, versions|
21
- if versions.size > 0
22
- cookbook = FFI_Yajl::Parser.parse(get_data(request, request.rest_path[0..1] + ['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 => list_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| Gem::Version.new(version.dup) }
35
- sorted[-1]
36
- end
37
- end
38
- end
39
- end
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[3]
9
+ case filter
10
+ when '_latest'
11
+ result = {}
12
+ filter_cookbooks(all_cookbooks_list(request), {}, 1) do |name, versions|
13
+ if versions.size > 0
14
+ result[name] = build_uri(request.base_uri, request.rest_path[0..1] + ['cookbooks', name, versions[0]])
15
+ end
16
+ end
17
+ json_response(200, result)
18
+ when '_recipes'
19
+ result = []
20
+ filter_cookbooks(all_cookbooks_list(request), {}, 1) do |name, versions|
21
+ if versions.size > 0
22
+ cookbook = FFI_Yajl::Parser.parse(get_data(request, request.rest_path[0..1] + ['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 => list_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| Gem::Version.new(version.dup) }
35
+ sorted[-1]
36
+ end
37
+ end
38
+ end
39
+ end
@@ -1,119 +1,119 @@
1
- require 'ffi_yajl'
2
- require 'chef_zero/endpoints/rest_object_endpoint'
3
- require 'chef_zero/rest_error_response'
4
- require 'chef_zero/chef_data/data_normalizer'
5
- require 'chef_zero/data_store/data_not_found_error'
6
-
7
- module ChefZero
8
- module Endpoints
9
- # /organizations/ORG/cookbooks/NAME/VERSION
10
- class CookbookVersionEndpoint < RestObjectEndpoint
11
- def get(request)
12
- if request.rest_path[4] == "_latest" || request.rest_path[4] == "latest"
13
- request.rest_path[4] = latest_version(list_data(request, request.rest_path[0..3]))
14
- end
15
- super(request)
16
- end
17
-
18
- def put(request)
19
- name = request.rest_path[3]
20
- version = request.rest_path[4]
21
- existing_cookbook = get_data(request, request.rest_path, :nil)
22
-
23
- # Honor frozen
24
- if existing_cookbook
25
- existing_cookbook_json = FFI_Yajl::Parser.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 = FFI_Yajl::Parser.parse(request.body, :create_additions => false)
32
- if !request_body['frozen?']
33
- request_body['frozen?'] = true
34
- request.body = FFI_Yajl::Encoder.encode(request_body, :pretty => true)
35
- end
36
- end
37
- end
38
-
39
- # Set the cookbook
40
- set_data(request, request.rest_path, request.body, :create_dir, :create)
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, request)
47
- end
48
- end
49
-
50
- already_json_response(existing_cookbook ? 200 : 201, populate_defaults(request, request.body))
51
- end
52
-
53
- def delete(request)
54
- if request.rest_path[4] == "_latest" || request.rest_path[4] == "latest"
55
- request.rest_path[4] = latest_version(list_data(request, request.rest_path[0..3]))
56
- end
57
-
58
- deleted_cookbook = get_data(request)
59
-
60
- response = super(request)
61
- cookbook_name = request.rest_path[3]
62
- cookbook_path = request.rest_path[0..1] + ['cookbooks', cookbook_name]
63
- if exists_data_dir?(request, cookbook_path) && list_data(request, cookbook_path).size == 0
64
- delete_data_dir(request, cookbook_path)
65
- end
66
-
67
- # Hoover deleted files, if they exist
68
- hoover_unused_checksums(get_checksums(deleted_cookbook), request)
69
- response
70
- end
71
-
72
- def get_checksums(cookbook)
73
- result = []
74
- FFI_Yajl::Parser.parse(cookbook, :create_additions => false).each_pair do |key, value|
75
- if value.is_a?(Array)
76
- value.each do |file|
77
- if file.is_a?(Hash) && file.has_key?('checksum')
78
- result << file['checksum']
79
- end
80
- end
81
- end
82
- end
83
- result.uniq
84
- end
85
-
86
- private
87
-
88
- def hoover_unused_checksums(deleted_checksums, request)
89
- data_store.list(request.rest_path[0..1] + ['cookbooks']).each do |cookbook_name|
90
- data_store.list(request.rest_path[0..1] + ['cookbooks', cookbook_name]).each do |version|
91
- cookbook = data_store.get(request.rest_path[0..1] + ['cookbooks', cookbook_name, version], request)
92
- deleted_checksums = deleted_checksums - get_checksums(cookbook)
93
- end
94
- end
95
- deleted_checksums.each do |checksum|
96
- # There can be a race here if multiple cookbooks are uploading.
97
- # This deals with an exception on delete, but things can still get deleted
98
- # that shouldn't be.
99
- begin
100
- delete_data(request, request.rest_path[0..1] + ['file_store', 'checksums', checksum], :data_store_exceptions)
101
- rescue ChefZero::DataStore::DataNotFoundError
102
- end
103
- end
104
- end
105
-
106
- def populate_defaults(request, response_json)
107
- # Inject URIs into each cookbook file
108
- cookbook = FFI_Yajl::Parser.parse(response_json, :create_additions => false)
109
- cookbook = ChefData::DataNormalizer.normalize_cookbook(self, request.rest_path[0..1], cookbook, request.rest_path[3], request.rest_path[4], request.base_uri, request.method)
110
- FFI_Yajl::Encoder.encode(cookbook, :pretty => true)
111
- end
112
-
113
- def latest_version(versions)
114
- sorted = versions.sort_by { |version| Gem::Version.new(version.dup) }
115
- sorted[-1]
116
- end
117
- end
118
- end
119
- end
1
+ require 'ffi_yajl'
2
+ require 'chef_zero/endpoints/rest_object_endpoint'
3
+ require 'chef_zero/rest_error_response'
4
+ require 'chef_zero/chef_data/data_normalizer'
5
+ require 'chef_zero/data_store/data_not_found_error'
6
+
7
+ module ChefZero
8
+ module Endpoints
9
+ # /organizations/ORG/cookbooks/NAME/VERSION
10
+ class CookbookVersionEndpoint < RestObjectEndpoint
11
+ def get(request)
12
+ if request.rest_path[4] == "_latest" || request.rest_path[4] == "latest"
13
+ request.rest_path[4] = latest_version(list_data(request, request.rest_path[0..3]))
14
+ end
15
+ super(request)
16
+ end
17
+
18
+ def put(request)
19
+ name = request.rest_path[3]
20
+ version = request.rest_path[4]
21
+ existing_cookbook = get_data(request, request.rest_path, :nil)
22
+
23
+ # Honor frozen
24
+ if existing_cookbook
25
+ existing_cookbook_json = FFI_Yajl::Parser.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 = FFI_Yajl::Parser.parse(request.body, :create_additions => false)
32
+ if !request_body['frozen?']
33
+ request_body['frozen?'] = true
34
+ request.body = FFI_Yajl::Encoder.encode(request_body, :pretty => true)
35
+ end
36
+ end
37
+ end
38
+
39
+ # Set the cookbook
40
+ set_data(request, request.rest_path, request.body, :create_dir, :create)
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, request)
47
+ end
48
+ end
49
+
50
+ already_json_response(existing_cookbook ? 200 : 201, populate_defaults(request, request.body))
51
+ end
52
+
53
+ def delete(request)
54
+ if request.rest_path[4] == "_latest" || request.rest_path[4] == "latest"
55
+ request.rest_path[4] = latest_version(list_data(request, request.rest_path[0..3]))
56
+ end
57
+
58
+ deleted_cookbook = get_data(request)
59
+
60
+ response = super(request)
61
+ cookbook_name = request.rest_path[3]
62
+ cookbook_path = request.rest_path[0..1] + ['cookbooks', cookbook_name]
63
+ if exists_data_dir?(request, cookbook_path) && list_data(request, cookbook_path).size == 0
64
+ delete_data_dir(request, cookbook_path)
65
+ end
66
+
67
+ # Hoover deleted files, if they exist
68
+ hoover_unused_checksums(get_checksums(deleted_cookbook), request)
69
+ response
70
+ end
71
+
72
+ def get_checksums(cookbook)
73
+ result = []
74
+ FFI_Yajl::Parser.parse(cookbook, :create_additions => false).each_pair do |key, value|
75
+ if value.is_a?(Array)
76
+ value.each do |file|
77
+ if file.is_a?(Hash) && file.has_key?('checksum')
78
+ result << file['checksum']
79
+ end
80
+ end
81
+ end
82
+ end
83
+ result.uniq
84
+ end
85
+
86
+ private
87
+
88
+ def hoover_unused_checksums(deleted_checksums, request)
89
+ data_store.list(request.rest_path[0..1] + ['cookbooks']).each do |cookbook_name|
90
+ data_store.list(request.rest_path[0..1] + ['cookbooks', cookbook_name]).each do |version|
91
+ cookbook = data_store.get(request.rest_path[0..1] + ['cookbooks', cookbook_name, version], request)
92
+ deleted_checksums = deleted_checksums - get_checksums(cookbook)
93
+ end
94
+ end
95
+ deleted_checksums.each do |checksum|
96
+ # There can be a race here if multiple cookbooks are uploading.
97
+ # This deals with an exception on delete, but things can still get deleted
98
+ # that shouldn't be.
99
+ begin
100
+ delete_data(request, request.rest_path[0..1] + ['file_store', 'checksums', checksum], :data_store_exceptions)
101
+ rescue ChefZero::DataStore::DataNotFoundError
102
+ end
103
+ end
104
+ end
105
+
106
+ def populate_defaults(request, response_json)
107
+ # Inject URIs into each cookbook file
108
+ cookbook = FFI_Yajl::Parser.parse(response_json, :create_additions => false)
109
+ cookbook = ChefData::DataNormalizer.normalize_cookbook(self, request.rest_path[0..1], cookbook, request.rest_path[3], request.rest_path[4], request.base_uri, request.method)
110
+ FFI_Yajl::Encoder.encode(cookbook, :pretty => true)
111
+ end
112
+
113
+ def latest_version(versions)
114
+ sorted = versions.sort_by { |version| Gem::Version.new(version.dup) }
115
+ sorted[-1]
116
+ end
117
+ end
118
+ end
119
+ end