chef 12.5.1-universal-mingw32 → 12.6.0-universal-mingw32

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 (221) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +6 -1
  3. data/README.md +6 -4
  4. data/Rakefile +1 -4
  5. data/chef-windows.gemspec +21 -0
  6. data/chef.gemspec +58 -0
  7. data/lib/chef/api_client/registration.rb +9 -4
  8. data/lib/chef/application.rb +3 -84
  9. data/lib/chef/application/apply.rb +9 -2
  10. data/lib/chef/application/client.rb +8 -3
  11. data/lib/chef/application/solo.rb +7 -1
  12. data/lib/chef/application/windows_service.rb +21 -6
  13. data/lib/chef/application/windows_service_manager.rb +2 -3
  14. data/lib/chef/audit/runner.rb +1 -0
  15. data/lib/chef/chef_class.rb +1 -11
  16. data/lib/chef/chef_fs/chef_fs_data_store.rb +181 -2
  17. data/lib/chef/chef_fs/file_system/cookbook_subdir.rb +5 -0
  18. data/lib/chef/chef_fs/file_system/file_system_entry.rb +11 -7
  19. data/lib/chef/client.rb +28 -1
  20. data/lib/chef/cookbook/cookbook_collection.rb +14 -1
  21. data/lib/chef/cookbook/cookbook_version_loader.rb +1 -1
  22. data/lib/chef/cookbook/metadata.rb +115 -9
  23. data/lib/chef/cookbook/remote_file_vendor.rb +1 -1
  24. data/lib/chef/cookbook_version.rb +6 -2
  25. data/lib/chef/data_bag.rb +1 -1
  26. data/lib/chef/data_bag_item.rb +1 -1
  27. data/lib/chef/digester.rb +5 -1
  28. data/lib/chef/dsl/chef_provisioning.rb +57 -0
  29. data/lib/chef/dsl/cheffish.rb +64 -0
  30. data/lib/chef/dsl/declare_resource.rb +108 -0
  31. data/lib/chef/dsl/platform_introspection.rb +3 -3
  32. data/lib/chef/dsl/recipe.rb +3 -73
  33. data/lib/chef/dsl/resources.rb +27 -1
  34. data/lib/chef/event_dispatch/base.rb +3 -0
  35. data/lib/chef/event_dispatch/dispatcher.rb +5 -0
  36. data/lib/chef/event_dispatch/events_output_stream.rb +8 -0
  37. data/lib/chef/exceptions.rb +21 -1
  38. data/lib/chef/file_access_control/unix.rb +12 -12
  39. data/lib/chef/file_content_management/deploy/cp.rb +2 -2
  40. data/lib/chef/file_content_management/deploy/mv_unix.rb +4 -4
  41. data/lib/chef/file_content_management/deploy/mv_windows.rb +1 -1
  42. data/lib/chef/formatters/base.rb +7 -0
  43. data/lib/chef/formatters/error_inspectors/compile_error_inspector.rb +2 -2
  44. data/lib/chef/formatters/indentable_output_stream.rb +5 -0
  45. data/lib/chef/http.rb +19 -3
  46. data/lib/chef/http/decompressor.rb +2 -2
  47. data/lib/chef/json_compat.rb +1 -0
  48. data/lib/chef/knife.rb +16 -2
  49. data/lib/chef/knife/bootstrap.rb +55 -10
  50. data/lib/chef/knife/cookbook_site_install.rb +5 -1
  51. data/lib/chef/knife/core/bootstrap_context.rb +2 -1
  52. data/lib/chef/knife/core/node_presenter.rb +1 -1
  53. data/lib/chef/knife/ssh.rb +30 -16
  54. data/lib/chef/knife/ssl_check.rb +4 -2
  55. data/lib/chef/knife/ssl_fetch.rb +3 -2
  56. data/lib/chef/knife/status.rb +14 -1
  57. data/lib/chef/log.rb +14 -0
  58. data/lib/chef/mixin/get_source_from_package.rb +7 -2
  59. data/lib/chef/mixin/properties.rb +302 -0
  60. data/lib/chef/mixin/proxified_socket.rb +38 -0
  61. data/lib/chef/mixin/subclass_directive.rb +37 -0
  62. data/lib/chef/node.rb +13 -5
  63. data/lib/chef/platform/query_helpers.rb +14 -3
  64. data/lib/chef/platform/service_helpers.rb +20 -38
  65. data/lib/chef/policy_builder/expand_node_object.rb +3 -0
  66. data/lib/chef/policy_builder/policyfile.rb +1 -0
  67. data/lib/chef/property.rb +51 -12
  68. data/lib/chef/provider.rb +40 -35
  69. data/lib/chef/provider/deploy.rb +1 -1
  70. data/lib/chef/provider/dsc_resource.rb +54 -20
  71. data/lib/chef/provider/execute.rb +25 -4
  72. data/lib/chef/provider/group.rb +1 -1
  73. data/lib/chef/provider/lwrp_base.rb +1 -0
  74. data/lib/chef/provider/package.rb +76 -30
  75. data/lib/chef/provider/package/dpkg.rb +152 -69
  76. data/lib/chef/provider/package/openbsd.rb +6 -8
  77. data/lib/chef/provider/package/solaris.rb +2 -0
  78. data/lib/chef/provider/package/windows.rb +95 -14
  79. data/lib/chef/provider/package/windows/exe.rb +129 -0
  80. data/lib/chef/provider/package/windows/msi.rb +37 -13
  81. data/lib/chef/provider/package/windows/registry_uninstall_entry.rb +89 -0
  82. data/lib/chef/provider/package/yum.rb +13 -3
  83. data/lib/chef/provider/powershell_script.rb +3 -0
  84. data/lib/chef/provider/remote_file/cache_control_data.rb +37 -4
  85. data/lib/chef/provider/remote_file/http.rb +1 -1
  86. data/lib/chef/provider/script.rb +1 -0
  87. data/lib/chef/provider/service.rb +13 -10
  88. data/lib/chef/provider/service/solaris.rb +43 -17
  89. data/lib/chef/provider/service/upstart.rb +3 -3
  90. data/lib/chef/provider/user.rb +1 -1
  91. data/lib/chef/provider/user/dscl.rb +111 -100
  92. data/lib/chef/provider/user/windows.rb +5 -3
  93. data/lib/chef/recipe.rb +3 -5
  94. data/lib/chef/resource.rb +77 -320
  95. data/lib/chef/resource/action_class.rb +4 -0
  96. data/lib/chef/resource/dpkg_package.rb +4 -3
  97. data/lib/chef/resource/dsc_resource.rb +40 -2
  98. data/lib/chef/resource/execute.rb +9 -1
  99. data/lib/chef/resource/ksh.rb +32 -0
  100. data/lib/chef/resource/lwrp_base.rb +6 -10
  101. data/lib/chef/resource/package.rb +8 -9
  102. data/lib/chef/resource/registry_key.rb +1 -1
  103. data/lib/chef/resource/resource_notification.rb +14 -1
  104. data/lib/chef/resource/script.rb +1 -1
  105. data/lib/chef/resource/windows_package.rb +1 -1
  106. data/lib/chef/resource_builder.rb +14 -7
  107. data/lib/chef/resource_reporter.rb +6 -0
  108. data/lib/chef/resources.rb +1 -7
  109. data/lib/chef/rest.rb +1 -1
  110. data/lib/chef/run_context.rb +45 -2
  111. data/lib/chef/run_list/run_list_expansion.rb +47 -0
  112. data/lib/chef/runner.rb +25 -0
  113. data/lib/chef/search/query.rb +16 -2
  114. data/lib/chef/util/diff.rb +2 -2
  115. data/lib/chef/util/powershell/ps_credential.rb +2 -3
  116. data/lib/chef/version.rb +1 -1
  117. data/lib/chef/win32/api/file.rb +51 -1
  118. data/lib/chef/win32/file.rb +5 -0
  119. data/lib/chef/win32/file/version_info.rb +93 -0
  120. data/lib/chef/win32/mutex.rb +1 -1
  121. data/spec/data/apt/chef-integration-test2-1.0/debian/changelog +5 -0
  122. data/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2.debhelper.log +45 -0
  123. data/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2.substvars +1 -0
  124. data/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2/DEBIAN/conffiles +1 -0
  125. data/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2/DEBIAN/control +10 -0
  126. data/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2/DEBIAN/md5sums +1 -0
  127. data/spec/data/apt/chef-integration-test2-1.0/debian/compat +1 -0
  128. data/spec/data/apt/chef-integration-test2-1.0/debian/conffiles +1 -0
  129. data/spec/data/apt/chef-integration-test2-1.0/debian/control +13 -0
  130. data/spec/data/apt/chef-integration-test2-1.0/debian/copyright +34 -0
  131. data/spec/data/apt/chef-integration-test2-1.0/debian/files +1 -0
  132. data/spec/data/apt/chef-integration-test2-1.0/debian/rules +13 -0
  133. data/spec/data/apt/chef-integration-test2-1.0/debian/source/format +1 -0
  134. data/spec/data/apt/chef-integration-test2_1.0-1.debian.tar.gz +0 -0
  135. data/spec/data/apt/chef-integration-test2_1.0-1.dsc +18 -0
  136. data/spec/data/apt/chef-integration-test2_1.0-1_amd64.build +91 -0
  137. data/spec/data/apt/chef-integration-test2_1.0-1_amd64.changes +31 -0
  138. data/spec/data/apt/chef-integration-test2_1.0-1_amd64.deb +0 -0
  139. data/spec/data/apt/chef-integration-test2_1.0.orig.tar.gz +0 -0
  140. data/spec/functional/application_spec.rb +1 -1
  141. data/spec/functional/audit/runner_spec.rb +4 -0
  142. data/spec/functional/knife/ssh_spec.rb +5 -5
  143. data/spec/functional/notifications_spec.rb +74 -4
  144. data/spec/functional/resource/aix_service_spec.rb +2 -2
  145. data/spec/functional/resource/dpkg_package_spec.rb +339 -0
  146. data/spec/functional/resource/ifconfig_spec.rb +3 -1
  147. data/spec/functional/resource/mount_spec.rb +5 -2
  148. data/spec/functional/resource/package_spec.rb +1 -1
  149. data/spec/functional/resource/user/windows_spec.rb +8 -0
  150. data/spec/functional/resource/windows_package_spec.rb +177 -0
  151. data/spec/functional/win32/version_info_spec.rb +50 -0
  152. data/spec/integration/client/client_spec.rb +80 -0
  153. data/spec/integration/knife/download_spec.rb +9 -0
  154. data/spec/integration/knife/upload_spec.rb +28 -1
  155. data/spec/integration/recipes/lwrp_inline_resources_spec.rb +93 -23
  156. data/spec/integration/recipes/resource_action_spec.rb +211 -116
  157. data/spec/integration/recipes/resource_converge_if_changed_spec.rb +72 -0
  158. data/spec/integration/solo/solo_spec.rb +34 -0
  159. data/spec/spec_helper.rb +11 -1
  160. data/spec/support/platform_helpers.rb +8 -0
  161. data/spec/support/shared/integration/integration_helper.rb +6 -0
  162. data/spec/support/shared/unit/execute_resource.rb +5 -0
  163. data/spec/support/shared/unit/platform_introspector.rb +7 -0
  164. data/spec/tiny_server.rb +6 -2
  165. data/spec/unit/api_client/registration_spec.rb +5 -4
  166. data/spec/unit/application_spec.rb +1 -181
  167. data/spec/unit/chef_fs/file_system/cookbook_subdir_spec.rb +34 -0
  168. data/spec/unit/cookbook/metadata_spec.rb +122 -2
  169. data/spec/unit/http_spec.rb +102 -0
  170. data/spec/unit/knife/bootstrap_spec.rb +55 -13
  171. data/spec/unit/knife/core/bootstrap_context_spec.rb +10 -3
  172. data/spec/unit/knife/ssl_check_spec.rb +7 -3
  173. data/spec/unit/knife/ssl_fetch_spec.rb +2 -2
  174. data/spec/unit/knife/status_spec.rb +13 -13
  175. data/spec/unit/knife_spec.rb +26 -2
  176. data/spec/unit/lwrp_spec.rb +1 -1
  177. data/spec/unit/mixin/properties_spec.rb +97 -0
  178. data/spec/unit/mixin/proxified_socket_spec.rb +94 -0
  179. data/spec/unit/mixin/subclass_directive_spec.rb +45 -0
  180. data/spec/unit/node_spec.rb +9 -1
  181. data/spec/unit/policy_builder/policyfile_spec.rb +2 -0
  182. data/spec/unit/property/validation_spec.rb +14 -12
  183. data/spec/unit/property_spec.rb +56 -0
  184. data/spec/unit/provider/deploy_spec.rb +1 -1
  185. data/spec/unit/provider/dsc_resource_spec.rb +63 -24
  186. data/spec/unit/provider/execute_spec.rb +95 -28
  187. data/spec/unit/provider/package/dpkg_spec.rb +185 -96
  188. data/spec/unit/provider/package/windows/exe_spec.rb +251 -0
  189. data/spec/unit/provider/package/windows/msi_spec.rb +94 -10
  190. data/spec/unit/provider/package/windows_spec.rb +227 -26
  191. data/spec/unit/provider/package/yum_spec.rb +6 -0
  192. data/spec/unit/provider/package_spec.rb +495 -366
  193. data/spec/unit/provider/remote_file/cache_control_data_spec.rb +62 -36
  194. data/spec/unit/provider/script_spec.rb +2 -2
  195. data/spec/unit/provider/service/solaris_smf_service_spec.rb +110 -39
  196. data/spec/unit/provider/service/upstart_service_spec.rb +19 -0
  197. data/spec/unit/provider/user/dscl_spec.rb +14 -0
  198. data/spec/unit/provider/user/windows_spec.rb +2 -2
  199. data/spec/unit/provider/user_spec.rb +9 -0
  200. data/spec/unit/provider_resolver_spec.rb +6 -30
  201. data/spec/unit/recipe_spec.rb +46 -20
  202. data/spec/unit/resource/chef_gem_spec.rb +1 -1
  203. data/spec/unit/resource/dsc_resource_spec.rb +14 -3
  204. data/spec/unit/resource/ksh_spec.rb +40 -0
  205. data/spec/unit/resource/registry_key_spec.rb +2 -2
  206. data/spec/unit/resource/resource_notification_spec.rb +44 -45
  207. data/spec/unit/resource_reporter_spec.rb +7 -0
  208. data/spec/unit/resource_spec.rb +268 -253
  209. data/spec/unit/rest_spec.rb +2 -2
  210. data/spec/unit/run_list/run_list_expansion_spec.rb +18 -3
  211. data/spec/unit/search/query_spec.rb +19 -1
  212. data/spec/unit/util/powershell/ps_credential_spec.rb +8 -1
  213. data/spec/unit/windows_service_spec.rb +83 -38
  214. data/tasks/external_tests.rb +19 -9
  215. data/tasks/rspec.rb +1 -1
  216. metadata +70 -21
  217. data/spec/support/pedant/Gemfile +0 -3
  218. data/spec/support/pedant/pedant_config.rb +0 -129
  219. data/spec/support/pedant/run_pedant.rb +0 -63
  220. data/spec/support/pedant/stickywicket.pem +0 -27
  221. data/spec/unit/provider/package_spec.rbe +0 -0
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Author:: Seth Chisamore (<schisamo@opscode.com>)
3
- # Copyright:: Copyright (c) 2011 Opscode, Inc.
3
+ # Copyright:: Copyright (c) 2011-2015 Chef Software, Inc.
4
4
  # License:: Apache License, Version 2.0
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -51,8 +51,7 @@ class Chef
51
51
  option :log_location,
