knife 18.6.13 → 18.7.9

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 (673) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +23 -23
  3. data/LICENSE +201 -201
  4. data/Rakefile +33 -33
  5. data/bin/knife +24 -24
  6. data/knife.gemspec +65 -65
  7. data/lib/chef/application/knife.rb +237 -237
  8. data/lib/chef/chef_fs/knife.rb +162 -162
  9. data/lib/chef/knife/acl_add.rb +57 -57
  10. data/lib/chef/knife/acl_base.rb +183 -183
  11. data/lib/chef/knife/acl_bulk_add.rb +78 -78
  12. data/lib/chef/knife/acl_bulk_remove.rb +83 -83
  13. data/lib/chef/knife/acl_remove.rb +62 -62
  14. data/lib/chef/knife/acl_show.rb +56 -56
  15. data/lib/chef/knife/bootstrap/chef_vault_handler.rb +160 -160
  16. data/lib/chef/knife/bootstrap/client_builder.rb +212 -212
  17. data/lib/chef/knife/bootstrap/templates/README.md +11 -11
  18. data/lib/chef/knife/bootstrap/templates/chef-full.erb +249 -249
  19. data/lib/chef/knife/bootstrap/templates/windows-chef-client-msi.erb +278 -278
  20. data/lib/chef/knife/bootstrap/train_connector.rb +334 -334
  21. data/lib/chef/knife/bootstrap.rb +1229 -1224
  22. data/lib/chef/knife/client_bulk_delete.rb +104 -104
  23. data/lib/chef/knife/client_create.rb +140 -140
  24. data/lib/chef/knife/client_delete.rb +62 -62
  25. data/lib/chef/knife/client_edit.rb +52 -52
  26. data/lib/chef/knife/client_key_create.rb +73 -73
  27. data/lib/chef/knife/client_key_delete.rb +80 -80
  28. data/lib/chef/knife/client_key_edit.rb +83 -83
  29. data/lib/chef/knife/client_key_list.rb +73 -73
  30. data/lib/chef/knife/client_key_show.rb +80 -80
  31. data/lib/chef/knife/client_list.rb +41 -41
  32. data/lib/chef/knife/client_reregister.rb +58 -58
  33. data/lib/chef/knife/client_show.rb +48 -48
  34. data/lib/chef/knife/config_get.rb +39 -39
  35. data/lib/chef/knife/config_get_profile.rb +37 -37
  36. data/lib/chef/knife/config_list.rb +139 -139
  37. data/lib/chef/knife/config_list_profiles.rb +37 -37
  38. data/lib/chef/knife/config_show.rb +127 -127
  39. data/lib/chef/knife/config_use.rb +61 -61
  40. data/lib/chef/knife/config_use_profile.rb +47 -47
  41. data/lib/chef/knife/configure.rb +150 -150
  42. data/lib/chef/knife/configure_client.rb +48 -48
  43. data/lib/chef/knife/cookbook_bulk_delete.rb +71 -71
  44. data/lib/chef/knife/cookbook_delete.rb +151 -151
  45. data/lib/chef/knife/cookbook_download.rb +142 -142
  46. data/lib/chef/knife/cookbook_list.rb +47 -47
  47. data/lib/chef/knife/cookbook_metadata.rb +106 -106
  48. data/lib/chef/knife/cookbook_metadata_from_file.rb +49 -49
  49. data/lib/chef/knife/cookbook_show.rb +98 -98
  50. data/lib/chef/knife/cookbook_upload.rb +313 -313
  51. data/lib/chef/knife/core/bootstrap_context.rb +274 -274
  52. data/lib/chef/knife/core/cookbook_scm_repo.rb +159 -159
  53. data/lib/chef/knife/core/cookbook_site_streaming_uploader.rb +249 -249
  54. data/lib/chef/knife/core/formatting_options.rb +49 -49
  55. data/lib/chef/knife/core/gem_glob_loader.rb +134 -134
  56. data/lib/chef/knife/core/generic_presenter.rb +238 -238
  57. data/lib/chef/knife/core/hashed_command_loader.rb +100 -100
  58. data/lib/chef/knife/core/node_editor.rb +130 -130
  59. data/lib/chef/knife/core/node_presenter.rb +133 -133
  60. data/lib/chef/knife/core/object_loader.rb +115 -115
  61. data/lib/chef/knife/core/status_presenter.rb +147 -147
  62. data/lib/chef/knife/core/subcommand_loader.rb +208 -208
  63. data/lib/chef/knife/core/text_formatter.rb +85 -85
  64. data/lib/chef/knife/core/ui.rb +339 -339
  65. data/lib/chef/knife/core/windows_bootstrap_context.rb +441 -443
  66. data/lib/chef/knife/data_bag_create.rb +81 -81
  67. data/lib/chef/knife/data_bag_delete.rb +49 -49
  68. data/lib/chef/knife/data_bag_edit.rb +74 -74
  69. data/lib/chef/knife/data_bag_from_file.rb +113 -113
  70. data/lib/chef/knife/data_bag_list.rb +42 -42
  71. data/lib/chef/knife/data_bag_secret_options.rb +122 -122
  72. data/lib/chef/knife/data_bag_show.rb +69 -69
  73. data/lib/chef/knife/delete.rb +125 -125
  74. data/lib/chef/knife/deps.rb +156 -156
  75. data/lib/chef/knife/diff.rb +83 -83
  76. data/lib/chef/knife/download.rb +85 -85
  77. data/lib/chef/knife/edit.rb +88 -88
  78. data/lib/chef/knife/environment_compare.rb +128 -128
  79. data/lib/chef/knife/environment_create.rb +52 -52
  80. data/lib/chef/knife/environment_delete.rb +44 -44
  81. data/lib/chef/knife/environment_edit.rb +44 -44
  82. data/lib/chef/knife/environment_from_file.rb +84 -84
  83. data/lib/chef/knife/environment_list.rb +41 -41
  84. data/lib/chef/knife/environment_show.rb +47 -47
  85. data/lib/chef/knife/exec.rb +99 -99
  86. data/lib/chef/knife/group_add.rb +55 -55
  87. data/lib/chef/knife/group_create.rb +49 -49
  88. data/lib/chef/knife/group_destroy.rb +53 -53
  89. data/lib/chef/knife/group_list.rb +43 -43
  90. data/lib/chef/knife/group_remove.rb +56 -56
  91. data/lib/chef/knife/group_show.rb +49 -49
  92. data/lib/chef/knife/key_create.rb +112 -112
  93. data/lib/chef/knife/key_create_base.rb +50 -50
  94. data/lib/chef/knife/key_delete.rb +55 -55
  95. data/lib/chef/knife/key_edit.rb +118 -118
  96. data/lib/chef/knife/key_edit_base.rb +55 -55
  97. data/lib/chef/knife/key_list.rb +90 -90
  98. data/lib/chef/knife/key_list_base.rb +45 -45
  99. data/lib/chef/knife/key_show.rb +53 -53
  100. data/lib/chef/knife/license.rb +52 -52
  101. data/lib/chef/knife/list.rb +177 -177
  102. data/lib/chef/knife/node_bulk_delete.rb +75 -75
  103. data/lib/chef/knife/node_create.rb +47 -47
  104. data/lib/chef/knife/node_delete.rb +46 -46
  105. data/lib/chef/knife/node_edit.rb +70 -70
  106. data/lib/chef/knife/node_environment_set.rb +53 -53
  107. data/lib/chef/knife/node_from_file.rb +51 -51
  108. data/lib/chef/knife/node_list.rb +44 -44
  109. data/lib/chef/knife/node_policy_set.rb +79 -79
  110. data/lib/chef/knife/node_run_list_add.rb +104 -104
  111. data/lib/chef/knife/node_run_list_remove.rb +67 -67
  112. data/lib/chef/knife/node_run_list_set.rb +66 -66
  113. data/lib/chef/knife/node_show.rb +63 -63
  114. data/lib/chef/knife/null.rb +12 -12
  115. data/lib/chef/knife/org_create.rb +70 -70
  116. data/lib/chef/knife/org_delete.rb +32 -32
  117. data/lib/chef/knife/org_edit.rb +48 -48
  118. data/lib/chef/knife/org_list.rb +44 -44
  119. data/lib/chef/knife/org_show.rb +31 -31
  120. data/lib/chef/knife/org_user_add.rb +62 -62
  121. data/lib/chef/knife/org_user_remove.rb +103 -103
  122. data/lib/chef/knife/raw.rb +123 -123
  123. data/lib/chef/knife/recipe_list.rb +32 -32
  124. data/lib/chef/knife/rehash.rb +50 -50
  125. data/lib/chef/knife/role_bulk_delete.rb +66 -66
  126. data/lib/chef/knife/role_create.rb +53 -53
  127. data/lib/chef/knife/role_delete.rb +46 -46
  128. data/lib/chef/knife/role_edit.rb +45 -45
  129. data/lib/chef/knife/role_env_run_list_add.rb +87 -87
  130. data/lib/chef/knife/role_env_run_list_clear.rb +55 -55
  131. data/lib/chef/knife/role_env_run_list_remove.rb +57 -57
  132. data/lib/chef/knife/role_env_run_list_replace.rb +60 -60
  133. data/lib/chef/knife/role_env_run_list_set.rb +70 -70
  134. data/lib/chef/knife/role_from_file.rb +51 -51
  135. data/lib/chef/knife/role_list.rb +42 -42
  136. data/lib/chef/knife/role_run_list_add.rb +87 -87
  137. data/lib/chef/knife/role_run_list_clear.rb +55 -55
  138. data/lib/chef/knife/role_run_list_remove.rb +56 -56
  139. data/lib/chef/knife/role_run_list_replace.rb +60 -60
  140. data/lib/chef/knife/role_run_list_set.rb +69 -69
  141. data/lib/chef/knife/role_show.rb +48 -48
  142. data/lib/chef/knife/search.rb +194 -194
  143. data/lib/chef/knife/serve.rb +65 -65
  144. data/lib/chef/knife/show.rb +72 -72
  145. data/lib/chef/knife/ssh.rb +657 -657
  146. data/lib/chef/knife/ssl_check.rb +284 -284
  147. data/lib/chef/knife/ssl_fetch.rb +162 -162
  148. data/lib/chef/knife/status.rb +95 -95
  149. data/lib/chef/knife/supermarket_download.rb +119 -119
  150. data/lib/chef/knife/supermarket_install.rb +192 -192
  151. data/lib/chef/knife/supermarket_list.rb +76 -76
  152. data/lib/chef/knife/supermarket_search.rb +53 -53
  153. data/lib/chef/knife/supermarket_share.rb +166 -166
  154. data/lib/chef/knife/supermarket_show.rb +66 -66
  155. data/lib/chef/knife/supermarket_unshare.rb +62 -62
  156. data/lib/chef/knife/tag_create.rb +52 -52
  157. data/lib/chef/knife/tag_delete.rb +60 -60
  158. data/lib/chef/knife/tag_list.rb +47 -47
  159. data/lib/chef/knife/upload.rb +87 -87
  160. data/lib/chef/knife/user_create.rb +180 -180
  161. data/lib/chef/knife/user_delete.rb +151 -151
  162. data/lib/chef/knife/user_dissociate.rb +42 -42
  163. data/lib/chef/knife/user_edit.rb +94 -94
  164. data/lib/chef/knife/user_invite_add.rb +43 -43
  165. data/lib/chef/knife/user_invite_list.rb +34 -34
  166. data/lib/chef/knife/user_invite_rescind.rb +63 -63
  167. data/lib/chef/knife/user_key_create.rb +73 -73
  168. data/lib/chef/knife/user_key_delete.rb +80 -80
  169. data/lib/chef/knife/user_key_edit.rb +83 -83
  170. data/lib/chef/knife/user_key_list.rb +73 -73
  171. data/lib/chef/knife/user_key_show.rb +80 -80
  172. data/lib/chef/knife/user_list.rb +43 -43
  173. data/lib/chef/knife/user_password.rb +70 -70
  174. data/lib/chef/knife/user_reregister.rb +59 -59
  175. data/lib/chef/knife/user_show.rb +52 -52
  176. data/lib/chef/knife/version.rb +24 -24
  177. data/lib/chef/knife/xargs.rb +282 -282
  178. data/lib/chef/knife/yaml_convert.rb +91 -91
  179. data/lib/chef/knife.rb +677 -677
  180. data/lib/chef/utils/licensing_config.rb +9 -9
  181. data/lib/chef/utils/licensing_handler.rb +72 -53
  182. data/spec/data/apt/chef-integration-test-1.0/debian/changelog +5 -5
  183. data/spec/data/apt/chef-integration-test-1.0/debian/compat +1 -1
  184. data/spec/data/apt/chef-integration-test-1.0/debian/control +13 -13
  185. data/spec/data/apt/chef-integration-test-1.0/debian/copyright +34 -34
  186. data/spec/data/apt/chef-integration-test-1.0/debian/files +1 -1
  187. data/spec/data/apt/chef-integration-test-1.0/debian/rules +13 -13
  188. data/spec/data/apt/chef-integration-test-1.0/debian/source/format +1 -1
  189. data/spec/data/apt/chef-integration-test-1.1/debian/changelog +11 -11
  190. data/spec/data/apt/chef-integration-test-1.1/debian/compat +1 -1
  191. data/spec/data/apt/chef-integration-test-1.1/debian/control +13 -13
  192. data/spec/data/apt/chef-integration-test-1.1/debian/copyright +34 -34
  193. data/spec/data/apt/chef-integration-test-1.1/debian/files +1 -1
  194. data/spec/data/apt/chef-integration-test-1.1/debian/rules +13 -13
  195. data/spec/data/apt/chef-integration-test-1.1/debian/source/format +1 -1
  196. data/spec/data/apt/chef-integration-test2-1.0/debian/changelog +5 -5
  197. data/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2/DEBIAN/conffiles +1 -1
  198. data/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2/DEBIAN/control +10 -10
  199. data/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2/DEBIAN/md5sums +1 -1
  200. data/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2.debhelper.log +45 -45
  201. data/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2.substvars +1 -1
  202. data/spec/data/apt/chef-integration-test2-1.0/debian/compat +1 -1
  203. data/spec/data/apt/chef-integration-test2-1.0/debian/conffiles +1 -1
  204. data/spec/data/apt/chef-integration-test2-1.0/debian/control +13 -13
  205. data/spec/data/apt/chef-integration-test2-1.0/debian/copyright +34 -34
  206. data/spec/data/apt/chef-integration-test2-1.0/debian/files +1 -1
  207. data/spec/data/apt/chef-integration-test2-1.0/debian/rules +13 -13
  208. data/spec/data/apt/chef-integration-test2-1.0/debian/source/format +1 -1
  209. data/spec/data/apt/chef-integration-test2_1.0-1.dsc +18 -18
  210. data/spec/data/apt/chef-integration-test2_1.0-1_amd64.build +91 -91
  211. data/spec/data/apt/chef-integration-test2_1.0-1_amd64.changes +31 -31
  212. data/spec/data/apt/chef-integration-test_1.0-1_amd64.changes +22 -22
  213. data/spec/data/apt/chef-integration-test_1.1-1_amd64.changes +22 -22
  214. data/spec/data/apt/var/www/apt/conf/distributions +7 -7
  215. data/spec/data/apt/var/www/apt/conf/incoming +4 -4
  216. data/spec/data/apt/var/www/apt/conf/pulls +3 -3
  217. data/spec/data/apt/var/www/apt/db/version +4 -4
  218. data/spec/data/apt/var/www/apt/dists/sid/Release +19 -19
  219. data/spec/data/apt/var/www/apt/dists/sid/main/binary-amd64/Packages +16 -16
  220. data/spec/data/apt/var/www/apt/dists/sid/main/binary-amd64/Release +5 -5
  221. data/spec/data/bootstrap/encrypted_data_bag_secret +1 -1
  222. data/spec/data/bootstrap/no_proxy.erb +2 -2
  223. data/spec/data/bootstrap/secret.erb +9 -9
  224. data/spec/data/bootstrap/test-hints.erb +12 -12
  225. data/spec/data/bootstrap/test.erb +1 -1
  226. data/spec/data/cb_version_cookbooks/tatft/README.rdoc +2 -2
  227. data/spec/data/cb_version_cookbooks/tatft/attributes/default.rb +1 -1
  228. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-600hhz-0 +1 -1
  229. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-ahd2gq-0 +1 -1
  230. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-api8ux-0 +1 -1
  231. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-b0r1m1-0 +1 -1
  232. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-bfygsi-0 +1 -1
  233. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-el14l6-0 +1 -1
  234. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-ivrl3y-0 +1 -1
  235. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-kkbs85-0 +1 -1
  236. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-ory1ux-0 +1 -1
  237. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-pgsq76-0 +1 -1
  238. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-ra8uim-0 +1 -1
  239. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-t7k1g-0 +1 -1
  240. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-t8g0sv-0 +1 -1
  241. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-ufy6g3-0 +1 -1
  242. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-x2d6j9-0 +1 -1
  243. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-xi0l6h-0 +1 -1
  244. data/spec/data/client.d_00/00-foo.rb +2 -2
  245. data/spec/data/client.d_00/01-bar.rb +1 -1
  246. data/spec/data/client.d_00/02-strings.rb +2 -2
  247. data/spec/data/client.d_00/bar +1 -1
  248. data/spec/data/client.d_01/foo/bar.rb +1 -1
  249. data/spec/data/client.d_02/foo.rb/foo.txt +1 -1
  250. data/spec/data/config.rb +6 -6
  251. data/spec/data/cookbooks/angrybash/metadata.rb +2 -2
  252. data/spec/data/cookbooks/angrybash/recipes/default.rb +8 -8
  253. data/spec/data/cookbooks/apache2/files/default/apache2_module_conf_generate.pl +2 -2
  254. data/spec/data/cookbooks/apache2/metadata.json +33 -33
  255. data/spec/data/cookbooks/apache2/metadata.rb +2 -2
  256. data/spec/data/cookbooks/apache2/recipes/default.rb +2 -2
  257. data/spec/data/cookbooks/borken/metadata.rb +2 -2
  258. data/spec/data/cookbooks/borken/recipes/default.rb +1 -1
  259. data/spec/data/cookbooks/borken/templates/default/borken.erb +1 -1
  260. data/spec/data/cookbooks/chefignore +8 -8
  261. data/spec/data/cookbooks/ignorken/files/default/not_me.rb +2 -2
  262. data/spec/data/cookbooks/ignorken/metadata.rb +2 -2
  263. data/spec/data/cookbooks/ignorken/recipes/ignoreme.rb +1 -1
  264. data/spec/data/cookbooks/ignorken/templates/ubuntu-12.10/not_me.rb +2 -2
  265. data/spec/data/cookbooks/irssi/files/default/irssi.response +2 -2
  266. data/spec/data/cookbooks/java/files/default/java.response +1 -1
  267. data/spec/data/cookbooks/java/metadata.json +33 -33
  268. data/spec/data/cookbooks/java/metadata.rb +2 -2
  269. data/spec/data/cookbooks/name-mismatch-versionnumber/README.md +4 -4
  270. data/spec/data/cookbooks/name-mismatch-versionnumber/metadata.rb +8 -8
  271. data/spec/data/cookbooks/name-mismatch-versionnumber/recipes/default.rb +8 -8
  272. data/spec/data/cookbooks/openldap/attributes/default.rb +16 -16
  273. data/spec/data/cookbooks/openldap/attributes/smokey.rb +1 -1
  274. data/spec/data/cookbooks/openldap/definitions/client.rb +5 -5
  275. data/spec/data/cookbooks/openldap/definitions/server.rb +5 -5
  276. data/spec/data/cookbooks/openldap/files/default/.dotfile +1 -1
  277. data/spec/data/cookbooks/openldap/files/default/remotedir/.a_dotdir/.a_dotfile_in_a_dotdir +1 -1
  278. data/spec/data/cookbooks/openldap/files/default/remotedir/not_a_template.erb +2 -2
  279. data/spec/data/cookbooks/openldap/files/default/remotedir/remote_dir_file1.txt +2 -2
  280. data/spec/data/cookbooks/openldap/files/default/remotedir/remote_dir_file2.txt +2 -2
  281. data/spec/data/cookbooks/openldap/files/default/remotedir/remotesubdir/.a_dotfile +1 -1
  282. data/spec/data/cookbooks/openldap/files/default/remotedir/remotesubdir/remote_subdir_file1.txt +2 -2
  283. data/spec/data/cookbooks/openldap/files/default/remotedir/remotesubdir/remote_subdir_file2.txt +2 -2
  284. data/spec/data/cookbooks/openldap/files/default/remotedir/subdir_with_no_file_just_a_subsubdir/the_subsubdir/some_file.txt +3 -3
  285. data/spec/data/cookbooks/openldap/libraries/openldap/version.rb +3 -3
  286. data/spec/data/cookbooks/openldap/libraries/openldap.rb +4 -4
  287. data/spec/data/cookbooks/openldap/metadata.rb +8 -8
  288. data/spec/data/cookbooks/openldap/recipes/default.rb +4 -4
  289. data/spec/data/cookbooks/openldap/recipes/gigantor.rb +3 -3
  290. data/spec/data/cookbooks/openldap/recipes/one.rb +15 -15
  291. data/spec/data/cookbooks/openldap/recipes/return.rb +2 -2
  292. data/spec/data/cookbooks/openldap/templates/default/helper_test.erb +1 -1
  293. data/spec/data/cookbooks/openldap/templates/default/helpers.erb +14 -14
  294. data/spec/data/cookbooks/openldap/templates/default/helpers_via_partial_test.erb +1 -1
  295. data/spec/data/cookbooks/openldap/templates/default/nested_openldap_partials.erb +1 -1
  296. data/spec/data/cookbooks/openldap/templates/default/nested_partial.erb +1 -1
  297. data/spec/data/cookbooks/openldap/templates/default/no_windows_line_endings.erb +4 -4
  298. data/spec/data/cookbooks/openldap/templates/default/openldap_nested_variable_stuff.erb +1 -1
  299. data/spec/data/cookbooks/openldap/templates/default/openldap_variable_stuff.conf.erb +1 -1
  300. data/spec/data/cookbooks/openldap/templates/default/test.erb +1 -1
  301. data/spec/data/cookbooks/preseed/files/default/preseed-file.seed +1 -1
  302. data/spec/data/cookbooks/preseed/files/default/preseed-template.seed +4 -4
  303. data/spec/data/cookbooks/preseed/metadata.rb +2 -2
  304. data/spec/data/cookbooks/preseed/templates/default/preseed-template-variables.seed +1 -1
  305. data/spec/data/cookbooks/preseed/templates/default/preseed-template.seed +1 -1
  306. data/spec/data/cookbooks/starter/chefignore +8 -8
  307. data/spec/data/cookbooks/starter/metadata.rb +2 -2
  308. data/spec/data/cookbooks/starter/recipes/default.rb +4 -4
  309. data/spec/data/cookbooks/supports-platform-constraints/metadata.rb +5 -5
  310. data/spec/data/cookbooks/wget/files/default/wget.response +2 -2
  311. data/spec/data/definitions/test.rb +4 -4
  312. data/spec/data/environment-config.rb +4 -4
  313. data/spec/data/file-providers-method-snapshot-chef-11-4.json +127 -127
  314. data/spec/data/fileedit/hosts +4 -4
  315. data/spec/data/incomplete-metadata-chef-repo/incomplete-metadata/README.md +4 -4
  316. data/spec/data/incomplete-metadata-chef-repo/incomplete-metadata/metadata.rb +13 -13
  317. data/spec/data/incomplete-metadata-chef-repo/incomplete-metadata/recipes/default.rb +8 -8
  318. data/spec/data/invalid-metadata-chef-repo/invalid-metadata/README.md +4 -4
  319. data/spec/data/invalid-metadata-chef-repo/invalid-metadata/metadata.rb +9 -9
  320. data/spec/data/invalid-metadata-chef-repo/invalid-metadata/recipes/default.rb +8 -8
  321. data/spec/data/kitchen/chefignore +6 -6
  322. data/spec/data/kitchen/openldap/attributes/default.rb +3 -3
  323. data/spec/data/kitchen/openldap/attributes/robinson.rb +2 -2
  324. data/spec/data/kitchen/openldap/definitions/client.rb +3 -3
  325. data/spec/data/kitchen/openldap/definitions/drewbarrymore.rb +2 -2
  326. data/spec/data/kitchen/openldap/recipes/gigantor.rb +2 -2
  327. data/spec/data/kitchen/openldap/recipes/ignoreme.rb +2 -2
  328. data/spec/data/kitchen/openldap/recipes/woot.rb +3 -3
  329. data/spec/data/knife_subcommand/test_explicit_category.rb +6 -6
  330. data/spec/data/knife_subcommand/test_name_mapping.rb +4 -4
  331. data/spec/data/knife_subcommand/test_yourself.rb +21 -21
  332. data/spec/data/lwrp/providers/buck_passer.rb +28 -28
  333. data/spec/data/lwrp/providers/buck_passer_2.rb +26 -26
  334. data/spec/data/lwrp/providers/embedded_resource_accesses_providers_scope.rb +28 -28
  335. data/spec/data/lwrp/providers/inline_compiler.rb +24 -24
  336. data/spec/data/lwrp/providers/monkey_name_printer.rb +5 -5
  337. data/spec/data/lwrp/providers/paint_drying_watcher.rb +7 -7
  338. data/spec/data/lwrp/providers/thumb_twiddler.rb +7 -7
  339. data/spec/data/lwrp/resources/bar.rb +4 -4
  340. data/spec/data/lwrp/resources/buck_passer.rb +6 -6
  341. data/spec/data/lwrp/resources/buck_passer_2.rb +4 -4
  342. data/spec/data/lwrp/resources/embedded_resource_accesses_providers_scope.rb +4 -4
  343. data/spec/data/lwrp/resources/foo.rb +6 -6
  344. data/spec/data/lwrp/resources/inline_compiler.rb +4 -4
  345. data/spec/data/lwrp/resources/monkey_name_printer.rb +6 -6
  346. data/spec/data/lwrp/resources/paint_drying_watcher.rb +4 -4
  347. data/spec/data/lwrp/resources/thumb_twiddler.rb +4 -4
  348. data/spec/data/lwrp/resources_with_default_attributes/nodeattr.rb +3 -3
  349. data/spec/data/lwrp_const_scoping/resources/conflict.rb +1 -1
  350. data/spec/data/lwrp_override/providers/buck_passer.rb +5 -5
  351. data/spec/data/lwrp_override/resources/foo.rb +11 -11
  352. data/spec/data/mac_users/10.9.plist.xml +560 -560
  353. data/spec/data/mac_users/10.9.shadow.xml +21 -21
  354. data/spec/data/metadata/quick_start/metadata.rb +14 -14
  355. data/spec/data/mixin/invalid_data.rb +3 -3
  356. data/spec/data/mixin/real_data.rb +2 -2
  357. data/spec/data/nested.json +2 -2
  358. data/spec/data/nodes/default.rb +15 -15
  359. data/spec/data/nodes/test.example.com.rb +17 -17
  360. data/spec/data/nodes/test.rb +15 -15
  361. data/spec/data/null_config.rb +1 -1
  362. data/spec/data/object_loader/environments/test.json +7 -7
  363. data/spec/data/object_loader/environments/test.rb +2 -2
  364. data/spec/data/object_loader/environments/test_json_class.json +8 -8
  365. data/spec/data/object_loader/nodes/test.json +7 -7
  366. data/spec/data/object_loader/nodes/test.rb +2 -2
  367. data/spec/data/object_loader/nodes/test_json_class.json +8 -8
  368. data/spec/data/object_loader/roles/test.json +7 -7
  369. data/spec/data/object_loader/roles/test.rb +2 -2
  370. data/spec/data/object_loader/roles/test_json_class.json +8 -8
  371. data/spec/data/partial_one.erb +1 -1
  372. data/spec/data/prefer_metadata_json/metadata.json +51 -51
  373. data/spec/data/prefer_metadata_json/metadata.rb +6 -6
  374. data/spec/data/recipes/test.rb +7 -7
  375. data/spec/data/root_alias_cookbooks/dup_attr/attributes/default.rb +1 -1
  376. data/spec/data/root_alias_cookbooks/dup_attr/attributes.rb +1 -1
  377. data/spec/data/root_alias_cookbooks/dup_attr/metadata.rb +2 -2
  378. data/spec/data/root_alias_cookbooks/dup_attr/recipe.rb +3 -3
  379. data/spec/data/root_alias_cookbooks/dup_recipe/attributes.rb +1 -1
  380. data/spec/data/root_alias_cookbooks/dup_recipe/metadata.rb +2 -2
  381. data/spec/data/root_alias_cookbooks/dup_recipe/recipe.rb +3 -3
  382. data/spec/data/root_alias_cookbooks/dup_recipe/recipes/default.rb +3 -3
  383. data/spec/data/root_alias_cookbooks/simple/attributes.rb +1 -1
  384. data/spec/data/root_alias_cookbooks/simple/metadata.rb +2 -2
  385. data/spec/data/root_alias_cookbooks/simple/recipe.rb +3 -3
  386. data/spec/data/rubygems.org/sexp_processor-info +49 -49
  387. data/spec/data/run_context/cookbooks/circular-dep1/attributes/default.rb +2 -2
  388. data/spec/data/run_context/cookbooks/circular-dep1/definitions/circular_dep1_res.rb +1 -1
  389. data/spec/data/run_context/cookbooks/circular-dep1/libraries/lib.rb +2 -2
  390. data/spec/data/run_context/cookbooks/circular-dep1/metadata.rb +2 -2
  391. data/spec/data/run_context/cookbooks/circular-dep1/providers/provider.rb +1 -1
  392. data/spec/data/run_context/cookbooks/circular-dep1/resources/resource.rb +2 -2
  393. data/spec/data/run_context/cookbooks/circular-dep2/attributes/default.rb +2 -2
  394. data/spec/data/run_context/cookbooks/circular-dep2/definitions/circular_dep2_res.rb +1 -1
  395. data/spec/data/run_context/cookbooks/circular-dep2/libraries/lib.rb +2 -2
  396. data/spec/data/run_context/cookbooks/circular-dep2/metadata.rb +2 -2
  397. data/spec/data/run_context/cookbooks/circular-dep2/providers/provider.rb +1 -1
  398. data/spec/data/run_context/cookbooks/circular-dep2/resources/resource.rb +2 -2
  399. data/spec/data/run_context/cookbooks/dependency1/attributes/aa_first.rb +2 -2
  400. data/spec/data/run_context/cookbooks/dependency1/attributes/default.rb +2 -2
  401. data/spec/data/run_context/cookbooks/dependency1/attributes/unparsed_file +1 -1
  402. data/spec/data/run_context/cookbooks/dependency1/attributes/zz_last.rb +2 -2
  403. data/spec/data/run_context/cookbooks/dependency1/definitions/dependency1_res.rb +1 -1
  404. data/spec/data/run_context/cookbooks/dependency1/definitions/unparsed_file +1 -1
  405. data/spec/data/run_context/cookbooks/dependency1/libraries/lib.rb +2 -2
  406. data/spec/data/run_context/cookbooks/dependency1/libraries/unparsed_file +1 -1
  407. data/spec/data/run_context/cookbooks/dependency1/providers/provider.rb +1 -1
  408. data/spec/data/run_context/cookbooks/dependency1/providers/unparsed_file +1 -1
  409. data/spec/data/run_context/cookbooks/dependency1/recipes/unparsed_file +1 -1
  410. data/spec/data/run_context/cookbooks/dependency1/resources/resource.rb +2 -2
  411. data/spec/data/run_context/cookbooks/dependency1/resources/unparsed_file +1 -1
  412. data/spec/data/run_context/cookbooks/dependency2/attributes/default.rb +2 -2
  413. data/spec/data/run_context/cookbooks/dependency2/definitions/dependency2_res.rb +1 -1
  414. data/spec/data/run_context/cookbooks/dependency2/libraries/lib.rb +2 -2
  415. data/spec/data/run_context/cookbooks/dependency2/providers/provider.rb +1 -1
  416. data/spec/data/run_context/cookbooks/dependency2/resources/resource.rb +2 -2
  417. data/spec/data/run_context/cookbooks/include/recipes/default.rb +24 -24
  418. data/spec/data/run_context/cookbooks/include/recipes/includee.rb +3 -3
  419. data/spec/data/run_context/cookbooks/no-default-attr/attributes/server.rb +2 -2
  420. data/spec/data/run_context/cookbooks/no-default-attr/definitions/no_default-attr_res.rb +1 -1
  421. data/spec/data/run_context/cookbooks/no-default-attr/providers/provider.rb +1 -1
  422. data/spec/data/run_context/cookbooks/no-default-attr/resources/resource.rb +2 -2
  423. data/spec/data/run_context/cookbooks/test/attributes/george.rb +1 -1
  424. data/spec/data/run_context/cookbooks/test/definitions/new_animals.rb +9 -9
  425. data/spec/data/run_context/cookbooks/test/definitions/new_cat.rb +5 -5
  426. data/spec/data/run_context/cookbooks/test/definitions/test_res.rb +1 -1
  427. data/spec/data/run_context/cookbooks/test/providers/provider.rb +1 -1
  428. data/spec/data/run_context/cookbooks/test/recipes/default.rb +5 -5
  429. data/spec/data/run_context/cookbooks/test/recipes/one.rb +7 -7
  430. data/spec/data/run_context/cookbooks/test/recipes/two.rb +7 -7
  431. data/spec/data/run_context/cookbooks/test/resources/resource.rb +3 -3
  432. data/spec/data/run_context/cookbooks/test-with-circular-deps/attributes/default.rb +2 -2
  433. data/spec/data/run_context/cookbooks/test-with-circular-deps/definitions/test_with-circular-deps_res.rb +1 -1
  434. data/spec/data/run_context/cookbooks/test-with-circular-deps/libraries/lib.rb +2 -2
  435. data/spec/data/run_context/cookbooks/test-with-circular-deps/metadata.rb +2 -2
  436. data/spec/data/run_context/cookbooks/test-with-circular-deps/providers/provider.rb +1 -1
  437. data/spec/data/run_context/cookbooks/test-with-circular-deps/resources/resource.rb +3 -3
  438. data/spec/data/run_context/cookbooks/test-with-deps/attributes/default.rb +2 -2
  439. data/spec/data/run_context/cookbooks/test-with-deps/definitions/test_with-deps_res.rb +1 -1
  440. data/spec/data/run_context/cookbooks/test-with-deps/libraries/lib.rb +1 -1
  441. data/spec/data/run_context/cookbooks/test-with-deps/metadata.rb +3 -3
  442. data/spec/data/run_context/cookbooks/test-with-deps/providers/provider.rb +1 -1
  443. data/spec/data/run_context/cookbooks/test-with-deps/resources/resource.rb +2 -2
  444. data/spec/data/run_context/nodes/run_context.rb +5 -5
  445. data/spec/data/sample_msu1.xml +10 -10
  446. data/spec/data/sample_msu2.xml +14 -14
  447. data/spec/data/sample_msu3.xml +16 -16
  448. data/spec/data/search_queries_to_transform.txt +98 -98
  449. data/spec/data/shef-config.rb +11 -11
  450. data/spec/data/snap_package/async_result_success.json +6 -6
  451. data/spec/data/snap_package/change_id_result.json +175 -175
  452. data/spec/data/snap_package/find_result_failure.json +10 -10
  453. data/spec/data/snap_package/find_result_success.json +70 -70
  454. data/spec/data/snap_package/get_by_name_result_failure.json +10 -10
  455. data/spec/data/snap_package/get_by_name_result_success.json +38 -38
  456. data/spec/data/snap_package/get_conf_success.json +10 -10
  457. data/spec/data/snap_package/result_failure.json +9 -9
  458. data/spec/data/ssl/5e707473.0 +18 -18
  459. data/spec/data/ssl/chef-rspec.cert +27 -27
  460. data/spec/data/ssl/chef-rspec.key +27 -27
  461. data/spec/data/ssl/key.pem +15 -15
  462. data/spec/data/ssl/private_key.pem +27 -27
  463. data/spec/data/ssl/private_key_with_whitespace.pem +32 -32
  464. data/spec/data/standalone_cookbook/chefignore +9 -9
  465. data/spec/data/standalone_cookbook/recipes/default.rb +2 -2
  466. data/spec/data/templates/failed.erb +5 -5
  467. data/spec/data/trusted_certs/example.crt +22 -22
  468. data/spec/data/trusted_certs/example_no_cn.crt +36 -36
  469. data/spec/data/trusted_certs/intermediate.pem +27 -27
  470. data/spec/data/trusted_certs/opscode.pem +57 -57
  471. data/spec/data/trusted_certs/root.pem +22 -22
  472. data/spec/data/windows_certificates/base64_test.cer +20 -20
  473. data/spec/data/windows_certificates/othertest.cer +20 -20
  474. data/spec/data/windows_certificates/test.cer +20 -20
  475. data/spec/data/windows_certificates/test.pem +20 -20
  476. data/spec/functional/configure_spec.rb +33 -33
  477. data/spec/functional/cookbook_delete_spec.rb +157 -156
  478. data/spec/functional/exec_spec.rb +55 -55
  479. data/spec/functional/rehash_spec.rb +39 -39
  480. data/spec/functional/smoke_test.rb +42 -42
  481. data/spec/functional/ssh_spec.rb +352 -352
  482. data/spec/functional/version_spec.rb +26 -26
  483. data/spec/integration/chef_fs_data_store_spec.rb +557 -557
  484. data/spec/integration/chef_repo_path_spec.rb +962 -962
  485. data/spec/integration/chef_repository_file_system_spec.rb +200 -200
  486. data/spec/integration/chefignore_spec.rb +301 -301
  487. data/spec/integration/client_bulk_delete_spec.rb +131 -131
  488. data/spec/integration/client_create_spec.rb +70 -70
  489. data/spec/integration/client_delete_spec.rb +64 -64
  490. data/spec/integration/client_key_create_spec.rb +66 -66
  491. data/spec/integration/client_key_delete_spec.rb +43 -43
  492. data/spec/integration/client_key_list_spec.rb +61 -61
  493. data/spec/integration/client_key_show_spec.rb +45 -45
  494. data/spec/integration/client_list_spec.rb +49 -49
  495. data/spec/integration/client_show_spec.rb +37 -37
  496. data/spec/integration/commands_spec.rb +55 -55
  497. data/spec/integration/common_options_spec.rb +174 -174
  498. data/spec/integration/config_list_spec.rb +220 -220
  499. data/spec/integration/config_show_spec.rb +192 -192
  500. data/spec/integration/config_use_spec.rb +198 -198
  501. data/spec/integration/cookbook_api_ipv6_spec.rb +113 -113
  502. data/spec/integration/cookbook_bulk_delete_spec.rb +65 -65
  503. data/spec/integration/cookbook_download_spec.rb +72 -72
  504. data/spec/integration/cookbook_list_spec.rb +55 -55
  505. data/spec/integration/cookbook_show_spec.rb +149 -149
  506. data/spec/integration/cookbook_upload_spec.rb +128 -128
  507. data/spec/integration/data_bag_create_spec.rb +125 -125
  508. data/spec/integration/data_bag_delete_spec.rb +59 -59
  509. data/spec/integration/data_bag_edit_spec.rb +105 -105
  510. data/spec/integration/data_bag_from_file_spec.rb +116 -116
  511. data/spec/integration/data_bag_list_spec.rb +44 -44
  512. data/spec/integration/data_bag_show_spec.rb +95 -95
  513. data/spec/integration/delete_spec.rb +1018 -1018
  514. data/spec/integration/deps_spec.rb +703 -703
  515. data/spec/integration/diff_spec.rb +605 -605
  516. data/spec/integration/download_spec.rb +1336 -1336
  517. data/spec/integration/environment_compare_spec.rb +75 -75
  518. data/spec/integration/environment_create_spec.rb +41 -41
  519. data/spec/integration/environment_delete_spec.rb +37 -37
  520. data/spec/integration/environment_from_file_spec.rb +116 -116
  521. data/spec/integration/environment_list_spec.rb +42 -42
  522. data/spec/integration/environment_show_spec.rb +77 -77
  523. data/spec/integration/list_spec.rb +1060 -1060
  524. data/spec/integration/node_bulk_delete_spec.rb +52 -52
  525. data/spec/integration/node_create_spec.rb +47 -47
  526. data/spec/integration/node_delete_spec.rb +48 -48
  527. data/spec/integration/node_environment_set_spec.rb +46 -46
  528. data/spec/integration/node_from_file_spec.rb +59 -59
  529. data/spec/integration/node_list_spec.rb +45 -45
  530. data/spec/integration/node_run_list_add_spec.rb +54 -54
  531. data/spec/integration/node_run_list_remove_spec.rb +36 -36
  532. data/spec/integration/node_run_list_set_spec.rb +41 -41
  533. data/spec/integration/node_show_spec.rb +36 -36
  534. data/spec/integration/raw_spec.rb +297 -297
  535. data/spec/integration/redirection_spec.rb +64 -64
  536. data/spec/integration/role_bulk_delete_spec.rb +52 -52
  537. data/spec/integration/role_create_spec.rb +41 -41
  538. data/spec/integration/role_delete_spec.rb +48 -48
  539. data/spec/integration/role_from_file_spec.rb +96 -96
  540. data/spec/integration/role_list_spec.rb +45 -45
  541. data/spec/integration/role_show_spec.rb +51 -51
  542. data/spec/integration/search_node_spec.rb +40 -40
  543. data/spec/integration/serve_spec.rb +92 -92
  544. data/spec/integration/show_spec.rb +197 -197
  545. data/spec/integration/upload_spec.rb +1616 -1616
  546. data/spec/knife_spec_helper.rb +241 -241
  547. data/spec/support/chef_helpers.rb +79 -79
  548. data/spec/support/key_helpers.rb +102 -102
  549. data/spec/support/platform_helpers.rb +255 -255
  550. data/spec/support/platforms/prof/gc.rb +51 -51
  551. data/spec/support/platforms/prof/win32.rb +45 -45
  552. data/spec/support/platforms/win32/spec_service.rb +57 -57
  553. data/spec/support/recipe_dsl_helper.rb +83 -83
  554. data/spec/support/shared/context/config.rb +18 -18
  555. data/spec/support/shared/functional/knife.rb +37 -37
  556. data/spec/support/shared/integration/integration_helper.rb +122 -122
  557. data/spec/support/shared/integration/knife_support.rb +192 -192
  558. data/spec/support/shared/matchers/exit_with_code.rb +32 -32
  559. data/spec/support/shared/matchers/match_environment_variable.rb +17 -17
  560. data/spec/support/shared/unit/knife_shared.rb +39 -39
  561. data/spec/support/shared/unit/mock_shellout.rb +49 -49
  562. data/spec/tiny_server.rb +193 -190
  563. data/spec/unit/application/knife_spec.rb +241 -241
  564. data/spec/unit/knife/bootstrap/chef_vault_handler_spec.rb +152 -152
  565. data/spec/unit/knife/bootstrap/client_builder_spec.rb +207 -207
  566. data/spec/unit/knife/bootstrap/train_connector_spec.rb +244 -244
  567. data/spec/unit/knife/bootstrap_spec.rb +2311 -2288
  568. data/spec/unit/knife/client_bulk_delete_spec.rb +166 -166
  569. data/spec/unit/knife/client_create_spec.rb +232 -232
  570. data/spec/unit/knife/client_delete_spec.rb +99 -99
  571. data/spec/unit/knife/client_edit_spec.rb +53 -53
  572. data/spec/unit/knife/client_list_spec.rb +34 -34
  573. data/spec/unit/knife/client_reregister_spec.rb +62 -62
  574. data/spec/unit/knife/client_show_spec.rb +52 -52
  575. data/spec/unit/knife/configure_client_spec.rb +81 -81
  576. data/spec/unit/knife/configure_spec.rb +190 -190
  577. data/spec/unit/knife/cookbook_bulk_delete_spec.rb +87 -87
  578. data/spec/unit/knife/cookbook_delete_spec.rb +239 -239
  579. data/spec/unit/knife/cookbook_download_spec.rb +255 -255
  580. data/spec/unit/knife/cookbook_list_spec.rb +88 -88
  581. data/spec/unit/knife/cookbook_metadata_from_file_spec.rb +72 -72
  582. data/spec/unit/knife/cookbook_metadata_spec.rb +182 -182
  583. data/spec/unit/knife/cookbook_show_spec.rb +253 -253
  584. data/spec/unit/knife/cookbook_upload_spec.rb +426 -426
  585. data/spec/unit/knife/core/bootstrap_context_spec.rb +287 -287
  586. data/spec/unit/knife/core/cookbook_scm_repo_spec.rb +187 -187
  587. data/spec/unit/knife/core/cookbook_site_streaming_uploader_spec.rb +198 -198
  588. data/spec/unit/knife/core/gem_glob_loader_spec.rb +242 -242
  589. data/spec/unit/knife/core/hashed_command_loader_spec.rb +112 -112
  590. data/spec/unit/knife/core/node_editor_spec.rb +211 -211
  591. data/spec/unit/knife/core/object_loader_spec.rb +81 -81
  592. data/spec/unit/knife/core/status_presenter_spec.rb +54 -54
  593. data/spec/unit/knife/core/subcommand_loader_spec.rb +64 -64
  594. data/spec/unit/knife/core/ui_spec.rb +656 -656
  595. data/spec/unit/knife/core/windows_bootstrap_context_spec.rb +282 -238
  596. data/spec/unit/knife/data_bag_create_spec.rb +175 -175
  597. data/spec/unit/knife/data_bag_edit_spec.rb +126 -126
  598. data/spec/unit/knife/data_bag_from_file_spec.rb +174 -174
  599. data/spec/unit/knife/data_bag_secret_options_spec.rb +173 -173
  600. data/spec/unit/knife/data_bag_show_spec.rb +139 -139
  601. data/spec/unit/knife/environment_compare_spec.rb +112 -112
  602. data/spec/unit/knife/environment_create_spec.rb +91 -91
  603. data/spec/unit/knife/environment_delete_spec.rb +71 -71
  604. data/spec/unit/knife/environment_edit_spec.rb +79 -79
  605. data/spec/unit/knife/environment_from_file_spec.rb +90 -90
  606. data/spec/unit/knife/environment_list_spec.rb +54 -54
  607. data/spec/unit/knife/environment_show_spec.rb +52 -52
  608. data/spec/unit/knife/key_create_spec.rb +223 -223
  609. data/spec/unit/knife/key_delete_spec.rb +133 -133
  610. data/spec/unit/knife/key_edit_spec.rb +264 -264
  611. data/spec/unit/knife/key_helper.rb +74 -74
  612. data/spec/unit/knife/key_list_spec.rb +216 -216
  613. data/spec/unit/knife/key_show_spec.rb +126 -126
  614. data/spec/unit/knife/license_spec.rb +89 -89
  615. data/spec/unit/knife/node_bulk_delete_spec.rb +94 -94
  616. data/spec/unit/knife/node_delete_spec.rb +77 -77
  617. data/spec/unit/knife/node_edit_spec.rb +116 -116
  618. data/spec/unit/knife/node_environment_set_spec.rb +61 -61
  619. data/spec/unit/knife/node_from_file_spec.rb +59 -59
  620. data/spec/unit/knife/node_list_spec.rb +62 -62
  621. data/spec/unit/knife/node_policy_set_spec.rb +122 -122
  622. data/spec/unit/knife/node_run_list_add_spec.rb +145 -145
  623. data/spec/unit/knife/node_run_list_remove_spec.rb +106 -106
  624. data/spec/unit/knife/node_run_list_set_spec.rb +115 -115
  625. data/spec/unit/knife/node_show_spec.rb +65 -65
  626. data/spec/unit/knife/org_create_spec.rb +76 -76
  627. data/spec/unit/knife/org_delete_spec.rb +41 -41
  628. data/spec/unit/knife/org_edit_spec.rb +49 -49
  629. data/spec/unit/knife/org_list_spec.rb +58 -58
  630. data/spec/unit/knife/org_show_spec.rb +45 -45
  631. data/spec/unit/knife/org_user_add_spec.rb +39 -39
  632. data/spec/unit/knife/raw_spec.rb +43 -43
  633. data/spec/unit/knife/role_bulk_delete_spec.rb +80 -80
  634. data/spec/unit/knife/role_create_spec.rb +80 -80
  635. data/spec/unit/knife/role_delete_spec.rb +67 -67
  636. data/spec/unit/knife/role_edit_spec.rb +77 -77
  637. data/spec/unit/knife/role_env_run_list_add_spec.rb +217 -217
  638. data/spec/unit/knife/role_env_run_list_clear_spec.rb +94 -94
  639. data/spec/unit/knife/role_env_run_list_remove_spec.rb +102 -102
  640. data/spec/unit/knife/role_env_run_list_replace_spec.rb +105 -105
  641. data/spec/unit/knife/role_env_run_list_set_spec.rb +99 -99
  642. data/spec/unit/knife/role_from_file_spec.rb +69 -69
  643. data/spec/unit/knife/role_list_spec.rb +54 -54
  644. data/spec/unit/knife/role_run_list_add_spec.rb +179 -179
  645. data/spec/unit/knife/role_run_list_clear_spec.rb +84 -84
  646. data/spec/unit/knife/role_run_list_remove_spec.rb +92 -92
  647. data/spec/unit/knife/role_run_list_replace_spec.rb +98 -98
  648. data/spec/unit/knife/role_run_list_set_spec.rb +89 -89
  649. data/spec/unit/knife/role_show_spec.rb +59 -59
  650. data/spec/unit/knife/search_spec.rb +147 -147
  651. data/spec/unit/knife/ssh_spec.rb +403 -403
  652. data/spec/unit/knife/ssl_check_spec.rb +256 -256
  653. data/spec/unit/knife/ssl_fetch_spec.rb +222 -222
  654. data/spec/unit/knife/status_spec.rb +112 -112
  655. data/spec/unit/knife/supermarket_download_spec.rb +152 -152
  656. data/spec/unit/knife/supermarket_install_spec.rb +203 -203
  657. data/spec/unit/knife/supermarket_list_spec.rb +70 -70
  658. data/spec/unit/knife/supermarket_search_spec.rb +85 -85
  659. data/spec/unit/knife/supermarket_share_spec.rb +208 -208
  660. data/spec/unit/knife/supermarket_unshare_spec.rb +78 -78
  661. data/spec/unit/knife/tag_create_spec.rb +23 -23
  662. data/spec/unit/knife/tag_delete_spec.rb +25 -25
  663. data/spec/unit/knife/tag_list_spec.rb +23 -23
  664. data/spec/unit/knife/user_create_spec.rb +282 -282
  665. data/spec/unit/knife/user_delete_spec.rb +171 -171
  666. data/spec/unit/knife/user_edit_spec.rb +54 -54
  667. data/spec/unit/knife/user_list_spec.rb +73 -73
  668. data/spec/unit/knife/user_password_spec.rb +64 -64
  669. data/spec/unit/knife/user_reregister_spec.rb +56 -56
  670. data/spec/unit/knife/user_show_spec.rb +91 -91
  671. data/spec/unit/knife_spec.rb +637 -637
  672. data/spec/unit/utils/licensing_handler_spec.rb +140 -0
  673. metadata +11 -7
