tylerrick-chef 0.10.8

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 (450) hide show
  1. data/LICENSE +201 -0
  2. data/README.rdoc +171 -0
  3. data/bin/chef-client +26 -0
  4. data/bin/chef-solo +25 -0
  5. data/bin/knife +26 -0
  6. data/bin/shef +34 -0
  7. data/distro/README +2 -0
  8. data/distro/arch/etc/conf.d/chef-client.conf +5 -0
  9. data/distro/arch/etc/conf.d/chef-expander.conf +8 -0
  10. data/distro/arch/etc/conf.d/chef-server-webui.conf +10 -0
  11. data/distro/arch/etc/conf.d/chef-server.conf +10 -0
  12. data/distro/arch/etc/conf.d/chef-solr.conf +8 -0
  13. data/distro/arch/etc/rc.d/chef-client +76 -0
  14. data/distro/arch/etc/rc.d/chef-expander +78 -0
  15. data/distro/arch/etc/rc.d/chef-server +78 -0
  16. data/distro/arch/etc/rc.d/chef-server-webui +78 -0
  17. data/distro/arch/etc/rc.d/chef-solr +78 -0
  18. data/distro/common/html/chef-client.8.html +146 -0
  19. data/distro/common/html/chef-expander.8.html +164 -0
  20. data/distro/common/html/chef-expanderctl.8.html +146 -0
  21. data/distro/common/html/chef-server-webui.8.html +185 -0
  22. data/distro/common/html/chef-server.8.html +182 -0
  23. data/distro/common/html/chef-solo.8.html +191 -0
  24. data/distro/common/html/chef-solr.8.html +165 -0
  25. data/distro/common/html/knife-bootstrap.1.html +243 -0
  26. data/distro/common/html/knife-client.1.html +219 -0
  27. data/distro/common/html/knife-configure.1.html +170 -0
  28. data/distro/common/html/knife-cookbook-site.1.html +241 -0
  29. data/distro/common/html/knife-cookbook.1.html +384 -0
  30. data/distro/common/html/knife-data-bag.1.html +234 -0
  31. data/distro/common/html/knife-environment.1.html +267 -0
  32. data/distro/common/html/knife-exec.1.html +134 -0
  33. data/distro/common/html/knife-index.1.html +125 -0
  34. data/distro/common/html/knife-node.1.html +250 -0
  35. data/distro/common/html/knife-recipe.1.html +92 -0
  36. data/distro/common/html/knife-role.1.html +200 -0
  37. data/distro/common/html/knife-search.1.html +288 -0
  38. data/distro/common/html/knife-ssh.1.html +157 -0
  39. data/distro/common/html/knife-status.1.html +128 -0
  40. data/distro/common/html/knife-tag.1.html +137 -0
  41. data/distro/common/html/knife.1.html +321 -0
  42. data/distro/common/html/shef.1.html +283 -0
  43. data/distro/common/man/man1/knife-bootstrap.1 +197 -0
  44. data/distro/common/man/man1/knife-client.1 +99 -0
  45. data/distro/common/man/man1/knife-configure.1 +88 -0
  46. data/distro/common/man/man1/knife-cookbook-site.1 +145 -0
  47. data/distro/common/man/man1/knife-cookbook.1 +345 -0
  48. data/distro/common/man/man1/knife-data-bag.1 +130 -0
  49. data/distro/common/man/man1/knife-environment.1 +178 -0
  50. data/distro/common/man/man1/knife-exec.1 +46 -0
  51. data/distro/common/man/man1/knife-index.1 +29 -0
  52. data/distro/common/man/man1/knife-node.1 +134 -0
  53. data/distro/common/man/man1/knife-role.1 +88 -0
  54. data/distro/common/man/man1/knife-search.1 +280 -0
  55. data/distro/common/man/man1/knife-ssh.1 +83 -0
  56. data/distro/common/man/man1/knife-status.1 +29 -0
  57. data/distro/common/man/man1/knife-tag.1 +43 -0
  58. data/distro/common/man/man1/knife.1 +292 -0
  59. data/distro/common/man/man1/shef.1 +256 -0
  60. data/distro/common/man/man8/chef-client.8 +104 -0
  61. data/distro/common/man/man8/chef-expander.8 +97 -0
  62. data/distro/common/man/man8/chef-expanderctl.8 +62 -0
  63. data/distro/common/man/man8/chef-server-webui.8 +155 -0
  64. data/distro/common/man/man8/chef-server.8 +147 -0
  65. data/distro/common/man/man8/chef-solo.8 +158 -0
  66. data/distro/common/man/man8/chef-solr.8 +122 -0
  67. data/distro/common/markdown/README +3 -0
  68. data/distro/common/markdown/man1/knife-bootstrap.mkd +142 -0
  69. data/distro/common/markdown/man1/knife-client.mkd +103 -0
  70. data/distro/common/markdown/man1/knife-configure.mkd +70 -0
  71. data/distro/common/markdown/man1/knife-cookbook-site.mkd +123 -0
  72. data/distro/common/markdown/man1/knife-cookbook.mkd +263 -0
  73. data/distro/common/markdown/man1/knife-data-bag.mkd +117 -0
  74. data/distro/common/markdown/man1/knife-environment.mkd +151 -0
  75. data/distro/common/markdown/man1/knife-exec.mkd +37 -0
  76. data/distro/common/markdown/man1/knife-index.mkd +30 -0
  77. data/distro/common/markdown/man1/knife-node.mkd +130 -0
  78. data/distro/common/markdown/man1/knife-role.mkd +85 -0
  79. data/distro/common/markdown/man1/knife-search.mkd +180 -0
  80. data/distro/common/markdown/man1/knife-ssh.mkd +72 -0
  81. data/distro/common/markdown/man1/knife-status.mkd +36 -0
  82. data/distro/common/markdown/man1/knife-tag.mkd +39 -0
  83. data/distro/common/markdown/man1/knife.mkd +220 -0
  84. data/distro/common/markdown/man1/shef.mkd +189 -0
  85. data/distro/common/markdown/man8/chef-client.mkd +75 -0
  86. data/distro/common/markdown/man8/chef-expander.mkd +82 -0
  87. data/distro/common/markdown/man8/chef-expanderctl.mkd +58 -0
  88. data/distro/common/markdown/man8/chef-server-webui.mkd +121 -0
  89. data/distro/common/markdown/man8/chef-server.mkd +121 -0
  90. data/distro/common/markdown/man8/chef-solo.mkd +107 -0
  91. data/distro/common/markdown/man8/chef-solr.mkd +89 -0
  92. data/distro/debian/etc/default/chef-client +4 -0
  93. data/distro/debian/etc/default/chef-expander +8 -0
  94. data/distro/debian/etc/default/chef-server +9 -0
  95. data/distro/debian/etc/default/chef-server-webui +9 -0
  96. data/distro/debian/etc/default/chef-solr +8 -0
  97. data/distro/debian/etc/init.d/chef-client +185 -0
  98. data/distro/debian/etc/init.d/chef-expander +176 -0
  99. data/distro/debian/etc/init.d/chef-server +122 -0
  100. data/distro/debian/etc/init.d/chef-server-webui +123 -0
  101. data/distro/debian/etc/init.d/chef-solr +176 -0
  102. data/distro/debian/etc/init/chef-client.conf +17 -0
  103. data/distro/debian/etc/init/chef-expander.conf +17 -0
  104. data/distro/debian/etc/init/chef-server-webui.conf +17 -0
  105. data/distro/debian/etc/init/chef-server.conf +17 -0
  106. data/distro/debian/etc/init/chef-solr.conf +17 -0
  107. data/distro/redhat/etc/init.d/chef-client +110 -0
  108. data/distro/redhat/etc/init.d/chef-expander +104 -0
  109. data/distro/redhat/etc/init.d/chef-server +112 -0
  110. data/distro/redhat/etc/init.d/chef-server-webui +112 -0
  111. data/distro/redhat/etc/init.d/chef-solr +104 -0
  112. data/distro/redhat/etc/logrotate.d/chef-client +8 -0
  113. data/distro/redhat/etc/logrotate.d/chef-expander +8 -0
  114. data/distro/redhat/etc/logrotate.d/chef-server +8 -0
  115. data/distro/redhat/etc/logrotate.d/chef-server-webui +8 -0
  116. data/distro/redhat/etc/logrotate.d/chef-solr +8 -0
  117. data/distro/redhat/etc/sysconfig/chef-client +15 -0
  118. data/distro/redhat/etc/sysconfig/chef-expander +7 -0
  119. data/distro/redhat/etc/sysconfig/chef-server +14 -0
  120. data/distro/redhat/etc/sysconfig/chef-server-webui +14 -0
  121. data/distro/redhat/etc/sysconfig/chef-solr +8 -0
  122. data/lib/chef.rb +41 -0
  123. data/lib/chef/api_client.rb +271 -0
  124. data/lib/chef/application.rb +152 -0
  125. data/lib/chef/application/agent.rb +18 -0
  126. data/lib/chef/application/client.rb +285 -0
  127. data/lib/chef/application/knife.rb +188 -0
  128. data/lib/chef/application/solo.rb +218 -0
  129. data/lib/chef/applications.rb +4 -0
  130. data/lib/chef/certificate.rb +194 -0
  131. data/lib/chef/checksum.rb +167 -0
  132. data/lib/chef/checksum/storage.rb +18 -0
  133. data/lib/chef/checksum/storage/filesystem.rb +56 -0
  134. data/lib/chef/checksum_cache.rb +189 -0
  135. data/lib/chef/client.rb +366 -0
  136. data/lib/chef/config.rb +248 -0
  137. data/lib/chef/cookbook/chefignore.rb +66 -0
  138. data/lib/chef/cookbook/cookbook_collection.rb +45 -0
  139. data/lib/chef/cookbook/cookbook_version_loader.rb +173 -0
  140. data/lib/chef/cookbook/file_system_file_vendor.rb +56 -0
  141. data/lib/chef/cookbook/file_vendor.rb +48 -0
  142. data/lib/chef/cookbook/metadata.rb +629 -0
  143. data/lib/chef/cookbook/remote_file_vendor.rb +88 -0
  144. data/lib/chef/cookbook/syntax_check.rb +136 -0
  145. data/lib/chef/cookbook_loader.rb +121 -0
  146. data/lib/chef/cookbook_site_streaming_uploader.rb +244 -0
  147. data/lib/chef/cookbook_uploader.rb +154 -0
  148. data/lib/chef/cookbook_version.rb +1096 -0
  149. data/lib/chef/cookbook_version_selector.rb +168 -0
  150. data/lib/chef/couchdb.rb +246 -0
  151. data/lib/chef/daemon.rb +172 -0
  152. data/lib/chef/data_bag.rb +235 -0
  153. data/lib/chef/data_bag_item.rb +273 -0
  154. data/lib/chef/encrypted_data_bag_item.rb +139 -0
  155. data/lib/chef/environment.rb +455 -0
  156. data/lib/chef/exceptions.rb +154 -0
  157. data/lib/chef/file_access_control.rb +144 -0
  158. data/lib/chef/file_cache.rb +218 -0
  159. data/lib/chef/handler.rb +227 -0
  160. data/lib/chef/handler/error_report.rb +33 -0
  161. data/lib/chef/handler/json_file.rb +58 -0
  162. data/lib/chef/index_queue.rb +29 -0
  163. data/lib/chef/index_queue/amqp_client.rb +116 -0
  164. data/lib/chef/index_queue/consumer.rb +76 -0
  165. data/lib/chef/index_queue/indexable.rb +109 -0
  166. data/lib/chef/json_compat.rb +52 -0
  167. data/lib/chef/knife.rb +520 -0
  168. data/lib/chef/knife/bootstrap.rb +207 -0
  169. data/lib/chef/knife/bootstrap/archlinux-gems.erb +60 -0
  170. data/lib/chef/knife/bootstrap/centos5-gems.erb +56 -0
  171. data/lib/chef/knife/bootstrap/fedora13-gems.erb +43 -0
  172. data/lib/chef/knife/bootstrap/ubuntu10.04-apt.erb +49 -0
  173. data/lib/chef/knife/bootstrap/ubuntu10.04-gems.erb +50 -0
  174. data/lib/chef/knife/client_bulk_delete.rb +65 -0
  175. data/lib/chef/knife/client_create.rb +79 -0
  176. data/lib/chef/knife/client_delete.rb +46 -0
  177. data/lib/chef/knife/client_edit.rb +45 -0
  178. data/lib/chef/knife/client_list.rb +42 -0
  179. data/lib/chef/knife/client_reregister.rb +58 -0
  180. data/lib/chef/knife/client_show.rb +52 -0
  181. data/lib/chef/knife/configure.rb +149 -0
  182. data/lib/chef/knife/configure_client.rb +50 -0
  183. data/lib/chef/knife/cookbook_bulk_delete.rb +72 -0
  184. data/lib/chef/knife/cookbook_create.rb +274 -0
  185. data/lib/chef/knife/cookbook_delete.rb +151 -0
  186. data/lib/chef/knife/cookbook_download.rb +137 -0
  187. data/lib/chef/knife/cookbook_list.rb +47 -0
  188. data/lib/chef/knife/cookbook_metadata.rb +107 -0
  189. data/lib/chef/knife/cookbook_metadata_from_file.rb +44 -0
  190. data/lib/chef/knife/cookbook_show.rb +102 -0
  191. data/lib/chef/knife/cookbook_site_download.rb +70 -0
  192. data/lib/chef/knife/cookbook_site_install.rb +148 -0
  193. data/lib/chef/knife/cookbook_site_list.rb +62 -0
  194. data/lib/chef/knife/cookbook_site_search.rb +51 -0
  195. data/lib/chef/knife/cookbook_site_share.rb +114 -0
  196. data/lib/chef/knife/cookbook_site_show.rb +57 -0
  197. data/lib/chef/knife/cookbook_site_unshare.rb +56 -0
  198. data/lib/chef/knife/cookbook_site_vendor.rb +46 -0
  199. data/lib/chef/knife/cookbook_test.rb +93 -0
  200. data/lib/chef/knife/cookbook_upload.rb +240 -0
  201. data/lib/chef/knife/core/bootstrap_context.rb +100 -0
  202. data/lib/chef/knife/core/cookbook_scm_repo.rb +148 -0
  203. data/lib/chef/knife/core/generic_presenter.rb +191 -0
  204. data/lib/chef/knife/core/node_editor.rb +127 -0
  205. data/lib/chef/knife/core/node_presenter.rb +136 -0
  206. data/lib/chef/knife/core/object_loader.rb +75 -0
  207. data/lib/chef/knife/core/subcommand_loader.rb +112 -0
  208. data/lib/chef/knife/core/text_formatter.rb +100 -0
  209. data/lib/chef/knife/core/ui.rb +216 -0
  210. data/lib/chef/knife/data_bag_create.rb +93 -0
  211. data/lib/chef/knife/data_bag_delete.rb +51 -0
  212. data/lib/chef/knife/data_bag_edit.rb +94 -0
  213. data/lib/chef/knife/data_bag_from_file.rb +87 -0
  214. data/lib/chef/knife/data_bag_list.rb +46 -0
  215. data/lib/chef/knife/data_bag_show.rb +81 -0
  216. data/lib/chef/knife/environment_create.rb +53 -0
  217. data/lib/chef/knife/environment_delete.rb +45 -0
  218. data/lib/chef/knife/environment_edit.rb +45 -0
  219. data/lib/chef/knife/environment_from_file.rb +50 -0
  220. data/lib/chef/knife/environment_list.rb +42 -0
  221. data/lib/chef/knife/environment_show.rb +46 -0
  222. data/lib/chef/knife/exec.rb +51 -0
  223. data/lib/chef/knife/help.rb +103 -0
  224. data/lib/chef/knife/help_topics.rb +4 -0
  225. data/lib/chef/knife/index_rebuild.rb +50 -0
  226. data/lib/chef/knife/node_bulk_delete.rb +80 -0
  227. data/lib/chef/knife/node_create.rb +50 -0
  228. data/lib/chef/knife/node_delete.rb +47 -0
  229. data/lib/chef/knife/node_edit.rb +72 -0
  230. data/lib/chef/knife/node_from_file.rb +50 -0
  231. data/lib/chef/knife/node_list.rb +46 -0
  232. data/lib/chef/knife/node_run_list_add.rb +67 -0
  233. data/lib/chef/knife/node_run_list_remove.rb +48 -0
  234. data/lib/chef/knife/node_show.rb +73 -0
  235. data/lib/chef/knife/recipe_list.rb +32 -0
  236. data/lib/chef/knife/role_bulk_delete.rb +70 -0
  237. data/lib/chef/knife/role_create.rb +55 -0
  238. data/lib/chef/knife/role_delete.rb +47 -0
  239. data/lib/chef/knife/role_edit.rb +48 -0
  240. data/lib/chef/knife/role_from_file.rb +56 -0
  241. data/lib/chef/knife/role_list.rb +43 -0
  242. data/lib/chef/knife/role_show.rb +54 -0
  243. data/lib/chef/knife/search.rb +141 -0
  244. data/lib/chef/knife/ssh.rb +367 -0
  245. data/lib/chef/knife/status.rb +104 -0
  246. data/lib/chef/knife/tag_create.rb +52 -0
  247. data/lib/chef/knife/tag_delete.rb +60 -0
  248. data/lib/chef/knife/tag_list.rb +47 -0
  249. data/lib/chef/log.rb +39 -0
  250. data/lib/chef/mash.rb +211 -0
  251. data/lib/chef/mixin/check_helper.rb +31 -0
  252. data/lib/chef/mixin/checksum.rb +32 -0
  253. data/lib/chef/mixin/command.rb +163 -0
  254. data/lib/chef/mixin/command/unix.rb +215 -0
  255. data/lib/chef/mixin/command/windows.rb +76 -0
  256. data/lib/chef/mixin/convert_to_class_name.rb +63 -0
  257. data/lib/chef/mixin/create_path.rb +57 -0
  258. data/lib/chef/mixin/deep_merge.rb +225 -0
  259. data/lib/chef/mixin/deprecation.rb +65 -0
  260. data/lib/chef/mixin/from_file.rb +50 -0
  261. data/lib/chef/mixin/get_source_from_package.rb +42 -0
  262. data/lib/chef/mixin/language.rb +163 -0
  263. data/lib/chef/mixin/language_include_attribute.rb +61 -0
  264. data/lib/chef/mixin/language_include_recipe.rb +52 -0
  265. data/lib/chef/mixin/params_validate.rb +225 -0
  266. data/lib/chef/mixin/recipe_definition_dsl_core.rb +78 -0
  267. data/lib/chef/mixin/shell_out.rb +41 -0
  268. data/lib/chef/mixin/template.rb +95 -0
  269. data/lib/chef/mixin/xml_escape.rb +140 -0
  270. data/lib/chef/mixins.rb +15 -0
  271. data/lib/chef/monkey_patches/dir.rb +36 -0
  272. data/lib/chef/monkey_patches/numeric.rb +15 -0
  273. data/lib/chef/monkey_patches/object.rb +9 -0
  274. data/lib/chef/monkey_patches/regexp.rb +34 -0
  275. data/lib/chef/monkey_patches/string.rb +49 -0
  276. data/lib/chef/monkey_patches/tempfile.rb +64 -0
  277. data/lib/chef/nil_argument.rb +3 -0
  278. data/lib/chef/node.rb +677 -0
  279. data/lib/chef/node/attribute.rb +487 -0
  280. data/lib/chef/openid_registration.rb +187 -0
  281. data/lib/chef/platform.rb +434 -0
  282. data/lib/chef/provider.rb +124 -0
  283. data/lib/chef/provider/breakpoint.rb +36 -0
  284. data/lib/chef/provider/cookbook_file.rb +100 -0
  285. data/lib/chef/provider/cron.rb +186 -0
  286. data/lib/chef/provider/cron/solaris.rb +195 -0
  287. data/lib/chef/provider/deploy.rb +418 -0
  288. data/lib/chef/provider/deploy/revision.rb +80 -0
  289. data/lib/chef/provider/deploy/timestamped.rb +33 -0
  290. data/lib/chef/provider/directory.rb +72 -0
  291. data/lib/chef/provider/env.rb +152 -0
  292. data/lib/chef/provider/env/windows.rb +75 -0
  293. data/lib/chef/provider/erl_call.rb +101 -0
  294. data/lib/chef/provider/execute.rb +65 -0
  295. data/lib/chef/provider/file.rb +226 -0
  296. data/lib/chef/provider/git.rb +243 -0
  297. data/lib/chef/provider/group.rb +133 -0
  298. data/lib/chef/provider/group/aix.rb +70 -0
  299. data/lib/chef/provider/group/dscl.rb +121 -0
  300. data/lib/chef/provider/group/gpasswd.rb +53 -0
  301. data/lib/chef/provider/group/groupadd.rb +91 -0
  302. data/lib/chef/provider/group/pw.rb +84 -0
  303. data/lib/chef/provider/group/suse.rb +53 -0
  304. data/lib/chef/provider/group/usermod.rb +57 -0
  305. data/lib/chef/provider/group/windows.rb +79 -0
  306. data/lib/chef/provider/http_request.rb +122 -0
  307. data/lib/chef/provider/ifconfig.rb +195 -0
  308. data/lib/chef/provider/link.rb +164 -0
  309. data/lib/chef/provider/log.rb +54 -0
  310. data/lib/chef/provider/mdadm.rb +91 -0
  311. data/lib/chef/provider/mount.rb +114 -0
  312. data/lib/chef/provider/mount/mount.rb +240 -0
  313. data/lib/chef/provider/mount/windows.rb +81 -0
  314. data/lib/chef/provider/ohai.rb +42 -0
  315. data/lib/chef/provider/package.rb +200 -0
  316. data/lib/chef/provider/package/apt.rb +145 -0
  317. data/lib/chef/provider/package/dpkg.rb +115 -0
  318. data/lib/chef/provider/package/easy_install.rb +136 -0
  319. data/lib/chef/provider/package/freebsd.rb +125 -0
  320. data/lib/chef/provider/package/macports.rb +105 -0
  321. data/lib/chef/provider/package/pacman.rb +101 -0
  322. data/lib/chef/provider/package/portage.rb +135 -0
  323. data/lib/chef/provider/package/rpm.rb +104 -0
  324. data/lib/chef/provider/package/rubygems.rb +465 -0
  325. data/lib/chef/provider/package/solaris.rb +130 -0
  326. data/lib/chef/provider/package/yum-dump.py +286 -0
  327. data/lib/chef/provider/package/yum.rb +1173 -0
  328. data/lib/chef/provider/package/zypper.rb +144 -0
  329. data/lib/chef/provider/remote_directory.rb +137 -0
  330. data/lib/chef/provider/remote_file.rb +118 -0
  331. data/lib/chef/provider/route.rb +193 -0
  332. data/lib/chef/provider/ruby_block.rb +34 -0
  333. data/lib/chef/provider/script.rb +55 -0
  334. data/lib/chef/provider/service.rb +122 -0
  335. data/lib/chef/provider/service/arch.rb +116 -0
  336. data/lib/chef/provider/service/debian.rb +130 -0
  337. data/lib/chef/provider/service/freebsd.rb +154 -0
  338. data/lib/chef/provider/service/gentoo.rb +53 -0
  339. data/lib/chef/provider/service/init.rb +71 -0
  340. data/lib/chef/provider/service/insserv.rb +52 -0
  341. data/lib/chef/provider/service/invokercd.rb +35 -0
  342. data/lib/chef/provider/service/redhat.rb +60 -0
  343. data/lib/chef/provider/service/simple.rb +120 -0
  344. data/lib/chef/provider/service/solaris.rb +85 -0
  345. data/lib/chef/provider/service/systemd.rb +102 -0
  346. data/lib/chef/provider/service/upstart.rb +198 -0
  347. data/lib/chef/provider/service/windows.rb +155 -0
  348. data/lib/chef/provider/subversion.rb +197 -0
  349. data/lib/chef/provider/template.rb +104 -0
  350. data/lib/chef/provider/user.rb +186 -0
  351. data/lib/chef/provider/user/dscl.rb +280 -0
  352. data/lib/chef/provider/user/pw.rb +113 -0
  353. data/lib/chef/provider/user/useradd.rb +137 -0
  354. data/lib/chef/provider/user/windows.rb +124 -0
  355. data/lib/chef/providers.rb +95 -0
  356. data/lib/chef/recipe.rb +129 -0
  357. data/lib/chef/resource.rb +609 -0
  358. data/lib/chef/resource/apt_package.rb +34 -0
  359. data/lib/chef/resource/bash.rb +33 -0
  360. data/lib/chef/resource/breakpoint.rb +35 -0
  361. data/lib/chef/resource/conditional.rb +90 -0
  362. data/lib/chef/resource/cookbook_file.rb +45 -0
  363. data/lib/chef/resource/cron.rb +188 -0
  364. data/lib/chef/resource/csh.rb +33 -0
  365. data/lib/chef/resource/deploy.rb +389 -0
  366. data/lib/chef/resource/deploy_revision.rb +40 -0
  367. data/lib/chef/resource/directory.rb +76 -0
  368. data/lib/chef/resource/dpkg_package.rb +34 -0
  369. data/lib/chef/resource/easy_install_package.rb +57 -0
  370. data/lib/chef/resource/env.rb +58 -0
  371. data/lib/chef/resource/erl_call.rb +83 -0
  372. data/lib/chef/resource/execute.rb +127 -0
  373. data/lib/chef/resource/file.rb +99 -0
  374. data/lib/chef/resource/freebsd_package.rb +35 -0
  375. data/lib/chef/resource/gem_package.rb +53 -0
  376. data/lib/chef/resource/git.rb +46 -0
  377. data/lib/chef/resource/group.rb +78 -0
  378. data/lib/chef/resource/http_request.rb +61 -0
  379. data/lib/chef/resource/ifconfig.rb +144 -0
  380. data/lib/chef/resource/link.rb +78 -0
  381. data/lib/chef/resource/log.rb +62 -0
  382. data/lib/chef/resource/macports_package.rb +29 -0
  383. data/lib/chef/resource/mdadm.rb +82 -0
  384. data/lib/chef/resource/mount.rb +134 -0
  385. data/lib/chef/resource/ohai.rb +40 -0
  386. data/lib/chef/resource/package.rb +80 -0
  387. data/lib/chef/resource/pacman_package.rb +33 -0
  388. data/lib/chef/resource/perl.rb +33 -0
  389. data/lib/chef/resource/portage_package.rb +33 -0
  390. data/lib/chef/resource/python.rb +33 -0
  391. data/lib/chef/resource/remote_directory.rb +109 -0
  392. data/lib/chef/resource/remote_file.rb +83 -0
  393. data/lib/chef/resource/route.rb +135 -0
  394. data/lib/chef/resource/rpm_package.rb +34 -0
  395. data/lib/chef/resource/ruby.rb +33 -0
  396. data/lib/chef/resource/ruby_block.rb +40 -0
  397. data/lib/chef/resource/scm.rb +147 -0
  398. data/lib/chef/resource/script.rb +60 -0
  399. data/lib/chef/resource/service.rb +151 -0
  400. data/lib/chef/resource/solaris_package.rb +36 -0
  401. data/lib/chef/resource/subversion.rb +36 -0
  402. data/lib/chef/resource/template.rb +69 -0
  403. data/lib/chef/resource/timestamped_deploy.rb +31 -0
  404. data/lib/chef/resource/user.rb +130 -0
  405. data/lib/chef/resource/yum_package.rb +63 -0
  406. data/lib/chef/resource_collection.rb +217 -0
  407. data/lib/chef/resource_collection/stepable_iterator.rb +124 -0
  408. data/lib/chef/resource_definition.rb +67 -0
  409. data/lib/chef/resource_definition_list.rb +38 -0
  410. data/lib/chef/resources.rb +64 -0
  411. data/lib/chef/rest.rb +400 -0
  412. data/lib/chef/rest/auth_credentials.rb +71 -0
  413. data/lib/chef/rest/cookie_jar.rb +31 -0
  414. data/lib/chef/rest/rest_request.rb +213 -0
  415. data/lib/chef/role.rb +343 -0
  416. data/lib/chef/run_context.rb +126 -0
  417. data/lib/chef/run_list.rb +165 -0
  418. data/lib/chef/run_list/run_list_expansion.rb +193 -0
  419. data/lib/chef/run_list/run_list_item.rb +92 -0
  420. data/lib/chef/run_list/versioned_recipe_list.rb +68 -0
  421. data/lib/chef/run_status.rb +121 -0
  422. data/lib/chef/runner.rb +105 -0
  423. data/lib/chef/sandbox.rb +153 -0
  424. data/lib/chef/search/query.rb +65 -0
  425. data/lib/chef/shef.rb +327 -0
  426. data/lib/chef/shef/ext.rb +569 -0
  427. data/lib/chef/shef/model_wrapper.rb +120 -0
  428. data/lib/chef/shef/shef_rest.rb +28 -0
  429. data/lib/chef/shef/shef_session.rb +285 -0
  430. data/lib/chef/shell_out.rb +249 -0
  431. data/lib/chef/shell_out/unix.rb +223 -0
  432. data/lib/chef/shell_out/windows.rb +588 -0
  433. data/lib/chef/solr_query.rb +187 -0
  434. data/lib/chef/solr_query/lucene.treetop +150 -0
  435. data/lib/chef/solr_query/lucene_nodes.rb +285 -0
  436. data/lib/chef/solr_query/query_transform.rb +65 -0
  437. data/lib/chef/solr_query/solr_http_request.rb +132 -0
  438. data/lib/chef/streaming_cookbook_uploader.rb +201 -0
  439. data/lib/chef/tasks/chef_repo.rake +334 -0
  440. data/lib/chef/util/file_edit.rb +122 -0
  441. data/lib/chef/util/windows.rb +56 -0
  442. data/lib/chef/util/windows/net_group.rb +101 -0
  443. data/lib/chef/util/windows/net_use.rb +121 -0
  444. data/lib/chef/util/windows/net_user.rb +198 -0
  445. data/lib/chef/util/windows/volume.rb +59 -0
  446. data/lib/chef/version.rb +23 -0
  447. data/lib/chef/version_class.rb +70 -0
  448. data/lib/chef/version_constraint.rb +116 -0
  449. data/lib/chef/webui_user.rb +231 -0
  450. metadata +764 -0