52
52
  :short => "-L LOGLOCATION",
53
53
  :long => "--logfile LOGLOCATION",
54
- :description => "Set the log file location for chef-service",
55
- :default => "#{ENV['SYSTEMDRIVE']}/chef/client.log"
54
+ :description => "Set the log file location for chef-service"
56
55
 
57
56
  option :help,
58
57
  :short => "-h",
@@ -106,6 +106,7 @@ class Chef
106
106
  RSpec.configure do |c|
107
107
  c.color = Chef::Config[:color]
108
108
  c.expose_dsl_globally = false
109
+ c.project_source_dirs = Array(Chef::Config[:cookbook_path])
109
110
  c.backtrace_exclusion_patterns << exclusion_pattern
110
111
  end
111
112
  end
@@ -205,17 +205,7 @@ class Chef
205
205
  # @api private this will likely be removed in favor of an as-yet unwritten
206
206
  # `Chef.log`
207
207
  def log_deprecation(message, location=nil)
208
- if !location
209
- # Pick the first caller that is *not* part of the Chef gem, that's the
210
- # thing the user wrote.
211
- chef_gem_path = File.expand_path("../..", __FILE__)
212
- caller(0..10).each do |c|
213
- if !c.start_with?(chef_gem_path)
214
- location = c
215
- break
216
- end
217
- end
218
- end
208
+ location ||= Chef::Log.caller_location
219
209
  # `run_context.events` is the primary deprecation target if we're in a