@@ -1,2288 +1,2311 @@
1
- #
2
- # Author:: Ian Meyer (<ianmmeyer@gmail.com>)
3
- # Copyright:: Copyright 2010-2016, Ian Meyer
4
- # Copyright:: Copyright (c) Chef Software 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 "knife_spec_helper"
21
-
22
- Chef::Knife::Bootstrap.load_deps
23
-
24
- describe Chef::Knife::Bootstrap do
25
- let(:bootstrap_template) { nil }
26
- let(:stderr) { StringIO.new }
27
- let(:bootstrap_cli_options) { [ ] }
28
- let(:linux_test) { true }
29
- let(:windows_test) { false }
30
- let(:linux_test) { false }
31
- let(:unix_test) { false }
32
- let(:ssh_test) { false }
33
-
34
- let(:connection) do
35
- double("TrainConnector",
36
- windows?: windows_test,
37
- linux?: linux_test,
38
- unix?: unix_test)
39
- end
40
-
41
- let(:knife) do
42
- Chef::Log.logger = Logger.new(StringIO.new)
43
- Chef::Config[:knife][:bootstrap_template] = bootstrap_template unless bootstrap_template.nil?
44
-
45
- k = Chef::Knife::Bootstrap.new(bootstrap_cli_options)
46
- allow(k.ui).to receive(:stderr).and_return(stderr)
47
- allow(k).to receive(:encryption_secret_provided_ignore_encrypt_flag?).and_return(false)
48
- allow(k).to receive(:connection).and_return connection
49
- k.merge_configs
50
- k
51
- end
52
-
53
- context "#check_eula_license" do
54
- let(:acceptor) { instance_double(LicenseAcceptance::Acceptor) }
55
-
56
- before do
57
- expect(LicenseAcceptance::Acceptor).to receive(:new).and_return(acceptor)
58
- end
59
-
60
- describe "when a license is not required" do
61
- it "does not set the chef_license" do
62
- expect(acceptor).to receive(:license_required?).and_return(false)
63
- knife.check_eula_license
64
- expect(Chef::Config[:chef_license]).to eq(nil)
65
- end
66
- end
67
-
68
- describe "when a license is required" do
69
- it "sets the chef_license" do
70
- expect(acceptor).to receive(:license_required?).and_return(true)
71
- expect(acceptor).to receive(:id_from_mixlib).and_return("id")
72
- expect(acceptor).to receive(:check_and_persist)
73
- expect(acceptor).to receive(:acceptance_value).and_return("accept-no-persist")
74
- knife.check_eula_license
75
- expect(Chef::Config[:chef_license]).to eq("accept-no-persist")
76
- end
77
- end
78
- end
79
-
80
- context "#bootstrap_template" do
81
- it "should default to chef-full" do
82
- expect(knife.bootstrap_template).to be_a_kind_of(String)
83
- expect(File.basename(knife.bootstrap_template)).to eq("chef-full")
84
- end
85
- end
86
-
87
- context "#render_template - when using the chef-full default template" do
88
- let(:rendered_template) do
89
- knife.merge_configs
90
- knife.render_template
91
- end
92
-
93
- it "should render client.rb" do
94
- expect(rendered_template).to match("cat > /etc/chef/client.rb <<'EOP'")
95
- expect(rendered_template).to match("chef_server_url \"https://localhost:443\"")
96
- expect(rendered_template).to match("validation_client_name \"chef-validator\"")
97
- expect(rendered_template).to match("log_location STDOUT")
98
- end
99
-
100
- it "should render first-boot.json" do
101
- expect(rendered_template).to match("cat > /etc/chef/first-boot.json <<'EOP'")
102
- expect(rendered_template).to match('{"run_list":\[\]}')
103
- end
104
-
105
- context "and encrypted_data_bag_secret was provided" do
106
- it "should render encrypted_data_bag_secret file" do
107
- expect(knife).to receive(:encryption_secret_provided_ignore_encrypt_flag?).and_return(true)
108
- expect(knife).to receive(:read_secret).and_return("secrets")
109
- expect(rendered_template).to match("cat > /etc/chef/encrypted_data_bag_secret <<'EOP'")
110
- expect(rendered_template).to match('{"run_list":\[\]}')
111
- expect(rendered_template).to match(/secrets/)
112
- end
113
- end
114
- end
115
-
116
- context "with --bootstrap-vault-item" do
117
- let(:bootstrap_cli_options) { [ "--bootstrap-vault-item", "vault1:item1", "--bootstrap-vault-item", "vault1:item2", "--bootstrap-vault-item", "vault2:item1" ] }
118
- it "sets the knife config cli option correctly" do
119
- expect(knife.config[:bootstrap_vault_item]).to eq({ "vault1" => %w{item1 item2}, "vault2" => ["item1"] })
120
- end
121
- end
122
-
123
- context "with --bootstrap-preinstall-command" do
124
- command = "while sudo fuser /var/lib/dpkg/lock >/dev/null 2>&1; do\n echo 'waiting for dpkg lock';\n sleep 1;\n done;"
125
- let(:bootstrap_cli_options) { [ "--bootstrap-preinstall-command", command ] }
126
- let(:rendered_template) do
127
- knife.merge_configs
128
- knife.render_template
129
- end
130
- it "configures the preinstall command in the bootstrap template correctly" do
131
- expect(rendered_template).to match(/command/)
132
- end
133
- end
134
-
135
- context "with --bootstrap-proxy" do
136
- let(:bootstrap_cli_options) { [ "--bootstrap-proxy", "1.1.1.1" ] }
137
- let(:rendered_template) do
138
- knife.merge_configs
139
- knife.render_template
140
- end
141
- it "configures the https_proxy environment variable in the bootstrap template correctly" do
142
- expect(rendered_template).to match(/https_proxy="1.1.1.1" export https_proxy/)
143
- end
144
- end
145
-
146
- context "with --bootstrap-no-proxy" do
147
- let(:bootstrap_cli_options) { [ "--bootstrap-no-proxy", "localserver" ] }
148
- let(:rendered_template) do
149
- knife.merge_configs
150
- knife.render_template
151
- end
152
- it "configures the https_proxy environment variable in the bootstrap template correctly" do
153
- expect(rendered_template).to match(/no_proxy="localserver" export no_proxy/)
154
- end
155
- end
156
-
157
- context "with :bootstrap_template and :template_file cli options" do
158
- let(:bootstrap_cli_options) { [ "--bootstrap-template", "my-template", "other-template" ] }
159
-
160
- it "should select bootstrap template" do
161
- expect(File.basename(knife.bootstrap_template)).to eq("my-template")
162
- end
163
- end
164
-
165
- context "when finding templates" do
166
- context "when :bootstrap_template config is set to a file" do
167
- context "that doesn't exist" do
168
- let(:bootstrap_template) { "/opt/blah/not/exists/template.erb" }
169
-
170
- it "raises an error" do
171
- expect { knife.find_template }.to raise_error(Errno::ENOENT)
172
- end
173
- end
174
-
175
- context "that exists" do
176
- let(:bootstrap_template) { File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "test.erb")) }
177
-
178
- it "loads the given file as the template" do
179
- expect(Chef::Log).to receive(:trace)
180
- expect(knife.find_template).to eq(File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "test.erb")))
181
- end
182
- end
183
- end
184
-
185
- context "when :bootstrap_template config is set to a template name" do
186
- let(:bootstrap_template) { "example" }
187
-
188
- let(:builtin_template_path) { File.expand_path(File.join(__dir__, "../../../lib/chef/knife/bootstrap/templates", "example.erb")) }
189
-
190
- let(:chef_config_dir_template_path) { "/knife/chef/config/bootstrap/example.erb" }
191
-
192
- let(:env_home_template_path) { "/env/home/.chef/bootstrap/example.erb" }
193
-
194
- let(:gem_files_template_path) { "/Users/schisamo/.rvm/gems/ruby-1.9.2-p180@chef-0.10/gems/knife-windows-0.5.4/lib/chef/knife/bootstrap/fake-bootstrap-template.erb" }
195
-
196
- def configure_chef_config_dir
197
- allow(Chef::Knife).to receive(:chef_config_dir).and_return("/knife/chef/config")
198
- end
199
-
200
- def configure_env_home
201
- allow(Chef::Util::PathHelper).to receive(:home).with(".chef", "bootstrap", "example.erb").and_yield(env_home_template_path)
202
- end
203
-
204
- def configure_gem_files
205
- allow(Gem).to receive(:find_files).and_return([ gem_files_template_path ])
206
- end
207
-
208
- before(:each) do
209
- expect(File).to receive(:exist?).with(bootstrap_template).and_return(false)
210
- end
211
-
212
- context "when file is available everywhere" do
213
- before do
214
- configure_chef_config_dir
215
- configure_env_home
216
- configure_gem_files
217
-
218
- expect(File).to receive(:exist?).with(builtin_template_path).and_return(true)
219
- end
220
-
221
- it "should load the template from built-in templates" do
222
- expect(knife.find_template).to eq(builtin_template_path)
223
- end
224
- end
225
-
226
- context "when file is available in chef_config_dir" do
227
- before do
228
- configure_chef_config_dir
229
- configure_env_home
230
- configure_gem_files
231
-
232
- expect(File).to receive(:exist?).with(builtin_template_path).and_return(false)
233
- expect(File).to receive(:exist?).with(chef_config_dir_template_path).and_return(true)
234
-
235
- it "should load the template from chef_config_dir" do
236
- knife.find_template.should eq(chef_config_dir_template_path)
237
- end
238
- end
239
- end
240
-
241
- context "when file is available in home directory" do
242
- before do
243
- configure_chef_config_dir
244
- configure_env_home
245
- configure_gem_files
246
-
247
- expect(File).to receive(:exist?).with(builtin_template_path).and_return(false)
248
- expect(File).to receive(:exist?).with(chef_config_dir_template_path).and_return(false)
249
- expect(File).to receive(:exist?).with(env_home_template_path).and_return(true)
250
- end
251
-
252
- it "should load the template from chef_config_dir" do
253
- expect(knife.find_template).to eq(env_home_template_path)
254
- end
255
- end
256
-
257
- context "when file is available in Gem files" do
258
- before do
259
- configure_chef_config_dir
260
- configure_env_home
261
- configure_gem_files
262
-
263
- expect(File).to receive(:exist?).with(builtin_template_path).and_return(false)
264
- expect(File).to receive(:exist?).with(chef_config_dir_template_path).and_return(false)
265
- expect(File).to receive(:exist?).with(env_home_template_path).and_return(false)
266
- expect(File).to receive(:exist?).with(gem_files_template_path).and_return(true)
267
- end
268
-
269
- it "should load the template from Gem files" do
270
- expect(knife.find_template).to eq(gem_files_template_path)
271
- end
272
- end
273
-
274
- context "when file is available in Gem files and home dir doesn't exist" do
275
- before do
276
- configure_chef_config_dir
277
- configure_gem_files
278
- allow(Chef::Util::PathHelper).to receive(:home).with(".chef", "bootstrap", "example.erb").and_return(nil)
279
-
280
- expect(File).to receive(:exist?).with(builtin_template_path).and_return(false)
281
- expect(File).to receive(:exist?).with(chef_config_dir_template_path).and_return(false)
282
- expect(File).to receive(:exist?).with(gem_files_template_path).and_return(true)
283
- end
284
-
285
- it "should load the template from Gem files" do
286
- expect(knife.find_template).to eq(gem_files_template_path)
287
- end
288
- end
289
- end
290
- end
291
-
292
- ["-t", "--bootstrap-template"].each do |t|
293
- context "when #{t} option is given in the command line" do
294
- it "sets the knife :bootstrap_template config" do
295
- knife.parse_options([t, "blahblah"])
296
- knife.merge_configs
297
- expect(knife.bootstrap_template).to eq("blahblah")
298
- end
299
- end
300
- end
301
-
302
- context "with run_list template" do
303
- let(:bootstrap_template) { File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "test.erb")) }
304
-
305
- it "should return an empty run_list" do
306
- expect(knife.render_template).to eq('{"run_list":[]}')
307
- end
308
-
309
- it "should have role[base] in the run_list" do
310
- knife.parse_options(["-r", "role[base]"])
311
- knife.merge_configs
312
- expect(knife.render_template).to eq('{"run_list":["role[base]"]}')
313
- end
314
-
315
- it "should have role[base] and recipe[cupcakes] in the run_list" do
316
- knife.parse_options(["-r", "role[base],recipe[cupcakes]"])
317
- knife.merge_configs
318
- expect(knife.render_template).to eq('{"run_list":["role[base]","recipe[cupcakes]"]}')
319
- end
320
-
321
- context "with bootstrap_attribute options" do
322
- let(:jsonfile) do
323
- file = Tempfile.new(["node", ".json"])
324
- File.open(file.path, "w") { |f| f.puts '{"foo":{"bar":"baz"}}' }
325
- file
326
- end
327
-
328
- it "should have foo => {bar => baz} in the first_boot from cli" do
329
- knife.parse_options(["-j", '{"foo":{"bar":"baz"}}'])
330
- knife.merge_configs
331
- expected_hash = FFI_Yajl::Parser.new.parse('{"foo":{"bar":"baz"},"run_list":[]}')
332
- actual_hash = FFI_Yajl::Parser.new.parse(knife.render_template)
333
- expect(actual_hash).to eq(expected_hash)
334
- end
335
-
336
- it "should have foo => {bar => baz} in the first_boot from file" do
337
- knife.parse_options(["--json-attribute-file", jsonfile.path])
338
- knife.merge_configs
339
- expected_hash = FFI_Yajl::Parser.new.parse('{"foo":{"bar":"baz"},"run_list":[]}')
340
- actual_hash = FFI_Yajl::Parser.new.parse(knife.render_template)
341
- expect(actual_hash).to eq(expected_hash)
342
- jsonfile.close
343
- end
344
-
345
- it "raises a Chef::Exceptions::BootstrapCommandInputError with the proper error message" do
346
- knife.parse_options(["-j", '{"foo":{"bar":"baz"}}'])
347
- knife.parse_options(["--json-attribute-file", jsonfile.path])
348
- knife.merge_configs
349
- allow(knife).to receive(:validate_name_args!)
350
- expect(knife).to receive(:check_eula_license)
351
-
352
- expect { knife.run }.to raise_error(Chef::Exceptions::BootstrapCommandInputError)
353
- jsonfile.close
354
- end
355
- end
356
- end
357
-
358
- context "with hints template" do
359
- let(:bootstrap_template) { File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "test-hints.erb")) }
360
-
361
- it "should create a hint file when told to" do
362
- knife.parse_options(["--hint", "openstack"])
363
- knife.merge_configs
364
- expect(knife.render_template).to match(%r{/etc/chef/ohai/hints/openstack.json})
365
- end
366
-
367
- it "should populate a hint file with JSON when given a file to read" do
368
- allow(::File).to receive(:read).and_return('{ "foo" : "bar" }')
369
- knife.parse_options(["--hint", "openstack=hints/openstack.json"])
370
- knife.merge_configs
371
- expect(knife.render_template).to match(/\{\"foo\":\"bar\"\}/)
372
- end
373
- end
374
-
375
- describe "specifying no_proxy with various entries" do
376
- subject(:knife) do
377
- k = described_class.new
378
- Chef::Config[:knife][:bootstrap_template] = template_file
379
- allow(k).to receive(:connection).and_return connection
380
- k.parse_options(options)
381
- k.merge_configs
382
- k
383
- end
384
-
385
- let(:options) { ["--bootstrap-no-proxy", setting] }
386
-
387
- let(:template_file) { File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "no_proxy.erb")) }
388
-
389
- let(:rendered_template) do
390
- knife.render_template
391
- end
392
-
393
- context "via --bootstrap-no-proxy" do
394
- let(:setting) { "api.opscode.com" }
395
-
396
- it "renders the client.rb with a single FQDN no_proxy entry" do
397
- expect(rendered_template).to match(/.*no_proxy\s*"api.opscode.com".*/)
398
- end
399
- end
400
-
401
- context "via --bootstrap-no-proxy multiple" do
402
- let(:setting) { "api.opscode.com,172.16.10.*" }
403
-
404
- it "renders the client.rb with comma-separated FQDN and wildcard IP address no_proxy entries" do
405
- expect(rendered_template).to match(/.*no_proxy\s*"api.opscode.com,172.16.10.\*".*/)
406
- end
407
- end
408
-
409
- context "via --ssl-verify-mode none" do
410
- let(:options) { ["--node-ssl-verify-mode", "none"] }
411
-
412
- it "renders the client.rb with ssl_verify_mode set to :verify_none" do
413
- expect(rendered_template).to match(/ssl_verify_mode :verify_none/)
414
- end
415
- end
416
-
417
- context "via --node-ssl-verify-mode peer" do
418
- let(:options) { ["--node-ssl-verify-mode", "peer"] }
419
-
420
- it "renders the client.rb with ssl_verify_mode set to :verify_peer" do
421
- expect(rendered_template).to match(/ssl_verify_mode :verify_peer/)
422
- end
423
- end
424
-
425
- context "via --node-ssl-verify-mode all" do
426
- let(:options) { ["--node-ssl-verify-mode", "all"] }
427
-
428
- it "raises error" do
429
- expect { rendered_template }.to raise_error(RuntimeError)
430
- end
431
- end
432
-
433
- context "via --node-verify-api-cert" do
434
- let(:options) { ["--node-verify-api-cert"] }
435
-
436
- it "renders the client.rb with verify_api_cert set to true" do
437
- expect(rendered_template).to match(/verify_api_cert true/)
438
- end
439
- end
440
-
441
- context "via --no-node-verify-api-cert" do
442
- let(:options) { ["--no-node-verify-api-cert"] }
443
-
444
- it "renders the client.rb with verify_api_cert set to false" do
445
- expect(rendered_template).to match(/verify_api_cert false/)
446
- end
447
- end
448
- end
449
-
450
- describe "specifying the encrypted data bag secret key" do
451
- let(:secret) { "supersekret" }
452
- let(:options) { [] }
453
- let(:bootstrap_template) { File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "secret.erb")) }
454
- let(:rendered_template) do
455
- knife.parse_options(options)
456
- knife.merge_configs
457
- knife.render_template
458
- end
459
-
460
- it "creates a secret file" do
461
- expect(knife).to receive(:encryption_secret_provided_ignore_encrypt_flag?).and_return(true)
462
- expect(knife).to receive(:read_secret).and_return(secret)
463
- expect(rendered_template).to match(/#{secret}/)
464
- end
465
-
466
- it "renders the client.rb with an encrypted_data_bag_secret entry" do
467
- expect(knife).to receive(:encryption_secret_provided_ignore_encrypt_flag?).and_return(true)
468
- expect(knife).to receive(:read_secret).and_return(secret)
469
- expect(rendered_template).to match(%r{encrypted_data_bag_secret\s*"/etc/chef/encrypted_data_bag_secret"})
470
- end
471
-
472
- end
473
-
474
- describe "when transferring trusted certificates" do
475
- let(:rendered_template) do
476
- knife.merge_configs
477
- knife.render_template
478
- end
479
-
480
- before do
481
- Chef::Config[:trusted_certs_dir] = Chef::Util::PathHelper.cleanpath(File.join(CHEF_SPEC_DATA, "trusted_certs"))
482
- end
483
-
484
- it "creates /etc/chef/trusted_certs" do
485
- expect(rendered_template).to match(%r{mkdir -p /etc/chef/trusted_certs})
486
- end
487
-
488
- it "copies the certificates in the directory" do
489
- certificates = Dir[File.join(Chef::Config[:trusted_certs_dir], "*.{crt,pem}")]
490
-
491
- certificates.each do |cert|
492
- expect(rendered_template).to match(%r{cat > /etc/chef/trusted_certs/#{File.basename(cert)} <<'EOP'})
493
- end
494
- end
495
-
496
- it "doesn't create /etc/chef/trusted_certs if :trusted_certs_dir is empty" do
497
- Dir.mktmpdir do |dir|
498
- Chef::Config[:trusted_certs_dir] = dir
499
- expect(rendered_template).not_to match(%r{mkdir -p /etc/chef/trusted_certs})
500
- end
501
- end
502
- end
503
-
504
- context "when doing fips things" do
505
- let(:template_file) { File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "no_proxy.erb")) }
506
-
507
- before do
508
- Chef::Config[:knife][:bootstrap_template] = template_file
509
- knife.merge_configs
510
- end
511
-
512
- let(:rendered_template) do
513
- knife.render_template
514
- end
515
-
516
- context "when knife is in fips mode" do
517
- before do
518
- Chef::Config[:fips] = true
519
- end
520
-
521
- it "renders 'fips true'" do
522
- expect(rendered_template).to match("fips")
523
- end
524
- end
525
-
526
- context "when knife is not in fips mode" do
527
- before do
528
- # This is required because the chef-fips pipeline does
529
- # has a default value of true for fips
530
- Chef::Config[:fips] = false
531
- end
532
-
533
- it "does not render anything about fips" do
534
- expect(rendered_template).not_to match("fips")
535
- end
536
- end
537
- end
538
-
539
- describe "when transferring client.d" do
540
-
541
- let(:rendered_template) do
542
- knife.merge_configs
543
- knife.render_template
544
- end
545
-
546
- before do
547
- Chef::Config[:client_d_dir] = client_d_dir
548
- end
549
-
550
- context "when client_d_dir is nil" do
551
- let(:client_d_dir) { nil }
552
-
553
- it "does not create /etc/chef/client.d" do
554
- expect(rendered_template).not_to match(%r{mkdir -p /etc/chef/client\.d})
555
- end
556
- end
557
-
558
- context "when client_d_dir is set" do
559
- let(:client_d_dir) do
560
- Chef::Util::PathHelper.cleanpath(
561
- File.join(__dir__, "../../data/client.d_00")
562
- )
563
- end
564
-
565
- it "creates /etc/chef/client.d" do
566
- expect(rendered_template).to match("mkdir -p /etc/chef/client\.d")
567
- end
568
-
569
- context "a flat directory structure" do
570
- it "escapes single-quotes" do
571
- expect(rendered_template).to match("cat > /etc/chef/client.d/02-strings.rb <<'EOP'")
572
- expect(rendered_template).to match("something '\\\\''/foo/bar'\\\\''")
573
- end
574
-
575
- it "creates a file 00-foo.rb" do
576
- expect(rendered_template).to match("cat > /etc/chef/client.d/00-foo.rb <<'EOP'")
577
- expect(rendered_template).to match("d6f9b976-289c-4149-baf7-81e6ffecf228")
578
- end
579
- it "creates a file bar" do
580
- expect(rendered_template).to match("cat > /etc/chef/client.d/bar <<'EOP'")
581
- expect(rendered_template).to match("1 / 0")
582
- end
583
- end
584
-
585
- context "a nested directory structure" do
586
- let(:client_d_dir) do
587
- Chef::Util::PathHelper.cleanpath(
588
- File.join(__dir__, "../../data/client.d_01")
589
- )
590
- end
591
- it "creates a file foo/bar.rb" do
592
- expect(rendered_template).to match("cat > /etc/chef/client.d/foo/bar.rb <<'EOP'")
593
- expect(rendered_template).to match("1 / 0")
594
- end
595
- end
596
- end
597
- end
598
-
599
- describe "#connection_protocol" do
600
- let(:host_descriptor) { "example.com" }
601
- let(:config) { {} }
602
- let(:knife_connection_protocol) { nil }
603
- before do
604
- allow(knife).to receive(:config).and_return config
605
- allow(knife).to receive(:host_descriptor).and_return host_descriptor
606
- if knife_connection_protocol
607
- Chef::Config[:knife][:connection_protocol] = knife_connection_protocol
608
- knife.merge_configs
609
- end
610
- end
611
-
612
- context "when protocol is part of the host argument" do
613
- let(:host_descriptor) { "winrm://myhost" }
614
-
615
- it "returns the value provided by the host argument" do
616
- expect(knife.connection_protocol).to eq "winrm"
617
- end
618
- end
619
-
620
- context "when protocol is provided via the CLI flag" do
621
- let(:config) { { connection_protocol: "winrm" } }
622
- it "returns that value" do
623
- expect(knife.connection_protocol).to eq "winrm"
624
- end
625
-
626
- end
627
- context "when protocol is provided via the host argument and the CLI flag" do
628
- let(:host_descriptor) { "ssh://example.com" }
629
- let(:config) { { connection_protocol: "winrm" } }
630
-
631
- it "returns the value provided by the host argument" do
632
- expect(knife.connection_protocol).to eq "ssh"
633
- end
634
- end
635
-
636
- context "when no explicit protocol is provided" do
637
- let(:config) { {} }
638
- let(:host_descriptor) { "example.com" }
639
- let(:knife_connection_protocol) { "winrm" }
640
- it "falls back to knife config" do
641
- expect(knife.connection_protocol).to eq "winrm"
642
- end
643
- context "and there is no knife bootstrap_protocol" do
644
- let(:knife_connection_protocol) { nil }
645
- it "falls back to 'ssh'" do
646
- expect(knife.connection_protocol).to eq "ssh"
647
- end
648
- end
649
- end
650
-
651
- end
652
-
653
- describe "#validate_protocol!" do
654
- let(:host_descriptor) { "example.com" }
655
- let(:config) { {} }
656
- let(:connection_protocol) { "ssh" }
657
- before do
658
- allow(knife).to receive(:config).and_return config
659
- allow(knife).to receive(:connection_protocol).and_return connection_protocol
660
- allow(knife).to receive(:host_descriptor).and_return host_descriptor
661
- end
662
-
663
- context "when protocol is provided both in the URL and via --protocol" do
664
-
665
- context "and they do not match" do
666
- let(:connection_protocol) { "ssh" }
667
- let(:config) { { connection_protocol: "winrm" } }
668
- it "outputs an error and exits" do
669
- expect(knife.ui).to receive(:error)
670
- expect { knife.validate_protocol! }.to raise_error SystemExit
671
- end
672
- end
673
-
674
- context "and they do match" do
675
- let(:connection_protocol) { "winrm" }
676
- let(:config) { { connection_protocol: "winrm" } }
677
- it "returns true" do
678
- expect(knife.validate_protocol!).to eq true
679
- end
680
- end
681
- end
682
-
683
- context "and the protocol is supported" do
684
-
685
- Chef::Knife::Bootstrap::SUPPORTED_CONNECTION_PROTOCOLS.each do |proto|
686
- let(:connection_protocol) { proto }
687
- it "returns true for #{proto}" do
688
- expect(knife.validate_protocol!).to eq true
689
- end
690
- end
691
- end
692
-
693
- context "and the protocol is not supported" do
694
- let(:connection_protocol) { "invalid" }
695
- it "outputs an error and exits" do
696
- expect(knife.ui).to receive(:error).with(/Unsupported protocol '#{connection_protocol}'/)
697
- expect { knife.validate_protocol! }.to raise_error SystemExit
698
- end
699
- end
700
- end
701
-
702
- describe "#validate_policy_options!" do
703
-
704
- context "when only policy_name is given" do
705
-
706
- let(:bootstrap_cli_options) { %w{ --policy-name my-app-server } }
707
-
708
- it "returns an error stating that policy_name and policy_group must be given together" do
709
- expect { knife.validate_policy_options! }.to raise_error(SystemExit)
710
- expect(stderr.string).to include("ERROR: --policy-name and --policy-group must be specified together")
711
- end
712
-
713
- end
714
-
715
- context "when only policy_group is given" do
716
-
717
- let(:bootstrap_cli_options) { %w{ --policy-group staging } }
718
-
719
- it "returns an error stating that policy_name and policy_group must be given together" do
720
- expect { knife.validate_policy_options! }.to raise_error(SystemExit)
721
- expect(stderr.string).to include("ERROR: --policy-name and --policy-group must be specified together")
722
- end
723
-
724
- end
725
-
726
- context "when both policy_name and policy_group are given, but run list is also given" do
727
-
728
- let(:bootstrap_cli_options) { %w{ --policy-name my-app --policy-group staging --run-list cookbook } }
729
-
730
- it "returns an error stating that policyfile and run_list are exclusive" do
731
- expect { knife.validate_policy_options! }.to raise_error(SystemExit)
732
- expect(stderr.string).to include("ERROR: Policyfile options and --run-list are exclusive")
733
- end
734
-
735
- end
736
-
737
- context "when policy_name and policy_group are given with no conflicting options" do
738
-
739
- let(:bootstrap_cli_options) { %w{ --policy-name my-app --policy-group staging } }
740
-
741
- it "passes options validation" do
742
- expect { knife.validate_policy_options! }.to_not raise_error
743
- end
744
-
745
- it "passes them into the bootstrap context" do
746
- expect(knife.bootstrap_context.first_boot).to have_key(:policy_name)
747
- expect(knife.bootstrap_context.first_boot).to have_key(:policy_group)
748
- end
749
-
750
- it "ensures that run_list is not set in the bootstrap context" do
751
- expect(knife.bootstrap_context.first_boot).to_not have_key(:run_list)
752
- end
753
-
754
- end
755
-
756
- # https://github.com/chef/chef/issues/4131
757
- # Arguably a bug in the plugin: it shouldn't be setting this to nil, but it
758
- # worked before, so make it work now.
759
- context "when a plugin sets the run list option to nil" do
760
- before do
761
- knife.config[:run_list] = nil
762
- end
763
-
764
- it "passes options validation" do
765
- expect { knife.validate_policy_options! }.to_not raise_error
766
- end
767
- end
768
- end
769
-
770
- # TODO - this is the only cli option we validate the _option_ itself -
771
- # so we'll know if someone accidentally deletes or renames use_sudo_password
772
- # Is this worht keeping? If so, then it seems we should expand it
773
- # to cover all options.
774
- context "validating use_sudo_password option" do
775
- it "use_sudo_password contains description and long params for help" do
776
- expect(knife.options).to(have_key(:use_sudo_password)) \
777
- && expect(knife.options[:use_sudo_password][:description].to_s).not_to(eq(""))\
778
- && expect(knife.options[:use_sudo_password][:long].to_s).not_to(eq(""))
779
- end
780
- end
781
-
782
- context "#connection_opts" do
783
- let(:connection_protocol) { "ssh" }
784
- before do
785
- allow(knife).to receive(:connection_protocol).and_return connection_protocol
786
- end
787
- context "behavioral test: " do
788
- let(:expected_connection_opts) do
789
- { base_opts: true,
790
- ssh_identity_opts: true,
791
- ssh_opts: true,
792
- gateway_opts: true,
793
- host_verify_opts: true,
794
- sudo_opts: true,
795
- winrm_opts: true }
796
- end
797
-
798
- it "queries and merges only expected configurations" do
799
- expect(knife).to receive(:base_opts).and_return({ base_opts: true })
800
- expect(knife).to receive(:host_verify_opts).and_return({ host_verify_opts: true })
801
- expect(knife).to receive(:gateway_opts).and_return({ gateway_opts: true })
802
- expect(knife).to receive(:sudo_opts).and_return({ sudo_opts: true })
803
- expect(knife).to receive(:winrm_opts).and_return({ winrm_opts: true })
804
- expect(knife).to receive(:ssh_opts).and_return({ ssh_opts: true })
805
- expect(knife).to receive(:ssh_identity_opts).and_return({ ssh_identity_opts: true })
806
- expect(knife.connection_opts).to match expected_connection_opts
807
- end
808
- end
809
-
810
- context "functional test: " do
811
- context "when protocol is winrm" do
812
- let(:connection_protocol) { "winrm" }
813
- # context "and neither CLI nor Chef::Config config entries have been provided"
814
- # end
815
- context "and all supported values are provided as Chef::Config entries" do
816
- before do
817
- # Set everything to easily identifiable and obviously fake values
818
- # to verify that Chef::Config is being sourced instead of knife.config
819
- knife.config = {}
820
- Chef::Config[:knife][:max_wait] = 9999.0
821
- Chef::Config[:knife][:winrm_user] = "winbob"
822
- Chef::Config[:knife][:winrm_port] = 9999
823
- Chef::Config[:knife][:ca_trust_file] = "trust.me"
824
- Chef::Config[:knife][:kerberos_realm] = "realm"
825
- Chef::Config[:knife][:kerberos_service] = "service"
826
- Chef::Config[:knife][:winrm_auth_method] = "kerberos" # default is negotiate
827
- Chef::Config[:knife][:winrm_basic_auth_only] = true
828
- Chef::Config[:knife][:winrm_no_verify_cert] = true
829
- Chef::Config[:knife][:session_timeout] = 9999
830
- Chef::Config[:knife][:winrm_ssl] = true
831
- Chef::Config[:knife][:winrm_ssl_peer_fingerprint] = "ABCDEF"
832
- end
833
-
834
- context "and no CLI options have been given" do
835
- let(:expected_result) do
836
- {
837
- logger: Chef::Log, # not configurable
838
- ca_trust_path: "trust.me",
839
- max_wait_until_ready: 9999, # converted to int
840
- operation_timeout: 9999,
841
- ssl_peer_fingerprint: "ABCDEF",
842
- winrm_transport: "kerberos",
843
- winrm_basic_auth_only: true,
844
- user: "winbob",
845
- port: 9999,
846
- self_signed: true,
847
- ssl: true,
848
- kerberos_realm: "realm",
849
- kerberos_service: "service",
850
- }
851
- end
852
-
853
- it "generates a config hash using the Chef::Config values" do
854
- knife.merge_configs
855
- expect(knife.connection_opts).to match expected_result
856
- end
857
-
858
- end
859
-
860
- context "and some CLI options have been given" do
861
- let(:expected_result) do
862
- {
863
- logger: Chef::Log, # not configurable
864
- ca_trust_path: "no trust",
865
- max_wait_until_ready: 9999,
866
- operation_timeout: 9999,
867
- ssl_peer_fingerprint: "ABCDEF",
868
- winrm_transport: "kerberos",
869
- winrm_basic_auth_only: true,
870
- user: "microsoftbob",
871
- port: 12,
872
- self_signed: true,
873
- ssl: true,
874
- kerberos_realm: "realm",
875
- kerberos_service: "service",
876
- password: "lobster",
877
- }
878
- end
879
-
880
- before do
881
- knife.config[:ca_trust_file] = "no trust"
882
- knife.config[:connection_user] = "microsoftbob"
883
- knife.config[:connection_port] = 12
884
- knife.config[:winrm_port] = "13" # indirectly verify we're not looking for the wrong CLI flag
885
- knife.config[:connection_password] = "lobster"
886
- end
887
-
888
- it "generates a config hash using the CLI options when available and falling back to Chef::Config values" do
889
- knife.merge_configs
890
- expect(knife.connection_opts).to match expected_result
891
- end
892
- end
893
-
894
- context "and all CLI options have been given" do
895
- before do
896
- # We'll force kerberos vi knife.config because it
897
- # causes additional options to populate - make sure
898
- # Chef::Config is different so we can be sure that we didn't
899
- # pull in the Chef::Config value
900
- Chef::Config[:knife][:winrm_auth_method] = "negotiate"
901
- knife.config[:connection_password] = "blue"
902
- knife.config[:max_wait] = 1000.0
903
- knife.config[:connection_user] = "clippy"
904
- knife.config[:connection_port] = 1000
905
- knife.config[:winrm_port] = 1001 # We should not see this value get used
906
-
907
- knife.config[:ca_trust_file] = "trust.the.internet"
908
- knife.config[:kerberos_realm] = "otherrealm"
909
- knife.config[:kerberos_service] = "otherservice"
910
- knife.config[:winrm_auth_method] = "kerberos" # default is negotiate
911
- knife.config[:winrm_basic_auth_only] = false
912
- knife.config[:winrm_no_verify_cert] = false
913
- knife.config[:session_timeout] = 1000
914
- knife.config[:winrm_ssl] = false
915
- knife.config[:winrm_ssl_peer_fingerprint] = "FEDCBA"
916
- end
917
- let(:expected_result) do
918
- {
919
- logger: Chef::Log, # not configurable
920
- ca_trust_path: "trust.the.internet",
921
- max_wait_until_ready: 1000, # converted to int
922
- operation_timeout: 1000,
923
- ssl_peer_fingerprint: "FEDCBA",
924
- winrm_transport: "kerberos",
925
- winrm_basic_auth_only: false,
926
- user: "clippy",
927
- port: 1000,
928
- self_signed: false,
929
- ssl: false,
930
- kerberos_realm: "otherrealm",
931
- kerberos_service: "otherservice",
932
- password: "blue",
933
- }
934
- end
935
- it "generates a config hash using the CLI options and pulling nothing from Chef::Config" do
936
- knife.merge_configs
937
- expect(knife.connection_opts).to match expected_result
938
- end
939
- end
940
- end # with underlying Chef::Config values
941
-
942
- context "and no values are provided from Chef::Config or CLI" do
943
- before do
944
- # We will use knife's actual config since these tests
945
- # have assumptions based on CLI default values
946
- end
947
- let(:expected_result) do
948
- {
949
- logger: Chef::Log,
950
- operation_timeout: 60,
951
- self_signed: false,
952
- ssl: false,
953
- ssl_peer_fingerprint: nil,
954
- winrm_basic_auth_only: false,
955
- winrm_transport: "negotiate",
956
- }
957
- end
958
- it "populates appropriate defaults" do
959
- knife.merge_configs
960
- expect(knife.connection_opts).to match expected_result
961
- end
962
- end
963
- end # winrm
964
-
965
- context "when protocol is ssh" do
966
- let(:connection_protocol) { "ssh" }
967
- # context "and neither CLI nor Chef::Config config entries have been provided"
968
- # end
969
- context "and all supported values are provided as Chef::Config entries" do
970
- before do
971
- # Set everything to easily identifiable and obviously fake values
972
- # to verify that Chef::Config is being sourced instead of knife.config
973
- knife.config = {}
974
- Chef::Config[:knife][:max_wait] = 9999.0
975
- Chef::Config[:knife][:session_timeout] = 9999
976
- Chef::Config[:knife][:ssh_user] = "sshbob"
977
- Chef::Config[:knife][:ssh_port] = 9999
978
- Chef::Config[:knife][:host_key_verify] = false
979
- Chef::Config[:knife][:ssh_gateway_identity] = "/gateway.pem"
980
- Chef::Config[:knife][:ssh_gateway] = "admin@mygateway.local:1234"
981
- Chef::Config[:knife][:ssh_identity_file] = "/identity.pem"
982
- Chef::Config[:knife][:use_sudo_password] = false # We have no password.
983
- end
984
-
985
- context "and no CLI options have been given" do
986
- let(:expected_result) do
987
- {
988
- logger: Chef::Log, # not configurable
989
- max_wait_until_ready: 9999, # converted to int
990
- connection_timeout: 9999,
991
- user: "sshbob",
992
- bastion_host: "mygateway.local",
993
- bastion_port: 1234,
994
- bastion_user: "admin",
995
- forward_agent: false,
996
- keys_only: true,
997
- key_files: ["/identity.pem", "/gateway.pem"],
998
- sudo: false,
999
- verify_host_key: "always",
1000
- port: 9999,
1001
- non_interactive: true,
1002
- }
1003
- end
1004
-
1005
- it "generates a correct config hash using the Chef::Config values" do
1006
- knife.merge_configs
1007
- expect(knife.connection_opts).to match expected_result
1008
- end
1009
- end
1010
-
1011
- context "and unsupported Chef::Config options are given in Chef::Config, not in CLI" do
1012
- before do
1013
- Chef::Config[:knife][:password] = "blah"
1014
- Chef::Config[:knife][:ssh_password] = "blah"
1015
- Chef::Config[:knife][:preserve_home] = true
1016
- Chef::Config[:knife][:use_sudo] = true
1017
- Chef::Config[:knife][:ssh_forward_agent] = "blah"
1018
- end
1019
- it "does not include the corresponding option in the connection options" do
1020
- knife.merge_configs
1021
- expect(knife.connection_opts.key?(:password)).to eq false
1022
- expect(knife.connection_opts.key?(:ssh_forward_agent)).to eq false
1023
- expect(knife.connection_opts.key?(:use_sudo)).to eq false
1024
- expect(knife.connection_opts.key?(:preserve_home)).to eq false
1025
- end
1026
- end
1027
-
1028
- context "and some CLI options have been given" do
1029
- before do
1030
- knife.config = {}
1031
- knife.config[:connection_user] = "sshalice"
1032
- knife.config[:connection_port] = 12
1033
- knife.config[:ssh_port] = "13" # canary to indirectly verify we're not looking for the wrong CLI flag
1034
- knife.config[:connection_password] = "feta cheese"
1035
- knife.config[:max_wait] = 150.0
1036
- knife.config[:session_timeout] = 120
1037
- knife.config[:use_sudo] = true
1038
- knife.config[:use_sudo_pasword] = true
1039
- knife.config[:ssh_forward_agent] = true
1040
- end
1041
-
1042
- let(:expected_result) do
1043
- {
1044
- logger: Chef::Log, # not configurable
1045
- max_wait_until_ready: 150, # cli (converted to int)
1046
- connection_timeout: 120, # cli
1047
- user: "sshalice", # cli
1048
- password: "feta cheese", # cli
1049
- bastion_host: "mygateway.local", # Config
1050
- bastion_port: 1234, # Config
1051
- bastion_user: "admin", # Config
1052
- forward_agent: true, # cli
1053
- keys_only: false, # implied false from config password present
1054
- key_files: ["/identity.pem", "/gateway.pem"], # Config
1055
- sudo: true, # ccli
1056
- verify_host_key: "always", # Config
1057
- port: 12, # cli
1058
- non_interactive: true,
1059
- }
1060
- end
1061
-
1062
- it "generates a config hash using the CLI options when available and falling back to Chef::Config values" do
1063
- knife.merge_configs
1064
- expect(knife.connection_opts).to match expected_result
1065
- end
1066
- end
1067
-
1068
- context "and all CLI options have been given" do
1069
- before do
1070
- knife.config = {}
1071
- knife.config[:max_wait] = 150.0
1072
- knife.config[:session_timeout] = 120
1073
- knife.config[:connection_user] = "sshroot"
1074
- knife.config[:connection_port] = 1000
1075
- knife.config[:connection_password] = "blah"
1076
- knife.config[:forward_agent] = true
1077
- knife.config[:use_sudo] = true
1078
- knife.config[:use_sudo_password] = true
1079
- knife.config[:preserve_home] = true
1080
- knife.config[:use_sudo_pasword] = true
1081
- knife.config[:ssh_forward_agent] = true
1082
- knife.config[:ssh_verify_host_key] = true
1083
- knife.config[:ssh_gateway_identity] = "/gateway-identity.pem"
1084
- knife.config[:ssh_gateway] = "me@example.com:10"
1085
- knife.config[:ssh_identity_file] = "/my-identity.pem"
1086
-
1087
- # We'll set these as canaries - if one of these values shows up
1088
- # in a failed test, then the behavior of not pulling from these keys
1089
- # out of knife.config is broken:
1090
- knife.config[:ssh_user] = "do not use"
1091
- knife.config[:ssh_port] = 1001
1092
- end
1093
- let(:expected_result) do
1094
- {
1095
- logger: Chef::Log, # not configurable
1096
- max_wait_until_ready: 150, # converted to int
1097
- connection_timeout: 120,
1098
- user: "sshroot",
1099
- password: "blah",
1100
- port: 1000,
1101
- bastion_host: "example.com",
1102
- bastion_port: 10,
1103
- bastion_user: "me",
1104
- forward_agent: true,
1105
- keys_only: false,
1106
- key_files: ["/my-identity.pem", "/gateway-identity.pem"],
1107
- sudo: true,
1108
- sudo_options: "-H",
1109
- sudo_password: "blah",
1110
- verify_host_key: true,
1111
- non_interactive: true,
1112
- }
1113
- end
1114
- it "generates a config hash using the CLI options and pulling nothing from Chef::Config" do
1115
- knife.merge_configs
1116
- expect(knife.connection_opts).to match expected_result
1117
- end
1118
- end
1119
- end
1120
- context "and no values are provided from Chef::Config or CLI" do
1121
- before do
1122
- # We will use knife's actual config since these tests
1123
- # have assumptions based on CLI default values
1124
- config = {}
1125
- end
1126
-
1127
- let(:expected_result) do
1128
- {
1129
- forward_agent: false,
1130
- key_files: [],
1131
- logger: Chef::Log,
1132
- keys_only: false,
1133
- sudo: false,
1134
- verify_host_key: "always",
1135
- non_interactive: true,
1136
- connection_timeout: 60,
1137
- }
1138
- end
1139
- it "populates appropriate defaults" do
1140
- knife.merge_configs
1141
- expect(knife.connection_opts).to match expected_result
1142
- end
1143
- end
1144
-
1145
- end # ssh
1146
- end # functional tests
1147
-
1148
- end # connection_opts
1149
-
1150
- context "#base_opts" do
1151
- let(:connection_protocol) { nil }
1152
-
1153
- before do
1154
- allow(knife).to receive(:connection_protocol).and_return connection_protocol
1155
- end
1156
-
1157
- context "for all protocols" do
1158
- context "when password is provided" do
1159
- before do
1160
- knife.config[:connection_port] = 250
1161
- knife.config[:connection_user] = "test"
1162
- knife.config[:connection_password] = "opscode"
1163
- end
1164
-
1165
- let(:expected_opts) do
1166
- {
1167
- port: 250,
1168
- user: "test",
1169
- logger: Chef::Log,
1170
- password: "opscode",
1171
- }
1172
- end
1173
- it "generates the correct options" do
1174
- expect(knife.base_opts).to eq expected_opts
1175
- end
1176
-
1177
- end
1178
-
1179
- context "when password is not provided" do
1180
- before do
1181
- knife.config[:connection_port] = 250
1182
- knife.config[:connection_user] = "test"
1183
- end
1184
-
1185
- let(:expected_opts) do
1186
- {
1187
- port: 250,
1188
- user: "test",
1189
- logger: Chef::Log,
1190
- }
1191
- end
1192
- it "generates the correct options" do
1193
- expect(knife.base_opts).to eq expected_opts
1194
- end
1195
- end
1196
- end
1197
- end
1198
-
1199
- context "#host_verify_opts" do
1200
- let(:connection_protocol) { nil }
1201
- before do
1202
- allow(knife).to receive(:connection_protocol).and_return connection_protocol
1203
- end
1204
-
1205
- context "for winrm" do
1206
- let(:connection_protocol) { "winrm" }
1207
- it "returns the expected configuration" do
1208
- knife.config[:winrm_no_verify_cert] = true
1209
- expect(knife.host_verify_opts).to eq( { self_signed: true } )
1210
- end
1211
- it "provides a correct default when no option given" do
1212
- expect(knife.host_verify_opts).to eq( { self_signed: false } )
1213
- end
1214
- end
1215
-
1216
- context "for ssh" do
1217
- let(:connection_protocol) { "ssh" }
1218
- it "returns the expected configuration" do
1219
- knife.config[:ssh_verify_host_key] = false
1220
- expect(knife.host_verify_opts).to eq( { verify_host_key: false } )
1221
- end
1222
- it "provides a correct default when no option given" do
1223
- expect(knife.host_verify_opts).to eq( { verify_host_key: "always" } )
1224
- end
1225
- end
1226
- end
1227
-
1228
- # TODO - test keys_only, password, config source behavior
1229
- context "#ssh_identity_opts" do
1230
- let(:connection_protocol) { nil }
1231
- before do
1232
- allow(knife).to receive(:connection_protocol).and_return connection_protocol
1233
- end
1234
-
1235
- context "for winrm" do
1236
- let(:connection_protocol) { "winrm" }
1237
- it "returns an empty hash" do
1238
- expect(knife.ssh_identity_opts).to eq({})
1239
- end
1240
- end
1241
-
1242
- context "for ssh" do
1243
- let(:connection_protocol) { "ssh" }
1244
- context "when an identity file is specified" do
1245
- before do
1246
- knife.config[:ssh_identity_file] = "/identity.pem"
1247
- end
1248
- it "generates the expected configuration" do
1249
- expect(knife.ssh_identity_opts).to eq({
1250
- key_files: [ "/identity.pem" ],
1251
- keys_only: true,
1252
- })
1253
- end
1254
- context "and a password is also specified" do
1255
- before do
1256
- knife.config[:connection_password] = "blah"
1257
- end
1258
- it "generates the expected configuration (key, keys_only false)" do
1259
- expect(knife.ssh_identity_opts).to eq({
1260
- key_files: [ "/identity.pem" ],
1261
- keys_only: false,
1262
- })
1263
- end
1264
- end
1265
-
1266
- context "and a gateway is not specified" do
1267
- context "but a gateway identity file is specified" do
1268
- it "does not include the gateway identity file in keys" do
1269
- expect(knife.ssh_identity_opts).to eq({
1270
- key_files: ["/identity.pem"],
1271
- keys_only: true,
1272
- })
1273
- end
1274
-
1275
- end
1276
-
1277
- end
1278
-
1279
- context "and a gatway is specified" do
1280
- before do
1281
- knife.config[:ssh_gateway] = "example.com"
1282
- end
1283
- context "and a gateway identity file is not specified" do
1284
- it "config includes only identity file and not gateway identity" do
1285
- expect(knife.ssh_identity_opts).to eq({
1286
- key_files: [ "/identity.pem" ],
1287
- keys_only: true,
1288
- })
1289
- end
1290
- end
1291
-
1292
- context "and a gateway identity file is also specified" do
1293
- before do
1294
- knife.config[:ssh_gateway_identity] = "/gateway.pem"
1295
- end
1296
-
1297
- it "generates the expected configuration (both keys, keys_only true)" do
1298
- expect(knife.ssh_identity_opts).to eq({
1299
- key_files: [ "/identity.pem", "/gateway.pem" ],
1300
- keys_only: true,
1301
- })
1302
- end
1303
- end
1304
- end
1305
- end
1306
-
1307
- context "when no identity file is specified" do
1308
- it "generates the expected configuration (no keys, keys_only false)" do
1309
- expect(knife.ssh_identity_opts).to eq( {
1310
- key_files: [],
1311
- keys_only: false,
1312
- })
1313
- end
1314
- context "and a gateway with gateway identity file is specified" do
1315
- before do
1316
- knife.config[:ssh_gateway] = "host"
1317
- knife.config[:ssh_gateway_identity] = "/gateway.pem"
1318
- end
1319
-
1320
- it "generates the expected configuration (gateway key, keys_only false)" do
1321
- expect(knife.ssh_identity_opts).to eq({
1322
- key_files: [ "/gateway.pem" ],
1323
- keys_only: false,
1324
- })
1325
- end
1326
- end
1327
- end
1328
- end
1329
- end
1330
-
1331
- context "#gateway_opts" do
1332
- let(:connection_protocol) { nil }
1333
- before do
1334
- allow(knife).to receive(:connection_protocol).and_return connection_protocol
1335
- end
1336
-
1337
- context "for winrm" do
1338
- let(:connection_protocol) { "winrm" }
1339
- it "returns an empty hash" do
1340
- expect(knife.gateway_opts).to eq({})
1341
- end
1342
- end
1343
-
1344
- context "for ssh" do
1345
- let(:connection_protocol) { "ssh" }
1346
- context "and ssh_gateway with hostname, user and port provided" do
1347
- before do
1348
- knife.config[:ssh_gateway] = "testuser@gateway:9021"
1349
- end
1350
- it "returns a proper bastion host config subset" do
1351
- expect(knife.gateway_opts).to eq({
1352
- bastion_user: "testuser",
1353
- bastion_host: "gateway",
1354
- bastion_port: 9021,
1355
- })
1356
- end
1357
- end
1358
- context "and ssh_gateway with only hostname is given" do
1359
- before do
1360
- knife.config[:ssh_gateway] = "gateway"
1361
- end
1362
- it "returns a proper bastion host config subset" do
1363
- expect(knife.gateway_opts).to eq({
1364
- bastion_user: nil,
1365
- bastion_host: "gateway",
1366
- bastion_port: nil,
1367
- })
1368
- end
1369
- end
1370
- context "and ssh_gateway with hostname and user is is given" do
1371
- before do
1372
- knife.config[:ssh_gateway] = "testuser@gateway"
1373
- end
1374
- it "returns a proper bastion host config subset" do
1375
- expect(knife.gateway_opts).to eq({
1376
- bastion_user: "testuser",
1377
- bastion_host: "gateway",
1378
- bastion_port: nil,
1379
- })
1380
- end
1381
- end
1382
-
1383
- context "and ssh_gateway with hostname and port is is given" do
1384
- before do
1385
- knife.config[:ssh_gateway] = "gateway:11234"
1386
- end
1387
- it "returns a proper bastion host config subset" do
1388
- expect(knife.gateway_opts).to eq({
1389
- bastion_user: nil,
1390
- bastion_host: "gateway",
1391
- bastion_port: 11234,
1392
- })
1393
- end
1394
- end
1395
-
1396
- context "and ssh_gateway is not provided" do
1397
- it "returns an empty hash" do
1398
- expect(knife.gateway_opts).to eq({})
1399
- end
1400
- end
1401
- end
1402
- end
1403
-
1404
- context "#sudo_opts" do
1405
- let(:connection_protocol) { nil }
1406
- let(:sudo_pass) { nil }
1407
- before do
1408
- allow(knife).to receive(:connection_protocol).and_return connection_protocol
1409
- end
1410
-
1411
- context "for winrm" do
1412
- let(:connection_protocol) { "winrm" }
1413
- it "returns an empty hash" do
1414
- expect(knife.sudo_opts(sudo_pass)).to eq({})
1415
- end
1416
- end
1417
-
1418
- context "for ssh" do
1419
- let(:connection_protocol) { "ssh" }
1420
- context "when use_sudo is set" do
1421
- before do
1422
- knife.config[:use_sudo] = true
1423
- end
1424
-
1425
- it "returns a config that enables sudo" do
1426
- expect(knife.sudo_opts(sudo_pass)).to eq( { sudo: true } )
1427
- end
1428
-
1429
- context "when use_sudo_password is also set" do
1430
- before do
1431
- knife.config[:use_sudo_password] = true
1432
- knife.config[:connection_password] = "opscode"
1433
- end
1434
- it "includes :connection_password value in a sudo-enabled configuration" do
1435
- expect(knife.sudo_opts(sudo_pass)).to eq({
1436
- sudo: true,
1437
- sudo_password: "opscode",
1438
- })
1439
- end
1440
-
1441
- context "when sudo_pass is set, connection_password is not set" do
1442
- before do
1443
- knife.config[:connection_password] = nil
1444
- end
1445
- let(:sudo_pass) { "progress" }
1446
- it "includes :connection_password value in a sudo-enabled configuration" do
1447
- expect(knife.sudo_opts(sudo_pass)).to eq({
1448
- sudo: true,
1449
- sudo_password: "progress",
1450
- })
1451
- end
1452
- end
1453
-
1454
- context "when connection_password is set, sudo_pass is not set" do
1455
- before do
1456
- knife.config[:connection_password] = "opscode"
1457
- end
1458
- let(:sudo_pass) { nil }
1459
- it "includes :connection_password value in a sudo-enabled configuration" do
1460
- expect(knife.sudo_opts(sudo_pass)).to eq({
1461
- sudo: true,
1462
- sudo_password: "opscode",
1463
- })
1464
- end
1465
- end
1466
-
1467
- context "when connection_password is not set, sudo_pass is not set" do
1468
- before do
1469
- knife.config[:connection_password] = nil
1470
- end
1471
- let(:sudo_pass) { nil }
1472
- it "includes :connection_password value in a sudo-enabled configuration" do
1473
- expect(knife.sudo_opts(sudo_pass)).to eq({
1474
- sudo: true,
1475
- sudo_password: nil,
1476
- })
1477
- end
1478
- end
1479
-
1480
- # connection_password will take precedence here
1481
- context "when connection_password is set, sudo_pass is set" do
1482
- before do
1483
- knife.config[:connection_password] = "opscode"
1484
- end
1485
- let(:sudo_pass) { "progress" }
1486
- it "includes :connection_password value in a sudo-enabled configuration" do
1487
- expect(knife.sudo_opts(sudo_pass)).to eq({
1488
- sudo: true,
1489
- sudo_password: "opscode",
1490
- })
1491
- end
1492
- end
1493
-
1494
- end
1495
-
1496
- context "when preserve_home is set" do
1497
- before do
1498
- knife.config[:preserve_home] = true
1499
- end
1500
- it "enables sudo with sudo_option to preserve home" do
1501
- expect(knife.sudo_opts(sudo_pass)).to eq({
1502
- sudo_options: "-H",
1503
- sudo: true,
1504
- })
1505
- end
1506
- end
1507
- end
1508
-
1509
- context "when use_sudo is not set" do
1510
- before do
1511
- knife.config[:use_sudo_password] = true
1512
- knife.config[:preserve_home] = true
1513
- end
1514
- it "returns configuration for sudo off, ignoring other related options" do
1515
- expect(knife.sudo_opts(sudo_pass)).to eq( { sudo: false } )
1516
- end
1517
- end
1518
- end
1519
- end
1520
-
1521
- context "#ssh_opts" do
1522
- let(:connection_protocol) { nil }
1523
- before do
1524
- allow(knife).to receive(:connection_protocol).and_return connection_protocol
1525
- end
1526
-
1527
- context "for ssh" do
1528
- let(:connection_protocol) { "ssh" }
1529
- let(:default_opts) do
1530
- {
1531
- non_interactive: true,
1532
- forward_agent: false,
1533
- connection_timeout: 60,
1534
- }
1535
- end
1536
-
1537
- context "by default" do
1538
- it "returns a configuration hash with appropriate defaults" do
1539
- expect(knife.ssh_opts).to eq default_opts
1540
- end
1541
- end
1542
-
1543
- context "when ssh_forward_agent has a value" do
1544
- before do
1545
- knife.config[:ssh_forward_agent] = true
1546
- end
1547
- it "returns a default configuration hash with forward_agent set to true" do
1548
- expect(knife.ssh_opts).to eq(default_opts.merge(forward_agent: true))
1549
- end
1550
- end
1551
- context "when session_timeout has a value" do
1552
- before do
1553
- knife.config[:session_timeout] = 120
1554
- end
1555
- it "returns a default configuration hash with updated timeout value." do
1556
- expect(knife.ssh_opts).to eq(default_opts.merge(connection_timeout: 120))
1557
- end
1558
- end
1559
-
1560
- end
1561
-
1562
- context "for winrm" do
1563
- let(:connection_protocol) { "winrm" }
1564
- it "returns an empty has because ssh is not winrm" do
1565
- expect(knife.ssh_opts).to eq({})
1566
- end
1567
- end
1568
-
1569
- end
1570
-
1571
- context "#winrm_opts" do
1572
- let(:connection_protocol) { nil }
1573
- before do
1574
- allow(knife).to receive(:connection_protocol).and_return connection_protocol
1575
- end
1576
-
1577
- context "for winrm" do
1578
- let(:connection_protocol) { "winrm" }
1579
- let(:expected) do
1580
- {
1581
- winrm_transport: "negotiate",
1582
- winrm_basic_auth_only: false,
1583
- ssl: false,
1584
- ssl_peer_fingerprint: nil,
1585
- operation_timeout: 60,
1586
- }
1587
- end
1588
-
1589
- it "generates a correct configuration hash with expected defaults" do
1590
- expect(knife.winrm_opts).to eq expected
1591
- end
1592
-
1593
- context "with ssl_peer_fingerprint" do
1594
- let(:ssl_peer_fingerprint_expected) do
1595
- expected.merge({ ssl_peer_fingerprint: "ABCD" })
1596
- end
1597
-
1598
- before do
1599
- knife.config[:winrm_ssl_peer_fingerprint] = "ABCD"
1600
- end
1601
-
1602
- it "generates a correct options hash with ssl_peer_fingerprint from the config provided" do
1603
- expect(knife.winrm_opts).to eq ssl_peer_fingerprint_expected
1604
- end
1605
- end
1606
-
1607
- context "with winrm_ssl" do
1608
- let(:ssl_expected) do
1609
- expected.merge({ ssl: true })
1610
- end
1611
- before do
1612
- knife.config[:winrm_ssl] = true
1613
- end
1614
-
1615
- it "generates a correct options hash with ssl from the config provided" do
1616
- expect(knife.winrm_opts).to eq ssl_expected
1617
- end
1618
- end
1619
-
1620
- context "with winrm_auth_method" do
1621
- let(:winrm_auth_method_expected) do
1622
- expected.merge({ winrm_transport: "freeaccess" })
1623
- end
1624
-
1625
- before do
1626
- knife.config[:winrm_auth_method] = "freeaccess"
1627
- end
1628
-
1629
- it "generates a correct options hash with winrm_transport from the config provided" do
1630
- expect(knife.winrm_opts).to eq winrm_auth_method_expected
1631
- end
1632
- end
1633
-
1634
- context "with ca_trust_file" do
1635
- let(:ca_trust_expected) do
1636
- expected.merge({ ca_trust_path: "/trust.me" })
1637
- end
1638
- before do
1639
- knife.config[:ca_trust_file] = "/trust.me"
1640
- end
1641
-
1642
- it "generates a correct options hash with ca_trust_file from the config provided" do
1643
- expect(knife.winrm_opts).to eq ca_trust_expected
1644
- end
1645
- end
1646
-
1647
- context "with kerberos auth" do
1648
- let(:kerberos_expected) do
1649
- expected.merge({
1650
- kerberos_service: "testsvc",
1651
- kerberos_realm: "TESTREALM",
1652
- winrm_transport: "kerberos",
1653
- })
1654
- end
1655
-
1656
- before do
1657
- knife.config[:winrm_auth_method] = "kerberos"
1658
- knife.config[:kerberos_service] = "testsvc"
1659
- knife.config[:kerberos_realm] = "TESTREALM"
1660
- end
1661
-
1662
- it "generates a correct options hash containing kerberos auth configuration from the config provided" do
1663
- expect(knife.winrm_opts).to eq kerberos_expected
1664
- end
1665
- end
1666
-
1667
- context "with winrm_basic_auth_only" do
1668
- before do
1669
- knife.config[:winrm_basic_auth_only] = true
1670
- end
1671
- let(:basic_auth_expected) do
1672
- expected.merge( { winrm_basic_auth_only: true } )
1673
- end
1674
- it "generates a correct options hash containing winrm_basic_auth_only from the config provided" do
1675
- expect(knife.winrm_opts).to eq basic_auth_expected
1676
- end
1677
- end
1678
- end
1679
-
1680
- context "for ssh" do
1681
- let(:connection_protocol) { "ssh" }
1682
- it "returns an empty hash because ssh is not winrm" do
1683
- expect(knife.winrm_opts).to eq({})
1684
- end
1685
- end
1686
- end
1687
- describe "#run" do
1688
- it "performs the steps we expect to run a bootstrap" do
1689
- expect(knife).to receive(:check_eula_license)
1690
- expect(knife).to receive(:validate_name_args!).ordered
1691
- expect(knife).to receive(:validate_protocol!).ordered
1692
- expect(knife).to receive(:validate_first_boot_attributes!).ordered
1693
- expect(knife).to receive(:validate_winrm_transport_opts!).ordered
1694
- expect(knife).to receive(:validate_policy_options!).ordered
1695
- expect(knife).to receive(:winrm_warn_no_ssl_verification).ordered
1696
- expect(knife).to receive(:warn_on_short_session_timeout).ordered
1697
- expect(knife).to receive(:connect!).ordered
1698
- expect(knife).to receive(:register_client).ordered
1699
- expect(knife).to receive(:render_template).and_return "content"
1700
- expect(knife).to receive(:upload_bootstrap).with("content").and_return "/remote/path.sh"
1701
- expect(knife).to receive(:perform_bootstrap).with("/remote/path.sh")
1702
- expect(connection).to receive(:del_file!) # Make sure cleanup happens
1703
-
1704
- knife.run
1705
-
1706
- # Post-run verify expected state changes (not many directly in #run)
1707
- expect($stdout.sync).to eq true
1708
- end
1709
- end
1710
-
1711
- describe "#register_client" do
1712
- let(:vault_handler_mock) { double("ChefVaultHandler") }
1713
- let(:client_builder_mock) { double("ClientBuilder") }
1714
- let(:node_name) { nil }
1715
- before do
1716
- allow(knife).to receive(:chef_vault_handler).and_return vault_handler_mock
1717
- allow(knife).to receive(:client_builder).and_return client_builder_mock
1718
- knife.config[:chef_node_name] = node_name
1719
- end
1720
-
1721
- shared_examples_for "creating the client locally" do
1722
- context "when a valid node name is present" do
1723
- let(:node_name) { "test" }
1724
- before do
1725
- allow(client_builder_mock).to receive(:client).and_return "client"
1726
- allow(client_builder_mock).to receive(:client_path).and_return "/key.pem"
1727
- end
1728
-
1729
- it "runs client_builder and vault_handler" do
1730
- expect(client_builder_mock).to receive(:run)
1731
- expect(vault_handler_mock).to receive(:run).with("client")
1732
- knife.register_client
1733
- end
1734
-
1735
- it "sets the path to the client key in the bootstrap context" do
1736
- allow(client_builder_mock).to receive(:run)
1737
- allow(vault_handler_mock).to receive(:run).with("client")
1738
- knife.register_client
1739
- expect(knife.bootstrap_context.client_pem).to eq "/key.pem"
1740
- end
1741
- end
1742
-
1743
- context "when no valid node name is present" do
1744
- let(:node_name) { nil }
1745
- it "shows an error and exits" do
1746
- expect(knife.ui).to receive(:error)
1747
- expect { knife.register_client }.to raise_error(SystemExit)
1748
- end
1749
- end
1750
- end
1751
- context "when chef_vault_handler says we're using vault" do
1752
- let(:vault_handler_mock) { double("ChefVaultHandler") }
1753
- before do
1754
- allow(vault_handler_mock).to receive(:doing_chef_vault?).and_return true
1755
- end
1756
- it_behaves_like "creating the client locally"
1757
- end
1758
-
1759
- context "when an non-existant validation key is specified in chef config" do
1760
- before do
1761
- Chef::Config[:validation_key] = "/blah"
1762
- allow(vault_handler_mock).to receive(:doing_chef_vault?).and_return false
1763
- allow(File).to receive(:exist?).with(%r{/blah}).and_return false
1764
- end
1765
- it_behaves_like "creating the client locally"
1766
- end
1767
-
1768
- context "when a valid validation key is given and we're doing old-style client creation" do
1769
- before do
1770
- Chef::Config[:validation_key] = "/blah"
1771
- allow(File).to receive(:exist?).with(%r{/blah}).and_return true
1772
- allow(vault_handler_mock).to receive(:doing_chef_vault?).and_return false
1773
- end
1774
-
1775
- it "shows a warning message" do
1776
- expect(knife.ui).to receive(:warn).twice
1777
- knife.register_client
1778
- end
1779
- end
1780
- end
1781
-
1782
- describe "#perform_bootstrap" do
1783
- let(:exit_status) { 0 }
1784
- let(:stdout) { "" }
1785
- let(:result_mock) { double("result", exit_status: exit_status, stderr: "A message", stdout: stdout) }
1786
-
1787
- before do
1788
- allow(connection).to receive(:hostname).and_return "testhost"
1789
- end
1790
- it "runs the remote script and logs the output" do
1791
- expect(knife.ui).to receive(:info).with(/Bootstrapping.*/)
1792
- expect(knife).to receive(:bootstrap_command)
1793
- .with("/path.sh")
1794
- .and_return("sh /path.sh")
1795
- expect(connection)
1796
- .to receive(:run_command)
1797
- .with("sh /path.sh")
1798
- .and_yield("output here", nil)
1799
- .and_return result_mock
1800
-
1801
- expect(knife.ui).to receive(:msg).with(/testhost/)
1802
- knife.perform_bootstrap("/path.sh")
1803
- end
1804
-
1805
- context "when the remote command fails" do
1806
- let(:exit_status) { 1 }
1807
- it "shows an error and exits" do
1808
- expect(knife.ui).to receive(:info).with(/Bootstrapping.*/)
1809
- expect(knife).to receive(:bootstrap_command)
1810
- .with("/path.sh")
1811
- .and_return("sh /path.sh")
1812
- expect(connection).to receive(:run_command).with("sh /path.sh").and_return result_mock
1813
- expect { knife.perform_bootstrap("/path.sh") }.to raise_error(SystemExit)
1814
- end
1815
- end
1816
-
1817
- context "when the remote command failed due to su auth error" do
1818
- let(:exit_status) { 1 }
1819
- let(:stdout) { "su: Authentication failure" }
1820
- let(:connection_obj) { double("connection", transport_options: {}) }
1821
- it "shows an error and exits" do
1822
- allow(connection).to receive(:connection).and_return(connection_obj)
1823
- expect(knife.ui).to receive(:info).with(/Bootstrapping.*/)
1824
- expect(knife).to receive(:bootstrap_command)
1825
- .with("/path.sh")
1826
- .and_return("su - USER -c 'sh /path.sh'")
1827
- expect(connection)
1828
- .to receive(:run_command)
1829
- .with("su - USER -c 'sh /path.sh'")
1830
- .and_yield("output here", nil)
1831
- .and_raise(Train::UserError)
1832
- expect { knife.perform_bootstrap("/path.sh") }.to raise_error(Train::UserError)
1833
- end
1834
- end
1835
- end
1836
-
1837
- describe "#connect!" do
1838
- before do
1839
- # These are not required at run-time because train will handle its own
1840
- # protocol loading. In this case, we're simulating train failures and have to load
1841
- # them ourselves.
1842
- require "net/ssh"
1843
- require "train/transports/ssh"
1844
- end
1845
-
1846
- context "in the normal case" do
1847
- it "connects using the connection_opts and notifies the operator of progress" do
1848
- expect(knife.ui).to receive(:info).with(/Connecting to.*/)
1849
- expect(knife).to receive(:connection_opts).and_return( { opts: "here" })
1850
- expect(knife).to receive(:do_connect).with( { opts: "here" } )
1851
- knife.connect!
1852
- end
1853
- end
1854
-
1855
- context "when a general non-auth-failure occurs" do
1856
- let(:expected_error) { RuntimeError.new }
1857
- before do
1858
- allow(knife).to receive(:do_connect).and_raise(expected_error)
1859
- end
1860
- it "re-raises the exception" do
1861
- expect { knife.connect! }.to raise_error(expected_error)
1862
- end
1863
- end
1864
-
1865
- context "when ssh fingerprint is invalid" do
1866
- let(:expected_error) { Train::Error.new("fingerprint AA:BB is unknown for \"blah,127.0.0.1\"") }
1867
- before do
1868
- allow(knife).to receive(:do_connect).and_raise(expected_error)
1869
- end
1870
- it "warns, prompts to accept, then connects with verify_host_key of accept_new" do
1871
- expect(knife).to receive(:do_connect).and_raise(expected_error)
1872
- expect(knife.ui).to receive(:confirm)
1873
- .with(/.*host 'blah \(127.0.0.1\)'.*AA:BB.*Are you sure you want to continue.*/m)
1874
- .and_return(true)
1875
- expect(knife).to receive(:do_connect) do |opts|
1876
- expect(opts[:verify_host_key]).to eq :accept_new
1877
- end
1878
- knife.connect!
1879
- end
1880
- end
1881
-
1882
- context "when an auth failure occurs" do
1883
- let(:expected_error) do
1884
- e = Train::Error.new
1885
- actual = Net::SSH::AuthenticationFailed.new
1886
- # Simulate train's nested error - they wrap
1887
- # ssh/network errors in a TrainError.
1888
- allow(e).to receive(:cause).and_return(actual)
1889
- e
1890
- end
1891
-
1892
- let(:expected_error_password_prompt) do
1893
- e = Train::ClientError.new
1894
- reason = :no_ssh_password_or_key_available
1895
- allow(e).to receive(:reason).and_return(reason)
1896
- e
1897
- end
1898
-
1899
- let(:expected_error_password_prompt_winrm) do
1900
- e = RuntimeError.new
1901
- message = "password is a required option"
1902
- allow(e).to receive(:message).and_return(message)
1903
- e
1904
- end
1905
-
1906
- context "and password auth was used" do
1907
- before do
1908
- allow(connection).to receive(:password_auth?).and_return true
1909
- end
1910
-
1911
- it "re-raises the error so as not to resubmit the same failing password" do
1912
- expect(knife).to receive(:do_connect).and_raise(expected_error)
1913
- expect { knife.connect! }.to raise_error(expected_error)
1914
- end
1915
- end
1916
-
1917
- context "and password auth was not used" do
1918
- before do
1919
- allow(connection).to receive(:password_auth?).and_return false
1920
- allow(connection).to receive(:user).and_return "testuser"
1921
- allow(knife).to receive(:connection_protocol).and_return connection_protocol
1922
- end
1923
-
1924
- context "when using ssh" do
1925
- let(:connection_protocol) { "ssh" }
1926
-
1927
- it "warns, prompts for password, then reconnects with a password-enabled configuration using the new password" do
1928
- expect(knife).to receive(:do_connect).and_raise(expected_error_password_prompt)
1929
- expect(knife.ui).to receive(:warn).with(/Failed to auth.*/)
1930
- expect(knife.ui).to receive(:ask).and_return("newpassword")
1931
- # Ensure that we set echo off to prevent showing password on the screen
1932
- expect(knife).to receive(:do_connect) do |opts|
1933
- expect(opts[:password]).to eq "newpassword"
1934
- end
1935
- knife.connect!
1936
- end
1937
- end
1938
-
1939
- context "when using winrm" do
1940
- let(:connection_protocol) { "winrm" }
1941
-
1942
- it "warns, prompts for password, then reconnects with a password-enabled configuration using the new password for" do
1943
- expect(knife).to receive(:do_connect).and_raise(expected_error_password_prompt_winrm)
1944
- expect(knife.ui).to receive(:warn).with(/Failed to auth.*/)
1945
- expect(knife.ui).to receive(:ask).and_return("newpassword")
1946
- # Ensure that we set echo off to prevent showing password on the screen
1947
- expect(knife).to receive(:do_connect) do |opts|
1948
- expect(opts[:password]).to eq "newpassword"
1949
- end
1950
- knife.connect!
1951
- end
1952
- end
1953
- end
1954
- end
1955
- end
1956
-
1957
- it "verifies that a server to bootstrap was given as a command line arg" do
1958
- knife.name_args = nil
1959
- expect(knife).to receive(:check_eula_license)
1960
- expect { knife.run }.to raise_error(SystemExit)
1961
- expect(stderr.string).to match(/ERROR:.+FQDN or ip/)
1962
- end
1963
-
1964
- describe "#bootstrap_context" do
1965
- context "under Windows" do
1966
- let(:windows_test) { true }
1967
- it "creates a WindowsBootstrapContext" do
1968
- require "chef/knife/core/windows_bootstrap_context"
1969
- expect(knife.bootstrap_context.class).to eq Chef::Knife::Core::WindowsBootstrapContext
1970
- end
1971
- end
1972
-
1973
- context "under linux" do
1974
- let(:linux_test) { true }
1975
- it "creates a BootstrapContext" do
1976
- require "chef/knife/core/bootstrap_context"
1977
- expect(knife.bootstrap_context.class).to eq Chef::Knife::Core::BootstrapContext
1978
- end
1979
- end
1980
- end
1981
-
1982
- describe "#config_value" do
1983
- before do
1984
- knife.config[:test_key_a] = "a from cli"
1985
- knife.config[:test_key_b] = "b from cli"
1986
- Chef::Config[:knife][:test_key_a] = "a from Chef::Config"
1987
- Chef::Config[:knife][:test_key_c] = "c from Chef::Config"
1988
- Chef::Config[:knife][:alt_test_key_c] = "alt c from Chef::Config"
1989
- knife.merge_configs
1990
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
1991
- end
1992
-
1993
- it "returns the Chef::Config value from the cli when the CLI key is set" do
1994
- expect(knife.config_value(:test_key_a, :alt_test_key_c)).to eq "a from cli"
1995
- end
1996
-
1997
- it "returns the Chef::Config value from the alternative key when the CLI key is not set" do
1998
- expect(knife.config_value(:test_key_d, :alt_test_key_c)).to eq "alt c from Chef::Config"
1999
- end
2000
-
2001
- it "returns the default value when the key is not provided by CLI or Chef::Config" do
2002
- expect(knife.config_value(:missing_key, :missing_key, "found")).to eq "found"
2003
- end
2004
- end
2005
-
2006
- describe "#upload_bootstrap" do
2007
- before do
2008
- allow(connection).to receive(:temp_dir).and_return(temp_dir)
2009
- allow(connection).to receive(:normalize_path) { |a| a }
2010
- end
2011
-
2012
- let(:content) { "bootstrap script content" }
2013
- context "under Windows" do
2014
- let(:windows_test) { true }
2015
- let(:temp_dir) { "C:/Temp/bootstrap" }
2016
- it "creates a bat file in the temp dir provided by connection, using given content" do
2017
- expect(connection).to receive(:upload_file_content!).with(content, "C:/Temp/bootstrap/bootstrap.bat")
2018
- expect(knife.upload_bootstrap(content)).to eq "C:/Temp/bootstrap/bootstrap.bat"
2019
- end
2020
- end
2021
-
2022
- context "under Linux" do
2023
- let(:linux_test) { true }
2024
- let(:temp_dir) { "/tmp/bootstrap" }
2025
- it "creates a 'sh file in the temp dir provided by connection, using given content" do
2026
- expect(connection).to receive(:upload_file_content!).with(content, "/tmp/bootstrap/bootstrap.sh")
2027
- expect(knife.upload_bootstrap(content)).to eq "/tmp/bootstrap/bootstrap.sh"
2028
- end
2029
- end
2030
- end
2031
-
2032
- describe "#bootstrap_command" do
2033
- context "under Windows" do
2034
- let(:windows_test) { true }
2035
- it "prefixes the command to run under cmd.exe" do
2036
- expect(knife.bootstrap_command("autoexec.bat")).to eq "cmd.exe /C autoexec.bat"
2037
- end
2038
-
2039
- end
2040
- context "under Linux" do
2041
- let(:linux_test) { true }
2042
- it "prefixes the command to run under sh" do
2043
- expect(knife.bootstrap_command("bootstrap.sh")).to eq "sh bootstrap.sh"
2044
- end
2045
-
2046
- context "with --su-user option" do
2047
- let(:connection_obj) { double("connection", transport_options: {}) }
2048
- before do
2049
- knife.config[:su_user] = "root"
2050
- allow(connection).to receive(:connection).and_return(connection_obj)
2051
- end
2052
- it "prefixes the command to run using su -USER -c" do
2053
- expect(knife.bootstrap_command("bootstrap.sh")).to eq "su - #{knife.config[:su_user]} -c 'sh bootstrap.sh'"
2054
- expect(connection_obj.transport_options.key?(:pty)).to eq true
2055
- end
2056
-
2057
- it "sudo appended if --sudo option enabled" do
2058
- knife.config[:use_sudo] = true
2059
- expect(knife.bootstrap_command("bootstrap.sh")).to eq "sudo su - #{knife.config[:su_user]} -c 'sh bootstrap.sh'"
2060
- expect(connection_obj.transport_options.key?(:pty)).to eq true
2061
- end
2062
- end
2063
- end
2064
- end
2065
-
2066
- describe "#default_bootstrap_template" do
2067
- context "under Windows" do
2068
- let(:windows_test) { true }
2069
- it "is windows-chef-client-msi" do
2070
- expect(knife.default_bootstrap_template).to eq "windows-chef-client-msi"
2071
- end
2072
-
2073
- end
2074
- context "under Linux" do
2075
- let(:linux_test) { true }
2076
- it "is chef-full" do
2077
- expect(knife.default_bootstrap_template).to eq "chef-full"
2078
- end
2079
- end
2080
- end
2081
-
2082
- describe "#do_connect" do
2083
- let(:host_descriptor) { "example.com" }
2084
- let(:connection) { double("TrainConnector") }
2085
- let(:connector_mock) { double("TargetResolver", targets: [ connection ]) }
2086
- before do
2087
- allow(knife).to receive(:host_descriptor).and_return host_descriptor
2088
- end
2089
-
2090
- it "creates a TrainConnector and connects it" do
2091
- expect(Chef::Knife::Bootstrap::TrainConnector).to receive(:new).and_return connection
2092
- expect(connection).to receive(:connect!)
2093
- knife.do_connect({})
2094
- end
2095
-
2096
- context "when sshd configured with requiretty" do
2097
- let(:pty_err_msg) { "Sudo requires a TTY. Please see the README on how to configure sudo to allow for non-interactive usage." }
2098
- let(:expected_error) { Train::UserError.new(pty_err_msg, :sudo_no_tty) }
2099
- before do
2100
- allow(connection).to receive(:connect!).and_raise(expected_error)
2101
- end
2102
- it "retry with pty true request option" do
2103
- expect(Chef::Knife::Bootstrap::TrainConnector).to receive(:new).and_return(connection).exactly(2).times
2104
- expect(knife.ui).to receive(:warn).with("#{pty_err_msg} - trying with pty request")
2105
- expect { knife.do_connect({}) }.to raise_error(expected_error)
2106
- end
2107
- end
2108
-
2109
- context "when a train sudo error is thrown for missing terminal" do
2110
- let(:ui_error_msg) { "Sudo password is required for this operation. Please enter password using -P or --ssh-password option" }
2111
- let(:expected_error) { Train::UserError.new(ui_error_msg, :sudo_missing_terminal) }
2112
- before do
2113
- allow(connection).to receive(:connect!).and_raise(expected_error)
2114
- end
2115
- it "outputs user friendly error message" do
2116
- expect { knife.do_connect({}) }.not_to raise_error
2117
- expect(stderr.string).to include(ui_error_msg)
2118
- end
2119
- end
2120
-
2121
- end
2122
-
2123
- describe "validate_winrm_transport_opts!" do
2124
- before do
2125
- allow(knife).to receive(:connection_protocol).and_return connection_protocol
2126
- end
2127
-
2128
- context "when using ssh" do
2129
- let(:connection_protocol) { "ssh" }
2130
- it "returns true" do
2131
- expect(knife.validate_winrm_transport_opts!).to eq true
2132
- end
2133
- end
2134
- context "when using winrm" do
2135
- let(:connection_protocol) { "winrm" }
2136
- context "with plaintext auth" do
2137
- before do
2138
- knife.config[:winrm_auth_method] = "plaintext"
2139
- end
2140
- context "with ssl" do
2141
- before do
2142
- knife.config[:winrm_ssl] = true
2143
- end
2144
- it "will not error because we won't send anything in plaintext regardless" do
2145
- expect(knife.validate_winrm_transport_opts!).to eq true
2146
- end
2147
- end
2148
- context "without ssl" do
2149
- before do
2150
- knife.config[:winrm_ssl] = false
2151
- end
2152
- context "and no validation key exists" do
2153
- before do
2154
- Chef::Config[:validation_key] = "validation_key.pem"
2155
- allow(File).to receive(:exist?).with(/.*validation_key.pem/).and_return false
2156
- end
2157
-
2158
- it "will error because we will generate and send a client key over the wire in plaintext" do
2159
- expect { knife.validate_winrm_transport_opts! }.to raise_error(SystemExit)
2160
- end
2161
-
2162
- end
2163
- context "and a validation key exists" do
2164
- before do
2165
- Chef::Config[:validation_key] = "validation_key.pem"
2166
- allow(File).to receive(:exist?).with(/.*validation_key.pem/).and_return true
2167
- end
2168
- # TODO - don't we still send validation key?
2169
- it "will not error because we don not send client key over the wire" do
2170
- expect(knife.validate_winrm_transport_opts!).to eq true
2171
- end
2172
- end
2173
- end
2174
- end
2175
-
2176
- context "with other auth" do
2177
- before do
2178
- knife.config[:winrm_auth_method] = "kerberos"
2179
- end
2180
-
2181
- context "and no validation key exists" do
2182
- before do
2183
-
2184
- Chef::Config[:validation_key] = "validation_key.pem"
2185
- allow(File).to receive(:exist?).with(/.*validation_key.pem/).and_return false
2186
- end
2187
-
2188
- it "will not error because we're not using plaintext auth" do
2189
- expect(knife.validate_winrm_transport_opts!).to eq true
2190
- end
2191
- end
2192
- context "and a validation key exists" do
2193
- before do
2194
- Chef::Config[:validation_key] = "validation_key.pem"
2195
- allow(File).to receive(:exist?).with(/.*validation_key.pem/).and_return true
2196
- end
2197
-
2198
- it "will not error because a client key won't be sent over the wire in plaintext when a validation key is present" do
2199
- expect(knife.validate_winrm_transport_opts!).to eq true
2200
- end
2201
- end
2202
-
2203
- end
2204
-
2205
- end
2206
-
2207
- end
2208
-
2209
- describe "#winrm_warn_no_ssl_verification" do
2210
- before do
2211
- allow(knife).to receive(:connection_protocol).and_return connection_protocol
2212
- end
2213
-
2214
- context "when using ssh" do
2215
- let(:connection_protocol) { "ssh" }
2216
- it "does not issue a warning" do
2217
- expect(knife.ui).to_not receive(:warn)
2218
- knife.winrm_warn_no_ssl_verification
2219
- end
2220
- end
2221
- context "when using winrm" do
2222
- let(:connection_protocol) { "winrm" }
2223
- context "winrm_no_verify_cert is set" do
2224
- before do
2225
- knife.config[:winrm_no_verify_cert] = true
2226
- end
2227
-
2228
- context "and ca_trust_file is present" do
2229
- before do
2230
- knife.config[:ca_trust_file] = "file"
2231
- end
2232
-
2233
- it "does not issue a warning" do
2234
- expect(knife.ui).to_not receive(:warn)
2235
- knife.winrm_warn_no_ssl_verification
2236
- end
2237
- end
2238
-
2239
- context "and winrm_ssl_peer_fingerprint is present" do
2240
- before do
2241
- knife.config[:winrm_ssl_peer_fingerprint] = "ABCD"
2242
- end
2243
- it "does not issue a warning" do
2244
- expect(knife.ui).to_not receive(:warn)
2245
- knife.winrm_warn_no_ssl_verification
2246
- end
2247
- end
2248
- context "and neither ca_trust_file nor winrm_ssl_peer_fingerprint is present" do
2249
- it "issues a warning" do
2250
- expect(knife.ui).to receive(:warn)
2251
- knife.winrm_warn_no_ssl_verification
2252
- end
2253
- end
2254
- end
2255
- end
2256
- end
2257
-
2258
- describe "#warn_on_short_session_timeout" do
2259
- let(:session_timeout) { 60 }
2260
-
2261
- before do
2262
- allow(knife).to receive(:session_timeout).and_return(session_timeout)
2263
- end
2264
-
2265
- context "timeout is not set at all" do
2266
- let(:session_timeout) { nil }
2267
- it "does not issue a warning" do
2268
- expect(knife.ui).to_not receive(:warn)
2269
- knife.warn_on_short_session_timeout
2270
- end
2271
- end
2272
-
2273
- context "timeout is more than 15" do
2274
- let(:session_timeout) { 16 }
2275
- it "does not issue a warning" do
2276
- expect(knife.ui).to_not receive(:warn)
2277
- knife.warn_on_short_session_timeout
2278
- end
2279
- end
2280
- context "timeout is 15 or less" do
2281
- let(:session_timeout) { 15 }
2282
- it "issues a warning" do
2283
- expect(knife.ui).to receive(:warn)
2284
- knife.warn_on_short_session_timeout
2285
- end
2286
- end
2287
- end
2288
- end
1
+ #
2
+ # Author:: Ian Meyer (<ianmmeyer@gmail.com>)
3
+ # Copyright:: Copyright 2010-2016, Ian Meyer
4
+ # Copyright:: Copyright (c) Chef Software 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 "knife_spec_helper"
21
+
22
+ Chef::Knife::Bootstrap.load_deps
23
+
24
+ describe Chef::Knife::Bootstrap do
25
+ let(:bootstrap_template) { nil }
26
+ let(:stderr) { StringIO.new }
27
+ let(:bootstrap_cli_options) { [ ] }
28
+ let(:linux_test) { true }
29
+ let(:windows_test) { false }
30
+ let(:linux_test) { false }
31
+ let(:unix_test) { false }
32
+ let(:ssh_test) { false }
33
+
34
+ let(:connection) do
35
+ double("TrainConnector",
36
+ windows?: windows_test,
37
+ linux?: linux_test,
38
+ unix?: unix_test)
39
+ end
40
+
41
+ let(:knife) do
42
+ Chef::Log.logger = Logger.new(StringIO.new)
43
+ Chef::Config[:knife][:bootstrap_template] = bootstrap_template unless bootstrap_template.nil?
44
+
45
+ k = Chef::Knife::Bootstrap.new(bootstrap_cli_options)
46
+ allow(k.ui).to receive(:stderr).and_return(stderr)
47
+ allow(k).to receive(:encryption_secret_provided_ignore_encrypt_flag?).and_return(false)
48
+ allow(k).to receive(:connection).and_return connection
49
+ k.merge_configs
50
+ k
51
+ end
52
+
53
+ context "#check_eula_license" do
54
+ let(:acceptor) { instance_double(LicenseAcceptance::Acceptor) }
55
+
56
+ before do
57
+ expect(LicenseAcceptance::Acceptor).to receive(:new).and_return(acceptor)
58
+ end
59
+
60
+ describe "when a license is not required" do
61
+ it "does not set the chef_license" do
62
+ expect(acceptor).to receive(:license_required?).and_return(false)
63
+ knife.check_eula_license
64
+ expect(Chef::Config[:chef_license]).to eq(nil)
65
+ end
66
+ end
67
+
68
+ describe "when a license is required" do
69
+ it "sets the chef_license" do
70
+ expect(acceptor).to receive(:license_required?).and_return(true)
71
+ expect(acceptor).to receive(:id_from_mixlib).and_return("id")
72
+ expect(acceptor).to receive(:check_and_persist)
73
+ expect(acceptor).to receive(:acceptance_value).and_return("accept-no-persist")
74
+ knife.check_eula_license
75
+ expect(Chef::Config[:chef_license]).to eq("accept-no-persist")
76
+ end
77
+ end
78
+
79
+ describe "alias" do
80
+ before do
81
+ expect(acceptor).to receive(:license_required?).and_return(false)
82
+ end
83
+
84
+ it "should be aliased to check_license" do
85
+ expect(knife).to respond_to(:check_license)
86
+ expect(knife).to respond_to(:check_eula_license)
87
+
88
+ knife.check_license
89
+ end
90
+
91
+ it "should logs check_eula_license using check_license" do
92
+ expect(Chef::Log).to receive(:debug).with("Checking if we need to accept Chef license to bootstrap node")
93
+ knife.check_license
94
+ end
95
+
96
+ it "should logs the same with check_eula_license" do
97
+ expect(Chef::Log).to receive(:debug).with("Checking if we need to accept Chef license to bootstrap node")
98
+ knife.check_eula_license
99
+ end
100
+ end
101
+ end
102
+
103
+ context "#bootstrap_template" do
104
+ it "should default to chef-full" do
105
+ expect(knife.bootstrap_template).to be_a_kind_of(String)
106
+ expect(File.basename(knife.bootstrap_template)).to eq("chef-full")
107
+ end
108
+ end
109
+
110
+ context "#render_template - when using the chef-full default template" do
111
+ let(:rendered_template) do
112
+ knife.merge_configs
113
+ knife.render_template
114
+ end
115
+
116
+ it "should render client.rb" do
117
+ expect(rendered_template).to match("cat > /etc/chef/client.rb <<'EOP'")
118
+ expect(rendered_template).to match("chef_server_url \"https://localhost:443\"")
119
+ expect(rendered_template).to match("validation_client_name \"chef-validator\"")
120
+ expect(rendered_template).to match("log_location STDOUT")
121
+ end
122
+
123
+ it "should render first-boot.json" do
124
+ expect(rendered_template).to match("cat > /etc/chef/first-boot.json <<'EOP'")
125
+ expect(rendered_template).to match('{"run_list":\[\]}')
126
+ end
127
+
128
+ context "and encrypted_data_bag_secret was provided" do
129
+ it "should render encrypted_data_bag_secret file" do
130
+ expect(knife).to receive(:encryption_secret_provided_ignore_encrypt_flag?).and_return(true)
131
+ expect(knife).to receive(:read_secret).and_return("secrets")
132
+ expect(rendered_template).to match("cat > /etc/chef/encrypted_data_bag_secret <<'EOP'")
133
+ expect(rendered_template).to match('{"run_list":\[\]}')
134
+ expect(rendered_template).to match(/secrets/)
135
+ end
136
+ end
137
+ end
138
+
139
+ context "with --bootstrap-vault-item" do
140
+ let(:bootstrap_cli_options) { [ "--bootstrap-vault-item", "vault1:item1", "--bootstrap-vault-item", "vault1:item2", "--bootstrap-vault-item", "vault2:item1" ] }
141
+ it "sets the knife config cli option correctly" do
142
+ expect(knife.config[:bootstrap_vault_item]).to eq({ "vault1" => %w{item1 item2}, "vault2" => ["item1"] })
143
+ end
144
+ end
145
+
146
+ context "with --bootstrap-preinstall-command" do
147
+ command = "while sudo fuser /var/lib/dpkg/lock >/dev/null 2>&1; do\n echo 'waiting for dpkg lock';\n sleep 1;\n done;"
148
+ let(:bootstrap_cli_options) { [ "--bootstrap-preinstall-command", command ] }
149
+ let(:rendered_template) do
150
+ knife.merge_configs
151
+ knife.render_template
152
+ end
153
+ it "configures the preinstall command in the bootstrap template correctly" do
154
+ expect(rendered_template).to match(/command/)
155
+ end
156
+ end
157
+
158
+ context "with --bootstrap-proxy" do
159
+ let(:bootstrap_cli_options) { [ "--bootstrap-proxy", "1.1.1.1" ] }
160
+ let(:rendered_template) do
161
+ knife.merge_configs
162
+ knife.render_template
163
+ end
164
+ it "configures the https_proxy environment variable in the bootstrap template correctly" do
165
+ expect(rendered_template).to match(/https_proxy="1.1.1.1" export https_proxy/)
166
+ end
167
+ end
168
+
169
+ context "with --bootstrap-no-proxy" do
170
+ let(:bootstrap_cli_options) { [ "--bootstrap-no-proxy", "localserver" ] }
171
+ let(:rendered_template) do
172
+ knife.merge_configs
173
+ knife.render_template
174
+ end
175
+ it "configures the https_proxy environment variable in the bootstrap template correctly" do
176
+ expect(rendered_template).to match(/no_proxy="localserver" export no_proxy/)
177
+ end
178
+ end
179
+
180
+ context "with :bootstrap_template and :template_file cli options" do
181
+ let(:bootstrap_cli_options) { [ "--bootstrap-template", "my-template", "other-template" ] }
182
+
183
+ it "should select bootstrap template" do
184
+ expect(File.basename(knife.bootstrap_template)).to eq("my-template")
185
+ end
186
+ end
187
+
188
+ context "when finding templates" do
189
+ context "when :bootstrap_template config is set to a file" do
190
+ context "that doesn't exist" do
191
+ let(:bootstrap_template) { "/opt/blah/not/exists/template.erb" }
192
+
193
+ it "raises an error" do
194
+ expect { knife.find_template }.to raise_error(Errno::ENOENT)
195
+ end
196
+ end
197
+
198
+ context "that exists" do
199
+ let(:bootstrap_template) { File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "test.erb")) }
200
+
201
+ it "loads the given file as the template" do
202
+ expect(Chef::Log).to receive(:trace)
203
+ expect(knife.find_template).to eq(File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "test.erb")))
204
+ end
205
+ end
206
+ end
207
+
208
+ context "when :bootstrap_template config is set to a template name" do
209
+ let(:bootstrap_template) { "example" }
210
+
211
+ let(:builtin_template_path) { File.expand_path(File.join(__dir__, "../../../lib/chef/knife/bootstrap/templates", "example.erb")) }
212
+
213
+ let(:chef_config_dir_template_path) { "/knife/chef/config/bootstrap/example.erb" }
214
+
215
+ let(:env_home_template_path) { "/env/home/.chef/bootstrap/example.erb" }
216
+
217
+ let(:gem_files_template_path) { "/Users/schisamo/.rvm/gems/ruby-1.9.2-p180@chef-0.10/gems/knife-windows-0.5.4/lib/chef/knife/bootstrap/fake-bootstrap-template.erb" }
218
+
219
+ def configure_chef_config_dir
220
+ allow(Chef::Knife).to receive(:chef_config_dir).and_return("/knife/chef/config")
221
+ end
222
+
223
+ def configure_env_home
224
+ allow(Chef::Util::PathHelper).to receive(:home).with(".chef", "bootstrap", "example.erb").and_yield(env_home_template_path)
225
+ end
226
+
227
+ def configure_gem_files
228
+ allow(Gem).to receive(:find_files).and_return([ gem_files_template_path ])
229
+ end
230
+
231
+ before(:each) do
232
+ expect(File).to receive(:exist?).with(bootstrap_template).and_return(false)
233
+ end
234
+
235
+ context "when file is available everywhere" do
236
+ before do
237
+ configure_chef_config_dir
238
+ configure_env_home
239
+ configure_gem_files
240
+
241
+ expect(File).to receive(:exist?).with(builtin_template_path).and_return(true)
242
+ end
243
+
244
+ it "should load the template from built-in templates" do
245
+ expect(knife.find_template).to eq(builtin_template_path)
246
+ end
247
+ end
248
+
249
+ context "when file is available in chef_config_dir" do
250
+ before do
251
+ configure_chef_config_dir
252
+ configure_env_home
253
+ configure_gem_files
254
+
255
+ expect(File).to receive(:exist?).with(builtin_template_path).and_return(false)
256
+ expect(File).to receive(:exist?).with(chef_config_dir_template_path).and_return(true)
257
+
258
+ it "should load the template from chef_config_dir" do
259
+ knife.find_template.should eq(chef_config_dir_template_path)
260
+ end
261
+ end
262
+ end
263
+
264
+ context "when file is available in home directory" do
265
+ before do
266
+ configure_chef_config_dir
267
+ configure_env_home
268
+ configure_gem_files
269
+
270
+ expect(File).to receive(:exist?).with(builtin_template_path).and_return(false)
271
+ expect(File).to receive(:exist?).with(chef_config_dir_template_path).and_return(false)
272
+ expect(File).to receive(:exist?).with(env_home_template_path).and_return(true)
273
+ end
274
+
275
+ it "should load the template from chef_config_dir" do
276
+ expect(knife.find_template).to eq(env_home_template_path)
277
+ end
278
+ end
279
+
280
+ context "when file is available in Gem files" do
281
+ before do
282
+ configure_chef_config_dir
283
+ configure_env_home
284
+ configure_gem_files
285
+
286
+ expect(File).to receive(:exist?).with(builtin_template_path).and_return(false)
287
+ expect(File).to receive(:exist?).with(chef_config_dir_template_path).and_return(false)
288
+ expect(File).to receive(:exist?).with(env_home_template_path).and_return(false)
289
+ expect(File).to receive(:exist?).with(gem_files_template_path).and_return(true)
290
+ end
291
+
292
+ it "should load the template from Gem files" do
293
+ expect(knife.find_template).to eq(gem_files_template_path)
294
+ end
295
+ end
296
+
297
+ context "when file is available in Gem files and home dir doesn't exist" do
298
+ before do
299
+ configure_chef_config_dir
300
+ configure_gem_files
301
+ allow(Chef::Util::PathHelper).to receive(:home).with(".chef", "bootstrap", "example.erb").and_return(nil)
302
+
303
+ expect(File).to receive(:exist?).with(builtin_template_path).and_return(false)
304
+ expect(File).to receive(:exist?).with(chef_config_dir_template_path).and_return(false)
305
+ expect(File).to receive(:exist?).with(gem_files_template_path).and_return(true)
306
+ end
307
+
308
+ it "should load the template from Gem files" do
309
+ expect(knife.find_template).to eq(gem_files_template_path)
310
+ end
311
+ end
312
+ end
313
+ end
314
+
315
+ ["-t", "--bootstrap-template"].each do |t|
316
+ context "when #{t} option is given in the command line" do
317
+ it "sets the knife :bootstrap_template config" do
318
+ knife.parse_options([t, "blahblah"])
319
+ knife.merge_configs
320
+ expect(knife.bootstrap_template).to eq("blahblah")
321
+ end
322
+ end
323
+ end
324
+
325
+ context "with run_list template" do
326
+ let(:bootstrap_template) { File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "test.erb")) }
327
+
328
+ it "should return an empty run_list" do
329
+ expect(knife.render_template).to eq('{"run_list":[]}')
330
+ end
331
+
332
+ it "should have role[base] in the run_list" do
333
+ knife.parse_options(["-r", "role[base]"])
334
+ knife.merge_configs
335
+ expect(knife.render_template).to eq('{"run_list":["role[base]"]}')
336
+ end
337
+
338
+ it "should have role[base] and recipe[cupcakes] in the run_list" do
339
+ knife.parse_options(["-r", "role[base],recipe[cupcakes]"])
340
+ knife.merge_configs
341
+ expect(knife.render_template).to eq('{"run_list":["role[base]","recipe[cupcakes]"]}')
342
+ end
343
+
344
+ context "with bootstrap_attribute options" do
345
+ let(:jsonfile) do
346
+ file = Tempfile.new(["node", ".json"])
347
+ File.open(file.path, "w") { |f| f.puts '{"foo":{"bar":"baz"}}' }
348
+ file
349
+ end
350
+
351
+ it "should have foo => {bar => baz} in the first_boot from cli" do
352
+ knife.parse_options(["-j", '{"foo":{"bar":"baz"}}'])
353
+ knife.merge_configs
354
+ expected_hash = FFI_Yajl::Parser.new.parse('{"foo":{"bar":"baz"},"run_list":[]}')
355
+ actual_hash = FFI_Yajl::Parser.new.parse(knife.render_template)
356
+ expect(actual_hash).to eq(expected_hash)
357
+ end
358
+
359
+ it "should have foo => {bar => baz} in the first_boot from file" do
360
+ knife.parse_options(["--json-attribute-file", jsonfile.path])
361
+ knife.merge_configs
362
+ expected_hash = FFI_Yajl::Parser.new.parse('{"foo":{"bar":"baz"},"run_list":[]}')
363
+ actual_hash = FFI_Yajl::Parser.new.parse(knife.render_template)
364
+ expect(actual_hash).to eq(expected_hash)
365
+ jsonfile.close
366
+ end
367
+
368
+ it "raises a Chef::Exceptions::BootstrapCommandInputError with the proper error message" do
369
+ knife.parse_options(["-j", '{"foo":{"bar":"baz"}}'])
370
+ knife.parse_options(["--json-attribute-file", jsonfile.path])
371
+ knife.merge_configs
372
+ allow(knife).to receive(:validate_name_args!)
373
+ expect(knife).to receive(:check_eula_license)
374
+
375
+ expect { knife.run }.to raise_error(Chef::Exceptions::BootstrapCommandInputError)
376
+ jsonfile.close
377
+ end
378
+ end
379
+ end
380
+
381
+ context "with hints template" do
382
+ let(:bootstrap_template) { File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "test-hints.erb")) }
383
+
384
+ it "should create a hint file when told to" do
385
+ knife.parse_options(["--hint", "openstack"])
386
+ knife.merge_configs
387
+ expect(knife.render_template).to match(%r{/etc/chef/ohai/hints/openstack.json})
388
+ end
389
+
390
+ it "should populate a hint file with JSON when given a file to read" do
391
+ allow(::File).to receive(:read).and_return('{ "foo" : "bar" }')
392
+ knife.parse_options(["--hint", "openstack=hints/openstack.json"])
393
+ knife.merge_configs
394
+ expect(knife.render_template).to match(/\{\"foo\":\"bar\"\}/)
395
+ end
396
+ end
397
+
398
+ describe "specifying no_proxy with various entries" do
399
+ subject(:knife) do
400
+ k = described_class.new
401
+ Chef::Config[:knife][:bootstrap_template] = template_file
402
+ allow(k).to receive(:connection).and_return connection
403
+ k.parse_options(options)
404
+ k.merge_configs
405
+ k
406
+ end
407
+
408
+ let(:options) { ["--bootstrap-no-proxy", setting] }
409
+
410
+ let(:template_file) { File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "no_proxy.erb")) }
411
+
412
+ let(:rendered_template) do
413
+ knife.render_template
414
+ end
415
+
416
+ context "via --bootstrap-no-proxy" do
417
+ let(:setting) { "api.opscode.com" }
418
+
419
+ it "renders the client.rb with a single FQDN no_proxy entry" do
420
+ expect(rendered_template).to match(/.*no_proxy\s*"api.opscode.com".*/)
421
+ end
422
+ end
423
+
424
+ context "via --bootstrap-no-proxy multiple" do
425
+ let(:setting) { "api.opscode.com,172.16.10.*" }
426
+
427
+ it "renders the client.rb with comma-separated FQDN and wildcard IP address no_proxy entries" do
428
+ expect(rendered_template).to match(/.*no_proxy\s*"api.opscode.com,172.16.10.\*".*/)
429
+ end
430
+ end
431
+
432
+ context "via --ssl-verify-mode none" do
433
+ let(:options) { ["--node-ssl-verify-mode", "none"] }
434
+
435
+ it "renders the client.rb with ssl_verify_mode set to :verify_none" do
436
+ expect(rendered_template).to match(/ssl_verify_mode :verify_none/)
437
+ end
438
+ end
439
+
440
+ context "via --node-ssl-verify-mode peer" do
441
+ let(:options) { ["--node-ssl-verify-mode", "peer"] }
442
+
443
+ it "renders the client.rb with ssl_verify_mode set to :verify_peer" do
444
+ expect(rendered_template).to match(/ssl_verify_mode :verify_peer/)
445
+ end
446
+ end
447
+
448
+ context "via --node-ssl-verify-mode all" do
449
+ let(:options) { ["--node-ssl-verify-mode", "all"] }
450
+
451
+ it "raises error" do
452
+ expect { rendered_template }.to raise_error(RuntimeError)
453
+ end
454
+ end
455
+
456
+ context "via --node-verify-api-cert" do
457
+ let(:options) { ["--node-verify-api-cert"] }
458
+
459
+ it "renders the client.rb with verify_api_cert set to true" do
460
+ expect(rendered_template).to match(/verify_api_cert true/)
461
+ end
462
+ end
463
+
464
+ context "via --no-node-verify-api-cert" do
465
+ let(:options) { ["--no-node-verify-api-cert"] }
466
+
467
+ it "renders the client.rb with verify_api_cert set to false" do
468
+ expect(rendered_template).to match(/verify_api_cert false/)
469
+ end
470
+ end
471
+ end
472
+
473
+ describe "specifying the encrypted data bag secret key" do
474
+ let(:secret) { "supersekret" }
475
+ let(:options) { [] }
476
+ let(:bootstrap_template) { File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "secret.erb")) }
477
+ let(:rendered_template) do
478
+ knife.parse_options(options)
479
+ knife.merge_configs
480
+ knife.render_template
481
+ end
482
+
483
+ it "creates a secret file" do
484
+ expect(knife).to receive(:encryption_secret_provided_ignore_encrypt_flag?).and_return(true)
485
+ expect(knife).to receive(:read_secret).and_return(secret)
486
+ expect(rendered_template).to match(/#{secret}/)
487
+ end
488
+
489
+ it "renders the client.rb with an encrypted_data_bag_secret entry" do
490
+ expect(knife).to receive(:encryption_secret_provided_ignore_encrypt_flag?).and_return(true)
491
+ expect(knife).to receive(:read_secret).and_return(secret)
492
+ expect(rendered_template).to match(%r{encrypted_data_bag_secret\s*"/etc/chef/encrypted_data_bag_secret"})
493
+ end
494
+
495
+ end
496
+
497
+ describe "when transferring trusted certificates" do
498
+ let(:rendered_template) do
499
+ knife.merge_configs
500
+ knife.render_template
501
+ end
502
+
503
+ before do
504
+ Chef::Config[:trusted_certs_dir] = Chef::Util::PathHelper.cleanpath(File.join(CHEF_SPEC_DATA, "trusted_certs"))
505
+ end
506
+
507
+ it "creates /etc/chef/trusted_certs" do
508
+ expect(rendered_template).to match(%r{mkdir -p /etc/chef/trusted_certs})
509
+ end
510
+
511
+ it "copies the certificates in the directory" do
512
+ certificates = Dir[File.join(Chef::Config[:trusted_certs_dir], "*.{crt,pem}")]
513
+
514
+ certificates.each do |cert|
515
+ expect(rendered_template).to match(%r{cat > /etc/chef/trusted_certs/#{File.basename(cert)} <<'EOP'})
516
+ end
517
+ end
518
+
519
+ it "doesn't create /etc/chef/trusted_certs if :trusted_certs_dir is empty" do
520
+ Dir.mktmpdir do |dir|
521
+ Chef::Config[:trusted_certs_dir] = dir
522
+ expect(rendered_template).not_to match(%r{mkdir -p /etc/chef/trusted_certs})
523
+ end
524
+ end
525
+ end
526
+
527
+ context "when doing fips things" do
528
+ let(:template_file) { File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "no_proxy.erb")) }
529
+
530
+ before do
531
+ Chef::Config[:knife][:bootstrap_template] = template_file
532
+ knife.merge_configs
533
+ end
534
+
535
+ let(:rendered_template) do
536
+ knife.render_template
537
+ end
538
+
539
+ context "when knife is in fips mode" do
540
+ before do
541
+ Chef::Config[:fips] = true
542
+ end
543
+
544
+ it "renders 'fips true'" do
545
+ expect(rendered_template).to match("fips")
546
+ end
547
+ end
548
+
549
+ context "when knife is not in fips mode" do
550
+ before do
551
+ # This is required because the chef-fips pipeline does
552
+ # has a default value of true for fips
553
+ Chef::Config[:fips] = false
554
+ end
555
+
556
+ it "does not render anything about fips" do
557
+ expect(rendered_template).not_to match("fips")
558
+ end
559
+ end
560
+ end
561
+
562
+ describe "when transferring client.d" do
563
+
564
+ let(:rendered_template) do
565
+ knife.merge_configs
566
+ knife.render_template
567
+ end
568
+
569
+ before do
570
+ Chef::Config[:client_d_dir] = client_d_dir
571
+ end
572
+
573
+ context "when client_d_dir is nil" do
574
+ let(:client_d_dir) { nil }
575
+
576
+ it "does not create /etc/chef/client.d" do
577
+ expect(rendered_template).not_to match(%r{mkdir -p /etc/chef/client\.d})
578
+ end
579
+ end
580
+
581
+ context "when client_d_dir is set" do
582
+ let(:client_d_dir) do
583
+ Chef::Util::PathHelper.cleanpath(
584
+ File.join(__dir__, "../../data/client.d_00")
585
+ )
586
+ end
587
+
588
+ it "creates /etc/chef/client.d" do
589
+ expect(rendered_template).to match("mkdir -p /etc/chef/client\.d")
590
+ end
591
+
592
+ context "a flat directory structure" do
593
+ it "escapes single-quotes" do
594
+ expect(rendered_template).to match("cat > /etc/chef/client.d/02-strings.rb <<'EOP'")
595
+ expect(rendered_template).to match("something '\\\\''/foo/bar'\\\\''")
596
+ end
597
+
598
+ it "creates a file 00-foo.rb" do
599
+ expect(rendered_template).to match("cat > /etc/chef/client.d/00-foo.rb <<'EOP'")
600
+ expect(rendered_template).to match("d6f9b976-289c-4149-baf7-81e6ffecf228")
601
+ end
602
+ it "creates a file bar" do
603
+ expect(rendered_template).to match("cat > /etc/chef/client.d/bar <<'EOP'")
604
+ expect(rendered_template).to match("1 / 0")
605
+ end
606
+ end
607
+
608
+ context "a nested directory structure" do
609
+ let(:client_d_dir) do
610
+ Chef::Util::PathHelper.cleanpath(
611
+ File.join(__dir__, "../../data/client.d_01")
612
+ )
613
+ end
614
+ it "creates a file foo/bar.rb" do
615
+ expect(rendered_template).to match("cat > /etc/chef/client.d/foo/bar.rb <<'EOP'")
616
+ expect(rendered_template).to match("1 / 0")
617
+ end
618
+ end
619
+ end
620
+ end
621
+
622
+ describe "#connection_protocol" do
623
+ let(:host_descriptor) { "example.com" }
624
+ let(:config) { {} }
625
+ let(:knife_connection_protocol) { nil }
626
+ before do
627
+ allow(knife).to receive(:config).and_return config
628
+ allow(knife).to receive(:host_descriptor).and_return host_descriptor
629
+ if knife_connection_protocol
630
+ Chef::Config[:knife][:connection_protocol] = knife_connection_protocol
631
+ knife.merge_configs
632
+ end
633
+ end
634
+
635
+ context "when protocol is part of the host argument" do
636
+ let(:host_descriptor) { "winrm://myhost" }
637
+
638
+ it "returns the value provided by the host argument" do
639
+ expect(knife.connection_protocol).to eq "winrm"
640
+ end
641
+ end
642
+
643
+ context "when protocol is provided via the CLI flag" do
644
+ let(:config) { { connection_protocol: "winrm" } }
645
+ it "returns that value" do
646
+ expect(knife.connection_protocol).to eq "winrm"
647
+ end
648
+
649
+ end
650
+ context "when protocol is provided via the host argument and the CLI flag" do
651
+ let(:host_descriptor) { "ssh://example.com" }
652
+ let(:config) { { connection_protocol: "winrm" } }
653
+
654
+ it "returns the value provided by the host argument" do
655
+ expect(knife.connection_protocol).to eq "ssh"
656
+ end
657
+ end
658
+
659
+ context "when no explicit protocol is provided" do
660
+ let(:config) { {} }
661
+ let(:host_descriptor) { "example.com" }
662
+ let(:knife_connection_protocol) { "winrm" }
663
+ it "falls back to knife config" do
664
+ expect(knife.connection_protocol).to eq "winrm"
665
+ end
666
+ context "and there is no knife bootstrap_protocol" do
667
+ let(:knife_connection_protocol) { nil }
668
+ it "falls back to 'ssh'" do
669
+ expect(knife.connection_protocol).to eq "ssh"
670
+ end
671
+ end
672
+ end
673
+
674
+ end
675
+
676
+ describe "#validate_protocol!" do
677
+ let(:host_descriptor) { "example.com" }
678
+ let(:config) { {} }
679
+ let(:connection_protocol) { "ssh" }
680
+ before do
681
+ allow(knife).to receive(:config).and_return config
682
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
683
+ allow(knife).to receive(:host_descriptor).and_return host_descriptor
684
+ end
685
+
686
+ context "when protocol is provided both in the URL and via --protocol" do
687
+
688
+ context "and they do not match" do
689
+ let(:connection_protocol) { "ssh" }
690
+ let(:config) { { connection_protocol: "winrm" } }
691
+ it "outputs an error and exits" do
692
+ expect(knife.ui).to receive(:error)
693
+ expect { knife.validate_protocol! }.to raise_error SystemExit
694
+ end
695
+ end
696
+
697
+ context "and they do match" do
698
+ let(:connection_protocol) { "winrm" }
699
+ let(:config) { { connection_protocol: "winrm" } }
700
+ it "returns true" do
701
+ expect(knife.validate_protocol!).to eq true
702
+ end
703
+ end
704
+ end
705
+
706
+ context "and the protocol is supported" do
707
+
708
+ Chef::Knife::Bootstrap::SUPPORTED_CONNECTION_PROTOCOLS.each do |proto|
709
+ let(:connection_protocol) { proto }
710
+ it "returns true for #{proto}" do
711
+ expect(knife.validate_protocol!).to eq true
712
+ end
713
+ end
714
+ end
715
+
716
+ context "and the protocol is not supported" do
717
+ let(:connection_protocol) { "invalid" }
718
+ it "outputs an error and exits" do
719
+ expect(knife.ui).to receive(:error).with(/Unsupported protocol '#{connection_protocol}'/)
720
+ expect { knife.validate_protocol! }.to raise_error SystemExit
721
+ end
722
+ end
723
+ end
724
+
725
+ describe "#validate_policy_options!" do
726
+
727
+ context "when only policy_name is given" do
728
+
729
+ let(:bootstrap_cli_options) { %w{ --policy-name my-app-server } }
730
+
731
+ it "returns an error stating that policy_name and policy_group must be given together" do
732
+ expect { knife.validate_policy_options! }.to raise_error(SystemExit)
733
+ expect(stderr.string).to include("ERROR: --policy-name and --policy-group must be specified together")
734
+ end
735
+
736
+ end
737
+
738
+ context "when only policy_group is given" do
739
+
740
+ let(:bootstrap_cli_options) { %w{ --policy-group staging } }
741
+
742
+ it "returns an error stating that policy_name and policy_group must be given together" do
743
+ expect { knife.validate_policy_options! }.to raise_error(SystemExit)
744
+ expect(stderr.string).to include("ERROR: --policy-name and --policy-group must be specified together")
745
+ end
746
+
747
+ end
748
+
749
+ context "when both policy_name and policy_group are given, but run list is also given" do
750
+
751
+ let(:bootstrap_cli_options) { %w{ --policy-name my-app --policy-group staging --run-list cookbook } }
752
+
753
+ it "returns an error stating that policyfile and run_list are exclusive" do
754
+ expect { knife.validate_policy_options! }.to raise_error(SystemExit)
755
+ expect(stderr.string).to include("ERROR: Policyfile options and --run-list are exclusive")
756
+ end
757
+
758
+ end
759
+
760
+ context "when policy_name and policy_group are given with no conflicting options" do
761
+
762
+ let(:bootstrap_cli_options) { %w{ --policy-name my-app --policy-group staging } }
763
+
764
+ it "passes options validation" do
765
+ expect { knife.validate_policy_options! }.to_not raise_error
766
+ end
767
+
768
+ it "passes them into the bootstrap context" do
769
+ expect(knife.bootstrap_context.first_boot).to have_key(:policy_name)
770
+ expect(knife.bootstrap_context.first_boot).to have_key(:policy_group)
771
+ end
772
+
773
+ it "ensures that run_list is not set in the bootstrap context" do
774
+ expect(knife.bootstrap_context.first_boot).to_not have_key(:run_list)
775
+ end
776
+
777
+ end
778
+
779
+ # https://github.com/chef/chef/issues/4131
780
+ # Arguably a bug in the plugin: it shouldn't be setting this to nil, but it
781
+ # worked before, so make it work now.
782
+ context "when a plugin sets the run list option to nil" do
783
+ before do
784
+ knife.config[:run_list] = nil
785
+ end
786
+
787
+ it "passes options validation" do
788
+ expect { knife.validate_policy_options! }.to_not raise_error
789
+ end
790
+ end
791
+ end
792
+
793
+ # TODO - this is the only cli option we validate the _option_ itself -
794
+ # so we'll know if someone accidentally deletes or renames use_sudo_password
795
+ # Is this worht keeping? If so, then it seems we should expand it
796
+ # to cover all options.
797
+ context "validating use_sudo_password option" do
798
+ it "use_sudo_password contains description and long params for help" do
799
+ expect(knife.options).to(have_key(:use_sudo_password)) \
800
+ && expect(knife.options[:use_sudo_password][:description].to_s).not_to(eq(""))\
801
+ && expect(knife.options[:use_sudo_password][:long].to_s).not_to(eq(""))
802
+ end
803
+ end
804
+
805
+ context "#connection_opts" do
806
+ let(:connection_protocol) { "ssh" }
807
+ before do
808
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
809
+ end
810
+ context "behavioral test: " do
811
+ let(:expected_connection_opts) do
812
+ { base_opts: true,
813
+ ssh_identity_opts: true,
814
+ ssh_opts: true,
815
+ gateway_opts: true,
816
+ host_verify_opts: true,
817
+ sudo_opts: true,
818
+ winrm_opts: true }
819
+ end
820
+
821
+ it "queries and merges only expected configurations" do
822
+ expect(knife).to receive(:base_opts).and_return({ base_opts: true })
823
+ expect(knife).to receive(:host_verify_opts).and_return({ host_verify_opts: true })
824
+ expect(knife).to receive(:gateway_opts).and_return({ gateway_opts: true })
825
+ expect(knife).to receive(:sudo_opts).and_return({ sudo_opts: true })
826
+ expect(knife).to receive(:winrm_opts).and_return({ winrm_opts: true })
827
+ expect(knife).to receive(:ssh_opts).and_return({ ssh_opts: true })
828
+ expect(knife).to receive(:ssh_identity_opts).and_return({ ssh_identity_opts: true })
829
+ expect(knife.connection_opts).to match expected_connection_opts
830
+ end
831
+ end
832
+
833
+ context "functional test: " do
834
+ context "when protocol is winrm" do
835
+ let(:connection_protocol) { "winrm" }
836
+ # context "and neither CLI nor Chef::Config config entries have been provided"
837
+ # end
838
+ context "and all supported values are provided as Chef::Config entries" do
839
+ before do
840
+ # Set everything to easily identifiable and obviously fake values
841
+ # to verify that Chef::Config is being sourced instead of knife.config
842
+ knife.config = {}
843
+ Chef::Config[:knife][:max_wait] = 9999.0
844
+ Chef::Config[:knife][:winrm_user] = "winbob"
845
+ Chef::Config[:knife][:winrm_port] = 9999
846
+ Chef::Config[:knife][:ca_trust_file] = "trust.me"
847
+ Chef::Config[:knife][:kerberos_realm] = "realm"
848
+ Chef::Config[:knife][:kerberos_service] = "service"
849
+ Chef::Config[:knife][:winrm_auth_method] = "kerberos" # default is negotiate
850
+ Chef::Config[:knife][:winrm_basic_auth_only] = true
851
+ Chef::Config[:knife][:winrm_no_verify_cert] = true
852
+ Chef::Config[:knife][:session_timeout] = 9999
853
+ Chef::Config[:knife][:winrm_ssl] = true
854
+ Chef::Config[:knife][:winrm_ssl_peer_fingerprint] = "ABCDEF"
855
+ end
856
+
857
+ context "and no CLI options have been given" do
858
+ let(:expected_result) do
859
+ {
860
+ logger: Chef::Log, # not configurable
861
+ ca_trust_path: "trust.me",
862
+ max_wait_until_ready: 9999, # converted to int
863
+ operation_timeout: 9999,
864
+ ssl_peer_fingerprint: "ABCDEF",
865
+ winrm_transport: "kerberos",
866
+ winrm_basic_auth_only: true,
867
+ user: "winbob",
868
+ port: 9999,
869
+ self_signed: true,
870
+ ssl: true,
871
+ kerberos_realm: "realm",
872
+ kerberos_service: "service",
873
+ }
874
+ end
875
+
876
+ it "generates a config hash using the Chef::Config values" do
877
+ knife.merge_configs
878
+ expect(knife.connection_opts).to match expected_result
879
+ end
880
+
881
+ end
882
+
883
+ context "and some CLI options have been given" do
884
+ let(:expected_result) do
885
+ {
886
+ logger: Chef::Log, # not configurable
887
+ ca_trust_path: "no trust",
888
+ max_wait_until_ready: 9999,
889
+ operation_timeout: 9999,
890
+ ssl_peer_fingerprint: "ABCDEF",
891
+ winrm_transport: "kerberos",
892
+ winrm_basic_auth_only: true,
893
+ user: "microsoftbob",
894
+ port: 12,
895
+ self_signed: true,
896
+ ssl: true,
897
+ kerberos_realm: "realm",
898
+ kerberos_service: "service",
899
+ password: "lobster",
900
+ }
901
+ end
902
+
903
+ before do
904
+ knife.config[:ca_trust_file] = "no trust"
905
+ knife.config[:connection_user] = "microsoftbob"
906
+ knife.config[:connection_port] = 12
907
+ knife.config[:winrm_port] = "13" # indirectly verify we're not looking for the wrong CLI flag
908
+ knife.config[:connection_password] = "lobster"
909
+ end
910
+
911
+ it "generates a config hash using the CLI options when available and falling back to Chef::Config values" do
912
+ knife.merge_configs
913
+ expect(knife.connection_opts).to match expected_result
914
+ end
915
+ end
916
+
917
+ context "and all CLI options have been given" do
918
+ before do
919
+ # We'll force kerberos vi knife.config because it
920
+ # causes additional options to populate - make sure
921
+ # Chef::Config is different so we can be sure that we didn't
922
+ # pull in the Chef::Config value
923
+ Chef::Config[:knife][:winrm_auth_method] = "negotiate"
924
+ knife.config[:connection_password] = "blue"
925
+ knife.config[:max_wait] = 1000.0
926
+ knife.config[:connection_user] = "clippy"
927
+ knife.config[:connection_port] = 1000
928
+ knife.config[:winrm_port] = 1001 # We should not see this value get used
929
+
930
+ knife.config[:ca_trust_file] = "trust.the.internet"
931
+ knife.config[:kerberos_realm] = "otherrealm"
932
+ knife.config[:kerberos_service] = "otherservice"
933
+ knife.config[:winrm_auth_method] = "kerberos" # default is negotiate
934
+ knife.config[:winrm_basic_auth_only] = false
935
+ knife.config[:winrm_no_verify_cert] = false
936
+ knife.config[:session_timeout] = 1000
937
+ knife.config[:winrm_ssl] = false
938
+ knife.config[:winrm_ssl_peer_fingerprint] = "FEDCBA"
939
+ end
940
+ let(:expected_result) do
941
+ {
942
+ logger: Chef::Log, # not configurable
943
+ ca_trust_path: "trust.the.internet",
944
+ max_wait_until_ready: 1000, # converted to int
945
+ operation_timeout: 1000,
946
+ ssl_peer_fingerprint: "FEDCBA",
947
+ winrm_transport: "kerberos",
948
+ winrm_basic_auth_only: false,
949
+ user: "clippy",
950
+ port: 1000,
951
+ self_signed: false,
952
+ ssl: false,
953
+ kerberos_realm: "otherrealm",
954
+ kerberos_service: "otherservice",
955
+ password: "blue",
956
+ }
957
+ end
958
+ it "generates a config hash using the CLI options and pulling nothing from Chef::Config" do
959
+ knife.merge_configs
960
+ expect(knife.connection_opts).to match expected_result
961
+ end
962
+ end
963
+ end # with underlying Chef::Config values
964
+
965
+ context "and no values are provided from Chef::Config or CLI" do
966
+ before do
967
+ # We will use knife's actual config since these tests
968
+ # have assumptions based on CLI default values
969
+ end
970
+ let(:expected_result) do
971
+ {
972
+ logger: Chef::Log,
973
+ operation_timeout: 60,
974
+ self_signed: false,
975
+ ssl: false,
976
+ ssl_peer_fingerprint: nil,
977
+ winrm_basic_auth_only: false,
978
+ winrm_transport: "negotiate",
979
+ }
980
+ end
981
+ it "populates appropriate defaults" do
982
+ knife.merge_configs
983
+ expect(knife.connection_opts).to match expected_result
984
+ end
985
+ end
986
+ end # winrm
987
+
988
+ context "when protocol is ssh" do
989
+ let(:connection_protocol) { "ssh" }
990
+ # context "and neither CLI nor Chef::Config config entries have been provided"
991
+ # end
992
+ context "and all supported values are provided as Chef::Config entries" do
993
+ before do
994
+ # Set everything to easily identifiable and obviously fake values
995
+ # to verify that Chef::Config is being sourced instead of knife.config
996
+ knife.config = {}
997
+ Chef::Config[:knife][:max_wait] = 9999.0
998
+ Chef::Config[:knife][:session_timeout] = 9999
999
+ Chef::Config[:knife][:ssh_user] = "sshbob"
1000
+ Chef::Config[:knife][:ssh_port] = 9999
1001
+ Chef::Config[:knife][:host_key_verify] = false
1002
+ Chef::Config[:knife][:ssh_gateway_identity] = "/gateway.pem"
1003
+ Chef::Config[:knife][:ssh_gateway] = "admin@mygateway.local:1234"
1004
+ Chef::Config[:knife][:ssh_identity_file] = "/identity.pem"
1005
+ Chef::Config[:knife][:use_sudo_password] = false # We have no password.
1006
+ end
1007
+
1008
+ context "and no CLI options have been given" do
1009
+ let(:expected_result) do
1010
+ {
1011
+ logger: Chef::Log, # not configurable
1012
+ max_wait_until_ready: 9999, # converted to int
1013
+ connection_timeout: 9999,
1014
+ user: "sshbob",
1015
+ bastion_host: "mygateway.local",
1016
+ bastion_port: 1234,
1017
+ bastion_user: "admin",
1018
+ forward_agent: false,
1019
+ keys_only: true,
1020
+ key_files: ["/identity.pem", "/gateway.pem"],
1021
+ sudo: false,
1022
+ verify_host_key: "always",
1023
+ port: 9999,
1024
+ non_interactive: true,
1025
+ }
1026
+ end
1027
+
1028
+ it "generates a correct config hash using the Chef::Config values" do
1029
+ knife.merge_configs
1030
+ expect(knife.connection_opts).to match expected_result
1031
+ end
1032
+ end
1033
+
1034
+ context "and unsupported Chef::Config options are given in Chef::Config, not in CLI" do
1035
+ before do
1036
+ Chef::Config[:knife][:password] = "blah"
1037
+ Chef::Config[:knife][:ssh_password] = "blah"
1038
+ Chef::Config[:knife][:preserve_home] = true
1039
+ Chef::Config[:knife][:use_sudo] = true
1040
+ Chef::Config[:knife][:ssh_forward_agent] = "blah"
1041
+ end
1042
+ it "does not include the corresponding option in the connection options" do
1043
+ knife.merge_configs
1044
+ expect(knife.connection_opts.key?(:password)).to eq false
1045
+ expect(knife.connection_opts.key?(:ssh_forward_agent)).to eq false
1046
+ expect(knife.connection_opts.key?(:use_sudo)).to eq false
1047
+ expect(knife.connection_opts.key?(:preserve_home)).to eq false
1048
+ end
1049
+ end
1050
+
1051
+ context "and some CLI options have been given" do
1052
+ before do
1053
+ knife.config = {}
1054
+ knife.config[:connection_user] = "sshalice"
1055
+ knife.config[:connection_port] = 12
1056
+ knife.config[:ssh_port] = "13" # canary to indirectly verify we're not looking for the wrong CLI flag
1057
+ knife.config[:connection_password] = "feta cheese"
1058
+ knife.config[:max_wait] = 150.0
1059
+ knife.config[:session_timeout] = 120
1060
+ knife.config[:use_sudo] = true
1061
+ knife.config[:use_sudo_pasword] = true
1062
+ knife.config[:ssh_forward_agent] = true
1063
+ end
1064
+
1065
+ let(:expected_result) do
1066
+ {
1067
+ logger: Chef::Log, # not configurable
1068
+ max_wait_until_ready: 150, # cli (converted to int)
1069
+ connection_timeout: 120, # cli
1070
+ user: "sshalice", # cli
1071
+ password: "feta cheese", # cli
1072
+ bastion_host: "mygateway.local", # Config
1073
+ bastion_port: 1234, # Config
1074
+ bastion_user: "admin", # Config
1075
+ forward_agent: true, # cli
1076
+ keys_only: false, # implied false from config password present
1077
+ key_files: ["/identity.pem", "/gateway.pem"], # Config
1078
+ sudo: true, # ccli
1079
+ verify_host_key: "always", # Config
1080
+ port: 12, # cli
1081
+ non_interactive: true,
1082
+ }
1083
+ end
1084
+
1085
+ it "generates a config hash using the CLI options when available and falling back to Chef::Config values" do
1086
+ knife.merge_configs
1087
+ expect(knife.connection_opts).to match expected_result
1088
+ end
1089
+ end
1090
+
1091
+ context "and all CLI options have been given" do
1092
+ before do
1093
+ knife.config = {}
1094
+ knife.config[:max_wait] = 150.0
1095
+ knife.config[:session_timeout] = 120
1096
+ knife.config[:connection_user] = "sshroot"
1097
+ knife.config[:connection_port] = 1000
1098
+ knife.config[:connection_password] = "blah"
1099
+ knife.config[:forward_agent] = true
1100
+ knife.config[:use_sudo] = true
1101
+ knife.config[:use_sudo_password] = true
1102
+ knife.config[:preserve_home] = true
1103
+ knife.config[:use_sudo_pasword] = true
1104
+ knife.config[:ssh_forward_agent] = true
1105
+ knife.config[:ssh_verify_host_key] = true
1106
+ knife.config[:ssh_gateway_identity] = "/gateway-identity.pem"
1107
+ knife.config[:ssh_gateway] = "me@example.com:10"
1108
+ knife.config[:ssh_identity_file] = "/my-identity.pem"
1109
+
1110
+ # We'll set these as canaries - if one of these values shows up
1111
+ # in a failed test, then the behavior of not pulling from these keys
1112
+ # out of knife.config is broken:
1113
+ knife.config[:ssh_user] = "do not use"
1114
+ knife.config[:ssh_port] = 1001
1115
+ end
1116
+ let(:expected_result) do
1117
+ {
1118
+ logger: Chef::Log, # not configurable
1119
+ max_wait_until_ready: 150, # converted to int
1120
+ connection_timeout: 120,
1121
+ user: "sshroot",
1122
+ password: "blah",
1123
+ port: 1000,
1124
+ bastion_host: "example.com",
1125
+ bastion_port: 10,
1126
+ bastion_user: "me",
1127
+ forward_agent: true,
1128
+ keys_only: false,
1129
+ key_files: ["/my-identity.pem", "/gateway-identity.pem"],
1130
+ sudo: true,
1131
+ sudo_options: "-H",
1132
+ sudo_password: "blah",
1133
+ verify_host_key: true,
1134
+ non_interactive: true,
1135
+ }
1136
+ end
1137
+ it "generates a config hash using the CLI options and pulling nothing from Chef::Config" do
1138
+ knife.merge_configs
1139
+ expect(knife.connection_opts).to match expected_result
1140
+ end
1141
+ end
1142
+ end
1143
+ context "and no values are provided from Chef::Config or CLI" do
1144
+ before do
1145
+ # We will use knife's actual config since these tests
1146
+ # have assumptions based on CLI default values
1147
+ config = {}
1148
+ end
1149
+
1150
+ let(:expected_result) do
1151
+ {
1152
+ forward_agent: false,
1153
+ key_files: [],
1154
+ logger: Chef::Log,
1155
+ keys_only: false,
1156
+ sudo: false,
1157
+ verify_host_key: "always",
1158
+ non_interactive: true,
1159
+ connection_timeout: 60,
1160
+ }
1161
+ end
1162
+ it "populates appropriate defaults" do
1163
+ knife.merge_configs
1164
+ expect(knife.connection_opts).to match expected_result
1165
+ end
1166
+ end
1167
+
1168
+ end # ssh
1169
+ end # functional tests
1170
+
1171
+ end # connection_opts
1172
+
1173
+ context "#base_opts" do
1174
+ let(:connection_protocol) { nil }
1175
+
1176
+ before do
1177
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
1178
+ end
1179
+
1180
+ context "for all protocols" do
1181
+ context "when password is provided" do
1182
+ before do
1183
+ knife.config[:connection_port] = 250
1184
+ knife.config[:connection_user] = "test"
1185
+ knife.config[:connection_password] = "opscode"
1186
+ end
1187
+
1188
+ let(:expected_opts) do
1189
+ {
1190
+ port: 250,
1191
+ user: "test",
1192
+ logger: Chef::Log,
1193
+ password: "opscode",
1194
+ }
1195
+ end
1196
+ it "generates the correct options" do
1197
+ expect(knife.base_opts).to eq expected_opts
1198
+ end
1199
+
1200
+ end
1201
+
1202
+ context "when password is not provided" do
1203
+ before do
1204
+ knife.config[:connection_port] = 250
1205
+ knife.config[:connection_user] = "test"
1206
+ end
1207
+
1208
+ let(:expected_opts) do
1209
+ {
1210
+ port: 250,
1211
+ user: "test",
1212
+ logger: Chef::Log,
1213
+ }
1214
+ end
1215
+ it "generates the correct options" do
1216
+ expect(knife.base_opts).to eq expected_opts
1217
+ end
1218
+ end
1219
+ end
1220
+ end
1221
+
1222
+ context "#host_verify_opts" do
1223
+ let(:connection_protocol) { nil }
1224
+ before do
1225
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
1226
+ end
1227
+
1228
+ context "for winrm" do
1229
+ let(:connection_protocol) { "winrm" }
1230
+ it "returns the expected configuration" do
1231
+ knife.config[:winrm_no_verify_cert] = true
1232
+ expect(knife.host_verify_opts).to eq( { self_signed: true } )
1233
+ end
1234
+ it "provides a correct default when no option given" do
1235
+ expect(knife.host_verify_opts).to eq( { self_signed: false } )
1236
+ end
1237
+ end
1238
+
1239
+ context "for ssh" do
1240
+ let(:connection_protocol) { "ssh" }
1241
+ it "returns the expected configuration" do
1242
+ knife.config[:ssh_verify_host_key] = false
1243
+ expect(knife.host_verify_opts).to eq( { verify_host_key: false } )
1244
+ end
1245
+ it "provides a correct default when no option given" do
1246
+ expect(knife.host_verify_opts).to eq( { verify_host_key: "always" } )
1247
+ end
1248
+ end
1249
+ end
1250
+
1251
+ # TODO - test keys_only, password, config source behavior
1252
+ context "#ssh_identity_opts" do
1253
+ let(:connection_protocol) { nil }
1254
+ before do
1255
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
1256
+ end
1257
+
1258
+ context "for winrm" do
1259
+ let(:connection_protocol) { "winrm" }
1260
+ it "returns an empty hash" do
1261
+ expect(knife.ssh_identity_opts).to eq({})
1262
+ end
1263
+ end
1264
+
1265
+ context "for ssh" do
1266
+ let(:connection_protocol) { "ssh" }
1267
+ context "when an identity file is specified" do
1268
+ before do
1269
+ knife.config[:ssh_identity_file] = "/identity.pem"
1270
+ end
1271
+ it "generates the expected configuration" do
1272
+ expect(knife.ssh_identity_opts).to eq({
1273
+ key_files: [ "/identity.pem" ],
1274
+ keys_only: true,
1275
+ })
1276
+ end
1277
+ context "and a password is also specified" do
1278
+ before do
1279
+ knife.config[:connection_password] = "blah"
1280
+ end
1281
+ it "generates the expected configuration (key, keys_only false)" do
1282
+ expect(knife.ssh_identity_opts).to eq({
1283
+ key_files: [ "/identity.pem" ],
1284
+ keys_only: false,
1285
+ })
1286
+ end
1287
+ end
1288
+
1289
+ context "and a gateway is not specified" do
1290
+ context "but a gateway identity file is specified" do
1291
+ it "does not include the gateway identity file in keys" do
1292
+ expect(knife.ssh_identity_opts).to eq({
1293
+ key_files: ["/identity.pem"],
1294
+ keys_only: true,
1295
+ })
1296
+ end
1297
+
1298
+ end
1299
+
1300
+ end
1301
+
1302
+ context "and a gatway is specified" do
1303
+ before do
1304
+ knife.config[:ssh_gateway] = "example.com"
1305
+ end
1306
+ context "and a gateway identity file is not specified" do
1307
+ it "config includes only identity file and not gateway identity" do
1308
+ expect(knife.ssh_identity_opts).to eq({
1309
+ key_files: [ "/identity.pem" ],
1310
+ keys_only: true,
1311
+ })
1312
+ end
1313
+ end
1314
+
1315
+ context "and a gateway identity file is also specified" do
1316
+ before do
1317
+ knife.config[:ssh_gateway_identity] = "/gateway.pem"
1318
+ end
1319
+
1320
+ it "generates the expected configuration (both keys, keys_only true)" do
1321
+ expect(knife.ssh_identity_opts).to eq({
1322
+ key_files: [ "/identity.pem", "/gateway.pem" ],
1323
+ keys_only: true,
1324
+ })
1325
+ end
1326
+ end
1327
+ end
1328
+ end
1329
+
1330
+ context "when no identity file is specified" do
1331
+ it "generates the expected configuration (no keys, keys_only false)" do
1332
+ expect(knife.ssh_identity_opts).to eq( {
1333
+ key_files: [],
1334
+ keys_only: false,
1335
+ })
1336
+ end
1337
+ context "and a gateway with gateway identity file is specified" do
1338
+ before do
1339
+ knife.config[:ssh_gateway] = "host"
1340
+ knife.config[:ssh_gateway_identity] = "/gateway.pem"
1341
+ end
1342
+
1343
+ it "generates the expected configuration (gateway key, keys_only false)" do
1344
+ expect(knife.ssh_identity_opts).to eq({
1345
+ key_files: [ "/gateway.pem" ],
1346
+ keys_only: false,
1347
+ })
1348
+ end
1349
+ end
1350
+ end
1351
+ end
1352
+ end
1353
+
1354
+ context "#gateway_opts" do
1355
+ let(:connection_protocol) { nil }
1356
+ before do
1357
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
1358
+ end
1359
+
1360
+ context "for winrm" do
1361
+ let(:connection_protocol) { "winrm" }
1362
+ it "returns an empty hash" do
1363
+ expect(knife.gateway_opts).to eq({})
1364
+ end
1365
+ end
1366
+
1367
+ context "for ssh" do
1368
+ let(:connection_protocol) { "ssh" }
1369
+ context "and ssh_gateway with hostname, user and port provided" do
1370
+ before do
1371
+ knife.config[:ssh_gateway] = "testuser@gateway:9021"
1372
+ end
1373
+ it "returns a proper bastion host config subset" do
1374
+ expect(knife.gateway_opts).to eq({
1375
+ bastion_user: "testuser",
1376
+ bastion_host: "gateway",
1377
+ bastion_port: 9021,
1378
+ })
1379
+ end
1380
+ end
1381
+ context "and ssh_gateway with only hostname is given" do
1382
+ before do
1383
+ knife.config[:ssh_gateway] = "gateway"
1384
+ end
1385
+ it "returns a proper bastion host config subset" do
1386
+ expect(knife.gateway_opts).to eq({
1387
+ bastion_user: nil,
1388
+ bastion_host: "gateway",
1389
+ bastion_port: nil,
1390
+ })
1391
+ end
1392
+ end
1393
+ context "and ssh_gateway with hostname and user is is given" do
1394
+ before do
1395
+ knife.config[:ssh_gateway] = "testuser@gateway"
1396
+ end
1397
+ it "returns a proper bastion host config subset" do
1398
+ expect(knife.gateway_opts).to eq({
1399
+ bastion_user: "testuser",
1400
+ bastion_host: "gateway",
1401
+ bastion_port: nil,
1402
+ })
1403
+ end
1404
+ end
1405
+
1406
+ context "and ssh_gateway with hostname and port is is given" do
1407
+ before do
1408
+ knife.config[:ssh_gateway] = "gateway:11234"
1409
+ end
1410
+ it "returns a proper bastion host config subset" do
1411
+ expect(knife.gateway_opts).to eq({
1412
+ bastion_user: nil,
1413
+ bastion_host: "gateway",
1414
+ bastion_port: 11234,
1415
+ })
1416
+ end
1417
+ end
1418
+
1419
+ context "and ssh_gateway is not provided" do
1420
+ it "returns an empty hash" do
1421
+ expect(knife.gateway_opts).to eq({})
1422
+ end
1423
+ end
1424
+ end
1425
+ end
1426
+
1427
+ context "#sudo_opts" do
1428
+ let(:connection_protocol) { nil }
1429
+ let(:sudo_pass) { nil }
1430
+ before do
1431
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
1432
+ end
1433
+
1434
+ context "for winrm" do
1435
+ let(:connection_protocol) { "winrm" }
1436
+ it "returns an empty hash" do
1437
+ expect(knife.sudo_opts(sudo_pass)).to eq({})
1438
+ end
1439
+ end
1440
+
1441
+ context "for ssh" do
1442
+ let(:connection_protocol) { "ssh" }
1443
+ context "when use_sudo is set" do
1444
+ before do
1445
+ knife.config[:use_sudo] = true
1446
+ end
1447
+
1448
+ it "returns a config that enables sudo" do
1449
+ expect(knife.sudo_opts(sudo_pass)).to eq( { sudo: true } )
1450
+ end
1451
+
1452
+ context "when use_sudo_password is also set" do
1453
+ before do
1454
+ knife.config[:use_sudo_password] = true
1455
+ knife.config[:connection_password] = "opscode"
1456
+ end
1457
+ it "includes :connection_password value in a sudo-enabled configuration" do
1458
+ expect(knife.sudo_opts(sudo_pass)).to eq({
1459
+ sudo: true,
1460
+ sudo_password: "opscode",
1461
+ })
1462
+ end
1463
+
1464
+ context "when sudo_pass is set, connection_password is not set" do
1465
+ before do
1466
+ knife.config[:connection_password] = nil
1467
+ end
1468
+ let(:sudo_pass) { "progress" }
1469
+ it "includes :connection_password value in a sudo-enabled configuration" do
1470
+ expect(knife.sudo_opts(sudo_pass)).to eq({
1471
+ sudo: true,
1472
+ sudo_password: "progress",
1473
+ })
1474
+ end
1475
+ end
1476
+
1477
+ context "when connection_password is set, sudo_pass is not set" do
1478
+ before do
1479
+ knife.config[:connection_password] = "opscode"
1480
+ end
1481
+ let(:sudo_pass) { nil }
1482
+ it "includes :connection_password value in a sudo-enabled configuration" do
1483
+ expect(knife.sudo_opts(sudo_pass)).to eq({
1484
+ sudo: true,
1485
+ sudo_password: "opscode",
1486
+ })
1487
+ end
1488
+ end
1489
+
1490
+ context "when connection_password is not set, sudo_pass is not set" do
1491
+ before do
1492
+ knife.config[:connection_password] = nil
1493
+ end
1494
+ let(:sudo_pass) { nil }
1495
+ it "includes :connection_password value in a sudo-enabled configuration" do
1496
+ expect(knife.sudo_opts(sudo_pass)).to eq({
1497
+ sudo: true,
1498
+ sudo_password: nil,
1499
+ })
1500
+ end
1501
+ end
1502
+
1503
+ # connection_password will take precedence here
1504
+ context "when connection_password is set, sudo_pass is set" do
1505
+ before do
1506
+ knife.config[:connection_password] = "opscode"
1507
+ end
1508
+ let(:sudo_pass) { "progress" }
1509
+ it "includes :connection_password value in a sudo-enabled configuration" do
1510
+ expect(knife.sudo_opts(sudo_pass)).to eq({
1511
+ sudo: true,
1512
+ sudo_password: "opscode",
1513
+ })
1514
+ end
1515
+ end
1516
+
1517
+ end
1518
+
1519
+ context "when preserve_home is set" do
1520
+ before do
1521
+ knife.config[:preserve_home] = true
1522
+ end
1523
+ it "enables sudo with sudo_option to preserve home" do
1524
+ expect(knife.sudo_opts(sudo_pass)).to eq({
1525
+ sudo_options: "-H",
1526
+ sudo: true,
1527
+ })
1528
+ end
1529
+ end
1530
+ end
1531
+
1532
+ context "when use_sudo is not set" do
1533
+ before do
1534
+ knife.config[:use_sudo_password] = true
1535
+ knife.config[:preserve_home] = true
1536
+ end
1537
+ it "returns configuration for sudo off, ignoring other related options" do
1538
+ expect(knife.sudo_opts(sudo_pass)).to eq( { sudo: false } )
1539
+ end
1540
+ end
1541
+ end
1542
+ end
1543
+
1544
+ context "#ssh_opts" do
1545
+ let(:connection_protocol) { nil }
1546
+ before do
1547
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
1548
+ end
1549
+
1550
+ context "for ssh" do
1551
+ let(:connection_protocol) { "ssh" }
1552
+ let(:default_opts) do
1553
+ {
1554
+ non_interactive: true,
1555
+ forward_agent: false,
1556
+ connection_timeout: 60,
1557
+ }
1558
+ end
1559
+
1560
+ context "by default" do
1561
+ it "returns a configuration hash with appropriate defaults" do
1562
+ expect(knife.ssh_opts).to eq default_opts
1563
+ end
1564
+ end
1565
+
1566
+ context "when ssh_forward_agent has a value" do
1567
+ before do
1568
+ knife.config[:ssh_forward_agent] = true
1569
+ end
1570
+ it "returns a default configuration hash with forward_agent set to true" do
1571
+ expect(knife.ssh_opts).to eq(default_opts.merge(forward_agent: true))
1572
+ end
1573
+ end
1574
+ context "when session_timeout has a value" do
1575
+ before do
1576
+ knife.config[:session_timeout] = 120
1577
+ end
1578
+ it "returns a default configuration hash with updated timeout value." do
1579
+ expect(knife.ssh_opts).to eq(default_opts.merge(connection_timeout: 120))
1580
+ end
1581
+ end
1582
+
1583
+ end
1584
+
1585
+ context "for winrm" do
1586
+ let(:connection_protocol) { "winrm" }
1587
+ it "returns an empty has because ssh is not winrm" do
1588
+ expect(knife.ssh_opts).to eq({})
1589
+ end
1590
+ end
1591
+
1592
+ end
1593
+
1594
+ context "#winrm_opts" do
1595
+ let(:connection_protocol) { nil }
1596
+ before do
1597
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
1598
+ end
1599
+
1600
+ context "for winrm" do
1601
+ let(:connection_protocol) { "winrm" }
1602
+ let(:expected) do
1603
+ {
1604
+ winrm_transport: "negotiate",
1605
+ winrm_basic_auth_only: false,
1606
+ ssl: false,
1607
+ ssl_peer_fingerprint: nil,
1608
+ operation_timeout: 60,
1609
+ }
1610
+ end
1611
+
1612
+ it "generates a correct configuration hash with expected defaults" do
1613
+ expect(knife.winrm_opts).to eq expected
1614
+ end
1615
+
1616
+ context "with ssl_peer_fingerprint" do
1617
+ let(:ssl_peer_fingerprint_expected) do
1618
+ expected.merge({ ssl_peer_fingerprint: "ABCD" })
1619
+ end
1620
+
1621
+ before do
1622
+ knife.config[:winrm_ssl_peer_fingerprint] = "ABCD"
1623
+ end
1624
+
1625
+ it "generates a correct options hash with ssl_peer_fingerprint from the config provided" do
1626
+ expect(knife.winrm_opts).to eq ssl_peer_fingerprint_expected
1627
+ end
1628
+ end
1629
+
1630
+ context "with winrm_ssl" do
1631
+ let(:ssl_expected) do
1632
+ expected.merge({ ssl: true })
1633
+ end
1634
+ before do
1635
+ knife.config[:winrm_ssl] = true
1636
+ end
1637
+
1638
+ it "generates a correct options hash with ssl from the config provided" do
1639
+ expect(knife.winrm_opts).to eq ssl_expected
1640
+ end
1641
+ end
1642
+
1643
+ context "with winrm_auth_method" do
1644
+ let(:winrm_auth_method_expected) do
1645
+ expected.merge({ winrm_transport: "freeaccess" })
1646
+ end
1647
+
1648
+ before do
1649
+ knife.config[:winrm_auth_method] = "freeaccess"
1650
+ end
1651
+
1652
+ it "generates a correct options hash with winrm_transport from the config provided" do
1653
+ expect(knife.winrm_opts).to eq winrm_auth_method_expected
1654
+ end
1655
+ end
1656
+
1657
+ context "with ca_trust_file" do
1658
+ let(:ca_trust_expected) do
1659
+ expected.merge({ ca_trust_path: "/trust.me" })
1660
+ end
1661
+ before do
1662
+ knife.config[:ca_trust_file] = "/trust.me"
1663
+ end
1664
+
1665
+ it "generates a correct options hash with ca_trust_file from the config provided" do
1666
+ expect(knife.winrm_opts).to eq ca_trust_expected
1667
+ end
1668
+ end
1669
+
1670
+ context "with kerberos auth" do
1671
+ let(:kerberos_expected) do
1672
+ expected.merge({
1673
+ kerberos_service: "testsvc",
1674
+ kerberos_realm: "TESTREALM",
1675
+ winrm_transport: "kerberos",
1676
+ })
1677
+ end
1678
+
1679
+ before do
1680
+ knife.config[:winrm_auth_method] = "kerberos"
1681
+ knife.config[:kerberos_service] = "testsvc"
1682
+ knife.config[:kerberos_realm] = "TESTREALM"
1683
+ end
1684
+
1685
+ it "generates a correct options hash containing kerberos auth configuration from the config provided" do
1686
+ expect(knife.winrm_opts).to eq kerberos_expected
1687
+ end
1688
+ end
1689
+
1690
+ context "with winrm_basic_auth_only" do
1691
+ before do
1692
+ knife.config[:winrm_basic_auth_only] = true
1693
+ end
1694
+ let(:basic_auth_expected) do
1695
+ expected.merge( { winrm_basic_auth_only: true } )
1696
+ end
1697
+ it "generates a correct options hash containing winrm_basic_auth_only from the config provided" do
1698
+ expect(knife.winrm_opts).to eq basic_auth_expected
1699
+ end
1700
+ end
1701
+ end
1702
+
1703
+ context "for ssh" do
1704
+ let(:connection_protocol) { "ssh" }
1705
+ it "returns an empty hash because ssh is not winrm" do
1706
+ expect(knife.winrm_opts).to eq({})
1707
+ end
1708
+ end
1709
+ end
1710
+ describe "#run" do
1711
+ it "performs the steps we expect to run a bootstrap" do
1712
+ expect(knife).to receive(:check_eula_license)
1713
+ expect(knife).to receive(:validate_name_args!).ordered
1714
+ expect(knife).to receive(:validate_protocol!).ordered
1715
+ expect(knife).to receive(:validate_first_boot_attributes!).ordered
1716
+ expect(knife).to receive(:validate_winrm_transport_opts!).ordered
1717
+ expect(knife).to receive(:validate_policy_options!).ordered
1718
+ expect(knife).to receive(:winrm_warn_no_ssl_verification).ordered
1719
+ expect(knife).to receive(:warn_on_short_session_timeout).ordered
1720
+ expect(knife).to receive(:connect!).ordered
1721
+ expect(knife).to receive(:register_client).ordered
1722
+ expect(knife).to receive(:render_template).and_return "content"
1723
+ expect(knife).to receive(:upload_bootstrap).with("content").and_return "/remote/path.sh"
1724
+ expect(knife).to receive(:perform_bootstrap).with("/remote/path.sh")
1725
+ expect(connection).to receive(:del_file!) # Make sure cleanup happens
1726
+
1727
+ knife.run
1728
+
1729
+ # Post-run verify expected state changes (not many directly in #run)
1730
+ expect($stdout.sync).to eq true
1731
+ end
1732
+ end
1733
+
1734
+ describe "#register_client" do
1735
+ let(:vault_handler_mock) { double("ChefVaultHandler") }
1736
+ let(:client_builder_mock) { double("ClientBuilder") }
1737
+ let(:node_name) { nil }
1738
+ before do
1739
+ allow(knife).to receive(:chef_vault_handler).and_return vault_handler_mock
1740
+ allow(knife).to receive(:client_builder).and_return client_builder_mock
1741
+ knife.config[:chef_node_name] = node_name
1742
+ end
1743
+
1744
+ shared_examples_for "creating the client locally" do
1745
+ context "when a valid node name is present" do
1746
+ let(:node_name) { "test" }
1747
+ before do
1748
+ allow(client_builder_mock).to receive(:client).and_return "client"
1749
+ allow(client_builder_mock).to receive(:client_path).and_return "/key.pem"
1750
+ end
1751
+
1752
+ it "runs client_builder and vault_handler" do
1753
+ expect(client_builder_mock).to receive(:run)
1754
+ expect(vault_handler_mock).to receive(:run).with("client")
1755
+ knife.register_client
1756
+ end
1757
+
1758
+ it "sets the path to the client key in the bootstrap context" do
1759
+ allow(client_builder_mock).to receive(:run)
1760
+ allow(vault_handler_mock).to receive(:run).with("client")
1761
+ knife.register_client
1762
+ expect(knife.bootstrap_context.client_pem).to eq "/key.pem"
1763
+ end
1764
+ end
1765
+
1766
+ context "when no valid node name is present" do
1767
+ let(:node_name) { nil }
1768
+ it "shows an error and exits" do
1769
+ expect(knife.ui).to receive(:error)
1770
+ expect { knife.register_client }.to raise_error(SystemExit)
1771
+ end
1772
+ end
1773
+ end
1774
+ context "when chef_vault_handler says we're using vault" do
1775
+ let(:vault_handler_mock) { double("ChefVaultHandler") }
1776
+ before do
1777
+ allow(vault_handler_mock).to receive(:doing_chef_vault?).and_return true
1778
+ end
1779
+ it_behaves_like "creating the client locally"
1780
+ end
1781
+
1782
+ context "when an non-existant validation key is specified in chef config" do
1783
+ before do
1784
+ Chef::Config[:validation_key] = "/blah"
1785
+ allow(vault_handler_mock).to receive(:doing_chef_vault?).and_return false
1786
+ allow(File).to receive(:exist?).with(%r{/blah}).and_return false
1787
+ end
1788
+ it_behaves_like "creating the client locally"
1789
+ end
1790
+
1791
+ context "when a valid validation key is given and we're doing old-style client creation" do
1792
+ before do
1793
+ Chef::Config[:validation_key] = "/blah"
1794
+ allow(File).to receive(:exist?).with(%r{/blah}).and_return true
1795
+ allow(vault_handler_mock).to receive(:doing_chef_vault?).and_return false
1796
+ end
1797
+
1798
+ it "shows a warning message" do
1799
+ expect(knife.ui).to receive(:warn).twice
1800
+ knife.register_client
1801
+ end
1802
+ end
1803
+ end
1804
+
1805
+ describe "#perform_bootstrap" do
1806
+ let(:exit_status) { 0 }
1807
+ let(:stdout) { "" }
1808
+ let(:result_mock) { double("result", exit_status: exit_status, stderr: "A message", stdout: stdout) }
1809
+
1810
+ before do
1811
+ allow(connection).to receive(:hostname).and_return "testhost"
1812
+ end
1813
+ it "runs the remote script and logs the output" do
1814
+ expect(knife.ui).to receive(:info).with(/Bootstrapping.*/)
1815
+ expect(knife).to receive(:bootstrap_command)
1816
+ .with("/path.sh")
1817
+ .and_return("sh /path.sh")
1818
+ expect(connection)
1819
+ .to receive(:run_command)
1820
+ .with("sh /path.sh")
1821
+ .and_yield("output here", nil)
1822
+ .and_return result_mock
1823
+
1824
+ expect(knife.ui).to receive(:msg).with(/testhost/)
1825
+ knife.perform_bootstrap("/path.sh")
1826
+ end
1827
+
1828
+ context "when the remote command fails" do
1829
+ let(:exit_status) { 1 }
1830
+ it "shows an error and exits" do
1831
+ expect(knife.ui).to receive(:info).with(/Bootstrapping.*/)
1832
+ expect(knife).to receive(:bootstrap_command)
1833
+ .with("/path.sh")
1834
+ .and_return("sh /path.sh")
1835
+ expect(connection).to receive(:run_command).with("sh /path.sh").and_return result_mock
1836
+ expect { knife.perform_bootstrap("/path.sh") }.to raise_error(SystemExit)
1837
+ end
1838
+ end
1839
+
1840
+ context "when the remote command failed due to su auth error" do
1841
+ let(:exit_status) { 1 }
1842
+ let(:stdout) { "su: Authentication failure" }
1843
+ let(:connection_obj) { double("connection", transport_options: {}) }
1844
+ it "shows an error and exits" do
1845
+ allow(connection).to receive(:connection).and_return(connection_obj)
1846
+ expect(knife.ui).to receive(:info).with(/Bootstrapping.*/)
1847
+ expect(knife).to receive(:bootstrap_command)
1848
+ .with("/path.sh")
1849
+ .and_return("su - USER -c 'sh /path.sh'")
1850
+ expect(connection)
1851
+ .to receive(:run_command)
1852
+ .with("su - USER -c 'sh /path.sh'")
1853
+ .and_yield("output here", nil)
1854
+ .and_raise(Train::UserError)
1855
+ expect { knife.perform_bootstrap("/path.sh") }.to raise_error(Train::UserError)
1856
+ end
1857
+ end
1858
+ end
1859
+
1860
+ describe "#connect!" do
1861
+ before do
1862
+ # These are not required at run-time because train will handle its own
1863
+ # protocol loading. In this case, we're simulating train failures and have to load
1864
+ # them ourselves.
1865
+ require "net/ssh"
1866
+ require "train/transports/ssh"
1867
+ end
1868
+
1869
+ context "in the normal case" do
1870
+ it "connects using the connection_opts and notifies the operator of progress" do
1871
+ expect(knife.ui).to receive(:info).with(/Connecting to.*/)
1872
+ expect(knife).to receive(:connection_opts).and_return( { opts: "here" })
1873
+ expect(knife).to receive(:do_connect).with( { opts: "here" } )
1874
+ knife.connect!
1875
+ end
1876
+ end
1877
+
1878
+ context "when a general non-auth-failure occurs" do
1879
+ let(:expected_error) { RuntimeError.new }
1880
+ before do
1881
+ allow(knife).to receive(:do_connect).and_raise(expected_error)
1882
+ end
1883
+ it "re-raises the exception" do
1884
+ expect { knife.connect! }.to raise_error(expected_error)
1885
+ end
1886
+ end
1887
+
1888
+ context "when ssh fingerprint is invalid" do
1889
+ let(:expected_error) { Train::Error.new("fingerprint AA:BB is unknown for \"blah,127.0.0.1\"") }
1890
+ before do
1891
+ allow(knife).to receive(:do_connect).and_raise(expected_error)
1892
+ end
1893
+ it "warns, prompts to accept, then connects with verify_host_key of accept_new" do
1894
+ expect(knife).to receive(:do_connect).and_raise(expected_error)
1895
+ expect(knife.ui).to receive(:confirm)
1896
+ .with(/.*host 'blah \(127.0.0.1\)'.*AA:BB.*Are you sure you want to continue.*/m)
1897
+ .and_return(true)
1898
+ expect(knife).to receive(:do_connect) do |opts|
1899
+ expect(opts[:verify_host_key]).to eq :accept_new
1900
+ end
1901
+ knife.connect!
1902
+ end
1903
+ end
1904
+
1905
+ context "when an auth failure occurs" do
1906
+ let(:expected_error) do
1907
+ e = Train::Error.new
1908
+ actual = Net::SSH::AuthenticationFailed.new
1909
+ # Simulate train's nested error - they wrap
1910
+ # ssh/network errors in a TrainError.
1911
+ allow(e).to receive(:cause).and_return(actual)
1912
+ e
1913
+ end
1914
+
1915
+ let(:expected_error_password_prompt) do
1916
+ e = Train::ClientError.new
1917
+ reason = :no_ssh_password_or_key_available
1918
+ allow(e).to receive(:reason).and_return(reason)
1919
+ e
1920
+ end
1921
+
1922
+ let(:expected_error_password_prompt_winrm) do
1923
+ e = RuntimeError.new
1924
+ message = "password is a required option"
1925
+ allow(e).to receive(:message).and_return(message)
1926
+ e
1927
+ end
1928
+
1929
+ context "and password auth was used" do
1930
+ before do
1931
+ allow(connection).to receive(:password_auth?).and_return true
1932
+ end
1933
+
1934
+ it "re-raises the error so as not to resubmit the same failing password" do
1935
+ expect(knife).to receive(:do_connect).and_raise(expected_error)
1936
+ expect { knife.connect! }.to raise_error(expected_error)
1937
+ end
1938
+ end
1939
+
1940
+ context "and password auth was not used" do
1941
+ before do
1942
+ allow(connection).to receive(:password_auth?).and_return false
1943
+ allow(connection).to receive(:user).and_return "testuser"
1944
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
1945
+ end
1946
+
1947
+ context "when using ssh" do
1948
+ let(:connection_protocol) { "ssh" }
1949
+
1950
+ it "warns, prompts for password, then reconnects with a password-enabled configuration using the new password" do
1951
+ expect(knife).to receive(:do_connect).and_raise(expected_error_password_prompt)
1952
+ expect(knife.ui).to receive(:warn).with(/Failed to auth.*/)
1953
+ expect(knife.ui).to receive(:ask).and_return("newpassword")
1954
+ # Ensure that we set echo off to prevent showing password on the screen
1955
+ expect(knife).to receive(:do_connect) do |opts|
1956
+ expect(opts[:password]).to eq "newpassword"
1957
+ end
1958
+ knife.connect!
1959
+ end
1960
+ end
1961
+
1962
+ context "when using winrm" do
1963
+ let(:connection_protocol) { "winrm" }
1964
+
1965
+ it "warns, prompts for password, then reconnects with a password-enabled configuration using the new password for" do
1966
+ expect(knife).to receive(:do_connect).and_raise(expected_error_password_prompt_winrm)
1967
+ expect(knife.ui).to receive(:warn).with(/Failed to auth.*/)
1968
+ expect(knife.ui).to receive(:ask).and_return("newpassword")
1969
+ # Ensure that we set echo off to prevent showing password on the screen
1970
+ expect(knife).to receive(:do_connect) do |opts|
1971
+ expect(opts[:password]).to eq "newpassword"
1972
+ end
1973
+ knife.connect!
1974
+ end
1975
+ end
1976
+ end
1977
+ end
1978
+ end
1979
+
1980
+ it "verifies that a server to bootstrap was given as a command line arg" do
1981
+ knife.name_args = nil
1982
+ expect(knife).to receive(:check_eula_license)
1983
+ expect { knife.run }.to raise_error(SystemExit)
1984
+ expect(stderr.string).to match(/ERROR:.+FQDN or ip/)
1985
+ end
1986
+
1987
+ describe "#bootstrap_context" do
1988
+ context "under Windows" do
1989
+ let(:windows_test) { true }
1990
+ it "creates a WindowsBootstrapContext" do
1991
+ require "chef/knife/core/windows_bootstrap_context"
1992
+ expect(knife.bootstrap_context.class).to eq Chef::Knife::Core::WindowsBootstrapContext
1993
+ end
1994
+ end
1995
+
1996
+ context "under linux" do
1997
+ let(:linux_test) { true }
1998
+ it "creates a BootstrapContext" do
1999
+ require "chef/knife/core/bootstrap_context"
2000
+ expect(knife.bootstrap_context.class).to eq Chef::Knife::Core::BootstrapContext
2001
+ end
2002
+ end
2003
+ end
2004
+
2005
+ describe "#config_value" do
2006
+ before do
2007
+ knife.config[:test_key_a] = "a from cli"
2008
+ knife.config[:test_key_b] = "b from cli"
2009
+ Chef::Config[:knife][:test_key_a] = "a from Chef::Config"
2010
+ Chef::Config[:knife][:test_key_c] = "c from Chef::Config"
2011
+ Chef::Config[:knife][:alt_test_key_c] = "alt c from Chef::Config"
2012
+ knife.merge_configs
2013
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
2014
+ end
2015
+
2016
+ it "returns the Chef::Config value from the cli when the CLI key is set" do
2017
+ expect(knife.config_value(:test_key_a, :alt_test_key_c)).to eq "a from cli"
2018
+ end
2019
+
2020
+ it "returns the Chef::Config value from the alternative key when the CLI key is not set" do
2021
+ expect(knife.config_value(:test_key_d, :alt_test_key_c)).to eq "alt c from Chef::Config"
2022
+ end
2023
+
2024
+ it "returns the default value when the key is not provided by CLI or Chef::Config" do
2025
+ expect(knife.config_value(:missing_key, :missing_key, "found")).to eq "found"
2026
+ end
2027
+ end
2028
+
2029
+ describe "#upload_bootstrap" do
2030
+ before do
2031
+ allow(connection).to receive(:temp_dir).and_return(temp_dir)
2032
+ allow(connection).to receive(:normalize_path) { |a| a }
2033
+ end
2034
+
2035
+ let(:content) { "bootstrap script content" }
2036
+ context "under Windows" do
2037
+ let(:windows_test) { true }
2038
+ let(:temp_dir) { "C:/Temp/bootstrap" }
2039
+ it "creates a bat file in the temp dir provided by connection, using given content" do
2040
+ expect(connection).to receive(:upload_file_content!).with(content, "C:/Temp/bootstrap/bootstrap.bat")
2041
+ expect(knife.upload_bootstrap(content)).to eq "C:/Temp/bootstrap/bootstrap.bat"
2042
+ end
2043
+ end
2044
+
2045
+ context "under Linux" do
2046
+ let(:linux_test) { true }
2047
+ let(:temp_dir) { "/tmp/bootstrap" }
2048
+ it "creates a 'sh file in the temp dir provided by connection, using given content" do
2049
+ expect(connection).to receive(:upload_file_content!).with(content, "/tmp/bootstrap/bootstrap.sh")
2050
+ expect(knife.upload_bootstrap(content)).to eq "/tmp/bootstrap/bootstrap.sh"
2051
+ end
2052
+ end
2053
+ end
2054
+
2055
+ describe "#bootstrap_command" do
2056
+ context "under Windows" do
2057
+ let(:windows_test) { true }
2058
+ it "prefixes the command to run under cmd.exe" do
2059
+ expect(knife.bootstrap_command("autoexec.bat")).to eq "cmd.exe /C autoexec.bat"
2060
+ end
2061
+
2062
+ end
2063
+ context "under Linux" do
2064
+ let(:linux_test) { true }
2065
+ it "prefixes the command to run under sh" do
2066
+ expect(knife.bootstrap_command("bootstrap.sh")).to eq "sh bootstrap.sh"
2067
+ end
2068
+
2069
+ context "with --su-user option" do
2070
+ let(:connection_obj) { double("connection", transport_options: {}) }
2071
+ before do
2072
+ knife.config[:su_user] = "root"
2073
+ allow(connection).to receive(:connection).and_return(connection_obj)
2074
+ end
2075
+ it "prefixes the command to run using su -USER -c" do
2076
+ expect(knife.bootstrap_command("bootstrap.sh")).to eq "su - #{knife.config[:su_user]} -c 'sh bootstrap.sh'"
2077
+ expect(connection_obj.transport_options.key?(:pty)).to eq true
2078
+ end
2079
+
2080
+ it "sudo appended if --sudo option enabled" do
2081
+ knife.config[:use_sudo] = true
2082
+ expect(knife.bootstrap_command("bootstrap.sh")).to eq "sudo su - #{knife.config[:su_user]} -c 'sh bootstrap.sh'"
2083
+ expect(connection_obj.transport_options.key?(:pty)).to eq true
2084
+ end
2085
+ end
2086
+ end
2087
+ end
2088
+
2089
+ describe "#default_bootstrap_template" do
2090
+ context "under Windows" do
2091
+ let(:windows_test) { true }
2092
+ it "is windows-chef-client-msi" do
2093
+ expect(knife.default_bootstrap_template).to eq "windows-chef-client-msi"
2094
+ end
2095
+
2096
+ end
2097
+ context "under Linux" do
2098
+ let(:linux_test) { true }
2099
+ it "is chef-full" do
2100
+ expect(knife.default_bootstrap_template).to eq "chef-full"
2101
+ end
2102
+ end
2103
+ end
2104
+
2105
+ describe "#do_connect" do
2106
+ let(:host_descriptor) { "example.com" }
2107
+ let(:connection) { double("TrainConnector") }
2108
+ let(:connector_mock) { double("TargetResolver", targets: [ connection ]) }
2109
+ before do
2110
+ allow(knife).to receive(:host_descriptor).and_return host_descriptor
2111
+ end
2112
+
2113
+ it "creates a TrainConnector and connects it" do
2114
+ expect(Chef::Knife::Bootstrap::TrainConnector).to receive(:new).and_return connection
2115
+ expect(connection).to receive(:connect!)
2116
+ knife.do_connect({})
2117
+ end
2118
+
2119
+ context "when sshd configured with requiretty" do
2120
+ let(:pty_err_msg) { "Sudo requires a TTY. Please see the README on how to configure sudo to allow for non-interactive usage." }
2121
+ let(:expected_error) { Train::UserError.new(pty_err_msg, :sudo_no_tty) }
2122
+ before do
2123
+ allow(connection).to receive(:connect!).and_raise(expected_error)
2124
+ end
2125
+ it "retry with pty true request option" do
2126
+ expect(Chef::Knife::Bootstrap::TrainConnector).to receive(:new).and_return(connection).exactly(2).times
2127
+ expect(knife.ui).to receive(:warn).with("#{pty_err_msg} - trying with pty request")
2128
+ expect { knife.do_connect({}) }.to raise_error(expected_error)
2129
+ end
2130
+ end
2131
+
2132
+ context "when a train sudo error is thrown for missing terminal" do
2133
+ let(:ui_error_msg) { "Sudo password is required for this operation. Please enter password using -P or --ssh-password option" }
2134
+ let(:expected_error) { Train::UserError.new(ui_error_msg, :sudo_missing_terminal) }
2135
+ before do
2136
+ allow(connection).to receive(:connect!).and_raise(expected_error)
2137
+ end
2138
+ it "outputs user friendly error message" do
2139
+ expect { knife.do_connect({}) }.not_to raise_error
2140
+ expect(stderr.string).to include(ui_error_msg)
2141
+ end
2142
+ end
2143
+
2144
+ end
2145
+
2146
+ describe "validate_winrm_transport_opts!" do
2147
+ before do
2148
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
2149
+ end
2150
+
2151
+ context "when using ssh" do
2152
+ let(:connection_protocol) { "ssh" }
2153
+ it "returns true" do
2154
+ expect(knife.validate_winrm_transport_opts!).to eq true
2155
+ end
2156
+ end
2157
+ context "when using winrm" do
2158
+ let(:connection_protocol) { "winrm" }
2159
+ context "with plaintext auth" do
2160
+ before do
2161
+ knife.config[:winrm_auth_method] = "plaintext"
2162
+ end
2163
+ context "with ssl" do
2164
+ before do
2165
+ knife.config[:winrm_ssl] = true
2166
+ end
2167
+ it "will not error because we won't send anything in plaintext regardless" do
2168
+ expect(knife.validate_winrm_transport_opts!).to eq true
2169
+ end
2170
+ end
2171
+ context "without ssl" do
2172
+ before do
2173
+ knife.config[:winrm_ssl] = false
2174
+ end
2175
+ context "and no validation key exists" do
2176
+ before do
2177
+ Chef::Config[:validation_key] = "validation_key.pem"
2178
+ allow(File).to receive(:exist?).with(/.*validation_key.pem/).and_return false
2179
+ end
2180
+
2181
+ it "will error because we will generate and send a client key over the wire in plaintext" do
2182
+ expect { knife.validate_winrm_transport_opts! }.to raise_error(SystemExit)
2183
+ end
2184
+
2185
+ end
2186
+ context "and a validation key exists" do
2187
+ before do
2188
+ Chef::Config[:validation_key] = "validation_key.pem"
2189
+ allow(File).to receive(:exist?).with(/.*validation_key.pem/).and_return true
2190
+ end
2191
+ # TODO - don't we still send validation key?
2192
+ it "will not error because we don not send client key over the wire" do
2193
+ expect(knife.validate_winrm_transport_opts!).to eq true
2194
+ end
2195
+ end
2196
+ end
2197
+ end
2198
+
2199
+ context "with other auth" do
2200
+ before do
2201
+ knife.config[:winrm_auth_method] = "kerberos"
2202
+ end
2203
+
2204
+ context "and no validation key exists" do
2205
+ before do
2206
+
2207
+ Chef::Config[:validation_key] = "validation_key.pem"
2208
+ allow(File).to receive(:exist?).with(/.*validation_key.pem/).and_return false
2209
+ end
2210
+
2211
+ it "will not error because we're not using plaintext auth" do
2212
+ expect(knife.validate_winrm_transport_opts!).to eq true
2213
+ end
2214
+ end
2215
+ context "and a validation key exists" do
2216
+ before do
2217
+ Chef::Config[:validation_key] = "validation_key.pem"
2218
+ allow(File).to receive(:exist?).with(/.*validation_key.pem/).and_return true
2219
+ end
2220
+
2221
+ it "will not error because a client key won't be sent over the wire in plaintext when a validation key is present" do
2222
+ expect(knife.validate_winrm_transport_opts!).to eq true
2223
+ end
2224
+ end
2225
+
2226
+ end
2227
+
2228
+ end
2229
+
2230
+ end
2231
+
2232
+ describe "#winrm_warn_no_ssl_verification" do
2233
+ before do
2234
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
2235
+ end
2236
+
2237
+ context "when using ssh" do
2238
+ let(:connection_protocol) { "ssh" }
2239
+ it "does not issue a warning" do
2240
+ expect(knife.ui).to_not receive(:warn)
2241
+ knife.winrm_warn_no_ssl_verification
2242
+ end
2243
+ end
2244
+ context "when using winrm" do
2245
+ let(:connection_protocol) { "winrm" }
2246
+ context "winrm_no_verify_cert is set" do
2247
+ before do
2248
+ knife.config[:winrm_no_verify_cert] = true
2249
+ end
2250
+
2251
+ context "and ca_trust_file is present" do
2252
+ before do
2253
+ knife.config[:ca_trust_file] = "file"
2254
+ end
2255
+
2256
+ it "does not issue a warning" do
2257
+ expect(knife.ui).to_not receive(:warn)
2258
+ knife.winrm_warn_no_ssl_verification
2259
+ end
2260
+ end
2261
+
2262
+ context "and winrm_ssl_peer_fingerprint is present" do
2263
+ before do
2264
+ knife.config[:winrm_ssl_peer_fingerprint] = "ABCD"
2265
+ end
2266
+ it "does not issue a warning" do
2267
+ expect(knife.ui).to_not receive(:warn)
2268
+ knife.winrm_warn_no_ssl_verification
2269
+ end
2270
+ end
2271
+ context "and neither ca_trust_file nor winrm_ssl_peer_fingerprint is present" do
2272
+ it "issues a warning" do
2273
+ expect(knife.ui).to receive(:warn)
2274
+ knife.winrm_warn_no_ssl_verification
2275
+ end
2276
+ end
2277
+ end
2278
+ end
2279
+ end
2280
+
2281
+ describe "#warn_on_short_session_timeout" do
2282
+ let(:session_timeout) { 60 }
2283
+
2284
+ before do
2285
+ allow(knife).to receive(:session_timeout).and_return(session_timeout)
2286
+ end
2287
+
2288
+ context "timeout is not set at all" do
2289
+ let(:session_timeout) { nil }
2290
+ it "does not issue a warning" do
2291
+ expect(knife.ui).to_not receive(:warn)
2292
+ knife.warn_on_short_session_timeout
2293
+ end
2294
+ end
2295
+
2296
+ context "timeout is more than 15" do
2297
+ let(:session_timeout) { 16 }
2298
+ it "does not issue a warning" do
2299
+ expect(knife.ui).to_not receive(:warn)
2300
+ knife.warn_on_short_session_timeout
2301
+ end
2302
+ end
2303
+ context "timeout is 15 or less" do
2304
+ let(:session_timeout) { 15 }
2305
+ it "issues a warning" do
2306
+ expect(knife.ui).to receive(:warn)
2307
+ knife.warn_on_short_session_timeout
2308
+ end
2309
+ end
2310
+ end
2311
+ end