chef-zero 4.2.3 → 4.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +201 -201
  3. data/README.md +155 -150
  4. data/Rakefile +31 -31
  5. data/bin/chef-zero +100 -100
  6. data/lib/chef_zero.rb +10 -7
  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 +207 -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 -88
  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 -0
  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 -29
  52. data/lib/chef_zero/endpoints/organization_authenticate_user_endpoint.rb +26 -26
  53. data/lib/chef_zero/endpoints/organization_endpoint.rb +46 -41
  54. data/lib/chef_zero/endpoints/organization_user_base.rb +15 -0
  55. data/lib/chef_zero/endpoints/organization_user_endpoint.rb +26 -48
  56. data/lib/chef_zero/endpoints/organization_users_endpoint.rb +43 -14
  57. data/lib/chef_zero/endpoints/organization_validator_key_endpoint.rb +20 -20
  58. data/lib/chef_zero/endpoints/organizations_endpoint.rb +62 -55
  59. data/lib/chef_zero/endpoints/policies_endpoint.rb +151 -154
  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 -192
  68. data/lib/chef_zero/endpoints/searches_endpoint.rb +18 -18
  69. data/lib/chef_zero/endpoints/server_api_version_endpoint.rb +14 -0
  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 -214
  78. data/lib/chef_zero/rest_error_response.rb +11 -11
  79. data/lib/chef_zero/rest_request.rb +69 -65
  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 -637
  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 -56
  95. data/spec/search_spec.rb +32 -32
  96. data/spec/server_spec.rb +92 -91
  97. data/spec/socketless_server_map_spec.rb +76 -76
  98. data/spec/support/oc_pedant.rb +132 -134
  99. data/spec/support/stickywicket.pem +27 -27
  100. metadata +10 -15
  101. data/spec/run_pedant.rb +0 -103
  102. data/spec/support/pedant.rb +0 -129
@@ -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