@@ -0,0 +1,154 @@
1
+
2
+ require 'set'
3
+ require 'rest_client'
4
+ require 'chef/exceptions'
5
+ require 'chef/knife/cookbook_metadata'
6
+ require 'chef/checksum_cache'
7
+ require 'chef/sandbox'
8
+ require 'chef/cookbook_version'
9
+ require 'chef/cookbook/syntax_check'
10
+ require 'chef/cookbook/file_system_file_vendor'
11
+
12
+ class Chef
13
+ class CookbookUploader
14
+
15
+ def self.work_queue
16
+ @work_queue ||= Queue.new
17
+ end
18
+
19
+ def self.setup_worker_threads
20
+ @worker_threads ||= begin
21
+ work_queue
22
+ (1...10).map do
23
+ Thread.new do
24
+ loop do
25
+ work_queue.pop.call
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ attr_reader :cookbook
33
+ attr_reader :path
34
+ attr_reader :opts
35
+ attr_reader :rest
36
+
37
+ # Creates a new CookbookUploader.
38
+ # ===Arguments:
39
+ # * cookbook::: A Chef::CookbookVersion describing the cookbook to be uploaded
40
+ # * path::: A String or Array of Strings representing the base paths to the
41
+ # cookbook repositories.
42
+ # * opts::: (optional) An options Hash
43
+ # ===Options:
44
+ # * :force indicates that the uploader should set the force option when
45
+ # uploading the cookbook. This allows frozen CookbookVersion
46
+ # documents on the server to be overwritten (otherwise a 409 is
47
+ # returned by the server)
48
+ # * :rest A Chef::REST object that you have configured the way you like it.
49
+ # If you don't provide this, one will be created using the values
50
+ # in Chef::Config.
51
+ def initialize(cookbook, path, opts={})
52
+ @cookbook, @path, @opts = cookbook, path, opts
53
+ @rest = opts[:rest] || Chef::REST.new(Chef::Config[:chef_server_url])
54
+ end
55
+
56
+ def upload_cookbook
57
+ Thread.abort_on_exception = true
58
+ Chef::Log.info("Saving #{cookbook.name}")
59
+
60
+ # Syntax Check
61
+ validate_cookbook
62
+ # generate checksums of cookbook files and create a sandbox
63
+ checksum_files = cookbook.checksums
64
+ checksums = checksum_files.inject({}){|memo,elt| memo[elt.first]=nil ; memo}
65
+ new_sandbox = rest.post_rest("sandboxes", { :checksums => checksums })
66
+
67
+ Chef::Log.info("Uploading files")
68
+
69
+ self.class.setup_worker_threads
70
+
71
+ checksums_to_upload = Set.new
72
+
73
+ # upload the new checksums and commit the sandbox
74
+ new_sandbox['checksums'].each do |checksum, info|
75
+ if info['needs_upload'] == true
76
+ checksums_to_upload << checksum
77
+ Chef::Log.info("Uploading #{checksum_files[checksum]} (checksum hex = #{checksum}) to #{info['url']}")
78
+ self.class.work_queue << uploader_function_for(checksum_files[checksum], checksum, info['url'], checksums_to_upload)
79
+ else
80
+ Chef::Log.debug("#{checksum_files[checksum]} has not changed")
81
+ end
82
+ end
83
+
84
+ until checksums_to_upload.empty?
85
+ sleep 0.1
86
+ end
87
+
88
+ sandbox_url = new_sandbox['uri']
89
+ Chef::Log.debug("Committing sandbox")
90
+ # Retry if S3 is claims a checksum doesn't exist (the eventual
91
+ # in eventual consistency)
92
+ retries = 0
93
+ begin
94
+ rest.put_rest(sandbox_url, {:is_completed => true})
95
+ rescue Net::HTTPServerException => e
96
+ if e.message =~ /^400/ && (retries += 1) <= 5
97
+ sleep 2
98
+ retry
99
+ else
100
+ raise
101
+ end
102
+ end
103
+
104
+ # files are uploaded, so save the manifest
105
+ save_url = opts[:force] ? cookbook.force_save_url : cookbook.save_url
106
+ rest.put_rest(save_url, cookbook)
107
+
108
+ Chef::Log.info("Upload complete!")
109
+ end
110
+
111
+ def worker_thread(work_queue)
112
+ end
113
+
114
+ def uploader_function_for(file, checksum, url, checksums_to_upload)
115
+ lambda do
116
+ # Checksum is the hexadecimal representation of the md5,
117
+ # but we need the base64 encoding for the content-md5
118
+ # header
119
+ checksum64 = Base64.encode64([checksum].pack("H*")).strip
120
+ timestamp = Time.now.utc.iso8601
121
+ file_contents = File.open(file, "rb") {|f| f.read}
122
+ # TODO - 5/28/2010, cw: make signing and sending the request streaming
123
+ sign_obj = Mixlib::Authentication::SignedHeaderAuth.signing_object(
124
+ :http_method => :put,
125
+ :path => URI.parse(url).path,
126
+ :body => file_contents,
127
+ :timestamp => timestamp,
128
+ :user_id => rest.client_name
129
+ )
130
+ headers = { 'content-type' => 'application/x-binary', 'content-md5' => checksum64, :accept => 'application/json' }
131
+ headers.merge!(sign_obj.sign(OpenSSL::PKey::RSA.new(rest.signing_key)))
132
+
133
+ begin
134
+ RestClient::Resource.new(url, :headers=>headers, :timeout=>1800, :open_timeout=>1800).put(file_contents)
135
+ checksums_to_upload.delete(checksum)
136
+ rescue RestClient::Exception => e
137
+ Chef::Knife.ui.error("Failed to upload #@cookbook : #{e.message}\n#{e.response.body}")
138
+ raise
139
+ end
140
+ end
141
+ end
142
+
143
+ def validate_cookbook
144
+ syntax_checker = Chef::Cookbook::SyntaxCheck.for_cookbook(cookbook.name, @user_cookbook_path)
145
+ Chef::Log.info("Validating ruby files")
146
+ exit(1) unless syntax_checker.validate_ruby_files
147
+ Chef::Log.info("Validating templates")
148
+ exit(1) unless syntax_checker.validate_templates
149
+ Chef::Log.info("Syntax OK")
150
+ true
151
+ end
152
+
153
+ end
154
+ end
@@ -0,0 +1,1096 @@
1
+ # Author:: Adam Jacob (<adam@opscode.com>)
2
+ # Author:: Nuo Yan (<nuo@opscode.com>)
3
+ # Author:: Christopher Walters (<cw@opscode.com>)
4
+ # Author:: Tim Hinderliter (<tim@opscode.com>)
5
+ # Author:: Seth Falcon (<seth@opscode.com>)
6
+ # Author:: Daniel DeLeo (<dan@opscode.com>)
7
+ # Copyright:: Copyright 2008-2011 Opscode, Inc.
8
+ # License:: Apache License, Version 2.0
9
+ #
10
+ # Licensed under the Apache License, Version 2.0 (the "License");
11
+ # you may not use this file except in compliance with the License.
12
+ # You may obtain a copy of the License at
13
+ #
14
+ # http://www.apache.org/licenses/LICENSE-2.0
15
+ #
16
+ # Unless required by applicable law or agreed to in writing, software
17
+ # distributed under the License is distributed on an "AS IS" BASIS,
18
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
+ # See the License for the specific language governing permissions and
20
+ # limitations under the License.
21
+
22
+ require 'chef/log'
23
+ require 'chef/client'
24
+ require 'chef/node'
25
+ require 'chef/resource_definition_list'
26
+ require 'chef/recipe'
27
+ require 'chef/cookbook/file_vendor'
28
+ require 'chef/checksum'
29
+ require 'chef/cookbook/metadata'
30
+ require 'chef/version_class'
31
+
32
+ class Chef
33
+
34
+ #== Chef::MinimalCookbookVersion
35
+ # MinimalCookbookVersion is a duck type of CookbookVersion, used
36
+ # internally by Chef Server as an optimization when determining the
37
+ # optimal cookbook set for a chef-client.
38
+ #
39
+ # MinimalCookbookVersion objects contain only enough information to
40
+ # solve the cookbook collection for a given run list. They *do not*
41
+ # contain enough information to generate the response.
42
+ #
43
+ # See also: Chef::CookbookVersionSelector
44
+ class MinimalCookbookVersion
45
+
46
+ include Comparable
47
+
48
+ ID = "id".freeze
49
+ NAME = 'name'.freeze
50
+ KEY = 'key'.freeze
51
+ VERSION = 'version'.freeze
52
+ VALUE = 'value'.freeze
53
+ DEPS = 'deps'.freeze
54
+
55
+ DEPENDENCIES = 'dependencies'.freeze
56
+
57
+ # Loads the full list of cookbooks, using a couchdb view to fetch
58
+ # only the id, name, version, and dependency constraints. This is
59
+ # enough information to solve for the cookbook collection for a
60
+ # given run list. After solving for the cookbook collection, you
61
+ # need to call +load_full_versions_of+ to convert
62
+ # MinimalCookbookVersion objects to their non-minimal counterparts
63
+ def self.load_all(couchdb)
64
+ # Example:
65
+ # {"id"=>"1a806f1c-b409-4d8e-abab-fa414ff5b96d", "key"=>"activemq", "value"=>{"version"=>"0.3.3", "deps"=>{"java"=>">= 0.0.0", "runit"=>">= 0.0.0"}}}
66
+ couchdb ||= Chef::CouchDB.new
67
+ couchdb.get_view("cookbooks", "all_with_version_and_deps")["rows"].map {|params| self.new(params) }
68
+ end
69
+
70
+ # Loads the non-minimal CookbookVersion objects corresponding to
71
+ # +minimal_cookbook_versions+ from couchdb using a bulk GET.
72
+ def self.load_full_versions_of(minimal_cookbook_versions, couchdb)
73
+ database_ids = Array(minimal_cookbook_versions).map {|mcv| mcv.couchdb_id }
74
+ couchdb ||= Chef::CouchDB.new
75
+ couchdb.bulk_get(*database_ids)
76
+ end
77
+
78
+ attr_reader :couchdb_id
79
+ attr_reader :name
80
+ attr_reader :version
81
+ attr_reader :deps
82
+
83
+ def initialize(params)
84
+ @couchdb_id = params[ID]
85
+ @name = params[KEY]
86
+ @version = params[VALUE][VERSION]
87
+ @deps = params[VALUE][DEPS]
88
+ end
89
+
90
+ # Returns the Cookbook::MinimalMetadata object for this cookbook
91
+ # version.
92
+ def metadata
93
+ @metadata ||= Cookbook::MinimalMetadata.new(@name, DEPENDENCIES => @deps)
94
+ end
95
+
96
+ def legit_version
97
+ @legit_version ||= Chef::Version.new(@version)
98
+ end
99
+
100
+ def <=>(o)
101
+ raise Chef::Exceptions::CookbookVersionNameMismatch if self.name != o.name
102
+ raise "Unexpected comparison to #{o}" unless o.respond_to?(:legit_version)
103
+ legit_version <=> o.legit_version
104
+ end
105
+ end
106
+
107
+ # == Chef::CookbookVersion
108
+ # CookbookVersion is a model object encapsulating the data about a Chef
109
+ # cookbook. Chef supports maintaining multiple versions of a cookbook on a
110
+ # single server; each version is represented by a distinct instance of this
111
+ # class.
112
+ #--
113
+ # TODO: timh/cw: 5-24-2010: mutators for files (e.g., recipe_filenames=,
114
+ # recipe_filenames.insert) should dirty the manifest so it gets regenerated.
115
+ class CookbookVersion
116
+ include Chef::IndexQueue::Indexable
117
+ include Comparable
118
+
119
+ COOKBOOK_SEGMENTS = [ :resources, :providers, :recipes, :definitions, :libraries, :attributes, :files, :templates, :root_files ]
120
+
121
+ DESIGN_DOCUMENT = {
122
+ "version" => 8,
123
+ "language" => "javascript",
124
+ "views" => {
125
+ "all" => {
126
+ "map" => <<-EOJS
127
+ function(doc) {
128
+ if (doc.chef_type == "cookbook_version") {
129
+ emit(doc.name, doc);
130
+ }
131
+ }
132
+ EOJS
133
+ },
134
+ "all_id" => {
135
+ "map" => <<-EOJS
136
+ function(doc) {
137
+ if (doc.chef_type == "cookbook_version") {
138
+ emit(doc.name, doc.name);
139
+ }
140
+ }
141
+ EOJS
142
+ },
143
+ "all_with_version" => {
144
+ "map" => <<-EOJS
145
+ function(doc) {
146
+ if (doc.chef_type == "cookbook_version") {
147
+ emit(doc.cookbook_name, doc.version);
148
+ }
149
+ }
150
+ EOJS
151
+ },
152
+ "all_with_version_and_deps" => {
153
+ "map" => <<-JS
154
+ function(doc) {
155
+ if (doc.chef_type == "cookbook_version") {
156
+ emit(doc.cookbook_name, {version: doc.version, deps: doc.metadata.dependencies});
157
+ }
158
+ }
159
+ JS
160
+ },
161
+ "all_latest_version" => {
162
+ "map" => %q@
163
+ function(doc) {
164
+ if (doc.chef_type == "cookbook_version") {
165
+ emit(doc.cookbook_name, doc.version);
166
+ }
167
+ }
168
+ @,
169
+ "reduce" => %q@
170
+ function(keys, values, rereduce) {
171
+ var result = null;
172
+
173
+ for (var idx in values) {
174
+ var value = values[idx];
175
+
176
+ if (idx == 0) {
177
+ result = value;
178
+ continue;
179
+ }
180
+
181
+ var valueParts = value.split('.').map(function(v) { return parseInt(v); });
182
+ var resultParts = result.split('.').map(function(v) { return parseInt(v); });
183
+
184
+ if (valueParts[0] != resultParts[0]) {
185
+ if (valueParts[0] > resultParts[0]) {
186
+ result = value;
187
+ }
188
+ }
189
+ else if (valueParts[1] != resultParts[1]) {
190
+ if (valueParts[1] > resultParts[1]) {
191
+ result = value;
192
+ }
193
+ }
194
+ else if (valueParts[2] != resultParts[2]) {
195
+ if (valueParts[2] > resultParts[2]) {
196
+ result = value;
197
+ }
198
+ }
199
+ }
200
+ return result;
201
+ }
202
+ @
203
+ },
204
+ "all_latest_version_by_id" => {
205
+ "map" => %q@
206
+ function(doc) {
207
+ if (doc.chef_type == "cookbook_version") {
208
+ emit(doc.cookbook_name, {version: doc.version, id:doc._id});
209
+ }
210
+ }
211
+ @,
212
+ "reduce" => %q@
213
+ function(keys, values, rereduce) {
214
+ var result = null;
215
+
216
+ for (var idx in values) {
217
+ var value = values[idx];
218
+
219
+ if (idx == 0) {
220
+ result = value;
221
+ continue;
222
+ }
223
+
224
+ var valueParts = value.version.split('.').map(function(v) { return parseInt(v); });
225
+ var resultParts = result.version.split('.').map(function(v) { return parseInt(v); });
226
+
227
+ if (valueParts[0] != resultParts[0]) {
228
+ if (valueParts[0] > resultParts[0]) {
229
+ result = value;
230
+ }
231
+ }
232
+ else if (valueParts[1] != resultParts[1]) {
233
+ if (valueParts[1] > resultParts[1]) {
234
+ result = value;
235
+ }
236
+ }
237
+ else if (valueParts[2] != resultParts[2]) {
238
+ if (valueParts[2] > resultParts[2]) {
239
+ result = value;
240
+ }
241
+ }
242
+ }
243
+ return result;
244
+ }
245
+ @
246
+ },
247
+ }
248
+ }
249
+
250
+ attr_accessor :root_dir
251
+ attr_accessor :definition_filenames
252
+ attr_accessor :template_filenames
253
+ attr_accessor :file_filenames
254
+ attr_accessor :library_filenames
255
+ attr_accessor :resource_filenames
256
+ attr_accessor :provider_filenames
257
+ attr_accessor :root_filenames
258
+ attr_accessor :name
259
+ attr_accessor :metadata
260
+ attr_accessor :metadata_filenames
261
+ attr_accessor :status
262
+ attr_accessor :couchdb_rev
263
+ attr_accessor :couchdb
264
+
265
+ attr_reader :couchdb_id
266
+
267
+ # attribute_filenames also has a setter that has non-default
268
+ # functionality.
269
+ attr_reader :attribute_filenames
270
+
271
+ # recipe_filenames also has a setter that has non-default
272
+ # functionality.
273
+ attr_reader :recipe_filenames
274
+
275
+ attr_reader :recipe_filenames_by_name
276
+ attr_reader :attribute_filenames_by_short_filename
277
+
278
+ # This is the one and only method that knows how cookbook files'
279
+ # checksums are generated.
280
+ def self.checksum_cookbook_file(filepath)
281
+ Chef::ChecksumCache.generate_md5_checksum_for_file(filepath)
282
+ rescue Errno::ENOENT
283
+ Chef::Log.debug("File #{filepath} does not exist, so there is no checksum to generate")
284
+ nil
285
+ end
286
+
287
+ # Keep track of the filenames that we use in both eager cookbook
288
+ # downloading (during sync_cookbooks) and lazy (during the run
289
+ # itself, through FileVendor). After the run is over, clean up the
290
+ # cache.
291
+ def self.valid_cache_entries
292
+ @valid_cache_entries ||= {}
293
+ end
294
+
295
+ def self.reset_cache_validity
296
+ @valid_cache_entries = nil
297
+ end
298
+
299
+ def self.cache
300
+ Chef::FileCache
301
+ end
302
+
303
+ # Setup a notification to clear the valid_cache_entries when a Chef client
304
+ # run starts
305
+ Chef::Client.when_run_starts do |run_status|
306
+ reset_cache_validity
307
+ end
308
+
309
+ # Synchronizes all the cookbooks from the chef-server.
310
+ #
311
+ # === Returns
312
+ # true:: Always returns true
313
+ def self.sync_cookbooks(cookbook_hash)
314
+ Chef::Log.info("Loading cookbooks [#{cookbook_hash.keys.sort.join(', ')}]")
315
+ Chef::Log.debug("Cookbooks detail: #{cookbook_hash.inspect}")
316
+
317
+ clear_obsoleted_cookbooks(cookbook_hash)
318
+
319
+ # Synchronize each of the node's cookbooks, and add to the
320
+ # valid_cache_entries hash.
321
+ cookbook_hash.values.each do |cookbook|
322
+ sync_cookbook_file_cache(cookbook)
323
+ end
324
+
325
+ true
326
+ end
327
+
328
+ # Iterates over cached cookbooks' files, removing files belonging to
329
+ # cookbooks that don't appear in +cookbook_hash+
330
+ def self.clear_obsoleted_cookbooks(cookbook_hash)
331
+ # Remove all cookbooks no longer relevant to this node
332
+ cache.find(File.join(%w{cookbooks ** *})).each do |cache_file|
333
+ cache_file =~ /^cookbooks\/([^\/]+)\//
334
+ unless cookbook_hash.has_key?($1)
335
+ Chef::Log.info("Removing #{cache_file} from the cache; its cookbook is no longer needed on this client.")
336
+ cache.delete(cache_file)
337
+ end
338
+ end
339
+ end
340
+
341
+ # Update the file caches for a given cache segment. Takes a segment name
342
+ # and a hash that matches one of the cookbooks/_attribute_files style
343
+ # remote file listings.
344
+ #
345
+ # === Parameters
346
+ # cookbook<Chef::Cookbook>:: The cookbook to update
347
+ # valid_cache_entries<Hash>:: Out-param; Added to this hash are the files that
348
+ # were referred to by this cookbook
349
+ def self.sync_cookbook_file_cache(cookbook)
350
+ Chef::Log.debug("Synchronizing cookbook #{cookbook.name}")
351
+
352
+ # files and templates are lazily loaded, and will be done later.
353
+ eager_segments = COOKBOOK_SEGMENTS.dup
354
+ eager_segments.delete(:files)
355
+ eager_segments.delete(:templates)
356
+
357
+ eager_segments.each do |segment|
358
+ segment_filenames = Array.new
359
+ cookbook.manifest[segment].each do |manifest_record|
360
+ # segment = cookbook segment
361
+ # remote_list = list of file hashes
362
+ #
363
+ # We need the list of known good attribute files, so we can delete any that are
364
+ # just laying about.
365
+
366
+ cache_filename = File.join("cookbooks", cookbook.name, manifest_record['path'])
367
+ valid_cache_entries[cache_filename] = true
368
+
369
+ current_checksum = nil
370
+ if cache.has_key?(cache_filename)
371
+ current_checksum = checksum_cookbook_file(cache.load(cache_filename, false))
372
+ end
373
+
374
+ # If the checksums are different between on-disk (current) and on-server
375
+ # (remote, per manifest), do the update. This will also execute if there
376
+ # is no current checksum.
377
+ if current_checksum != manifest_record['checksum']
378
+ raw_file = chef_server_rest.get_rest(manifest_record[:url], true)
379
+
380
+ Chef::Log.info("Storing updated #{cache_filename} in the cache.")
381
+ cache.move_to(raw_file.path, cache_filename)
382
+ else
383
+ Chef::Log.debug("Not storing #{cache_filename}, as the cache is up to date.")
384
+ end
385
+
386
+ # make the segment filenames a full path.
387
+ full_path_cache_filename = cache.load(cache_filename, false)
388
+ segment_filenames << full_path_cache_filename
389
+ end
390
+
391
+ # replace segment filenames with a full-path one.
392
+ if segment.to_sym == :recipes
393
+ cookbook.recipe_filenames = segment_filenames
394
+ elsif segment.to_sym == :attributes
395
+ cookbook.attribute_filenames = segment_filenames
396
+ else
397
+ cookbook.segment_filenames(segment).replace(segment_filenames)
398
+ end
399
+ end
400
+ end
401
+
402
+ def self.cleanup_file_cache
403
+ unless Chef::Config[:solo]
404
+ # Delete each file in the cache that we didn't encounter in the
405
+ # manifest.
406
+ cache.find(File.join(%w{cookbooks ** *})).each do |cache_filename|
407
+ unless valid_cache_entries[cache_filename]
408
+ Chef::Log.info("Removing #{cache_filename} from the cache; it is no longer needed by chef-client.")
409
+ cache.delete(cache_filename)
410
+ end
411
+ end
412
+ end
413
+ end
414
+
415
+ # Register a notification to cleanup unused files from cookbooks
416
+ Chef::Client.when_run_completes_successfully do |run_status|
417
+ cleanup_file_cache
418
+ end
419
+
420
+ # Creates a new Chef::CookbookVersion object.
421
+ #
422
+ # === Returns
423
+ # object<Chef::CookbookVersion>:: Duh. :)
424
+ def initialize(name, couchdb=nil)
425
+ @name = name
426
+ @frozen = false
427
+ @attribute_filenames = Array.new
428
+ @definition_filenames = Array.new
429
+ @template_filenames = Array.new
430
+ @file_filenames = Array.new
431
+ @recipe_filenames = Array.new
432
+ @recipe_filenames_by_name = Hash.new
433
+ @library_filenames = Array.new
434
+ @resource_filenames = Array.new
435
+ @provider_filenames = Array.new
436
+ @metadata_filenames = Array.new
437
+ @root_dir = nil
438
+ @root_filenames = Array.new
439
+ @couchdb_id = nil
440
+ @couchdb = couchdb || Chef::CouchDB.new
441
+ @couchdb_rev = nil
442
+ @status = :ready
443
+ @manifest = nil
444
+ @file_vendor = nil
445
+ @metadata = Chef::Cookbook::Metadata.new
446
+ end
447
+
448
+ def version
449
+ metadata.version
450
+ end
451
+
452
+ # Indicates if this version is frozen or not. Freezing a coobkook version
453
+ # indicates that a new cookbook with the same name and version number
454
+ # shoule
455
+ def frozen_version?
456
+ @frozen
457
+ end
458
+
459
+ def freeze_version
460
+ @frozen = true
461
+ end
462
+
463
+ def version=(new_version)
464
+ manifest["version"] = new_version
465
+ metadata.version(new_version)
466
+ end
467
+
468
+ # A manifest is a Mash that maps segment names to arrays of manifest
469
+ # records (see #preferred_manifest_record for format of manifest records),
470
+ # as well as describing cookbook metadata. The manifest follows a form
471
+ # like the following:
472
+ #
473
+ # {
474
+ # :cookbook_name = "apache2",
475
+ # :version = "1.0",
476
+ # :name = "Apache 2"
477
+ # :metadata = ???TODO: timh/cw: 5-24-2010: describe this format,
478
+ #
479
+ # :files => [
480
+ # {
481
+ # :name => "afile.rb",
482
+ # :path => "files/ubuntu-9.10/afile.rb",
483
+ # :checksum => "2222",
484
+ # :specificity => "ubuntu-9.10"
485
+ # },
486
+ # ],
487
+ # :templates => [ manifest_record1, ... ],
488
+ # ...
489
+ # }
490
+ def manifest
491
+ unless @manifest
492
+ generate_manifest
493
+ end
494
+ @manifest
495
+ end
496
+
497
+ def manifest=(new_manifest)
498
+ @manifest = Mash.new new_manifest
499
+ @checksums = extract_checksums_from_manifest(@manifest)
500
+ @manifest_records_by_path = extract_manifest_records_by_path(@manifest)
501
+
502
+ COOKBOOK_SEGMENTS.each do |segment|
503
+ next unless @manifest.has_key?(segment)
504
+ filenames = @manifest[segment].map{|manifest_record| manifest_record['name']}
505
+
506
+ if segment == :recipes
507
+ self.recipe_filenames = filenames
508
+ elsif segment == :attributes
509
+ self.attribute_filenames = filenames
510
+ else
511
+ segment_filenames(segment).clear
512
+ filenames.each { |filename| segment_filenames(segment) << filename }
513
+ end
514
+ end
515
+ end
516
+
517
+ # Returns a hash of checksums to either nil or the on disk path (which is
518
+ # done by generate_manifest).
519
+ def checksums
520
+ unless @checksums
521
+ generate_manifest
522
+ end
523
+ @checksums
524
+ end
525
+
526
+ def manifest_records_by_path
527
+ @manifest_records_by_path || generate_manifest
528
+ @manifest_records_by_path
529
+ end
530
+
531
+ def full_name
532
+ "#{name}-#{version}"
533
+ end
534
+
535
+ def attribute_filenames=(*filenames)
536
+ @attribute_filenames = filenames.flatten
537
+ @attribute_filenames_by_short_filename = filenames_by_name(attribute_filenames)
538
+ attribute_filenames
539
+ end
540
+
541
+ ## BACKCOMPAT/DEPRECATED - Remove these and fix breakage before release [DAN - 5/20/2010]##
542
+ alias :attribute_files :attribute_filenames
543
+ alias :attribute_files= :attribute_filenames=
544
+
545
+ # Return recipe names in the form of cookbook_name::recipe_name
546
+ def fully_qualified_recipe_names
547
+ results = Array.new
548
+ recipe_filenames_by_name.each_key do |rname|
549
+ results << "#{name}::#{rname}"
550
+ end
551
+ results
552
+ end
553
+
554
+ def recipe_filenames=(*filenames)
555
+ @recipe_filenames = filenames.flatten
556
+ @recipe_filenames_by_name = filenames_by_name(recipe_filenames)
557
+ recipe_filenames
558
+ end
559
+
560
+ ## BACKCOMPAT/DEPRECATED - Remove these and fix breakage before release [DAN - 5/20/2010]##
561
+ alias :recipe_files :recipe_filenames
562
+ alias :recipe_files= :recipe_filenames=
563
+
564
+ # called from DSL
565
+ def load_recipe(recipe_name, run_context)
566
+ unless recipe_filenames_by_name.has_key?(recipe_name)
567
+ raise ArgumentError, "Cannot find a recipe matching #{recipe_name} in cookbook #{name}"
568
+ end
569
+
570
+ Chef::Log.debug("Found recipe #{recipe_name} in cookbook #{name}")
571
+ recipe = Chef::Recipe.new(name, recipe_name, run_context)
572
+ recipe_filename = recipe_filenames_by_name[recipe_name]
573
+
574
+ unless recipe_filename
575
+ raise Chef::Exceptions::RecipeNotFound, "could not find recipe #{recipe_name} for cookbook #{name}"
576
+ end
577
+
578
+ recipe.from_file(recipe_filename)
579
+ recipe
580
+ end
581
+
582
+ def segment_filenames(segment)
583
+ unless COOKBOOK_SEGMENTS.include?(segment)
584
+ raise ArgumentError, "invalid segment #{segment}: must be one of #{COOKBOOK_SEGMENTS.join(', ')}"
585
+ end
586
+
587
+ case segment.to_sym
588
+ when :resources
589
+ @resource_filenames
590
+ when :providers
591
+ @provider_filenames
592
+ when :recipes
593
+ @recipe_filenames
594
+ when :libraries
595
+ @library_filenames
596
+ when :definitions
597
+ @definition_filenames
598
+ when :attributes
599
+ @attribute_filenames
600
+ when :files
601
+ @file_filenames
602
+ when :templates
603
+ @template_filenames
604
+ when :root_files
605
+ @root_filenames
606
+ end
607
+ end
608
+
609
+ # Determine the most specific manifest record for the given
610
+ # segment/filename, given information in the node. Throws
611
+ # FileNotFound if there is no such segment and filename in the
612
+ # manifest.
613
+ #
614
+ # A manifest record is a Mash that follows the following form:
615
+ # {
616
+ # :name => "example.rb",
617
+ # :path => "files/default/example.rb",
618
+ # :specificity => "default",
619
+ # :checksum => "1234"
620
+ # }
621
+ def preferred_manifest_record(node, segment, filename)
622
+ preferences = preferences_for_path(node, segment, filename)
623
+
624
+ # ensure that we generate the manifest, which will also generate
625
+ # @manifest_records_by_path
626
+ manifest
627
+
628
+ # in order of prefernce, look for the filename in the manifest
629
+ found_pref = preferences.find {|preferred_filename| @manifest_records_by_path[preferred_filename] }
630
+ if found_pref
631
+ @manifest_records_by_path[found_pref]
632
+ else
633
+ if segment == :files || segment == :templates
634
+ error_message = "Cookbook '#{name}' (#{version}) does not contain a file at any of these locations:\n"
635
+ error_locations = [
636
+ " #{segment}/#{node[:platform]}-#{node[:platform_version]}/#{filename}",
637
+ " #{segment}/#{node[:platform]}/#{filename}",
638
+ " #{segment}/default/#{filename}",
639
+ ]
640
+ error_message << error_locations.join("\n")
641
+ existing_files = segment_filenames(segment)
642
+ # Show the files that the cookbook does have. If the user made a typo,
643
+ # hopefully they'll see it here.
644
+ unless existing_files.empty?
645
+ error_message << "\n\nThis cookbook _does_ contain: ['#{existing_files.join("','")}']"
646
+ end
647
+ raise Chef::Exceptions::FileNotFound, error_message
648
+ else
649
+ raise Chef::Exceptions::FileNotFound, "cookbook #{name} does not contain file #{segment}/#{filename}"
650
+ end
651
+ end
652
+ end
653
+
654
+ def preferred_filename_on_disk_location(node, segment, filename, current_filepath=nil)
655
+ manifest_record = preferred_manifest_record(node, segment, filename)
656
+ if current_filepath && (manifest_record['checksum'] == self.class.checksum_cookbook_file(current_filepath))
657
+ nil
658
+ else
659
+ file_vendor.get_filename(manifest_record['path'])
660
+ end
661
+ end
662
+
663
+ def relative_filenames_in_preferred_directory(node, segment, dirname)
664
+ preferences = preferences_for_path(node, segment, dirname)
665
+ filenames_by_pref = Hash.new
666
+ preferences.each { |pref| filenames_by_pref[pref] = Array.new }
667
+
668
+ manifest[segment].each do |manifest_record|
669
+ manifest_record_path = manifest_record[:path]
670
+
671
+ # find the NON SPECIFIC filenames, but prefer them by filespecificity.
672
+ # For example, if we have a file:
673
+ # 'files/default/somedir/somefile.conf' we only keep
674
+ # 'somedir/somefile.conf'. If there is also
675
+ # 'files/$hostspecific/somedir/otherfiles' that matches the requested
676
+ # hostname specificity, that directory will win, as it is more specific.
677
+ #
678
+ # This is clearly ugly b/c the use case is for remote directory, where
679
+ # we're just going to make cookbook_files out of these and make the
680
+ # cookbook find them by filespecificity again. but it's the shortest
681
+ # path to "success" for now.
682
+ if manifest_record_path =~ /(#{Regexp.escape(segment.to_s)}\/[^\/]+\/#{Regexp.escape(dirname)})\/.+$/
683
+ specificity_dirname = $1
684
+ non_specific_path = manifest_record_path[/#{Regexp.escape(segment.to_s)}\/[^\/]+\/#{Regexp.escape(dirname)}\/(.+)$/, 1]
685
+ # Record the specificity_dirname only if it's in the list of
686
+ # valid preferences
687
+ if filenames_by_pref[specificity_dirname]
688
+ filenames_by_pref[specificity_dirname] << non_specific_path
689
+ end
690
+ end
691
+ end
692
+
693
+ best_pref = preferences.find { |pref| !filenames_by_pref[pref].empty? }
694
+
695
+ raise Chef::Exceptions::FileNotFound, "cookbook #{name} has no directory #{segment}/default/#{dirname}" unless best_pref
696
+
697
+ filenames_by_pref[best_pref]
698
+
699
+ end
700
+
701
+ # Determine the manifest records from the most specific directory
702
+ # for the given node. See #preferred_manifest_record for a
703
+ # description of entries of the returned Array.
704
+ def preferred_manifest_records_for_directory(node, segment, dirname)
705
+ preferences = preferences_for_path(node, segment, dirname)
706
+ records_by_pref = Hash.new
707
+ preferences.each { |pref| records_by_pref[pref] = Array.new }
708
+
709
+ manifest[segment].each do |manifest_record|
710
+ manifest_record_path = manifest_record[:path]
711
+
712
+ # extract the preference part from the path.
713
+ if manifest_record_path =~ /(#{Regexp.escape(segment.to_s)}\/[^\/]+\/#{Regexp.escape(dirname)})\/.+$/
714
+ # Note the specificy_dirname includes the segment and
715
+ # dirname argument as above, which is what
716
+ # preferences_for_path returns. It could be
717
+ # "files/ubuntu-9.10/dirname", for example.
718
+ specificity_dirname = $1
719
+
720
+ # Record the specificity_dirname only if it's in the list of
721
+ # valid preferences
722
+ if records_by_pref[specificity_dirname]
723
+ records_by_pref[specificity_dirname] << manifest_record
724
+ end
725
+ end
726
+ end
727
+
728
+ best_pref = preferences.find { |pref| !records_by_pref[pref].empty? }
729
+
730
+ raise Chef::Exceptions::FileNotFound, "cookbook #{name} (#{version}) has no directory #{segment}/default/#{dirname}" unless best_pref
731
+
732
+ records_by_pref[best_pref]
733
+ end
734
+
735
+
736
+ # Given a node, segment and path (filename or directory name),
737
+ # return the priority-ordered list of preference locations to
738
+ # look.
739
+ def preferences_for_path(node, segment, path)
740
+ # only files and templates can be platform-specific
741
+ if segment.to_sym == :files || segment.to_sym == :templates
742
+ begin
743
+ platform, version = Chef::Platform.find_platform_and_version(node)
744
+ rescue ArgumentError => e
745
+ # Skip platform/version if they were not found by find_platform_and_version
746
+ if e.message =~ /Cannot find a (?:platform|version)/
747
+ platform = "/unknown_platform/"
748
+ version = "/unknown_platform_version/"
749
+ else
750
+ raise
751
+ end
752
+ end
753
+
754
+ fqdn = node[:fqdn]
755
+
756
+ # Break version into components, eg: "5.7.1" => [ "5.7.1", "5.7", "5" ]
757
+ search_versions = []
758
+ parts = version.to_s.split('.')
759
+
760
+ parts.size.times do
761
+ search_versions << parts.join('.')
762
+ parts.pop
763
+ end
764
+
765
+ # Most specific to least specific places to find the path
766
+ search_path = [ File.join(segment.to_s, "host-#{fqdn}", path) ]
767
+ search_versions.each do |v|
768
+ search_path << File.join(segment.to_s, "#{platform}-#{v}", path)
769
+ end
770
+ search_path << File.join(segment.to_s, platform.to_s, path)
771
+ search_path << File.join(segment.to_s, "default", path)
772
+
773
+ search_path
774
+ else
775
+ [File.join(segment, path)]
776
+ end
777
+ end
778
+ private :preferences_for_path
779
+
780
+ def to_hash
781
+ result = manifest.dup
782
+ result['frozen?'] = frozen_version?
783
+ result['chef_type'] = 'cookbook_version'
784
+ result["_rev"] = couchdb_rev if couchdb_rev
785
+ result.to_hash
786
+ end
787
+
788
+ def to_json(*a)
789
+ result = self.to_hash
790
+ result['json_class'] = self.class.name
791
+ result.to_json(*a)
792
+ end
793
+
794
+ def self.json_create(o)
795
+ cookbook_version = new(o["cookbook_name"])
796
+ if o.has_key?('_rev')
797
+ cookbook_version.couchdb_rev = o["_rev"] if o.has_key?("_rev")
798
+ o.delete("_rev")
799
+ end
800
+ if o.has_key?("_id")
801
+ cookbook_version.couchdb_id = o["_id"] if o.has_key?("_id")
802
+ cookbook_version.index_id = cookbook_version.couchdb_id
803
+ o.delete("_id")
804
+ end
805
+ # We want the Chef::Cookbook::Metadata object to always be inflated
806
+ cookbook_version.metadata = Chef::Cookbook::Metadata.from_hash(o["metadata"])
807
+ cookbook_version.manifest = o
808
+
809
+ # We don't need the following step when we decide to stop supporting deprecated operators in the metadata (e.g. <<, >>)
810
+ cookbook_version.manifest["metadata"] = JSON.parse(cookbook_version.metadata.to_json)
811
+
812
+ cookbook_version.freeze_version if o["frozen?"]
813
+ cookbook_version
814
+ end
815
+
816
+ def generate_manifest_with_urls(&url_generator)
817
+ rendered_manifest = manifest.dup
818
+ COOKBOOK_SEGMENTS.each do |segment|
819
+ if rendered_manifest.has_key?(segment)
820
+ rendered_manifest[segment].each do |manifest_record|
821
+ url_options = { :cookbook_name => name.to_s, :cookbook_version => version, :checksum => manifest_record["checksum"] }
822
+ manifest_record["url"] = url_generator.call(url_options)
823
+ end
824
+ end
825
+ end
826
+ rendered_manifest
827
+ end
828
+
829
+ def metadata_json_file
830
+ File.join(root_dir, "metadata.json")
831
+ end
832
+
833
+ def metadata_rb_file
834
+ File.join(root_dir, "metadata.rb")
835
+ end
836
+
837
+ def reload_metadata!
838
+ if File.exists?(metadata_json_file)
839
+ metadata.from_json(IO.read(metadata_json_file))
840
+ end
841
+ end
842
+
843
+ ##
844
+ # REST API
845
+ ##
846
+ def self.chef_server_rest
847
+ Chef::REST.new(Chef::Config[:chef_server_url])
848
+ end
849
+
850
+ def chef_server_rest
851
+ self.class.chef_server_rest
852
+ end
853
+
854
+ # Return the URL to save (PUT) this object to the server via the
855
+ # REST api. If there is an existing document on the server and it
856
+ # is marked frozen, a PUT will result in a 409 Conflict.
857
+ def save_url
858
+ "cookbooks/#{name}/#{version}"
859
+ end
860
+
861
+ # Adds the `force=true` parameter to the upload URL. This allows
862
+ # the user to overwrite a frozen cookbook (a PUT against the
863
+ # normal #save_url raises a 409 Conflict in this case).
864
+ def force_save_url
865
+ "cookbooks/#{name}/#{version}?force=true"
866
+ end
867
+
868
+ def destroy
869
+ chef_server_rest.delete_rest("cookbooks/#{name}/#{version}")
870
+ self
871
+ end
872
+
873
+ def self.load(name, version="_latest")
874
+ version = "_latest" if version == "latest"
875
+ chef_server_rest.get_rest("cookbooks/#{name}/#{version}")
876
+ end
877
+
878
+ def self.list
879
+ chef_server_rest.get_rest('cookbooks')
880
+ end
881
+
882
+ ##
883
+ # Given a +cookbook_name+, get a list of all versions that exist on the
884
+ # server.
885
+ # ===Returns
886
+ # [String]:: Array of cookbook versions, which are strings like 'x.y.z'
887
+ # nil:: if the cookbook doesn't exist. an error will also be logged.
888
+ def self.available_versions(cookbook_name)
889
+ chef_server_rest.get_rest("cookbooks/#{cookbook_name}")[cookbook_name]["versions"].map do |cb|
890
+ cb["version"]
891
+ end
892
+ rescue Net::HTTPServerException => e
893
+ if e.to_s =~ /^404/
894
+ Chef::Log.error("Cannot find a cookbook named #{cookbook_name}")
895
+ nil
896
+ else
897
+ raise
898
+ end
899
+ end
900
+
901
+ # Get the newest version of all cookbooks
902
+ def self.latest_cookbooks
903
+ chef_server_rest.get_rest('cookbooks/_latest')
904
+ end
905
+
906
+ ##
907
+ # Couchdb
908
+ ##
909
+
910
+ def self.cdb_by_name(cookbook_name, couchdb=nil)
911
+ cdb = (couchdb || Chef::CouchDB.new)
912
+ options = { :startkey => cookbook_name, :endkey => cookbook_name }
913
+ rs = cdb.get_view("cookbooks", "all_with_version", options)
914
+ rs["rows"].inject({}) { |memo, row| memo.has_key?(row["key"]) ? memo[row["key"]] << row["value"] : memo[row["key"]] = [ row["value"] ]; memo }
915
+ end
916
+
917
+ def self.create_design_document(couchdb=nil)
918
+ (couchdb || Chef::CouchDB.new).create_design_document("cookbooks", DESIGN_DOCUMENT)
919
+ end
920
+
921
+ def self.cdb_list_latest(inflate=false, couchdb=nil)
922
+ couchdb ||= Chef::CouchDB.new
923
+ if inflate
924
+ doc_ids = cdb_list_latest_ids.map {|i|i["id"]}
925
+ couchdb.bulk_get(doc_ids)
926
+ else
927
+ results = couchdb.get_view("cookbooks", "all_latest_version", :group=>true)["rows"]
928
+ results.inject({}) { |mapped, row| mapped[row["key"]] = row["value"]; mapped}
929
+ end
930
+ end
931
+
932
+ def self.cdb_list_latest_ids(inflate=false, couchdb=nil)
933
+ couchdb ||= Chef::CouchDB.new
934
+ results = couchdb.get_view("cookbooks", "all_latest_version_by_id", :group=>true)["rows"]
935
+ results.map { |name_and_id| name_and_id["value"]}
936
+ end
937
+
938
+ def self.cdb_list(inflate=false, couchdb=nil)
939
+ couchdb ||= Chef::CouchDB.new
940
+ if inflate
941
+ couchdb.list("cookbooks", true)["rows"].collect{|r| r["value"]}
942
+ else
943
+ # If you modify this, please make sure the desc sorted order on the versions doesn't get broken.
944
+ couchdb.get_view("cookbooks", "all_with_version")["rows"].inject({}) { |mapped, row| mapped[row["key"]]||=Array.new; mapped[row["key"]].push(Chef::Version.new(row["value"])); mapped[row["key"]].sort!.reverse!; mapped}
945
+ end
946
+ end
947
+
948
+ def self.cdb_load(name, version='latest', couchdb=nil)
949
+ cdb = couchdb || Chef::CouchDB.new
950
+ if version == "latest" || version == "_latest"
951
+ rs = cdb.get_view("cookbooks", "all_latest_version", :key => name, :descending => true, :group => true, :reduce => true)["rows"].first
952
+ cdb.load("cookbook_version", "#{rs["key"]}-#{rs["value"]}")
953
+ else
954
+ cdb.load("cookbook_version", "#{name}-#{version}")
955
+ end
956
+ end
957
+
958
+ def cdb_destroy
959
+ (couchdb || Chef::CouchDB.new).delete("cookbook_version", full_name, couchdb_rev)
960
+ end
961
+
962
+ # Runs on Chef Server (API); deletes the cookbook from couchdb and also destroys associated
963
+ # checksum documents
964
+ def purge
965
+ checksums.keys.each do |checksum|
966
+ begin
967
+ Chef::Checksum.cdb_load(checksum, couchdb).purge
968
+ rescue Chef::Exceptions::CouchDBNotFound
969
+ end
970
+ end
971
+ cdb_destroy
972
+ end
973
+
974
+ def cdb_save
975
+ @couchdb_rev = couchdb.store("cookbook_version", full_name, self)["rev"]
976
+ end
977
+
978
+ def couchdb_id=(value)
979
+ @couchdb_id = value
980
+ @index_id = value
981
+ end
982
+
983
+ def <=>(o)
984
+ raise Chef::Exceptions::CookbookVersionNameMismatch if self.name != o.name
985
+ # FIXME: can we change the interface to the Metadata class such
986
+ # that metadata.version returns a Chef::Version instance instead
987
+ # of a string?
988
+ Chef::Version.new(self.version) <=> Chef::Version.new(o.version)
989
+ end
990
+
991
+ private
992
+
993
+ # For each filename, produce a mapping of base filename (i.e. recipe name
994
+ # or attribute file) to on disk location
995
+ def filenames_by_name(filenames)
996
+ filenames.select{|filename| filename =~ /\.rb$/}.inject({}){|memo, filename| memo[File.basename(filename, '.rb')] = filename ; memo }
997
+ end
998
+
999
+ # See #manifest for a description of the manifest return value.
1000
+ # See #preferred_manifest_record for a description an individual manifest record.
1001
+ def generate_manifest
1002
+ manifest = Mash.new({
1003
+ :recipes => Array.new,
1004
+ :definitions => Array.new,
1005
+ :libraries => Array.new,
1006
+ :attributes => Array.new,
1007
+ :files => Array.new,
1008
+ :templates => Array.new,
1009
+ :resources => Array.new,
1010
+ :providers => Array.new,
1011
+ :root_files => Array.new
1012
+ })
1013
+ checksums_to_on_disk_paths = {}
1014
+
1015
+ COOKBOOK_SEGMENTS.each do |segment|
1016
+ segment_filenames(segment).each do |segment_file|
1017
+ next if File.directory?(segment_file)
1018
+
1019
+ file_name = nil
1020
+ path = nil
1021
+ specificity = "default"
1022
+
1023
+ if segment == :root_files
1024
+ matcher = segment_file.match(".+/#{Regexp.escape(name.to_s)}/(.+)")
1025
+ file_name = matcher[1]
1026
+ path = file_name
1027
+ elsif segment == :templates || segment == :files
1028
+ matcher = segment_file.match("/#{Regexp.escape(name.to_s)}/(#{Regexp.escape(segment.to_s)}/(.+?)/(.+))")
1029
+ unless matcher
1030
+ Chef::Log.debug("Skipping file #{segment_file}, as it isn't in any of the proper directories (platform-version, platform or default)")
1031
+ Chef::Log.debug("You probably need to move #{segment_file} into the 'default' sub-directory")
1032
+ next
1033
+ end
1034
+ path = matcher[1]
1035
+ specificity = matcher[2]
1036
+ file_name = matcher[3]
1037
+ else
1038
+ matcher = segment_file.match("/#{Regexp.escape(name.to_s)}/(#{Regexp.escape(segment.to_s)}/(.+))")
1039
+ path = matcher[1]
1040
+ file_name = matcher[2]
1041
+ end
1042
+
1043
+ csum = self.class.checksum_cookbook_file(segment_file)
1044
+ checksums_to_on_disk_paths[csum] = segment_file
1045
+ rs = Mash.new({
1046
+ :name => file_name,
1047
+ :path => path,
1048
+ :checksum => csum
1049
+ })
1050
+ rs[:specificity] = specificity
1051
+
1052
+ manifest[segment] << rs
1053
+ end
1054
+ end
1055
+
1056
+ manifest[:cookbook_name] = name.to_s
1057
+ manifest[:metadata] = metadata
1058
+ manifest[:version] = metadata.version
1059
+ manifest[:name] = full_name
1060
+
1061
+ @checksums = checksums_to_on_disk_paths
1062
+ @manifest = manifest
1063
+ @manifest_records_by_path = extract_manifest_records_by_path(manifest)
1064
+ end
1065
+
1066
+ def file_vendor
1067
+ unless @file_vendor
1068
+ @file_vendor = Chef::Cookbook::FileVendor.create_from_manifest(manifest)
1069
+ end
1070
+ @file_vendor
1071
+ end
1072
+
1073
+ def extract_checksums_from_manifest(manifest)
1074
+ checksums = {}
1075
+ COOKBOOK_SEGMENTS.each do |segment|
1076
+ next unless manifest.has_key?(segment)
1077
+ manifest[segment].each do |manifest_record|
1078
+ checksums[manifest_record[:checksum]] = nil
1079
+ end
1080
+ end
1081
+ checksums
1082
+ end
1083
+
1084
+ def extract_manifest_records_by_path(manifest)
1085
+ manifest_records_by_path = {}
1086
+ COOKBOOK_SEGMENTS.each do |segment|
1087
+ next unless manifest.has_key?(segment)
1088
+ manifest[segment].each do |manifest_record|
1089
+ manifest_records_by_path[manifest_record[:path]] = manifest_record
1090
+ end
1091
+ end
1092
+ manifest_records_by_path
1093
+ end
1094
+
1095
+ end
1096
+ end