220
210
  # run. If we are not yet in a run, print to `Chef::Log`.
221
211
  if run_context && run_context.events
@@ -66,12 +66,65 @@ class Chef
66
66
  # - ChefFSDataStore lets cookbooks be uploaded into a temporary memory
67
67
  # storage, and when the cookbook is committed, copies the files onto the
68
68
  # disk in the correct place (/cookbooks/apache2/recipes/default.rb).
69
+ #
69
70
  # 3. Data bags:
70
71
  # - The Chef server expects data bags in /data/BAG/ITEM
71
72
  # - The repository stores data bags in /data_bags/BAG/ITEM
72
73
  #
73
74
  # 4. JSON filenames are generally NAME.json in the repository (e.g. /nodes/foo.json).
74
75
  #
76
+ # 5. Org membership:
77
+ # chef-zero stores user membership in an org as a series of empty files.
78
+ # If an org has jkeiser and cdoherty as members, chef-zero expects these
79
+ # files to exist:
80
+ #
81
+ # - `users/jkeiser` (content: '{}')
82
+ # - `users/cdoherty` (content: '{}')
83
+ #
84
+ # ChefFS, on the other hand, stores user membership in an org as a single
85
+ # file, `members.json`, with content:
86
+ #
87
+ # ```json
88
+ # [
89
+ # { "user": { "username": "jkeiser" } },
90
+ # { "user": { "username": "cdoherty" } }
91
+ # ]
92
+ # ```
93
+ #
94
+ # To translate between the two, we need to intercept requests to `users`
95
+ # like so:
96
+ #
97
+ # - `list(users)` -> `get(/members.json)`
98
+ # - `get(users/NAME)` -> `get(/members.json)`, see if it's in there
99
+ # - `create(users/NAME)` -> `get(/members.json)`, add name, `set(/members.json)`
100
+ # - `delete(users/NAME)` -> `get(/members.json)`, remove name, `set(/members.json)`
101
+ #
102
+ # 6. Org invitations:
103
+ # chef-zero stores org membership invitations as a series of empty files.
104
+ # If an org has invited jkeiser and cdoherty (and they have not yet accepted
105
+ # the invite), chef-zero expects these files to exist:
106
+ #
107
+ # - `association_requests/jkeiser` (content: '{}')
108
+ # - `association_requests/cdoherty` (content: '{}')
109
+ #
110
+ # ChefFS, on the other hand, stores invitations as a single file,
111
+ # `invitations.json`, with content:
112
+ #
113
+ # ```json
114
+ # [
115
+ # { "id" => "jkeiser-chef", 'username' => 'jkeiser' },
116
+ # { "id" => "cdoherty-chef", 'username' => 'cdoherty' }
117
+ # ]
118
+ # ```
119
+ #
120
+ # To translate between the two, we need to intercept requests to `users`
121
+ # like so:
122
+ #
123
+ # - `list(association_requests)` -> `get(/invitations.json)`
124
+ # - `get(association_requests/NAME)` -> `get(/invitations.json)`, see if it's in there
125
+ # - `create(association_requests/NAME)` -> `get(/invitations.json)`, add name, `set(/invitations.json)`
126
+ # - `delete(association_requests/NAME)` -> `get(/invitations.json)`, remove name, `set(/invitations.json)`
127
+ #
75
128
  class ChefFSDataStore
