naked-chef 0.9.15.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (363) hide show
  1. data/LICENSE +201 -0
  2. data/README.rdoc +172 -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 +82 -0
  15. data/distro/arch/etc/rc.d/chef-server-webui +82 -0
  16. data/distro/arch/etc/rc.d/chef-solr +82 -0
  17. data/distro/arch/etc/rc.d/chef-solr-indexer +82 -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 +127 -0
  63. data/lib/chef/application/agent.rb +18 -0
  64. data/lib/chef/application/client.rb +246 -0
  65. data/lib/chef/application/knife.rb +171 -0
  66. data/lib/chef/application/solo.rb +215 -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 +173 -0
  71. data/lib/chef/client.rb +304 -0
  72. data/lib/chef/config.rb +240 -0
  73. data/lib/chef/cookbook/cookbook_collection.rb +44 -0
  74. data/lib/chef/cookbook/file_system_file_vendor.rb +54 -0
  75. data/lib/chef/cookbook/file_vendor.rb +48 -0
  76. data/lib/chef/cookbook/metadata.rb +500 -0
  77. data/lib/chef/cookbook/metadata/version.rb +87 -0
  78. data/lib/chef/cookbook/remote_file_vendor.rb +87 -0
  79. data/lib/chef/cookbook/syntax_check.rb +136 -0
  80. data/lib/chef/cookbook_loader.rb +227 -0
  81. data/lib/chef/cookbook_site_streaming_uploader.rb +244 -0
  82. data/lib/chef/cookbook_uploader.rb +103 -0
  83. data/lib/chef/cookbook_version.rb +934 -0
  84. data/lib/chef/couchdb.rb +239 -0
  85. data/lib/chef/daemon.rb +172 -0
  86. data/lib/chef/data_bag.rb +223 -0
  87. data/lib/chef/data_bag_item.rb +267 -0
  88. data/lib/chef/exceptions.rb +72 -0
  89. data/lib/chef/file_access_control.rb +140 -0
  90. data/lib/chef/file_cache.rb +218 -0
  91. data/lib/chef/handler.rb +206 -0
  92. data/lib/chef/handler/json_file.rb +58 -0
  93. data/lib/chef/index_queue.rb +29 -0
  94. data/lib/chef/index_queue/amqp_client.rb +113 -0
  95. data/lib/chef/index_queue/consumer.rb +76 -0
  96. data/lib/chef/index_queue/indexable.rb +75 -0
  97. data/lib/chef/json_compat.rb +52 -0
  98. data/lib/chef/knife.rb +522 -0
  99. data/lib/chef/knife/bluebox_images_list.rb +54 -0
  100. data/lib/chef/knife/bluebox_server_create.rb +157 -0
  101. data/lib/chef/knife/bluebox_server_delete.rb +63 -0
  102. data/lib/chef/knife/bluebox_server_list.rb +59 -0
  103. data/lib/chef/knife/bootstrap.rb +177 -0
  104. data/lib/chef/knife/bootstrap/archlinux-gems.erb +44 -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 +43 -0
  110. data/lib/chef/knife/bootstrap/windows-gems.erb +34 -0
  111. data/lib/chef/knife/client_bulk_delete.rb +40 -0
  112. data/lib/chef/knife/client_create.rb +70 -0
  113. data/lib/chef/knife/client_delete.rb +45 -0
  114. data/lib/chef/knife/client_edit.rb +45 -0
  115. data/lib/chef/knife/client_list.rb +40 -0
  116. data/lib/chef/knife/client_reregister.rb +56 -0
  117. data/lib/chef/knife/client_show.rb +50 -0
  118. data/lib/chef/knife/configure.rb +140 -0
  119. data/lib/chef/knife/configure_client.rb +52 -0
  120. data/lib/chef/knife/cookbook_bulk_delete.rb +58 -0
  121. data/lib/chef/knife/cookbook_create.rb +209 -0
  122. data/lib/chef/knife/cookbook_delete.rb +143 -0
  123. data/lib/chef/knife/cookbook_download.rb +130 -0
  124. data/lib/chef/knife/cookbook_list.rb +41 -0
  125. data/lib/chef/knife/cookbook_metadata.rb +82 -0
  126. data/lib/chef/knife/cookbook_metadata_from_file.rb +40 -0
  127. data/lib/chef/knife/cookbook_show.rb +90 -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 +109 -0
  132. data/lib/chef/knife/cookbook_site_show.rb +57 -0
  133. data/lib/chef/knife/cookbook_site_unshare.rb +53 -0
  134. data/lib/chef/knife/cookbook_site_vendor.rb +139 -0
  135. data/lib/chef/knife/cookbook_test.rb +82 -0
  136. data/lib/chef/knife/cookbook_upload.rb +95 -0
  137. data/lib/chef/knife/data_bag_create.rb +59 -0
  138. data/lib/chef/knife/data_bag_delete.rb +49 -0
  139. data/lib/chef/knife/data_bag_edit.rb +50 -0
  140. data/lib/chef/knife/data_bag_from_file.rb +45 -0
  141. data/lib/chef/knife/data_bag_list.rb +43 -0
  142. data/lib/chef/knife/data_bag_show.rb +41 -0
  143. data/lib/chef/knife/ec2_instance_data.rb +46 -0
  144. data/lib/chef/knife/ec2_server_create.rb +218 -0
  145. data/lib/chef/knife/ec2_server_delete.rb +87 -0
  146. data/lib/chef/knife/ec2_server_list.rb +89 -0
  147. data/lib/chef/knife/exec.rb +52 -0
  148. data/lib/chef/knife/index_rebuild.rb +51 -0
  149. data/lib/chef/knife/node_bulk_delete.rb +43 -0
  150. data/lib/chef/knife/node_create.rb +47 -0
  151. data/lib/chef/knife/node_delete.rb +44 -0
  152. data/lib/chef/knife/node_edit.rb +44 -0
  153. data/lib/chef/knife/node_from_file.rb +42 -0
  154. data/lib/chef/knife/node_list.rb +41 -0
  155. data/lib/chef/knife/node_run_list_add.rb +64 -0
  156. data/lib/chef/knife/node_run_list_remove.rb +45 -0
  157. data/lib/chef/knife/node_show.rb +54 -0
  158. data/lib/chef/knife/rackspace_server_create.rb +184 -0
  159. data/lib/chef/knife/rackspace_server_delete.rb +57 -0
  160. data/lib/chef/knife/rackspace_server_list.rb +59 -0
  161. data/lib/chef/knife/recipe_list.rb +32 -0
  162. data/lib/chef/knife/role_bulk_delete.rb +44 -0
  163. data/lib/chef/knife/role_create.rb +52 -0
  164. data/lib/chef/knife/role_delete.rb +44 -0
  165. data/lib/chef/knife/role_edit.rb +45 -0
  166. data/lib/chef/knife/role_from_file.rb +46 -0
  167. data/lib/chef/knife/role_list.rb +40 -0
  168. data/lib/chef/knife/role_show.rb +51 -0
  169. data/lib/chef/knife/search.rb +94 -0
  170. data/lib/chef/knife/slicehost_images_list.rb +53 -0
  171. data/lib/chef/knife/slicehost_server_create.rb +103 -0
  172. data/lib/chef/knife/slicehost_server_delete.rb +61 -0
  173. data/lib/chef/knife/slicehost_server_list.rb +64 -0
  174. data/lib/chef/knife/ssh.rb +338 -0
  175. data/lib/chef/knife/status.rb +87 -0
  176. data/lib/chef/knife/terremark_server_create.rb +152 -0
  177. data/lib/chef/knife/terremark_server_delete.rb +87 -0
  178. data/lib/chef/knife/terremark_server_list.rb +77 -0
  179. data/lib/chef/knife/windows_bootstrap.rb +154 -0
  180. data/lib/chef/log.rb +93 -0
  181. data/lib/chef/mixin/check_helper.rb +31 -0
  182. data/lib/chef/mixin/checksum.rb +32 -0
  183. data/lib/chef/mixin/command.rb +220 -0
  184. data/lib/chef/mixin/command/unix.rb +215 -0
  185. data/lib/chef/mixin/command/windows.rb +72 -0
  186. data/lib/chef/mixin/convert_to_class_name.rb +63 -0
  187. data/lib/chef/mixin/create_path.rb +56 -0
  188. data/lib/chef/mixin/deep_merge.rb +225 -0
  189. data/lib/chef/mixin/deprecation.rb +65 -0
  190. data/lib/chef/mixin/find_preferred_file.rb +92 -0
  191. data/lib/chef/mixin/from_file.rb +50 -0
  192. data/lib/chef/mixin/language.rb +165 -0
  193. data/lib/chef/mixin/language_include_attribute.rb +61 -0
  194. data/lib/chef/mixin/language_include_recipe.rb +52 -0
  195. data/lib/chef/mixin/params_validate.rb +225 -0
  196. data/lib/chef/mixin/recipe_definition_dsl_core.rb +81 -0
  197. data/lib/chef/mixin/shell_out.rb +38 -0
  198. data/lib/chef/mixin/template.rb +95 -0
  199. data/lib/chef/mixin/xml_escape.rb +140 -0
  200. data/lib/chef/mixins.rb +16 -0
  201. data/lib/chef/monkey_patches/dir.rb +36 -0
  202. data/lib/chef/monkey_patches/numeric.rb +7 -0
  203. data/lib/chef/monkey_patches/regexp.rb +34 -0
  204. data/lib/chef/monkey_patches/string.rb +28 -0
  205. data/lib/chef/monkey_patches/tempfile.rb +64 -0
  206. data/lib/chef/node.rb +599 -0
  207. data/lib/chef/node/attribute.rb +487 -0
  208. data/lib/chef/openid_registration.rb +187 -0
  209. data/lib/chef/platform.rb +382 -0
  210. data/lib/chef/provider.rb +124 -0
  211. data/lib/chef/provider/breakpoint.rb +36 -0
  212. data/lib/chef/provider/cookbook_file.rb +100 -0
  213. data/lib/chef/provider/cron.rb +186 -0
  214. data/lib/chef/provider/cron/solaris.rb +195 -0
  215. data/lib/chef/provider/deploy.rb +320 -0
  216. data/lib/chef/provider/deploy/revision.rb +77 -0
  217. data/lib/chef/provider/deploy/timestamped.rb +33 -0
  218. data/lib/chef/provider/directory.rb +72 -0
  219. data/lib/chef/provider/env.rb +152 -0
  220. data/lib/chef/provider/env/windows.rb +75 -0
  221. data/lib/chef/provider/erl_call.rb +100 -0
  222. data/lib/chef/provider/execute.rb +58 -0
  223. data/lib/chef/provider/file.rb +213 -0
  224. data/lib/chef/provider/git.rb +235 -0
  225. data/lib/chef/provider/group.rb +133 -0
  226. data/lib/chef/provider/group/dscl.rb +121 -0
  227. data/lib/chef/provider/group/gpasswd.rb +53 -0
  228. data/lib/chef/provider/group/groupadd.rb +78 -0
  229. data/lib/chef/provider/group/pw.rb +84 -0
  230. data/lib/chef/provider/group/usermod.rb +57 -0
  231. data/lib/chef/provider/group/windows.rb +79 -0
  232. data/lib/chef/provider/http_request.rb +122 -0
  233. data/lib/chef/provider/ifconfig.rb +132 -0
  234. data/lib/chef/provider/link.rb +161 -0
  235. data/lib/chef/provider/log.rb +54 -0
  236. data/lib/chef/provider/mdadm.rb +91 -0
  237. data/lib/chef/provider/mount.rb +117 -0
  238. data/lib/chef/provider/mount/mount.rb +232 -0
  239. data/lib/chef/provider/mount/windows.rb +80 -0
  240. data/lib/chef/provider/ohai.rb +41 -0
  241. data/lib/chef/provider/package.rb +160 -0
  242. data/lib/chef/provider/package/apt.rb +110 -0
  243. data/lib/chef/provider/package/dpkg.rb +112 -0
  244. data/lib/chef/provider/package/easy_install.rb +136 -0
  245. data/lib/chef/provider/package/freebsd.rb +123 -0
  246. data/lib/chef/provider/package/macports.rb +105 -0
  247. data/lib/chef/provider/package/pacman.rb +101 -0
  248. data/lib/chef/provider/package/portage.rb +124 -0
  249. data/lib/chef/provider/package/rpm.rb +101 -0
  250. data/lib/chef/provider/package/rubygems.rb +462 -0
  251. data/lib/chef/provider/package/solaris.rb +127 -0
  252. data/lib/chef/provider/package/yum-dump.py +128 -0
  253. data/lib/chef/provider/package/yum.rb +261 -0
  254. data/lib/chef/provider/package/zypper.rb +133 -0
  255. data/lib/chef/provider/remote_directory.rb +140 -0
  256. data/lib/chef/provider/remote_file.rb +120 -0
  257. data/lib/chef/provider/route.rb +195 -0
  258. data/lib/chef/provider/ruby_block.rb +33 -0
  259. data/lib/chef/provider/script.rb +55 -0
  260. data/lib/chef/provider/service.rb +128 -0
  261. data/lib/chef/provider/service/arch.rb +109 -0
  262. data/lib/chef/provider/service/debian.rb +130 -0
  263. data/lib/chef/provider/service/freebsd.rb +156 -0
  264. data/lib/chef/provider/service/gentoo.rb +54 -0
  265. data/lib/chef/provider/service/init.rb +71 -0
  266. data/lib/chef/provider/service/insserv.rb +52 -0
  267. data/lib/chef/provider/service/redhat.rb +60 -0
  268. data/lib/chef/provider/service/simple.rb +118 -0
  269. data/lib/chef/provider/service/solaris.rb +85 -0
  270. data/lib/chef/provider/service/upstart.rb +192 -0
  271. data/lib/chef/provider/service/windows.rb +146 -0
  272. data/lib/chef/provider/subversion.rb +194 -0
  273. data/lib/chef/provider/template.rb +105 -0
  274. data/lib/chef/provider/user.rb +187 -0
  275. data/lib/chef/provider/user/dscl.rb +280 -0
  276. data/lib/chef/provider/user/pw.rb +113 -0
  277. data/lib/chef/provider/user/useradd.rb +137 -0
  278. data/lib/chef/provider/user/windows.rb +124 -0
  279. data/lib/chef/providers.rb +92 -0
  280. data/lib/chef/recipe.rb +130 -0
  281. data/lib/chef/resource.rb +543 -0
  282. data/lib/chef/resource/apt_package.rb +34 -0
  283. data/lib/chef/resource/bash.rb +33 -0
  284. data/lib/chef/resource/breakpoint.rb +35 -0
  285. data/lib/chef/resource/cookbook_file.rb +45 -0
  286. data/lib/chef/resource/cron.rb +188 -0
  287. data/lib/chef/resource/csh.rb +33 -0
  288. data/lib/chef/resource/deploy.rb +371 -0
  289. data/lib/chef/resource/deploy_revision.rb +35 -0
  290. data/lib/chef/resource/directory.rb +76 -0
  291. data/lib/chef/resource/dpkg_package.rb +34 -0
  292. data/lib/chef/resource/easy_install_package.rb +57 -0
  293. data/lib/chef/resource/env.rb +58 -0
  294. data/lib/chef/resource/erl_call.rb +83 -0
  295. data/lib/chef/resource/execute.rb +127 -0
  296. data/lib/chef/resource/file.rb +99 -0
  297. data/lib/chef/resource/freebsd_package.rb +35 -0
  298. data/lib/chef/resource/gem_package.rb +49 -0
  299. data/lib/chef/resource/git.rb +36 -0
  300. data/lib/chef/resource/group.rb +70 -0
  301. data/lib/chef/resource/http_request.rb +61 -0
  302. data/lib/chef/resource/ifconfig.rb +134 -0
  303. data/lib/chef/resource/link.rb +78 -0
  304. data/lib/chef/resource/log.rb +62 -0
  305. data/lib/chef/resource/macports_package.rb +29 -0
  306. data/lib/chef/resource/mdadm.rb +82 -0
  307. data/lib/chef/resource/mount.rb +135 -0
  308. data/lib/chef/resource/ohai.rb +40 -0
  309. data/lib/chef/resource/package.rb +80 -0
  310. data/lib/chef/resource/pacman_package.rb +33 -0
  311. data/lib/chef/resource/perl.rb +33 -0
  312. data/lib/chef/resource/portage_package.rb +33 -0
  313. data/lib/chef/resource/python.rb +33 -0
  314. data/lib/chef/resource/remote_directory.rb +109 -0
  315. data/lib/chef/resource/remote_file.rb +83 -0
  316. data/lib/chef/resource/route.rb +135 -0
  317. data/lib/chef/resource/rpm_package.rb +34 -0
  318. data/lib/chef/resource/ruby.rb +33 -0
  319. data/lib/chef/resource/ruby_block.rb +40 -0
  320. data/lib/chef/resource/scm.rb +146 -0
  321. data/lib/chef/resource/script.rb +60 -0
  322. data/lib/chef/resource/service.rb +160 -0
  323. data/lib/chef/resource/solaris_package.rb +37 -0
  324. data/lib/chef/resource/subversion.rb +36 -0
  325. data/lib/chef/resource/template.rb +69 -0
  326. data/lib/chef/resource/timestamped_deploy.rb +31 -0
  327. data/lib/chef/resource/user.rb +130 -0
  328. data/lib/chef/resource/yum_package.rb +44 -0
  329. data/lib/chef/resource_collection.rb +217 -0
  330. data/lib/chef/resource_collection/stepable_iterator.rb +124 -0
  331. data/lib/chef/resource_definition.rb +67 -0
  332. data/lib/chef/resource_definition_list.rb +38 -0
  333. data/lib/chef/resources.rb +64 -0
  334. data/lib/chef/rest.rb +395 -0
  335. data/lib/chef/rest/auth_credentials.rb +78 -0
  336. data/lib/chef/rest/cookie_jar.rb +31 -0
  337. data/lib/chef/rest/rest_request.rb +188 -0
  338. data/lib/chef/role.rb +287 -0
  339. data/lib/chef/run_context.rb +110 -0
  340. data/lib/chef/run_list.rb +150 -0
  341. data/lib/chef/run_list/run_list_expansion.rb +172 -0
  342. data/lib/chef/run_list/run_list_item.rb +78 -0
  343. data/lib/chef/run_status.rb +121 -0
  344. data/lib/chef/runner.rb +107 -0
  345. data/lib/chef/sandbox.rb +153 -0
  346. data/lib/chef/search/query.rb +60 -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 +279 -0
  352. data/lib/chef/shell_out.rb +418 -0
  353. data/lib/chef/streaming_cookbook_uploader.rb +201 -0
  354. data/lib/chef/tasks/chef_repo.rake +256 -0
  355. data/lib/chef/util/file_edit.rb +122 -0
  356. data/lib/chef/util/windows.rb +56 -0
  357. data/lib/chef/util/windows/net_group.rb +101 -0
  358. data/lib/chef/util/windows/net_use.rb +121 -0
  359. data/lib/chef/util/windows/net_user.rb +198 -0
  360. data/lib/chef/util/windows/volume.rb +59 -0
  361. data/lib/chef/version.rb +21 -0
  362. data/lib/chef/webui_user.rb +231 -0
  363. metadata +606 -0
