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,130 @@
1
+ #
2
+ # Author:: Toomas Pelberg (<toomasp@gmx.net>)
3
+ # Copyright:: Copyright (c) 2010 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ require 'chef/provider/package'
19
+ require 'chef/mixin/command'
20
+ require 'chef/resource/package'
21
+ require 'chef/mixin/get_source_from_package'
22
+
23
+ class Chef
24
+ class Provider
25
+ class Package
26
+ class Solaris < Chef::Provider::Package
27
+
28
+ include Chef::Mixin::GetSourceFromPackage
29
+
30
+ # def initialize(*args)
31
+ # super
32
+ # @current_resource = Chef::Resource::Package.new(@new_resource.name)
33
+ # end
34
+
35
+ def load_current_resource
36
+ @current_resource = Chef::Resource::Package.new(@new_resource.name)
37
+ @current_resource.package_name(@new_resource.package_name)
38
+ @new_resource.version(nil)
39
+
40
+ if @new_resource.source
41
+ unless ::File.exists?(@new_resource.source)
42
+ raise Chef::Exceptions::Package, "Package #{@new_resource.name} not found: #{@new_resource.source}"
43
+ end
44
+
45
+ Chef::Log.debug("#{@new_resource} checking pkg status")
46
+ status = popen4("pkginfo -l -d #{@new_resource.source} #{@new_resource.package_name}") do |pid, stdin, stdout, stderr|
47
+ stdout.each do |line|
48
+ case line
49
+ when /VERSION:\s+(.+)/
50
+ @new_resource.version($1)
51
+ end
52
+ end
53
+ end
54
+ elsif Array(@new_resource.action).include?(:install)
55
+ raise Chef::Exceptions::Package, "Source for package #{@new_resource.name} required for action install"
56
+ end
57
+
58
+ Chef::Log.debug("#{@new_resource} checking install state")
59
+ status = popen4("pkginfo -l #{@current_resource.package_name}") do |pid, stdin, stdout, stderr|
60
+ stdout.each do |line|
61
+ case line
62
+ when /VERSION:\s+(.+)/
63
+ Chef::Log.debug("#{@new_resource} version #{$1} is already installed")
64
+ @current_resource.version($1)
65
+ end
66
+ end
67
+ end
68
+
69
+ unless status.exitstatus == 0 || status.exitstatus == 1
70
+ raise Chef::Exceptions::Package, "pkginfo failed - #{status.inspect}!"
71
+ end
72
+
73
+ unless @current_resource.version.nil?
74
+ @current_resource.version(nil)
75
+ end
76
+
77
+ @current_resource
78
+ end
79
+
80
+ def candidate_version
81
+ return @candidate_version if @candidate_version
82
+ status = popen4("pkginfo -l -d #{@new_resource.source} #{new_resource.package_name}") do |pid, stdin, stdout, stderr|
83
+ stdout.each_line do |line|
84
+ case line
85
+ when /VERSION:\s+(.+)/
86
+ @candidate_version = $1
87
+ @new_resource.version($1)
88
+ Chef::Log.debug("#{@new_resource} setting install candidate version to #{@candidate_version}")
89
+ end
90
+ end
91
+ end
92
+ unless status.exitstatus == 0
93
+ raise Chef::Exceptions::Package, "pkginfo -l -d #{@new_resource.source} - #{status.inspect}!"
94
+ end
95
+ @candidate_version
96
+ end
97
+
98
+ def install_package(name, version)
99
+ Chef::Log.debug("#{@new_resource} package install options: #{@new_resource.options}")
100
+ if @new_resource.options.nil?
101
+ run_command_with_systems_locale(
102
+ :command => "pkgadd -n -d #{@new_resource.source} all"
103
+ )
104
+ Chef::Log.debug("#{@new_resource} installed version #{@new_resource.version} from: #{@new_resource.source}")
105
+ else
106
+ run_command_with_systems_locale(
107
+ :command => "pkgadd -n#{expand_options(@new_resource.options)} -d #{@new_resource.source} all"
108
+ )
109
+ Chef::Log.debug("#{@new_resource} installed version #{@new_resource.version} from: #{@new_resource.source}")
110
+ end
111
+ end
112
+
113
+ def remove_package(name, version)
114
+ if @new_resource.options.nil?
115
+ run_command_with_systems_locale(
116
+ :command => "pkgrm -n #{name}"
117
+ )
118
+ Chef::Log.debug("#{@new_resource} removed version #{@new_resource.version}")
119
+ else
120
+ run_command_with_systems_locale(
121
+ :command => "pkgrm -n#{expand_options(@new_resource.options)} #{name}"
122
+ )
123
+ Chef::Log.debug("#{@new_resource} removed version #{@new_resource.version}")
124
+ end
125
+ end
126
+
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,286 @@
1
+ #
2
+ # Author:: Matthew Kent (<mkent@magoazul.com>)
3
+ # Copyright:: Copyright (c) 2009, 2011 Matthew Kent
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ # yum-dump.py
20
+ # Inspired by yumhelper.py by David Lutterkort
21
+ #
22
+ # Produce a list of installed, available and re-installable packages using yum
23
+ # and dump the results to stdout.
24
+ #
25
+ # yum-dump invokes yum similarly to the command line interface which makes it
26
+ # subject to most of the configuration paramaters in yum.conf. yum-dump will
27
+ # also load yum plugins in the same manor as yum - these can affect the output.
28
+ #
29
+ # Can be run as non root, but that won't update the cache.
30
+ #
31
+ # Intended to support yum 2.x and 3.x
32
+
33
+ import os
34
+ import sys
35
+ import time
36
+ import yum
37
+ import re
38
+ import errno
39
+
40
+ from yum import Errors
41
+ from optparse import OptionParser
42
+ from distutils import version
43
+
44
+ YUM_PID_FILE='/var/run/yum.pid'
45
+
46
+ # Seconds to wait for exclusive access to yum
47
+ LOCK_TIMEOUT = 10
48
+
49
+ YUM_VER = version.StrictVersion(yum.__version__)
50
+ YUM_MAJOR = YUM_VER.version[0]
51
+
52
+ if YUM_MAJOR > 3 or YUM_MAJOR < 2:
53
+ print >> sys.stderr, "yum-dump Error: Can't match supported yum version" \
54
+ " (%s)" % yum.__version__
55
+ sys.exit(1)
56
+
57
+ # Required for Provides output
58
+ if YUM_MAJOR == 2:
59
+ import rpm
60
+ import rpmUtils.miscutils
61
+
62
+ def setup(yb, options):
63
+ # Only want our output
64
+ #
65
+ if YUM_MAJOR == 3:
66
+ try:
67
+ if YUM_VER >= version.StrictVersion("3.2.22"):
68
+ yb.preconf.errorlevel=0
69
+ yb.preconf.debuglevel=0
70
+
71
+ # initialize the config
72
+ yb.conf
73
+ else:
74
+ yb.doConfigSetup(errorlevel=0, debuglevel=0)
75
+ except yum.Errors.ConfigError, e:
76
+ # supresses an ignored exception at exit
77
+ yb.preconf = None
78
+ print >> sys.stderr, "yum-dump Config Error: %s" % e
79
+ return 1
80
+ except ValueError, e:
81
+ yb.preconf = None
82
+ print >> sys.stderr, "yum-dump Options Error: %s" % e
83
+ return 1
84
+ elif YUM_MAJOR == 2:
85
+ yb.doConfigSetup()
86
+
87
+ def __log(a,b): pass
88
+
89
+ yb.log = __log
90
+ yb.errorlog = __log
91
+
92
+ # Give Chef every possible package version, it can decide what to do with them
93
+ if YUM_MAJOR == 3:
94
+ yb.conf.showdupesfromrepos = True
95
+ elif YUM_MAJOR == 2:
96
+ yb.conf.setConfigOption('showdupesfromrepos', True)
97
+
98
+ # Optionally run only on cached repositories, but non root must use the cache
99
+ if os.geteuid() != 0:
100
+ if YUM_MAJOR == 3:
101
+ yb.conf.cache = True
102
+ elif YUM_MAJOR == 2:
103
+ yb.conf.setConfigOption('cache', True)
104
+ else:
105
+ if YUM_MAJOR == 3:
106
+ yb.conf.cache = options.cache
107
+ elif YUM_MAJOR == 2:
108
+ yb.conf.setConfigOption('cache', options.cache)
109
+
110
+ return 0
111
+
112
+ def dump_packages(yb, list, output_provides):
113
+ packages = {}
114
+
115
+ if YUM_MAJOR == 2:
116
+ yb.doTsSetup()
117
+ yb.doRepoSetup()
118
+ yb.doSackSetup()
119
+
120
+ db = yb.doPackageLists(list)
121
+
122
+ for pkg in db.installed:
123
+ pkg.type = 'i'
124
+ packages[str(pkg)] = pkg
125
+
126
+ if YUM_VER >= version.StrictVersion("3.2.21"):
127
+ for pkg in db.available:
128
+ pkg.type = 'a'
129
+ packages[str(pkg)] = pkg
130
+
131
+ # These are both installed and available
132
+ for pkg in db.reinstall_available:
133
+ pkg.type = 'r'
134
+ packages[str(pkg)] = pkg
135
+ else:
136
+ # Old style method - no reinstall list
137
+ for pkg in yb.pkgSack.returnPackages():
138
+
139
+ if str(pkg) in packages:
140
+ if packages[str(pkg)].type == "i":
141
+ packages[str(pkg)].type = 'r'
142
+ continue
143
+
144
+ pkg.type = 'a'
145
+ packages[str(pkg)] = pkg
146
+
147
+ unique_packages = packages.values()
148
+
149
+ unique_packages.sort(lambda x, y: cmp(x.name, y.name))
150
+
151
+ for pkg in unique_packages:
152
+ if output_provides == "all" or \
153
+ (output_provides == "installed" and (pkg.type == "i" or pkg.type == "r")):
154
+
155
+ # yum 2 doesn't have provides_print, implement it ourselves using methods
156
+ # based on requires gathering in packages.py
157
+ if YUM_MAJOR == 2:
158
+ provlist = []
159
+
160
+ # Installed and available are gathered in different ways
161
+ if pkg.type == 'i' or pkg.type == 'r':
162
+ names = pkg.hdr[rpm.RPMTAG_PROVIDENAME]
163
+ flags = pkg.hdr[rpm.RPMTAG_PROVIDEFLAGS]
164
+ ver = pkg.hdr[rpm.RPMTAG_PROVIDEVERSION]
165
+ if names is not None:
166
+ tmplst = zip(names, flags, ver)
167
+
168
+ for (n, f, v) in tmplst:
169
+ prov = rpmUtils.miscutils.formatRequire(n, v, f)
170
+ provlist.append(prov)
171
+ # This is slow :(
172
+ elif pkg.type == 'a':
173
+ for prcoTuple in pkg.returnPrco('provides'):
174
+ prcostr = pkg.prcoPrintable(prcoTuple)
175
+ provlist.append(prcostr)
176
+
177
+ provides = provlist
178
+ else:
179
+ provides = pkg.provides_print
180
+ else:
181
+ provides = "[]"
182
+
183
+ print '%s %s %s %s %s %s %s' % (
184
+ pkg.name,
185
+ pkg.epoch,
186
+ pkg.version,
187
+ pkg.release,
188
+ pkg.arch,
189
+ provides,
190
+ pkg.type )
191
+
192
+ return 0
193
+
194
+ def yum_dump(options):
195
+ lock_obtained = False
196
+
197
+ yb = yum.YumBase()
198
+
199
+ status = setup(yb, options)
200
+ if status != 0:
201
+ return status
202
+
203
+ if options.output_options:
204
+ print "[option installonlypkgs] %s" % " ".join(yb.conf.installonlypkgs)
205
+
206
+ # Non root can't handle locking on rhel/centos 4
207
+ if os.geteuid() != 0:
208
+ return dump_packages(yb, options.package_list, options.output_provides)
209
+
210
+ # Wrap the collection and output of packages in yum's global lock to prevent
211
+ # any inconsistencies.
212
+ try:
213
+ # Spin up to LOCK_TIMEOUT
214
+ countdown = LOCK_TIMEOUT
215
+ while True:
216
+ try:
217
+ yb.doLock(YUM_PID_FILE)
218
+ lock_obtained = True
219
+ except Errors.LockError, e:
220
+ time.sleep(1)
221
+ countdown -= 1
222
+ if countdown == 0:
223
+ print >> sys.stderr, "yum-dump Locking Error! Couldn't obtain an " \
224
+ "exclusive yum lock in %d seconds. Giving up." % LOCK_TIMEOUT
225
+ return 200
226
+ else:
227
+ break
228
+
229
+ return dump_packages(yb, options.package_list, options.output_provides)
230
+
231
+ # Ensure we clear the lock and cleanup any resources
232
+ finally:
233
+ try:
234
+ yb.closeRpmDB()
235
+ if lock_obtained == True:
236
+ yb.doUnlock(YUM_PID_FILE)
237
+ except Errors.LockError, e:
238
+ print >> sys.stderr, "yum-dump Unlock Error: %s" % e
239
+ return 200
240
+
241
+ def main():
242
+ usage = "Usage: %prog [options]\n" + \
243
+ "Output a list of installed, available and re-installable packages via yum"
244
+ parser = OptionParser(usage=usage)
245
+ parser.add_option("-C", "--cache",
246
+ action="store_true", dest="cache", default=False,
247
+ help="run entirely from cache, don't update cache")
248
+ parser.add_option("-o", "--options",
249
+ action="store_true", dest="output_options", default=False,
250
+ help="output select yum options useful to Chef")
251
+ parser.add_option("-p", "--installed-provides",
252
+ action="store_const", const="installed", dest="output_provides", default="none",
253
+ help="output Provides for installed packages, big/wide output")
254
+ parser.add_option("-P", "--all-provides",
255
+ action="store_const", const="all", dest="output_provides", default="none",
256
+ help="output Provides for all package, slow, big/wide output")
257
+ parser.add_option("-i", "--installed",
258
+ action="store_const", const="installed", dest="package_list", default="all",
259
+ help="output only installed packages")
260
+ parser.add_option("-a", "--available",
261
+ action="store_const", const="available", dest="package_list", default="all",
262
+ help="output only available and re-installable packages")
263
+
264
+ (options, args) = parser.parse_args()
265
+
266
+ try:
267
+ return yum_dump(options)
268
+
269
+ except yum.Errors.RepoError, e:
270
+ print >> sys.stderr, "yum-dump Repository Error: %s" % e
271
+ return 1
272
+
273
+ except yum.Errors.YumBaseError, e:
274
+ print >> sys.stderr, "yum-dump General Error: %s" % e
275
+ return 1
276
+
277
+ try:
278
+ status = main()
279
+ # Suppress a nasty broken pipe error when output is piped to utilities like 'head'
280
+ except IOError, e:
281
+ if e.errno == errno.EPIPE:
282
+ sys.exit(1)
283
+ else:
284
+ raise
285
+
286
+ sys.exit(status)
@@ -0,0 +1,1173 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Copyright:: Copyright (c) 2008 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef/provider/package'
20
+ require 'chef/mixin/command'
21
+ require 'chef/resource/package'
22
+ require 'singleton'
23
+ require 'chef/mixin/get_source_from_package'
24
+
25
+
26
+ class Chef
27
+ class Provider
28
+ class Package
29
+ class Yum < Chef::Provider::Package
30
+
31
+ class RPMUtils
32
+ class << self
33
+
34
+ # RPM::Version version_parse equivalent
35
+ def version_parse(evr)
36
+ return if evr.nil?
37
+
38
+ epoch = nil
39
+ # assume this is a version
40
+ version = evr
41
+ release = nil
42
+
43
+ lead = 0
44
+ tail = evr.size
45
+
46
+ if evr =~ %r{^([\d]+):}
47
+ epoch = $1.to_i
48
+ lead = $1.length + 1
49
+ elsif evr[0].ord == ":".ord
50
+ epoch = 0
51
+ lead = 1
52
+ end
53
+
54
+ if evr =~ %r{:?.*-(.*)$}
55
+ release = $1
56
+ tail = evr.length - release.length - lead - 1
57
+
58
+ if release.empty?
59
+ release = nil
60
+ end
61
+ end
62
+
63
+ version = evr[lead,tail]
64
+ if version.empty?
65
+ version = nil
66
+ end
67
+
68
+ [ epoch, version, release ]
69
+ end
70
+
71
+ # verify
72
+ def isalnum(x)
73
+ isalpha(x) or isdigit(x)
74
+ end
75
+
76
+ def isalpha(x)
77
+ v = x.ord
78
+ (v >= 65 and v <= 90) or (v >= 97 and v <= 122)
79
+ end
80
+
81
+ def isdigit(x)
82
+ v = x.ord
83
+ v >= 48 and v <= 57
84
+ end
85
+
86
+ # based on the reference spec in lib/rpmvercmp.c in rpm 4.9.0
87
+ def rpmvercmp(x, y)
88
+ # easy! :)
89
+ return 0 if x == y
90
+
91
+ if x.nil?
92
+ x = ""
93
+ end
94
+
95
+ if y.nil?
96
+ y = ""
97
+ end
98
+
99
+ # not so easy :(
100
+ #
101
+ # takes 2 strings like
102
+ #
103
+ # x = "1.20.b18.el5"
104
+ # y = "1.20.b17.el5"
105
+ #
106
+ # breaks into purely alpha and numeric segments and compares them using
107
+ # some rules
108
+ #
109
+ # * 10 > 1
110
+ # * 1 > a
111
+ # * z > a
112
+ # * Z > A
113
+ # * z > Z
114
+ # * leading zeros are ignored
115
+ # * separators (periods, commas) are ignored
116
+ # * "1.20.b18.el5.extrastuff" > "1.20.b18.el5"
117
+
118
+ x_pos = 0 # overall string element reference position
119
+ x_pos_max = x.length - 1 # number of elements in string, starting from 0
120
+ x_seg_pos = 0 # segment string element reference position
121
+ x_comp = nil # segment to compare
122
+
123
+ y_pos = 0
124
+ y_seg_pos = 0
125
+ y_pos_max = y.length - 1
126
+ y_comp = nil
127
+
128
+ while (x_pos <= x_pos_max and y_pos <= y_pos_max)
129
+ # first we skip over anything non alphanumeric
130
+ while (x_pos <= x_pos_max) and (isalnum(x[x_pos]) == false)
131
+ x_pos += 1 # +1 over pos_max if end of string
132
+ end
133
+ while (y_pos <= y_pos_max) and (isalnum(y[y_pos]) == false)
134
+ y_pos += 1
135
+ end
136
+
137
+ # if we hit the end of either we are done matching segments
138
+ if (x_pos == x_pos_max + 1) or (y_pos == y_pos_max + 1)
139
+ break
140
+ end
141
+
142
+ # we are now at the start of a alpha or numeric segment
143
+ x_seg_pos = x_pos
144
+ y_seg_pos = y_pos
145
+
146
+ # grab segment so we can compare them
147
+ if isdigit(x[x_seg_pos].ord)
148
+ x_seg_is_num = true
149
+
150
+ # already know it's a digit
151
+ x_seg_pos += 1
152
+
153
+ # gather up our digits
154
+ while (x_seg_pos <= x_pos_max) and isdigit(x[x_seg_pos])
155
+ x_seg_pos += 1
156
+ end
157
+ # copy the segment but not the unmatched character that x_seg_pos will
158
+ # refer to
159
+ x_comp = x[x_pos,x_seg_pos - x_pos]
160
+
161
+ while (y_seg_pos <= y_pos_max) and isdigit(y[y_seg_pos])
162
+ y_seg_pos += 1
163
+ end
164
+ y_comp = y[y_pos,y_seg_pos - y_pos]
165
+ else
166
+ # we are comparing strings
167
+ x_seg_is_num = false
168
+
169
+ while (x_seg_pos <= x_pos_max) and isalpha(x[x_seg_pos])
170
+ x_seg_pos += 1
171
+ end
172
+ x_comp = x[x_pos,x_seg_pos - x_pos]
173
+
174
+ while (y_seg_pos <= y_pos_max) and isalpha(y[y_seg_pos])
175
+ y_seg_pos += 1
176
+ end
177
+ y_comp = y[y_pos,y_seg_pos - y_pos]
178
+ end
179
+
180
+ # if y_seg_pos didn't advance in the above loop it means the segments are
181
+ # different types
182
+ if y_pos == y_seg_pos
183
+ # numbers always win over letters
184
+ return x_seg_is_num ? 1 : -1
185
+ end
186
+
187
+ # move the ball forward before we mess with the segments
188
+ x_pos += x_comp.length # +1 over pos_max if end of string
189
+ y_pos += y_comp.length
190
+
191
+ # we are comparing numbers - simply convert them
192
+ if x_seg_is_num
193
+ x_comp = x_comp.to_i
194
+ y_comp = y_comp.to_i
195
+ end
196
+
197
+ # compares ints or strings
198
+ # don't return if equal - try the next segment
199
+ if x_comp > y_comp
200
+ return 1
201
+ elsif x_comp < y_comp
202
+ return -1
203
+ end
204
+
205
+ # if we've reached here than the segments are the same - try again
206
+ end
207
+
208
+ # we must have reached the end of one or both of the strings and they
209
+ # matched up until this point
210
+
211
+ # segments matched completely but the segment separators were different -
212
+ # rpm reference code treats these as equal.
213
+ if (x_pos == x_pos_max + 1) and (y_pos == y_pos_max + 1)
214
+ return 0
215
+ end
216
+
217
+ # the most unprocessed characters left wins
218
+ if (x_pos_max - x_pos) > (y_pos_max - y_pos)
219
+ return 1
220
+ else
221
+ return -1
222
+ end
223
+ end
224
+
225
+ end # self
226
+ end # RPMUtils
227
+
228
+ class RPMVersion
229
+ include Comparable
230
+
231
+ def initialize(*args)
232
+ if args.size == 1
233
+ @e, @v, @r = RPMUtils.version_parse(args[0])
234
+ elsif args.size == 3
235
+ @e = args[0].to_i
236
+ @v = args[1]
237
+ @r = args[2]
238
+ else
239
+ raise ArgumentError, "Expecting either 'epoch-version-release' or 'epoch, " +
240
+ "version, release'"
241
+ end
242
+ end
243
+ attr_reader :e, :v, :r
244
+ alias :epoch :e
245
+ alias :version :v
246
+ alias :release :r
247
+
248
+ def self.parse(*args)
249
+ self.new(*args)
250
+ end
251
+
252
+ def <=>(y)
253
+ compare_versions(y)
254
+ end
255
+
256
+ def compare(y)
257
+ compare_versions(y, false)
258
+ end
259
+
260
+ def partial_compare(y)
261
+ compare_versions(y, true)
262
+ end
263
+
264
+ # RPM::Version rpm_version_to_s equivalent
265
+ def to_s
266
+ if @r.nil?
267
+ @v
268
+ else
269
+ "#{@v}-#{@r}"
270
+ end
271
+ end
272
+
273
+ def evr
274
+ "#{@e}:#{@v}-#{@r}"
275
+ end
276
+
277
+ private
278
+
279
+ # Rough RPM::Version rpm_version_cmp equivalent - except much slower :)
280
+ #
281
+ # partial lets epoch and version segment equality be good enough to return equal, eg:
282
+ #
283
+ # 2:1.2-1 == 2:1.2
284
+ # 2:1.2-1 == 2:
285
+ #
286
+ def compare_versions(y, partial=false)
287
+ x = self
288
+
289
+ # compare epoch
290
+ if (x.e.nil? == false and x.e > 0) and y.e.nil?
291
+ return 1
292
+ elsif x.e.nil? and (y.e.nil? == false and y.e > 0)
293
+ return -1
294
+ elsif x.e.nil? == false and y.e.nil? == false
295
+ if x.e < y.e
296
+ return -1
297
+ elsif x.e > y.e
298
+ return 1
299
+ end
300
+ end
301
+
302
+ # compare version
303
+ if partial and (x.v.nil? or y.v.nil?)
304
+ return 0
305
+ elsif x.v.nil? == false and y.v.nil?
306
+ return 1
307
+ elsif x.v.nil? and y.v.nil? == false
308
+ return -1
309
+ elsif x.v.nil? == false and y.v.nil? == false
310
+ cmp = RPMUtils.rpmvercmp(x.v, y.v)
311
+ return cmp if cmp != 0
312
+ end
313
+
314
+ # compare release
315
+ if partial and (x.r.nil? or y.r.nil?)
316
+ return 0
317
+ elsif x.r.nil? == false and y.r.nil?
318
+ return 1
319
+ elsif x.r.nil? and y.r.nil? == false
320
+ return -1
321
+ elsif x.r.nil? == false and y.r.nil? == false
322
+ cmp = RPMUtils.rpmvercmp(x.r, y.r)
323
+ return cmp
324
+ end
325
+
326
+ return 0
327
+ end
328
+ end
329
+
330
+ class RPMPackage
331
+ include Comparable
332
+
333
+ def initialize(*args)
334
+ if args.size == 4
335
+ @n = args[0]
336
+ @version = RPMVersion.new(args[1])
337
+ @a = args[2]
338
+ @provides = args[3]
339
+ elsif args.size == 6
340
+ @n = args[0]
341
+ e = args[1].to_i
342
+ v = args[2]
343
+ r = args[3]
344
+ @version = RPMVersion.new(e,v,r)
345
+ @a = args[4]
346
+ @provides = args[5]
347
+ else
348
+ raise ArgumentError, "Expecting either 'name, epoch-version-release, arch, provides' " +
349
+ "or 'name, epoch, version, release, arch, provides'"
350
+ end
351
+
352
+ # We always have one, ourselves!
353
+ if @provides.empty?
354
+ @provides = [ RPMProvide.new(@n, @version.evr, :==) ]
355
+ end
356
+ end
357
+ attr_reader :n, :a, :version, :provides
358
+ alias :name :n
359
+ alias :arch :a
360
+
361
+ def <=>(y)
362
+ compare(y)
363
+ end
364
+
365
+ def compare(y)
366
+ x = self
367
+
368
+ # easy! :)
369
+ return 0 if x.nevra == y.nevra
370
+
371
+ # compare name
372
+ if x.n.nil? == false and y.n.nil?
373
+ return 1
374
+ elsif x.n.nil? and y.n.nil? == false
375
+ return -1
376
+ elsif x.n.nil? == false and y.n.nil? == false
377
+ if x.n < y.n
378
+ return -1
379
+ elsif x.n > y.n
380
+ return 1
381
+ end
382
+ end
383
+
384
+ # compare version
385
+ if x.version > y.version
386
+ return 1
387
+ elsif x.version < y.version
388
+ return -1
389
+ end
390
+
391
+ # compare arch
392
+ if x.a.nil? == false and y.a.nil?
393
+ return 1
394
+ elsif x.a.nil? and y.a.nil? == false
395
+ return -1
396
+ elsif x.a.nil? == false and y.a.nil? == false
397
+ if x.a < y.a
398
+ return -1
399
+ elsif x.a > y.a
400
+ return 1
401
+ end
402
+ end
403
+
404
+ return 0
405
+ end
406
+
407
+ def to_s
408
+ nevra
409
+ end
410
+
411
+ def nevra
412
+ "#{@n}-#{@version.evr}.#{@a}"
413
+ end
414
+ end
415
+
416
+ # Simple implementation from rpm and ruby-rpm reference code
417
+ class RPMDependency
418
+ def initialize(*args)
419
+ if args.size == 3
420
+ @name = args[0]
421
+ @version = RPMVersion.new(args[1])
422
+ # Our requirement to other dependencies
423
+ @flag = args[2] || :==
424
+ elsif args.size == 5
425
+ @name = args[0]
426
+ e = args[1].to_i
427
+ v = args[2]
428
+ r = args[3]
429
+ @version = RPMVersion.new(e,v,r)
430
+ @flag = args[4] || :==
431
+ else
432
+ raise ArgumentError, "Expecting either 'name, epoch-version-release, flag' or " +
433
+ "'name, epoch, version, release, flag'"
434
+ end
435
+ end
436
+ attr_reader :name, :version, :flag
437
+
438
+ # Parses 2 forms:
439
+ #
440
+ # "mtr >= 2:0.71-3.0"
441
+ # "mta"
442
+ def self.parse(string)
443
+ if string =~ %r{^(\S+)\s+(>|>=|=|==|<=|<)\s+(\S+)$}
444
+ name = $1
445
+ if $2 == "="
446
+ flag = :==
447
+ else
448
+ flag = :"#{$2}"
449
+ end
450
+ version = $3
451
+
452
+ return self.new(name, version, flag)
453
+ else
454
+ name = string
455
+ return self.new(name, nil, nil)
456
+ end
457
+ end
458
+
459
+ # Test if another RPMDependency satisfies our requirements
460
+ def satisfy?(y)
461
+ unless y.kind_of?(RPMDependency)
462
+ raise ArgumentError, "Expecting an RPMDependency object"
463
+ end
464
+
465
+ x = self
466
+
467
+ # Easy!
468
+ if x.name != y.name
469
+ return false
470
+ end
471
+
472
+ # Partial compare
473
+ #
474
+ # eg: x.version 2.3 == y.version 2.3-1
475
+ sense = x.version.partial_compare(y.version)
476
+
477
+ # Thanks to rpmdsCompare() rpmds.c
478
+ if sense < 0 and (x.flag == :> || x.flag == :>=) || (y.flag == :<= || y.flag == :<)
479
+ return true
480
+ elsif sense > 0 and (x.flag == :< || x.flag == :<=) || (y.flag == :>= || y.flag == :>)
481
+ return true
482
+ elsif sense == 0 and (
483
+ ((x.flag == :== or x.flag == :<= or x.flag == :>=) and (y.flag == :== or y.flag == :<= or y.flag == :>=)) or
484
+ (x.flag == :< and y.flag == :<) or
485
+ (x.flag == :> and y.flag == :>)
486
+ )
487
+ return true
488
+ end
489
+
490
+ return false
491
+ end
492
+ end
493
+
494
+ class RPMProvide < RPMDependency; end
495
+ class RPMRequire < RPMDependency; end
496
+
497
+ class RPMDbPackage < RPMPackage
498
+ # <rpm parts>, installed, available
499
+ def initialize(*args)
500
+ # state
501
+ @available = args.pop
502
+ @installed = args.pop
503
+ super(*args)
504
+ end
505
+ attr_reader :available, :installed
506
+ end
507
+
508
+ # Simple storage for RPMPackage objects - keeps them unique and sorted
509
+ class RPMDb
510
+ def initialize
511
+ # package name => [ RPMPackage, RPMPackage ] of different versions
512
+ @rpms = Hash.new
513
+ # package nevra => RPMPackage for lookups
514
+ @index = Hash.new
515
+ # provide name (aka feature) => [RPMPackage, RPMPackage] each providing this feature
516
+ @provides = Hash.new
517
+ # RPMPackages listed as available
518
+ @available = Set.new
519
+ # RPMPackages listed as installed
520
+ @installed = Set.new
521
+ end
522
+
523
+ def [](package_name)
524
+ self.lookup(package_name)
525
+ end
526
+
527
+ # Lookup package_name and return a descending array of package objects
528
+ def lookup(package_name)
529
+ pkgs = @rpms[package_name]
530
+ if pkgs
531
+ return pkgs.sort.reverse
532
+ else
533
+ return nil
534
+ end
535
+ end
536
+
537
+ def lookup_provides(provide_name)
538
+ @provides[provide_name]
539
+ end
540
+
541
+ # Using the package name as a key, and nevra for an index, keep a unique list of packages.
542
+ # The available/installed state can be overwritten for existing packages.
543
+ def push(*args)
544
+ args.flatten.each do |new_rpm|
545
+ unless new_rpm.kind_of?(RPMDbPackage)
546
+ raise ArgumentError, "Expecting an RPMDbPackage object"
547
+ end
548
+
549
+ @rpms[new_rpm.n] ||= Array.new
550
+
551
+ # we may already have this one, like when the installed list is refreshed
552
+ idx = @index[new_rpm.nevra]
553
+ if idx
554
+ # grab the existing package if it's not
555
+ curr_rpm = idx
556
+ else
557
+ @rpms[new_rpm.n] << new_rpm
558
+
559
+ new_rpm.provides.each do |provide|
560
+ @provides[provide.name] ||= Array.new
561
+ @provides[provide.name] << new_rpm
562
+ end
563
+
564
+ curr_rpm = new_rpm
565
+ end
566
+
567
+ # Track the nevra -> RPMPackage association to avoid having to compare versions
568
+ # with @rpms[new_rpm.n] on the next round
569
+ @index[new_rpm.nevra] = curr_rpm
570
+
571
+ # these are overwritten for existing packages
572
+ if new_rpm.available
573
+ @available << curr_rpm
574
+ end
575
+ if new_rpm.installed
576
+ @installed << curr_rpm
577
+ end
578
+ end
579
+ end
580
+
581
+ def <<(*args)
582
+ self.push(args)
583
+ end
584
+
585
+ def clear
586
+ @rpms.clear
587
+ @index.clear
588
+ @provides.clear
589
+ clear_available
590
+ clear_installed
591
+ end
592
+
593
+ def clear_available
594
+ @available.clear
595
+ end
596
+
597
+ def clear_installed
598
+ @installed.clear
599
+ end
600
+
601
+ def size
602
+ @rpms.size
603
+ end
604
+ alias :length :size
605
+
606
+ def available_size
607
+ @available.size
608
+ end
609
+
610
+ def installed_size
611
+ @installed.size
612
+ end
613
+
614
+ def available?(package)
615
+ @available.include?(package)
616
+ end
617
+
618
+ def installed?(package)
619
+ @installed.include?(package)
620
+ end
621
+
622
+ def whatprovides(rpmdep)
623
+ unless rpmdep.kind_of?(RPMDependency)
624
+ raise ArgumentError, "Expecting an RPMDependency object"
625
+ end
626
+
627
+ what = []
628
+
629
+ packages = lookup_provides(rpmdep.name)
630
+ if packages
631
+ packages.each do |pkg|
632
+ pkg.provides.each do |provide|
633
+ if provide.satisfy?(rpmdep)
634
+ what << pkg
635
+ end
636
+ end
637
+ end
638
+ end
639
+
640
+ return what
641
+ end
642
+ end
643
+
644
+ # Cache for our installed and available packages, pulled in from yum-dump.py
645
+ class YumCache
646
+ include Chef::Mixin::Command
647
+ include Singleton
648
+
649
+ def initialize
650
+ @rpmdb = RPMDb.new
651
+
652
+ # Next time @rpmdb is accessed:
653
+ # :all - Trigger a run of "yum-dump.py --options --installed-provides", updates
654
+ # yum's cache and parses options from /etc/yum.conf. Pulls in Provides
655
+ # dependency data for installed packages only - this data is slow to
656
+ # gather.
657
+ # :provides - Same as :all but pulls in Provides data for available packages as well.
658
+ # Used as a last resort when we can't find a Provides match.
659
+ # :installed - Trigger a run of "yum-dump.py --installed", only reads the local rpm
660
+ # db. Used between client runs for a quick refresh.
661
+ # :none - Do nothing, a call to one of the reload methods is required.
662
+ @next_refresh = :all
663
+
664
+ @allow_multi_install = []
665
+
666
+ # these are for subsequent runs if we are on an interval
667
+ Chef::Client.when_run_starts do
668
+ YumCache.instance.reload
669
+ end
670
+ end
671
+
672
+ # Cache management
673
+ #
674
+
675
+ def refresh
676
+ case @next_refresh
677
+ when :none
678
+ return nil
679
+ when :installed
680
+ reset_installed
681
+ # fast
682
+ opts=" --installed"
683
+ when :all
684
+ reset
685
+ # medium
686
+ opts=" --options --installed-provides"
687
+ when :provides
688
+ reset
689
+ # slow!
690
+ opts=" --options --all-provides"
691
+ else
692
+ raise ArgumentError, "Unexpected value in next_refresh: #{@next_refresh}"
693
+ end
694
+
695
+ one_line = false
696
+ error = nil
697
+
698
+ helper = ::File.join(::File.dirname(__FILE__), 'yum-dump.py')
699
+
700
+ status = popen4("/usr/bin/python #{helper}#{opts}", :waitlast => true) do |pid, stdin, stdout, stderr|
701
+ stdout.each do |line|
702
+ one_line = true
703
+
704
+ line.chomp!
705
+
706
+ if line =~ %r{\[option (.*)\] (.*)}
707
+ if $1 == "installonlypkgs"
708
+ @allow_multi_install = $2.split
709
+ else
710
+ raise Chef::Exceptions::Package, "Strange, unknown option line '#{line}' from yum-dump.py"
711
+ end
712
+ next
713
+ end
714
+
715
+ if line =~ %r{^(\S+) ([0-9]+) (\S+) (\S+) (\S+) \[(.*)\] ([i,a,r])$}
716
+ name = $1
717
+ epoch = $2
718
+ version = $3
719
+ release = $4
720
+ arch = $5
721
+ provides = parse_provides($6)
722
+ type = $7
723
+ else
724
+ Chef::Log.warn("Problem parsing line '#{line}' from yum-dump.py! " +
725
+ "Please check your yum configuration.")
726
+ next
727
+ end
728
+
729
+ case type
730
+ when "i"
731
+ # if yum-dump was called with --installed this may not be true, but it's okay
732
+ # since we don't touch the @available Set in reload_installed
733
+ available = false
734
+ installed = true
735
+ when "a"
736
+ available = true
737
+ installed = false
738
+ when "r"
739
+ available = true
740
+ installed = true
741
+ end
742
+
743
+ pkg = RPMDbPackage.new(name, epoch, version, release, arch, provides, installed, available)
744
+ @rpmdb << pkg
745
+ end
746
+
747
+ error = stderr.readlines
748
+ end
749
+
750
+ if status.exitstatus != 0
751
+ raise Chef::Exceptions::Package, "Yum failed - #{status.inspect} - returns: #{error}"
752
+ else
753
+ unless one_line
754
+ Chef::Log.warn("Odd, no output from yum-dump.py. Please check " +
755
+ "your yum configuration.")
756
+ end
757
+ end
758
+
759
+ # A reload method must be called before the cache is altered
760
+ @next_refresh = :none
761
+ end
762
+
763
+ def reload
764
+ @next_refresh = :all
765
+ end
766
+
767
+ def reload_installed
768
+ @next_refresh = :installed
769
+ end
770
+
771
+ def reload_provides
772
+ @next_refresh = :provides
773
+ end
774
+
775
+ def reset
776
+ @rpmdb.clear
777
+ end
778
+
779
+ def reset_installed
780
+ @rpmdb.clear_installed
781
+ end
782
+
783
+ # Querying the cache
784
+ #
785
+
786
+ # Check for package by name or name+arch
787
+ def package_available?(package_name)
788
+ refresh
789
+
790
+ if @rpmdb.lookup(package_name)
791
+ return true
792
+ else
793
+ if package_name =~ %r{^(.*)\.(.*)$}
794
+ pkg_name = $1
795
+ pkg_arch = $2
796
+
797
+ if matches = @rpmdb.lookup(pkg_name)
798
+ matches.each do |m|
799
+ return true if m.arch == pkg_arch
800
+ end
801
+ end
802
+ end
803
+ end
804
+
805
+ return false
806
+ end
807
+
808
+ # Returns a array of packages satisfying an RPMDependency
809
+ def packages_from_require(rpmdep)
810
+ refresh
811
+ @rpmdb.whatprovides(rpmdep)
812
+ end
813
+
814
+ def version_available?(package_name, desired_version, arch=nil)
815
+ version(package_name, arch, true, false) do |v|
816
+ return true if desired_version == v
817
+ end
818
+
819
+ return false
820
+ end
821
+
822
+ def available_version(package_name, arch=nil)
823
+ version(package_name, arch, true, false)
824
+ end
825
+ alias :candidate_version :available_version
826
+
827
+ def installed_version(package_name, arch=nil)
828
+ version(package_name, arch, false, true)
829
+ end
830
+
831
+ def allow_multi_install
832
+ refresh
833
+ @allow_multi_install
834
+ end
835
+
836
+ private
837
+
838
+ def version(package_name, arch=nil, is_available=false, is_installed=false)
839
+ refresh
840
+ packages = @rpmdb[package_name]
841
+ if packages
842
+ packages.each do |pkg|
843
+ if is_available
844
+ next unless @rpmdb.available?(pkg)
845
+ end
846
+ if is_installed
847
+ next unless @rpmdb.installed?(pkg)
848
+ end
849
+ if arch
850
+ next unless pkg.arch == arch
851
+ end
852
+
853
+ if block_given?
854
+ yield pkg.version.to_s
855
+ else
856
+ # first match is latest version
857
+ return pkg.version.to_s
858
+ end
859
+ end
860
+ end
861
+
862
+ if block_given?
863
+ return self
864
+ else
865
+ return nil
866
+ end
867
+ end
868
+
869
+ # Parse provides from yum-dump.py output
870
+ def parse_provides(string)
871
+ ret = []
872
+ # ['atk = 1.12.2-1.fc6', 'libatk-1.0.so.0']
873
+ string.split(", ").each do |seg|
874
+ # 'atk = 1.12.2-1.fc6'
875
+ if seg =~ %r{^'(.*)'$}
876
+ ret << RPMProvide.parse($1)
877
+ end
878
+ end
879
+
880
+ return ret
881
+ end
882
+
883
+ end # YumCache
884
+
885
+ include Chef::Mixin::GetSourceFromPackage
886
+
887
+ def initialize(new_resource, run_context)
888
+ super
889
+
890
+ @yum = YumCache.instance
891
+ end
892
+
893
+ # Extra attributes
894
+ #
895
+
896
+ def arch
897
+ if @new_resource.respond_to?("arch")
898
+ @new_resource.arch
899
+ else
900
+ nil
901
+ end
902
+ end
903
+
904
+ def flush_cache
905
+ if @new_resource.respond_to?("flush_cache")
906
+ @new_resource.flush_cache
907
+ else
908
+ { :before => false, :after => false }
909
+ end
910
+ end
911
+
912
+ def allow_downgrade
913
+ if @new_resource.respond_to?("allow_downgrade")
914
+ @new_resource.allow_downgrade
915
+ else
916
+ false
917
+ end
918
+ end
919
+
920
+ # Helpers
921
+ #
922
+
923
+ def yum_arch
924
+ arch ? ".#{arch}" : nil
925
+ end
926
+
927
+ def yum_command(command)
928
+ status, stdout, stderr = output_of_command(command, {})
929
+
930
+ # This is fun: rpm can encounter errors in the %post/%postun scripts which aren't
931
+ # considered fatal - meaning the rpm is still successfully installed. These issue
932
+ # cause yum to emit a non fatal warning but still exit(1). As there's currently no
933
+ # way to suppress this behavior and an exit(1) will break a Chef run we make an
934
+ # effort to trap these and re-run the same install command - it will either fail a
935
+ # second time or succeed.
936
+ #
937
+ # A cleaner solution would have to be done in python and better hook into
938
+ # yum/rpm to handle exceptions as we see fit.
939
+ if status.exitstatus == 1
940
+ stdout.each_line do |l|
941
+ # rpm-4.4.2.3 lib/psm.c line 2182
942
+ if l =~ %r{^error: %(post|postun)\(.*\) scriptlet failed, exit status \d+$}
943
+ Chef::Log.warn("#{@new_resource} caught non-fatal scriptlet issue: \"#{l}\". Can't trust yum exit status " +
944
+ "so running install again to verify.")
945
+ status, stdout, stderr = output_of_command(command, {})
946
+ break
947
+ end
948
+ end
949
+ end
950
+
951
+ if status.exitstatus > 0
952
+ command_output = "STDOUT: #{stdout}"
953
+ command_output << "STDERR: #{stderr}"
954
+ handle_command_failures(status, command_output, {})
955
+ end
956
+ end
957
+
958
+ # Standard Provider methods for Parent
959
+ #
960
+
961
+ def load_current_resource
962
+ if flush_cache[:before]
963
+ @yum.reload
964
+ end
965
+
966
+ # At this point package_name could be:
967
+ #
968
+ # 1) a package name, eg: "foo"
969
+ # 2) a package name.arch, eg: "foo.i386"
970
+ # 3) or a dependency, eg: "foo >= 1.1"
971
+
972
+ # Check if we have name or name+arch which has a priority over a dependency
973
+ unless @yum.package_available?(@new_resource.package_name)
974
+ # If they aren't in the installed packages they could be a dependency
975
+ parse_dependency
976
+ end
977
+
978
+ # Don't overwrite an existing arch
979
+ unless arch
980
+ parse_arch
981
+ end
982
+
983
+ @current_resource = Chef::Resource::Package.new(@new_resource.name)
984
+ @current_resource.package_name(@new_resource.package_name)
985
+
986
+ if @new_resource.source
987
+ unless ::File.exists?(@new_resource.source)
988
+ raise Chef::Exceptions::Package, "Package #{@new_resource.name} not found: #{@new_resource.source}"
989
+ end
990
+
991
+ Chef::Log.debug("#{@new_resource} checking rpm status")
992
+ status = popen4("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{@new_resource.source}") do |pid, stdin, stdout, stderr|
993
+ stdout.each do |line|
994
+ case line
995
+ when /([\w\d_.-]+)\s([\w\d_.-]+)/
996
+ @current_resource.package_name($1)
997
+ @new_resource.version($2)
998
+ end
999
+ end
1000
+ end
1001
+ end
1002
+
1003
+ if @new_resource.version
1004
+ new_resource = "#{@new_resource.package_name}-#{@new_resource.version}#{yum_arch}"
1005
+ else
1006
+ new_resource = "#{@new_resource.package_name}#{yum_arch}"
1007
+ end
1008
+
1009
+ Chef::Log.debug("#{@new_resource} checking yum info for #{new_resource}")
1010
+
1011
+ installed_version = @yum.installed_version(@new_resource.package_name, arch)
1012
+ @current_resource.version(installed_version)
1013
+
1014
+ @candidate_version = @yum.candidate_version(@new_resource.package_name, arch)
1015
+
1016
+ Chef::Log.debug("#{@new_resource} installed version: #{installed_version || "(none)"} candidate version: " +
1017
+ "#{@candidate_version || "(none)"}")
1018
+
1019
+ @current_resource
1020
+ end
1021
+
1022
+ def install_package(name, version)
1023
+ if @new_resource.source
1024
+ yum_command("yum -d0 -e0 -y#{expand_options(@new_resource.options)} localinstall #{@new_resource.source}")
1025
+ else
1026
+ # Work around yum not exiting with an error if a package doesn't exist for CHEF-2062
1027
+ if @yum.version_available?(name, version, arch)
1028
+ method = "install"
1029
+
1030
+ # More Yum fun:
1031
+ #
1032
+ # yum install of an old name+version will exit(1)
1033
+ # yum install of an old name+version+arch will exit(0) for some reason
1034
+ #
1035
+ # Some packages can be installed multiple times like the kernel
1036
+ unless @yum.allow_multi_install.include?(name)
1037
+ if RPMVersion.parse(@current_resource.version) > RPMVersion.parse(version)
1038
+ # Unless they want this...
1039
+ if allow_downgrade
1040
+ method = "downgrade"
1041
+ else
1042
+ # we bail like yum when the package is older
1043
+ raise Chef::Exceptions::Package, "Installed package #{name}-#{@current_resource.version} is newer " +
1044
+ "than candidate package #{name}-#{version}"
1045
+ end
1046
+ end
1047
+ end
1048
+
1049
+ yum_command("yum -d0 -e0 -y#{expand_options(@new_resource.options)} #{method} #{name}-#{version}#{yum_arch}")
1050
+ else
1051
+ raise Chef::Exceptions::Package, "Version #{version} of #{name} not found. Did you specify both version " +
1052
+ "and release? (version-release, e.g. 1.84-10.fc6)"
1053
+ end
1054
+ end
1055
+ if flush_cache[:after]
1056
+ @yum.reload
1057
+ else
1058
+ @yum.reload_installed
1059
+ end
1060
+ end
1061
+
1062
+ # Keep upgrades from trying to install an older candidate version. Can happen when a new
1063
+ # version is installed then removed from a repository, now the older available version
1064
+ # shows up as a viable install candidate.
1065
+ #
1066
+ # Can be done in upgrade_package but an upgraded from->to log message slips out
1067
+ #
1068
+ # Hacky - better overall solution? Custom compare in Package provider?
1069
+ def action_upgrade
1070
+ # Ensure the candidate is newer
1071
+ if RPMVersion.parse(candidate_version) > RPMVersion.parse(@current_resource.version)
1072
+ super
1073
+ # Candidate is older
1074
+ else
1075
+ Chef::Log.debug("#{@new_resource} is at the latest version - nothing to do")
1076
+ end
1077
+ end
1078
+
1079
+ def upgrade_package(name, version)
1080
+ install_package(name, version)
1081
+ end
1082
+
1083
+ def remove_package(name, version)
1084
+ if version
1085
+ yum_command("yum -d0 -e0 -y#{expand_options(@new_resource.options)} remove #{name}-#{version}#{yum_arch}")
1086
+ else
1087
+ yum_command("yum -d0 -e0 -y#{expand_options(@new_resource.options)} remove #{name}#{yum_arch}")
1088
+ end
1089
+ if flush_cache[:after]
1090
+ @yum.reload
1091
+ else
1092
+ @yum.reload_installed
1093
+ end
1094
+ end
1095
+
1096
+ def purge_package(name, version)
1097
+ remove_package(name, version)
1098
+ end
1099
+
1100
+ private
1101
+
1102
+ def parse_arch
1103
+ # Allow for foo.x86_64 style package_name like yum uses in it's output
1104
+ #
1105
+ if @new_resource.package_name =~ %r{^(.*)\.(.*)$}
1106
+ new_package_name = $1
1107
+ new_arch = $2
1108
+ # foo.i386 and foo.beta1 are both valid package names or expressions of an arch.
1109
+ # Ensure we don't have an existing package matching package_name, then ensure we at
1110
+ # least have a match for the new_package+new_arch before we overwrite. If neither
1111
+ # then fall through to standard package handling.
1112
+ if (@yum.installed_version(@new_resource.package_name).nil? and @yum.candidate_version(@new_resource.package_name).nil?) and
1113
+ (@yum.installed_version(new_package_name, new_arch) or @yum.candidate_version(new_package_name, new_arch))
1114
+ @new_resource.package_name(new_package_name)
1115
+ @new_resource.arch(new_arch)
1116
+ end
1117
+ end
1118
+ end
1119
+
1120
+ # If we don't have the package we could have been passed a 'whatprovides' feature
1121
+ #
1122
+ # eg: yum install "perl(Config)"
1123
+ # yum install "mtr = 2:0.71-3.1"
1124
+ # yum install "mtr > 2:0.71"
1125
+ #
1126
+ # We support resolving these out of the Provides data imported from yum-dump.py and
1127
+ # matching them up with an actual package so the standard resource handling can apply.
1128
+ #
1129
+ # There is currently no support for filename matching.
1130
+ def parse_dependency
1131
+ # Transform the package_name into a requirement
1132
+ yum_require = RPMRequire.parse(@new_resource.package_name)
1133
+ # and gather all the packages that have a Provides feature satisfying the requirement.
1134
+ # It could be multiple be we can only manage one
1135
+ packages = @yum.packages_from_require(yum_require)
1136
+
1137
+ if packages.empty?
1138
+ # Don't bother if we are just ensuring a package is removed - we don't need Provides data
1139
+ actions = Array(@new_resource.action)
1140
+ unless actions.size == 1 and (actions[0] == :remove || actions[0] == :purge)
1141
+ Chef::Log.debug("#{@new_resource} couldn't match #{@new_resource.package_name} in " +
1142
+ "installed Provides, loading available Provides - this may take a moment")
1143
+ @yum.reload_provides
1144
+ packages = @yum.packages_from_require(yum_require)
1145
+ end
1146
+ end
1147
+
1148
+ unless packages.empty?
1149
+ new_package_name = packages.first.name
1150
+ Chef::Log.debug("#{@new_resource} no package found for #{@new_resource.package_name} " +
1151
+ "but matched Provides for #{new_package_name}")
1152
+
1153
+ # Ensure it's not the same package under a different architecture
1154
+ unique_names = []
1155
+ packages.each do |pkg|
1156
+ unique_names << "#{pkg.name}-#{pkg.version.evr}"
1157
+ end
1158
+ unique_names.uniq!
1159
+
1160
+ if unique_names.size > 1
1161
+ Chef::Log.warn("#{@new_resource} matched multiple Provides for #{@new_resource.package_name} " +
1162
+ "but we can only use the first match: #{new_package_name}. Please use a more " +
1163
+ "specific version.")
1164
+ end
1165
+
1166
+ @new_resource.package_name(new_package_name)
1167
+ end
1168
+ end
1169
+
1170
+ end
1171
+ end
1172
+ end
1173
+ end