76
129
  #
77
130
  # Create a new ChefFSDataStore
@@ -83,9 +136,10 @@ class Chef
83
136
  # Generally will be a +ChefFS::FileSystem::ChefRepositoryFileSystemRoot+
84
137
  # object, created from +ChefFS::Config.local_fs+.
85
138
  #
86
- def initialize(chef_fs)
139
+ def initialize(chef_fs, chef_config=Chef::Config)
87
140
  @chef_fs = chef_fs
88
141
  @memory_store = ChefZero::DataStore::MemoryStore.new
142
+ @repo_mode = chef_config[:repo_mode]
89
143
  end
90
144
 
91
145
  def publish_description
@@ -93,6 +147,7 @@ class Chef
93
147
  end
94
148
 
95
149
  attr_reader :chef_fs
150
+ attr_reader :repo_mode
96
151
 
97
152
  def create_dir(path, name, *options)
98
153
  if use_memory_store?(path)
@@ -108,6 +163,24 @@ class Chef
108
163
  end
109
164
  end
110
165
 
166
+ #
167
+ # If you want to get the contents of /data/x/y from the server,
168
+ # you say chef_fs.child('data').child('x').child('y').read.
169
+ # It will make exactly one network request: GET /data/x/y
170
+ # And that will return 404 if it doesn't exist.
171
+ #
172
+ # ChefFS objects do not go to the network until you ask them for data.
173
+ # This means you can construct a /data/x/y ChefFS entry early.
174
+ #
175
+ # Alternative:
176
+ # chef_fs.child('data') could have done a GET /data preemptively,
177
+ # allowing it to know whether child('x') was valid (GET /data gives you
178
+ # a list of data bags). Then child('x') could have done a GET /data/x,
179
+ # allowing it to know whether child('y') (the item) existed. Finally,
180
+ # we would do the GET /data/x/y to read the contents. Three network
181
+ # requests instead of 1.
182
+ #
183
+
111
184
  def create(path, name, data, *options)