@@ -0,0 +1,29 @@
1
+ #
2
+ # Author:: Daniel DeLeo (<dan@kallistec.com>)
3
+ # Copyright:: Copyright (c) 2009 Daniel DeLeo
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 "singleton"
20
+ require "bunny"
21
+
22
+ require "chef/index_queue/amqp_client"
23
+ require "chef/index_queue/indexable"
24
+ require "chef/index_queue/consumer"
25
+
26
+ class Chef
27
+ module IndexQueue
28
+ end
29
+ end
@@ -0,0 +1,113 @@
1
+ #
2
+ # Author:: Daniel DeLeo (<dan@kallistec.com>)
3
+ # Copyright:: Copyright (c) 2009 Daniel DeLeo
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
+ class Chef
20
+ module IndexQueue
21
+ class AmqpClient
22
+ include Singleton
23
+
24
+ def initialize
25
+ reset!
26
+ end
27
+
28
+ def reset!
29
+ @amqp_client && amqp_client.connected? && amqp_client.stop
30
+ @amqp_client = nil
31
+ @exchange = nil
32
+ @queue = nil
33
+ end
34
+
35
+ def stop
36
+ @queue && @queue.subscription && @queue.unsubscribe
37
+ @amqp_client && @amqp_client.stop
38
+ end
39
+
40
+ def amqp_client
41
+ unless @amqp_client
42
+ begin
43
+ @amqp_client = Bunny.new(amqp_opts)
44
+ Chef::Log.debug "Starting AMQP connection with client settings: #{@amqp_client.inspect}"
45
+ @amqp_client.start
46
+ @amqp_client.qos(:prefetch_count => 1)
47
+ rescue Bunny::ServerDownError => e
48
+ Chef::Log.fatal "Could not connect to rabbitmq. Is it running, reachable, and configured correctly?"
49
+ raise e
50
+ rescue Bunny::ProtocolError => e
51
+ Chef::Log.fatal "Connection to rabbitmq refused. Check your rabbitmq configuration and chef's amqp* settings"
52
+ raise e
53
+ end
54
+ end
55
+ @amqp_client
56
+ end
57
+
58
+ def exchange
59
+ @exchange ||= amqp_client.exchange("chef-indexer", :durable => true, :type => :fanout)
60
+ end
61
+
62
+ def queue
63
+ unless @queue
64
+ @queue = amqp_client.queue("chef-index-consumer-" + consumer_id, :durable => durable_queue?)
65
+ @queue.bind(exchange)
66
+ end
67
+ @queue
68
+ end
69
+
70
+ def disconnected!
71
+ Chef::Log.error("Disconnected from the AMQP Broker (RabbitMQ)")
72
+ @amqp_client = nil
73
+ reset!
74
+ end
75
+
76
+ def send_action(action, data)
77
+ retries = 0
78
+ begin
79
+ exchange.publish(Chef::JSONCompat.to_json({"action" => action.to_s, "payload" => data}))
80
+ rescue Bunny::ServerDownError, Bunny::ConnectionError, Errno::ECONNRESET
81
+ disconnected!
82
+ if (retries += 1) < 2
83
+ Chef::Log.info("Attempting to reconnect to the AMQP broker")
84
+ retry
85
+ else
86
+ Chef::Log.fatal("Could not re-connect to the AMQP broker, giving up")
87
+ raise
88
+ end
89
+ end
90
+ end
91
+
92
+ private
93
+
94
+ def durable_queue?
95
+ !!Chef::Config[:amqp_consumer_id]
96
+ end
97
+
98
+ def consumer_id
99
+ Chef::Config[:amqp_consumer_id] || UUIDTools::UUID.random_create.to_s
100
+ end
101
+
102
+ def amqp_opts
103
+ { :spec => '08',
104
+ :host => Chef::Config[:amqp_host],
105
+ :port => Chef::Config[:amqp_port],
106
+ :user => Chef::Config[:amqp_user],
107
+ :pass => Chef::Config[:amqp_pass],
108
+ :vhost => Chef::Config[:amqp_vhost]}
109
+ end
110
+
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,76 @@
1
+ #
2
+ # Author:: Daniel DeLeo (<dan@kallistec.com>)
3
+ # Copyright:: Copyright (c) 2009 Daniel DeLeo
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
+ class Chef
20
+ module IndexQueue
21
+ module Consumer
22
+ module ClassMethods
23
+ def expose(*methods)
24
+ @exposed_methods = Array(@exposed_methods)
25
+ @exposed_methods += methods
26
+ end
27
+
28
+ def exposed_methods
29
+ @exposed_methods || []
30
+ end
31
+
32
+ def whitelisted?(method_name)
33
+ exposed_methods.include?(method_name)
34
+ end
35
+ end
36
+
37
+ def self.included(including_class)
38
+ including_class.send(:extend, ClassMethods)
39
+ end
40
+
41
+ def run
42
+ Chef::Log.debug("Starting Index Queue Consumer")
43
+ AmqpClient.instance.queue # triggers connection setup
44
+
45
+ begin
46
+ AmqpClient.instance.queue.subscribe(:ack => true, :timeout => false) do |message|
47
+ call_action_for_message(message)
48
+ end
49
+ rescue Bunny::ConnectionError, Errno::ECONNRESET, Bunny::ServerDownError
50
+ AmqpClient.instance.disconnected!
51
+ Chef::Log.warn "Connection to rabbitmq lost. attempting to reconnect"
52
+ sleep 1
53
+ retry
54
+ end
55
+ end
56
+ alias :start :run
57
+
58
+ def call_action_for_message(message)
59
+ amqp_payload = Chef::JSONCompat.from_json(message[:payload], :create_additions => false, :max_nesting => false)
60
+ action = amqp_payload["action"].to_sym
61
+ app_payload = amqp_payload["payload"]
62
+ assert_method_whitelisted(action)
63
+ send(action, app_payload)
64
+ end
65
+
66
+ private
67
+
68
+ def assert_method_whitelisted(method_name)
69
+ unless self.class.whitelisted?(method_name)
70
+ raise ArgumentError, "non-exposed method #{method_name} called via index queue"
71
+ end
72
+ end
73
+
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,75 @@
1
+ #
2
+ # Author:: Daniel DeLeo (<dan@kallistec.com>)
3
+ # Copyright:: Copyright (c) 2009 Daniel DeLeo
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
+ class Chef
20
+ module IndexQueue
21
+ module Indexable
22
+
23
+ module ClassMethods
24
+
25
+ def index_object_type(explicit_type_name=nil)
26
+ @index_object_type = explicit_type_name.to_s if explicit_type_name
27
+ @index_object_type
28
+ end
29
+
30
+ # Resets all metadata used for indexing to nil. Used for testing
31
+ def reset_index_metadata!
32
+ @index_object_type = nil
33
+ end
34
+
35
+ end
36
+
37
+ def self.included(including_class)
38
+ including_class.send(:extend, ClassMethods)
39
+ end
40
+
41
+ attr_accessor :index_id
42
+
43
+ def index_object_type
44
+ self.class.index_object_type || Mixin::ConvertToClassName.snake_case_basename(self.class.name)
45
+ end
46
+
47
+ def with_indexer_metadata(indexer_metadata={})
48
+ # changing input param symbol keys to strings, as the keys in hash that goes to solr are expected to be strings [cb]
49
+ # Ruby 1.9 hates you, cb [dan]
50
+ with_metadata = {}
51
+ indexer_metadata.each_key do |key|
52
+ with_metadata[key.to_s] = indexer_metadata[key]
53
+ end
54
+
55
+ with_metadata["type"] ||= self.index_object_type
56
+ with_metadata["id"] ||= self.index_id
57
+ with_metadata["database"] ||= Chef::Config[:couchdb_database]
58
+ with_metadata["item"] ||= self.to_hash
59
+
60
+ raise ArgumentError, "Type, Id, or Database missing in index operation: #{with_metadata.inspect}" if (with_metadata["id"].nil? or with_metadata["type"].nil?)
61
+ with_metadata
62
+ end
63
+
64
+ def add_to_index(metadata={})
65
+ Chef::Log.debug("pushing item to index queue for addition: #{self.with_indexer_metadata(metadata)}")
66
+ AmqpClient.instance.send_action(:add, self.with_indexer_metadata(metadata))
67
+ end
68
+
69
+ def delete_from_index(metadata={})
70
+ Chef::Log.debug("pushing item to index queue for deletion: #{self.with_indexer_metadata(metadata)}")
71
+ AmqpClient.instance.send_action(:delete, self.with_indexer_metadata(metadata))
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,52 @@
1
+ #
2
+ # Author:: Tim Hinderliter (<tim@opscode.com>)
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
+ # Wrapper class for interacting with JSON.
19
+
20
+ require 'json'
21
+
22
+ class Chef
23
+ class JSONCompat
24
+ JSON_MAX_NESTING = 1000
25
+
26
+ class <<self
27
+ # See PL-538. Increase the max nesting for JSON, which defaults to 19,
28
+ # and isn't enough for some deep node (for example) structures.
29
+ def opts_add_max_nesting(opts)
30
+ if opts.nil? || !opts.has_key?(:max_nesting)
31
+ opts = opts.nil? ? Hash.new : opts.clone
32
+ opts[:max_nesting] = JSON_MAX_NESTING
33
+ end
34
+ opts
35
+ end
36
+
37
+ # Just call the JSON gem's parse method with a modified :max_nesting field
38
+ def from_json(source, opts = {})
39
+ ::JSON.parse(source, opts_add_max_nesting(opts))
40
+ end
41
+
42
+ def to_json(obj, opts = nil)
43
+ #::JSON.generate(obj, opts_add_max_nesting(opts))
44
+ obj.to_json(opts_add_max_nesting(opts))
45
+ end
46
+
47
+ def to_json_pretty(obj, opts = nil)
48
+ ::JSON.pretty_generate(obj, opts_add_max_nesting(opts))
49
+ end
50
+ end
51
+ end
52
+ end
data/lib/chef/knife.rb ADDED
@@ -0,0 +1,522 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Author:: Christopher Brown (<cb@opscode.com>)
4
+ # Copyright:: Copyright (c) 2009 Opscode, Inc.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require 'chef/version'
21
+ require 'mixlib/cli'
22
+ require 'chef/mixin/convert_to_class_name'
23
+
24
+ require 'pp'
25
+
26
+ class Chef
27
+ class Knife
28
+ include Mixlib::CLI
29
+ extend Chef::Mixin::ConvertToClassName
30
+
31
+ # The "require paths" of the core knife subcommands bundled with chef
32
+ DEFAULT_SUBCOMMAND_FILES = Dir[File.expand_path(File.join(File.dirname(__FILE__), 'knife', '*.rb'))]
33
+ DEFAULT_SUBCOMMAND_FILES.map! { |knife_file| knife_file[/#{CHEF_ROOT}#{Regexp.escape(File::SEPARATOR)}(.*)\.rb/,1] }
34
+
35
+ attr_accessor :name_args
36
+
37
+ def self.msg(msg="")
38
+ puts msg
39
+ end
40
+
41
+ def self.inherited(subclass)
42
+ unless subclass.unnamed?
43
+ subcommands[subclass.snake_case_name] = subclass
44
+ end
45
+ end
46
+
47
+ # Explicitly set the category for the current command to +new_category+
48
+ # The category is normally determined from the first word of the command
49
+ # name, but some commands make more sense using two or more words
50
+ # ===Arguments
51
+ # new_category::: A String to set the category to (see examples)
52
+ # ===Examples:
53
+ # Data bag commands would be in the 'data' category by default. To put them
54
+ # in the 'data bag' category:
55
+ # category('data bag')
56
+ def self.category(new_category)
57
+ @category = new_category
58
+ end
59
+
60
+ def self.subcommand_category
61
+ @category || snake_case_name.split('_').first unless unnamed?
62
+ end
63
+
64
+ def self.snake_case_name
65
+ convert_to_snake_case(name.split('::').last) unless unnamed?
66
+ end
67
+
68
+ # Does this class have a name? (Classes created via Class.new don't)
69
+ def self.unnamed?
70
+ name.nil? || name.empty?
71
+ end
72
+
73
+ def self.subcommands
74
+ @@subcommands ||= {}
75
+ end
76
+
77
+ def self.subcommands_by_category
78
+ unless @subcommands_by_category
79
+ @subcommands_by_category = Hash.new { |hash, key| hash[key] = [] }
80
+ subcommands.each do |snake_cased, klass|
81
+ @subcommands_by_category[klass.subcommand_category] << snake_cased
82
+ end
83
+ end
84
+ @subcommands_by_category
85
+ end
86
+
87
+ # Load all the sub-commands
88
+ def self.load_commands
89
+ DEFAULT_SUBCOMMAND_FILES.each { |subcommand| require subcommand }
90
+ subcommands
91
+ end
92
+
93
+ # Print the list of subcommands knife knows about. If +preferred_category+
94
+ # is given, only subcommands in that category are shown
95
+ def self.list_commands(preferred_category=nil)
96
+ load_commands
97
+ category_desc = preferred_category ? preferred_category + " " : ''
98
+ msg "Available #{category_desc}subcommands: (for details, knife SUB-COMMAND --help)\n\n"
99
+
100
+ if preferred_category && subcommands_by_category.key?(preferred_category)
101
+ commands_to_show = {preferred_category => subcommands_by_category[preferred_category]}
102
+ else
103
+ commands_to_show = subcommands_by_category
104
+ end
105
+
106
+ commands_to_show.sort.each do |category, commands|
107
+ msg "** #{category.upcase} COMMANDS **"
108
+ commands.each do |command|
109
+ msg subcommands[command].banner if subcommands[command]
110
+ end
111
+ msg
112
+ end
113
+ end
114
+
115
+ # Run knife for the given +args+ (ARGV), adding +options+ to the list of
116
+ # CLI options that the subcommand knows how to handle.
117
+ # ===Arguments
118
+ # args::: usually ARGV
119
+ # options::: A Mixlib::CLI option parser hash. These +options+ are how
120
+ # subcommands know about global knife CLI options
121
+ def self.run(args, options={})
122
+ load_commands
123
+ subcommand_class = subcommand_class_from(args)
124
+ subcommand_class.options.merge!(options)
125
+ instance = subcommand_class.new(args)
126
+ instance.configure_chef
127
+ instance.run
128
+ end
129
+
130
+ def self.guess_category(args)
131
+ category_words = args.select {|arg| arg =~ /^([[:alnum:]]|_)+$/ }
132
+ matching_category = nil
133
+ while (!matching_category) && (!category_words.empty?)
134
+ candidate_category = category_words.join(' ')
135
+ matching_category = candidate_category if subcommands_by_category.key?(candidate_category)
136
+ matching_category || category_words.pop
137
+ end
138
+ matching_category
139
+ end
140
+
141
+ def self.subcommand_class_from(args)
142
+ command_words = args.select {|arg| arg =~ /^([[:alnum:]]|_)+$/ }
143
+ subcommand_class = nil
144
+
145
+ while ( !subcommand_class ) && ( !command_words.empty? )
146
+ snake_case_class_name = command_words.join("_")
147
+ unless subcommand_class = subcommands[snake_case_class_name]
148
+ command_words.pop
149
+ end
150
+ end
151
+ subcommand_class || subcommand_not_found!(args)
152
+ end
153
+
154
+ protected
155
+
156
+ def load_late_dependency(dep, gem_name = nil)
157
+ begin
158
+ require dep
159
+ rescue LoadError
160
+ gem_name ||= dep.gsub('/', '-')
161
+ Chef::Log.fatal "#{gem_name} is not installed. run \"gem install #{gem_name}\" to install it."
162
+ exit 1
163
+ end
164
+ end
165
+
166
+ private
167
+
168
+ # :nodoc:
169
+ # Error out and print usage. probably becuase the arguments given by the
170
+ # user could not be resolved to a subcommand.
171
+ def self.subcommand_not_found!(args)
172
+ unless want_help?(args)
173
+ Chef::Log.fatal("Cannot find sub command for: '#{args.join(' ')}'")
174
+ end
175
+ Chef::Knife.list_commands(guess_category(args))
176
+ exit 10
177
+ end
178
+
179
+ # :nodoc:
180
+ # TODO: duplicated with chef/application/knife
181
+ # all logic should be removed from that and Chef::Knife should own it.
182
+ def self.want_help?(args)
183
+ (args.any? { |arg| arg =~ /^(:?(:?\-\-)?help|\-h)$/})
184
+ end
185
+
186
+ public
187
+
188
+ # Create a new instance of the current class configured for the given
189
+ # arguments and options
190
+ def initialize(argv=[])
191
+ super() # having to call super in initialize is the most annoying anti-pattern :(
192
+
193
+ command_name_words = self.class.snake_case_name.split('_')
194
+
195
+ # Mixlib::CLI ignores the embedded name_args
196
+ @name_args = parse_options(argv)
197
+ @name_args.reject! { |name_arg| command_name_words.delete(name_arg) }
198
+
199
+ # knife node run_list add requires that we have extra logic to handle
200
+ # the case that command name words could be joined by an underscore :/
201
+ command_name_words = command_name_words.join('_')
202
+ @name_args.reject! { |name_arg| command_name_words == name_arg }
203
+
204
+ if config[:help]
205
+ msg opt_parser
206
+ exit 1
207
+ end
208
+ end
209
+
210
+ def parse_options(args)
211
+ super
212
+ rescue OptionParser::InvalidOption => e
213
+ puts "Error: " + e.to_s
214
+ show_usage
215
+ exit(1)
216
+ end
217
+
218
+ def ask_question(question, opts={})
219
+ question = question + "[#{opts[:default]}] " if opts[:default]
220
+
221
+ if opts[:default] and config[:defaults]
222
+
223
+ opts[:default]
224
+
225
+ else
226
+
227
+ stdout.print question
228
+ a = stdin.readline.strip
229
+
230
+ if opts[:default]
231
+ a.empty? ? opts[:default] : a
232
+ else
233
+ a
234
+ end
235
+
236
+ end
237
+
238
+ end
239
+
240
+ def configure_chef
241
+ unless config[:config_file]
242
+ full_path = Dir.pwd.split(File::SEPARATOR)
243
+ (full_path.length - 1).downto(0) do |i|
244
+ config_file_to_check = File.join([ full_path[0..i], ".chef", "knife.rb" ].flatten)
245
+ if File.exists?(config_file_to_check)
246
+ config[:config_file] = config_file_to_check
247
+ break
248
+ end
249
+ end
250
+ # If we haven't set a config yet and $HOME is set, and the home
251
+ # knife.rb exists, use it:
252
+ if (!config[:config_file]) && ENV['HOME'] && File.exist?(File.join(ENV['HOME'], '.chef', 'knife.rb'))
253
+ config[:config_file] = File.join(ENV['HOME'], '.chef', 'knife.rb')
254
+ end
255
+ end
256
+
257
+ # Don't try to load a knife.rb if it doesn't exist.
258
+ if config[:config_file]
259
+ Chef::Config.from_file(config[:config_file])
260
+ else
261
+ # ...but do log a message if no config was found.
262
+ self.msg("No knife configuration file found")
263
+ end
264
+
265
+ Chef::Config[:log_level] = config[:log_level] if config[:log_level]
266
+ Chef::Config[:log_location] = config[:log_location] if config[:log_location]
267
+ Chef::Config[:node_name] = config[:node_name] if config[:node_name]
268
+ Chef::Config[:client_key] = config[:client_key] if config[:client_key]
269
+ Chef::Config[:chef_server_url] = config[:chef_server_url] if config[:chef_server_url]
270
+ Chef::Log.init(Chef::Config[:log_location])
271
+ Chef::Log.level(Chef::Config[:log_level])
272
+
273
+ Chef::Log.debug("Using configuration from #{config[:config_file]}")
274
+
275
+ if Chef::Config[:node_name].nil?
276
+ raise ArgumentError, "No user specified, pass via -u or specifiy 'node_name' in #{config[:config_file] ? config[:config_file] : "~/.chef/knife.rb"}"
277
+ end
278
+ end
279
+
280
+ def pretty_print(data)
281
+ puts data
282
+ end
283
+
284
+ def output(data)
285
+ case config[:format]
286
+ when "json", nil
287
+ stdout.puts Chef::JSONCompat.to_json_pretty(data)
288
+ when "yaml"
289
+ require 'yaml'
290
+ stdout.puts YAML::dump(data)
291
+ when "text"
292
+ # If you were looking for some attribute and there is only one match
293
+ # just dump the attribute value
294
+ if data.length == 1 and config[:attribute]
295
+ stdout.puts data.values[0]
296
+ else
297
+ PP.pp(data, stdout)
298
+ end
299
+ else
300
+ raise ArgumentError, "Unknown output format #{config[:format]}"
301
+ end
302
+ end
303
+
304
+ def format_list_for_display(list)
305
+ config[:with_uri] ? list : list.keys.sort { |a,b| a <=> b }
306
+ end
307
+
308
+ def format_for_display(item)
309
+ data = item.kind_of?(Chef::DataBagItem) ? item.raw_data : item
310
+
311
+ if config[:attribute]
312
+ config[:attribute].split(".").each do |attr|
313
+ if data.respond_to?(:[])
314
+ data = data[attr]
315
+ elsif data.nil?
316
+ nil # don't get no method error on nil
317
+ else data.respond_to?(attr.to_sym)
318
+ data = data.send(attr.to_sym)
319
+ end
320
+ end
321
+ { config[:attribute] => data.kind_of?(Chef::Node::Attribute) ? data.to_hash : data }
322
+ elsif config[:run_list]
323
+ data = data.run_list.run_list
324
+ { "run_list" => data }
325
+ elsif config[:id_only]
326
+ data.respond_to?(:name) ? data.name : data["id"]
327
+ else
328
+ data
329
+ end
330
+ end
331
+
332
+ def edit_data(data, parse_output=true)
333
+ output = Chef::JSONCompat.to_json_pretty(data)
334
+
335
+ if (!config[:no_editor])
336
+ filename = "knife-edit-"
337
+ 0.upto(20) { filename += rand(9).to_s }
338
+ filename << ".js"
339
+ filename = File.join(Dir.tmpdir, filename)
340
+ tf = File.open(filename, "w")
341
+ tf.sync = true
342
+ tf.puts output
343
+ tf.close
344
+ raise "Please set EDITOR environment variable" unless system("#{config[:editor]} #{tf.path}")
345
+ tf = File.open(filename, "r")
346
+ output = tf.gets(nil)
347
+ tf.close
348
+ File.unlink(filename)
349
+ end
350
+
351
+ parse_output ? Chef::JSONCompat.from_json(output) : output
352
+ end
353
+
354
+ def confirm(question, append_instructions=true)
355
+ return true if config[:yes]
356
+
357
+ stdout.print question
358
+ stdout.print "? (Y/N) " if append_instructions
359
+ answer = stdin.readline
360
+ answer.chomp!
361
+ case answer
362
+ when "Y", "y"
363
+ true
364
+ when "N", "n"
365
+ self.msg("You said no, so I'm done here.")
366
+ exit 3
367
+ else
368
+ self.msg("I have no idea what to do with #{answer}")
369
+ self.msg("Just say Y or N, please.")
370
+ confirm(question)
371
+ end
372
+ end
373
+
374
+ def show_usage
375
+ stdout.puts("USAGE: " + self.opt_parser.to_s)
376
+ end
377
+
378
+ def load_from_file(klass, from_file, bag=nil)
379
+ relative_path = ""
380
+ if klass == Chef::Role
381
+ relative_path = "roles"
382
+ elsif klass == Chef::Node
383
+ relative_path = "nodes"
384
+ elsif klass == Chef::DataBagItem
385
+ relative_path = "data_bags/#{bag}"
386
+ end
387
+
388
+ relative_file = File.expand_path(File.join(Dir.pwd, relative_path, from_file))
389
+ filename = nil
390
+
391
+ if file_exists_and_is_readable?(from_file)
392
+ filename = from_file
393
+ elsif file_exists_and_is_readable?(relative_file)
394
+ filename = relative_file
395
+ else
396
+ Chef::Log.fatal("Cannot find file #{from_file}")
397
+ exit 30
398
+ end
399
+
400
+ case from_file
401
+ when /\.(js|json)$/
402
+ Chef::JSONCompat.from_json(IO.read(filename))
403
+ when /\.rb$/
404
+ r = klass.new
405
+ r.from_file(filename)
406
+ r
407
+ else
408
+ Chef::Log.fatal("File must end in .js, .json, or .rb")
409
+ exit 30
410
+ end
411
+ end
412
+
413
+ def file_exists_and_is_readable?(file)
414
+ File.exists?(file) && File.readable?(file)
415
+ end
416
+
417
+ def edit_object(klass, name)
418
+ object = klass.load(name)
419
+
420
+ output = edit_data(object)
421
+
422
+ # Only make the save if the user changed the object.
423
+ #
424
+ # Output JSON for the original (object) and edited (output), then parse
425
+ # them without reconstituting the objects into real classes
426
+ # (create_additions=false). Then, compare the resulting simple objects,
427
+ # which will be Array/Hash/String/etc.
428
+ #
429
+ # We wouldn't have to do these shenanigans if all the editable objects
430
+ # implemented to_hash, or if to_json against a hash returned a string
431
+ # with stable key order.
432
+ object_parsed_again = Chef::JSONCompat.from_json(Chef::JSONCompat.to_json(object), :create_additions => false)
433
+ output_parsed_again = Chef::JSONCompat.from_json(Chef::JSONCompat.to_json(output), :create_additions => false)
434
+ if object_parsed_again != output_parsed_again
435
+ output.save
436
+ self.msg("Saved #{output}")
437
+ else
438
+ self.msg("Object unchanged, not saving")
439
+ end
440
+
441
+ output(format_for_display(object)) if config[:print_after]
442
+ end
443
+
444
+ def create_object(object, pretty_name=nil, &block)
445
+ output = edit_data(object)
446
+
447
+ if Kernel.block_given?
448
+ output = block.call(output)
449
+ else
450
+ output.save
451
+ end
452
+
453
+ pretty_name ||= output
454
+
455
+ self.msg("Created (or updated) #{pretty_name}")
456
+
457
+ output(output) if config[:print_after]
458
+ end
459
+
460
+ def delete_object(klass, name, delete_name=nil, &block)
461
+ confirm("Do you really want to delete #{name}")
462
+
463
+ if Kernel.block_given?
464
+ object = block.call
465
+ else
466
+ object = klass.load(name)
467
+ object.destroy
468
+ end
469
+
470
+ output(format_for_display(object)) if config[:print_after]
471
+
472
+ obj_name = delete_name ? "#{delete_name}[#{name}]" : object
473
+ self.msg("Deleted #{obj_name}!")
474
+ end
475
+
476
+ def bulk_delete(klass, fancy_name, delete_name=nil, list=nil, regex=nil, &block)
477
+ object_list = list ? list : klass.list(true)
478
+
479
+ if regex
480
+ to_delete = Hash.new
481
+ object_list.each_key do |object|
482
+ next if regex && object !~ /#{regex}/
483
+ to_delete[object] = object_list[object]
484
+ end
485
+ else
486
+ to_delete = object_list
487
+ end
488
+
489
+ output(format_list_for_display(to_delete))
490
+
491
+ confirm("Do you really want to delete the above items")
492
+
493
+ to_delete.each do |name, object|
494
+ if Kernel.block_given?
495
+ block.call(name, object)
496
+ else
497
+ object.destroy
498
+ end
499
+ output(format_for_display(object)) if config[:print_after]
500
+ self.msg("Deleted #{fancy_name} #{name}")
501
+ end
502
+ end
503
+
504
+ def msg(message)
505
+ stdout.puts message
506
+ end
507
+
508
+ def stdout
509
+ STDOUT
510
+ end
511
+
512
+ def stdin
513
+ STDIN
514
+ end
515
+
516
+ def rest
517
+ @rest ||= Chef::REST.new(Chef::Config[:chef_server_url])
518
+ end
519
+
520
+ end
521
+ end
522
+