TrueCar-chef 0.10.0.beta.3

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