112
185
  if use_memory_store?(path)
113
186
  @memory_store.create(path, name, data, *options)
@@ -115,6 +188,32 @@ class Chef
115
188
  elsif path[0] == 'cookbooks' && path.length == 2
116
189
  # Do nothing. The entry gets created when the cookbook is created.
117
190
 
191
+ # create [/organizations/ORG]/users/NAME (with content '{}')
192
+ # Manipulate the `members.json` file that contains a list of all users
193
+ elsif is_org? && path == [ 'users' ]
194
+ update_json('members.json', []) do |members|
195
+ # Format of each entry: { "user": { "username": "jkeiser" } }
196
+ if members.any? { |member| member['user']['username'] == name }
197
+ raise ChefZero::DataStore::DataAlreadyExistsError.new(path, entry)
198
+ end
199
+
200
+ # Actually add the user
201
+ members << { "user" => { "username" => name } }
202
+ end
203
+
204
+ # create [/organizations/ORG]/association_requests/NAME (with content '{}')
205
+ # Manipulate the `invitations.json` file that contains a list of all users
206
+ elsif is_org? && path == [ 'association_requests' ]
207
+ update_json('invitations.json', []) do |invitations|
208
+ # Format of each entry: { "id" => "jkeiser-chef", 'username' => 'jkeiser' }
209
+ if invitations.any? { |member| member['username'] == name }
210
+ raise ChefZero::DataStore::DataAlreadyExistsError.new(path)
211
+ end
212
+
213
+ # Actually add the user (TODO insert org name??)
214
+ invitations << { "username" => name }
215
+ end
216
+
118
217
  else
119
218
  if !data.is_a?(String)
120
219
  raise "set only works with strings"
@@ -142,6 +241,24 @@ class Chef
142
241
  raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e)
143
242
  end
144
243
 
244
+ # GET [/organizations/ORG]/users/NAME -> /users/NAME
245
+ # Manipulates members.json
246
+ elsif is_org? && path[0] == 'users' && path.length == 2
247
+ if get_json('members.json', []).any? { |member| member['user']['username'] == path[1] }
248
+ '{}'
249
+ else
250
+ raise ChefZero::DataStore::DataNotFoundError.new(path)
251
+ end
252
+
253
+ # GET [/organizations/ORG]/association_requests/NAME -> /users/NAME
254
+ # Manipulates invites.json
255
+ elsif is_org? && path[0] == 'association_requests' && path.length == 2
256
+ if get_json('invites.json', []).any? { |member| member['user']['username'] == path[1] }
257
+ '{}'
258
+ else
259
+ raise ChefZero::DataStore::DataNotFoundError.new(path)
260
+ end
261
+
145
262
  else
146
263
  with_entry(path) do |entry|
147
264
  if path[0] == 'cookbooks' && path.length == 3
@@ -209,6 +326,29 @@ class Chef
209
326
  def delete(path)
210
327
  if use_memory_store?(path)
211
328
  @memory_store.delete(path)
329
+
330
+ # DELETE [/organizations/ORG]/users/NAME
331
+ # Manipulates members.json
332
+ elsif is_org? && path[0] == 'users' && path.length == 2
333
+ update_json('members.json', []) do |members|
334
+ result = members.reject { |member| member['user']['username'] == path[1] }
335
+ if result.size == members.size
336
+ raise ChefZero::DataStore::DataNotFoundError.new(path)
337
+ end
338
+ result
339
+ end
340
+
341
+ # DELETE [/organizations/ORG]/users/NAME
342
+ # Manipulates members.json
343
+ elsif is_org? && path[0] == 'association_requests' && path.length == 2
344
+ update_json('invitations.json', []) do |invitations|
345
+ result = invitations.reject { |invitation| invitation['username'] == path[1] }
346
+ if result.size == invitations.size
347
+ raise ChefZero::DataStore::DataNotFoundError.new(path)
348
+ end
349
+ result
350
+ end
351
+
212
352
  else
213
353
  with_entry(path) do |entry|
214
354
  begin
@@ -394,9 +534,22 @@ class Chef
394
534
  end
395
535
  end
396
536
  end
537
+
538
+ elsif path[0] == 'acls'
539
+ # /acls/containers|nodes|.../x.json
540
+ # /acls/organization.json
541
+ if path.length == 3 || path == [ 'acls', 'organization' ]
542
+ path = path.dup
543
+ path[-1] = "#{path[-1]}.json"
544
+ end
545
+
546
+ # /acls/containers|nodes|... do NOT drop into the next elsif, and do
547
+ # not get .json appended
548
+
549
+ # /nodes|clients|.../x.json
397
550
  elsif path.length == 2
398
551
  path = path.dup
399
- path[1] = "#{path[1]}.json"
552
+ path[-1] = "#{path[-1]}.json"
400
553
  end
401
554
  path
402
555
  end
@@ -477,6 +630,32 @@ class Chef
477
630
  metadata = ChefZero::CookbookData.metadata_from(dir, path[1], nil, [])
478
631
  metadata[:version] || '0.0.0'
479
632
  end
633
+
634
+ def update_json(path, default_value)
635
+ entry = Chef::ChefFS::FileSystem.resolve_path(chef_fs, path)
636
+ begin
637
+ input = Chef::JSONCompat.parse(entry.read)
638
+ output = yield input.dup
639
+ entry.write(Chef::JSONCompat.to_json_pretty(output)) if output != input
640
+ rescue Chef::ChefFS::FileSystem::NotFoundError
641
+ # Send the default value to the caller, and create the entry if the caller updates it
642
+ output = yield default_value
643
+ entry.parent.create_child(entry.name, Chef::JSONCompat.to_json_pretty(output)) if output != []
644
+ end
645
+ end
646
+
647
+ def get_json(path, default_value)
648
+ entry = Chef::ChefFS::FileSystem.resolve_path(chef_fs, path)
649
+ begin
650
+ Chef::JSONCompat.parse(entry.read)
651
+ rescue Chef::ChefFS::FileSystem::NotFoundError
652
+ default_value
653
+ end
654
+ end
655
+
656
+ def is_org?
657
+ repo_mode == 'hosted_everything'
658
+ end
480
659
  end
481
660
  end
482
661
  end
@@ -45,6 +45,11 @@ class Chef
45
45
  true
46
46
  end
47
47
 
48
+ def make_child_entry(name)
49
+ result = @children.select { |child| child.name == name }.first if @children
50
+ result || NonexistentFSObject.new(name, self)
51
+ end
52
+
48
53
  def rest
49
54
  parent.rest
50
55
  end
@@ -72,18 +72,22 @@ class Chef
72
72
  end
73
73
 
74
74
  def delete(recurse)
75
- if dir?
76
- if !recurse
77
- raise MustDeleteRecursivelyError.new(self, $!)
75
+ begin
76
+ if dir?
77
+ if !recurse
78
+ raise MustDeleteRecursivelyError.new(self, $!)
79
+ end
80
+ FileUtils.rm_r(file_path)
81
+ else
82
+ File.delete(file_path)
78
83
  end
79
- FileUtils.rm_rf(file_path)
80
- else
81
- File.delete(file_path)
84
+ rescue Errno::ENOENT
85
+ raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!)
82
86
  end
83
87
  end
84
88
 
85
89
  def exists?
86
- File.exists?(file_path) && parent.can_have_child?(name, dir?)
90
+ File.exists?(file_path) && (parent.nil? || parent.can_have_child?(name, dir?))
87
91
  end
88
92
 
89
93
  def read
@@ -232,6 +232,8 @@ class Chef
232
232
  # @return Always returns true.
233
233
  #
234
234
  def run
235
+ start_profiling
236
+
235
237
  run_error = nil
236
238
 
237
239
  runlock = RunLock.new(Chef::Config.lockfile)
@@ -271,7 +273,7 @@ class Chef
271
273
 
272
274
  if Chef::Config[:why_run] == true
273
275
  # why_run should probably be renamed to why_converge
274
- Chef::Log.debug("Not running controls in 'why_run' mode - this mode is used to see potential converge changes")
276
+ Chef::Log.debug("Not running controls in 'why-run' mode - this mode is used to see potential converge changes")
275
277
  elsif Chef::Config[:audit_mode] != :disabled
276
278
  audit_error = run_audits(run_context)
277
279
  end
@@ -284,6 +286,9 @@ class Chef
284
286
  run_completed_successfully
285
287
  events.run_completed(node)
286
288
 
289
+ # keep this inside the main loop to get exception backtraces
290
+ end_profiling
291
+
287
292
  # rebooting has to be the last thing we do, no exceptions.
288
293
  Chef::Platform::Rebooter.reboot_if_needed!(node)
289
294
  rescue Exception => run_error
@@ -891,6 +896,28 @@ class Chef
891
896
  attr_reader :override_runlist
892
897
  attr_reader :specific_recipes
893
898
 
899
+ def profiling_prereqs!
900
+ require 'ruby-prof'
901
+ rescue LoadError
902
+ raise "You must have the ruby-prof gem installed in order to use --profile-ruby"
903
+ end
904
+
905
+ def start_profiling
906
+ return unless Chef::Config[:profile_ruby]
907
+ profiling_prereqs!
908
+ RubyProf.start
909
+ end
910
+
911
+ def end_profiling
912
+ return unless Chef::Config[:profile_ruby]
913
+ profiling_prereqs!
914
+ path = Chef::FileCache.create_cache_path("graph_profile.out", false)
915
+ File.open(path, "w+") do |file|
916
+ RubyProf::GraphPrinter.new(RubyProf.stop).print(file, {})
917
+ end
918
+ Chef::Log.warn("Ruby execution profile dumped to #{path}")
919
+ end
920
+
894
921
  def empty_directory?(path)
895
922
  !File.exists?(path) || (Dir.entries(path).size <= 2)
896
923
  end
@@ -1,7 +1,7 @@
1
1
  #--
2
2
  # Author:: Tim Hinderliter (<tim@opscode.com>)
3
3
  # Author:: Christopher Walters (<cw@opscode.com>)
4
- # Copyright:: Copyright (c) 2010 Opscode, Inc.
4
+ # Copyright:: Copyright (c) 2010-2015 Chef Software, Inc.
5
5
  # License:: Apache License, Version 2.0
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -41,5 +41,18 @@ class Chef
41
41
  cookbook_versions.each{ |cookbook_name, cookbook_version| self[cookbook_name] = cookbook_version }
42
42
  end
43
43
 
44
+ # Validates that the cookbook metadata allows it to run on this instance.
45
+ #
46
+ # Currently checks chef_version and ohai_version in the cookbook metadata
47
+ # against the running Chef::VERSION and Ohai::VERSION.
48
+ #
49
+ # @raises [Chef::Exceptions::CookbookChefVersionMismatch] if the Chef::VERSION fails validation
50
+ # @raises [Chef::Exceptions::CookbookOhaiVersionMismatch] if the Ohai::VERSION fails validation
51
+ def validate!
52
+ each do |cookbook_name, cookbook_version|
53
+ cookbook_version.metadata.validate_chef_version!
54
+ cookbook_version.metadata.validate_ohai_version!
55
+ end
56
+ end
44
57
  end
45
58
  end
@@ -91,7 +91,7 @@ class Chef
91
91
  remove_ignored_files
92
92
 
93
93
  if empty?
94
- Chef::Log.warn "found a directory #{cookbook_name} in the cookbook path, but it contains no cookbook files. skipping."
94
+ Chef::Log.warn "Found a directory #{cookbook_name} in the cookbook path, but it contains no cookbook files. skipping."
95
95
  end
96
96
  @cookbook_settings
97
97
  end
@@ -2,7 +2,7 @@
2
2
  # Author:: Adam Jacob (<adam@opscode.com>)
3
3
  # Author:: AJ Christensen (<aj@opscode.com>)
4
4
  # Author:: Seth Falcon (<seth@opscode.com>)
5
- # Copyright:: Copyright 2008-2010 Opscode, Inc.
5
+ # Copyright:: Copyright 2008-2015 Chef Software, Inc.
6
6
  # License:: Apache License, Version 2.0
7
7
  #
8
8
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -55,19 +55,23 @@ class Chef
55
55
  SOURCE_URL = 'source_url'.freeze
56
56
  ISSUES_URL = 'issues_url'.freeze
57
57
  PRIVACY = 'privacy'.freeze
58
+ CHEF_VERSIONS = 'chef_versions'.freeze
59
+ OHAI_VERSIONS = 'ohai_versions'.freeze
58
60
 
59
61
  COMPARISON_FIELDS = [ :name, :description, :long_description, :maintainer,
60
62
  :maintainer_email, :license, :platforms, :dependencies,
61
63
  :recommendations, :suggestions, :conflicting, :providing,
62
64
  :replacing, :attributes, :groupings, :recipes, :version,
63
- :source_url, :issues_url, :privacy ]
65
+ :source_url, :issues_url, :privacy, :chef_versions, :ohai_versions ]
64
66
 
65
- VERSION_CONSTRAINTS = {:depends => DEPENDENCIES,
66
- :recommends => RECOMMENDATIONS,
67
- :suggests => SUGGESTIONS,
68
- :conflicts => CONFLICTING,
69
- :provides => PROVIDING,
70
- :replaces => REPLACING }
67
+ VERSION_CONSTRAINTS = {:depends => DEPENDENCIES,
68
+ :recommends => RECOMMENDATIONS,
69
+ :suggests => SUGGESTIONS,
70
+ :conflicts => CONFLICTING,
71
+ :provides => PROVIDING,
72
+ :replaces => REPLACING,
73
+ :chef_version => CHEF_VERSIONS,
74
+ :ohai_version => OHAI_VERSIONS }
71
75
 
72
76
  include Chef::Mixin::ParamsValidate
73
77
  include Chef::Mixin::FromFile
@@ -84,6 +88,11 @@ class Chef
84
88
  attr_reader :recipes
85
89
  attr_reader :version
86
90
 
91
+ # @return [Array<Gem::Dependency>] Array of supported Chef versions
92
+ attr_reader :chef_versions
93
+ # @return [Array<Gem::Dependency>] Array of supported Ohai versions
94
+ attr_reader :ohai_versions
95
+
87
96
  # Builds a new Chef::Cookbook::Metadata object.
88
97
  #
89
98
  # === Parameters
@@ -118,6 +127,8 @@ class Chef
118
127
  @source_url = ''
119
128
  @issues_url = ''
120
129
  @privacy = false
130
+ @chef_versions = []
131
+ @ohai_versions = []
121
132
 
122
133
  @errors = []
123
134
  end
@@ -386,6 +397,28 @@ class Chef
386
397
  @replacing[cookbook]
387
398
  end
388
399
 
400
+ # Metadata DSL to set a valid chef_version. May be declared multiple times
401
+ # with the result being 'OR'd such that if any statements match, the version
402
+ # is considered supported. Uses Gem::Requirement for its implementation.
403
+ #
404
+ # @param version_args [Array<String>] Version constraint in String form
405
+ # @return [Array<Gem::Dependency>] Current chef_versions array
406
+ def chef_version(*version_args)
407
+ @chef_versions << Gem::Dependency.new('chef', *version_args) unless version_args.empty?
408
+ @chef_versions
409
+ end
410
+
411
+ # Metadata DSL to set a valid ohai_version. May be declared multiple times
412
+ # with the result being 'OR'd such that if any statements match, the version
413
+ # is considered supported. Uses Gem::Requirement for its implementation.
414
+ #
415
+ # @param version_args [Array<String>] Version constraint in String form
416
+ # @return [Array<Gem::Dependency>] Current ohai_versions array
417
+ def ohai_version(*version_args)
418
+ @ohai_versions << Gem::Dependency.new('ohai', *version_args) unless version_args.empty?
419
+ @ohai_versions
420
+ end
421
+
389
422
  # Adds a description for a recipe.
390
423
  #
391
424
  # === Parameters
@@ -481,6 +514,40 @@ class Chef
481
514
  @groupings[name]
482
515
  end
483
516
 
517
+ # Convert an Array of Gem::Dependency objects (chef_version/ohai_version) to an Array.
518
+ #
519
+ # Gem::Dependencey#to_s is not useful, and there is no #to_json defined on it or its component
520
+ # objets, so we have to write our own rendering method.
521
+ #
522
+ # [ Gem::Dependency.new(">= 12.5"), Gem::Dependency.new(">= 11.18.0", "< 12.0") ]
523
+ #
524
+ # results in:
525
+ #
526
+ # [ [ ">= 12.5" ], [ ">= 11.18.0", "< 12.0" ] ]
527
+ #
528
+ # @param deps [Array<Gem::Dependency>] Multiple Gem-style version constraints
529
+ # @return [Array<Array<String>]] Simple object representation of version constraints (for json)
530
+ def gem_requirements_to_array(*deps)
531
+ deps.map do |dep|
532
+ dep.requirement.requirements.map do |op, version|
533
+ "#{op} #{version}"
534
+ end.sort
535
+ end
536
+ end
537
+
538
+ # Convert an Array of Gem::Dependency objects (chef_version/ohai_version) to a hash.
539
+ #
540
+ # This is the inverse of #gem_requirements_to_array
541
+ #
542
+ # @param what [String] What version constraint we are constructing ('chef' or 'ohai' presently)
543
+ # @param array [Array<Array<String>]] Simple object representation of version constraints (from json)
544
+ # @return [Array<Gem::Dependency>] Multiple Gem-style version constraints
545
+ def gem_requirements_from_array(what, array)
546
+ array.map do |dep|
547
+ Gem::Dependency.new(what, *dep)
548
+ end
549
+ end
550
+
484
551
  def to_hash
485
552
  {
486
553
  NAME => self.name,
@@ -502,7 +569,9 @@ class Chef
502
569
  VERSION => self.version,
503
570
  SOURCE_URL => self.source_url,
504
571
  ISSUES_URL => self.issues_url,
505
- PRIVACY => self.privacy
572
+ PRIVACY => self.privacy,
573
+ CHEF_VERSIONS => gem_requirements_to_array(*self.chef_versions),
574
+ OHAI_VERSIONS => gem_requirements_to_array(*self.ohai_versions)
506
575
  }
507
576
  end
508
577
 
@@ -537,6 +606,8 @@ class Chef
537
606
  @source_url = o[SOURCE_URL] if o.has_key?(SOURCE_URL)
538
607
  @issues_url = o[ISSUES_URL] if o.has_key?(ISSUES_URL)
539
608
  @privacy = o[PRIVACY] if o.has_key?(PRIVACY)
609
+ @chef_versions = gem_requirements_from_array("chef", o[CHEF_VERSIONS]) if o.has_key?(CHEF_VERSIONS)
610
+ @ohai_versions = gem_requirements_from_array("ohai", o[OHAI_VERSIONS]) if o.has_key?(OHAI_VERSIONS)
540
611
  self
541
612
  end
542
613
 
@@ -612,8 +683,43 @@ class Chef
612
683
  )
613
684
  end
614
685
 
686
+ # Validates that the Ohai::VERSION of the running chef-client matches one of the
687
+ # configured ohai_version statements in this cookbooks metadata.
688
+ #
689
+ # @raises [Chef::Exceptions::CookbookOhaiVersionMismatch] if the cookbook fails validation
690
+ def validate_ohai_version!
691
+ unless gem_dep_matches?("ohai", Gem::Version.new(Ohai::VERSION), *ohai_versions)
692
+ raise Exceptions::CookbookOhaiVersionMismatch.new(Ohai::VERSION, name, version, *ohai_versions)
693
+ end
694
+ end
695
+
696
+ # Validates that the Chef::VERSION of the running chef-client matches one of the
697
+ # configured chef_version statements in this cookbooks metadata.
698
+ #
699
+ # @raises [Chef::Exceptions::CookbookChefVersionMismatch] if the cookbook fails validation
700
+ def validate_chef_version!
701
+ unless gem_dep_matches?("chef", Gem::Version.new(Chef::VERSION), *chef_versions)
702
+ raise Exceptions::CookbookChefVersionMismatch.new(Chef::VERSION, name, version, *chef_versions)
703
+ end
704
+ end
705
+
615
706
  private
616
707
 
708
+ # Helper to match a gem style version (ohai_version/chef_version) against a set of
709
+ # Gem::Dependency version constraints. If none are present, it always matches. if
710
+ # multiple are present, one must match. Returns false if none matches.
711
+ #
712
+ # @param what [String] the name of the constraint (e.g. 'chef' or 'ohai')
713
+ # @param version [String] the version to compare against the constraints
714
+ # @param deps [Array<Gem::Dependency>] Multiple Gem-style version constraints
715
+ # @return [Boolean] true if no constraints or a match, false if no match
716
+ def gem_dep_matches?(what, version, *deps)
717
+ # always match if we have no chef_version at all
718
+ return true unless deps.length > 0
719
+ # match if we match any of the chef_version lines
720
+ deps.any? { |dep| dep.match?(what, version) }
721
+ end
722
+
617
723
  def run_validation
618
724
  if name.nil?
619
725
  @errors = ["The `name' attribute is required in cookbook metadata"]