knife 18.0.185 → 18.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (667) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +29 -29
  3. data/LICENSE +201 -201
  4. data/Rakefile +33 -33
  5. data/bin/knife +24 -24
  6. data/knife.gemspec +60 -60
  7. data/lib/chef/application/knife.rb +234 -234
  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 +242 -242
  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 +1193 -1193
  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 +264 -264
  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 +232 -232
  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 +338 -338
  65. data/lib/chef/knife/core/windows_bootstrap_context.rb +405 -405
  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 -84
  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/list.rb +177 -177
  101. data/lib/chef/knife/node_bulk_delete.rb +75 -75
  102. data/lib/chef/knife/node_create.rb +47 -47
  103. data/lib/chef/knife/node_delete.rb +46 -46
  104. data/lib/chef/knife/node_edit.rb +70 -70
  105. data/lib/chef/knife/node_environment_set.rb +53 -53
  106. data/lib/chef/knife/node_from_file.rb +51 -51
  107. data/lib/chef/knife/node_list.rb +44 -44
  108. data/lib/chef/knife/node_policy_set.rb +79 -79
  109. data/lib/chef/knife/node_run_list_add.rb +104 -104
  110. data/lib/chef/knife/node_run_list_remove.rb +67 -67
  111. data/lib/chef/knife/node_run_list_set.rb +66 -66
  112. data/lib/chef/knife/node_show.rb +63 -63
  113. data/lib/chef/knife/null.rb +12 -12
  114. data/lib/chef/knife/org_create.rb +70 -70
  115. data/lib/chef/knife/org_delete.rb +32 -32
  116. data/lib/chef/knife/org_edit.rb +48 -48
  117. data/lib/chef/knife/org_list.rb +44 -44
  118. data/lib/chef/knife/org_show.rb +31 -31
  119. data/lib/chef/knife/org_user_add.rb +62 -62
  120. data/lib/chef/knife/org_user_remove.rb +103 -103
  121. data/lib/chef/knife/raw.rb +123 -123
  122. data/lib/chef/knife/recipe_list.rb +32 -32
  123. data/lib/chef/knife/rehash.rb +50 -50
  124. data/lib/chef/knife/role_bulk_delete.rb +66 -66
  125. data/lib/chef/knife/role_create.rb +53 -53
  126. data/lib/chef/knife/role_delete.rb +46 -46
  127. data/lib/chef/knife/role_edit.rb +45 -45
  128. data/lib/chef/knife/role_env_run_list_add.rb +87 -87
  129. data/lib/chef/knife/role_env_run_list_clear.rb +55 -55
  130. data/lib/chef/knife/role_env_run_list_remove.rb +57 -57
  131. data/lib/chef/knife/role_env_run_list_replace.rb +60 -60
  132. data/lib/chef/knife/role_env_run_list_set.rb +70 -70
  133. data/lib/chef/knife/role_from_file.rb +51 -51
  134. data/lib/chef/knife/role_list.rb +42 -42
  135. data/lib/chef/knife/role_run_list_add.rb +87 -87
  136. data/lib/chef/knife/role_run_list_clear.rb +55 -55
  137. data/lib/chef/knife/role_run_list_remove.rb +56 -56
  138. data/lib/chef/knife/role_run_list_replace.rb +60 -60
  139. data/lib/chef/knife/role_run_list_set.rb +69 -69
  140. data/lib/chef/knife/role_show.rb +48 -48
  141. data/lib/chef/knife/search.rb +194 -194
  142. data/lib/chef/knife/serve.rb +65 -65
  143. data/lib/chef/knife/show.rb +72 -72
  144. data/lib/chef/knife/ssh.rb +657 -657
  145. data/lib/chef/knife/ssl_check.rb +284 -284
  146. data/lib/chef/knife/ssl_fetch.rb +162 -162
  147. data/lib/chef/knife/status.rb +95 -95
  148. data/lib/chef/knife/supermarket_download.rb +119 -119
  149. data/lib/chef/knife/supermarket_install.rb +192 -192
  150. data/lib/chef/knife/supermarket_list.rb +76 -76
  151. data/lib/chef/knife/supermarket_search.rb +53 -53
  152. data/lib/chef/knife/supermarket_share.rb +166 -166
  153. data/lib/chef/knife/supermarket_show.rb +66 -66
  154. data/lib/chef/knife/supermarket_unshare.rb +62 -62
  155. data/lib/chef/knife/tag_create.rb +52 -52
  156. data/lib/chef/knife/tag_delete.rb +60 -60
  157. data/lib/chef/knife/tag_list.rb +47 -47
  158. data/lib/chef/knife/upload.rb +87 -86
  159. data/lib/chef/knife/user_create.rb +180 -180
  160. data/lib/chef/knife/user_delete.rb +151 -151
  161. data/lib/chef/knife/user_dissociate.rb +42 -42
  162. data/lib/chef/knife/user_edit.rb +94 -94
  163. data/lib/chef/knife/user_invite_add.rb +43 -43
  164. data/lib/chef/knife/user_invite_list.rb +34 -34
  165. data/lib/chef/knife/user_invite_rescind.rb +63 -63
  166. data/lib/chef/knife/user_key_create.rb +73 -73
  167. data/lib/chef/knife/user_key_delete.rb +80 -80
  168. data/lib/chef/knife/user_key_edit.rb +83 -83
  169. data/lib/chef/knife/user_key_list.rb +73 -73
  170. data/lib/chef/knife/user_key_show.rb +80 -80
  171. data/lib/chef/knife/user_list.rb +43 -43
  172. data/lib/chef/knife/user_password.rb +70 -70
  173. data/lib/chef/knife/user_reregister.rb +59 -59
  174. data/lib/chef/knife/user_show.rb +52 -52
  175. data/lib/chef/knife/version.rb +24 -24
  176. data/lib/chef/knife/xargs.rb +282 -282
  177. data/lib/chef/knife/yaml_convert.rb +91 -91
  178. data/lib/chef/knife.rb +673 -673
  179. data/spec/data/apt/chef-integration-test-1.0/debian/changelog +5 -5
  180. data/spec/data/apt/chef-integration-test-1.0/debian/compat +1 -1
  181. data/spec/data/apt/chef-integration-test-1.0/debian/control +13 -13
  182. data/spec/data/apt/chef-integration-test-1.0/debian/copyright +34 -34
  183. data/spec/data/apt/chef-integration-test-1.0/debian/files +1 -1
  184. data/spec/data/apt/chef-integration-test-1.0/debian/rules +13 -13
  185. data/spec/data/apt/chef-integration-test-1.0/debian/source/format +1 -1
  186. data/spec/data/apt/chef-integration-test-1.1/debian/changelog +11 -11
  187. data/spec/data/apt/chef-integration-test-1.1/debian/compat +1 -1
  188. data/spec/data/apt/chef-integration-test-1.1/debian/control +13 -13
  189. data/spec/data/apt/chef-integration-test-1.1/debian/copyright +34 -34
  190. data/spec/data/apt/chef-integration-test-1.1/debian/files +1 -1
  191. data/spec/data/apt/chef-integration-test-1.1/debian/rules +13 -13
  192. data/spec/data/apt/chef-integration-test-1.1/debian/source/format +1 -1
  193. data/spec/data/apt/chef-integration-test2-1.0/debian/changelog +5 -5
  194. data/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2/DEBIAN/conffiles +1 -1
  195. data/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2/DEBIAN/control +10 -10
  196. data/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2/DEBIAN/md5sums +1 -1
  197. data/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2.debhelper.log +45 -45
  198. data/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2.substvars +1 -1
  199. data/spec/data/apt/chef-integration-test2-1.0/debian/compat +1 -1
  200. data/spec/data/apt/chef-integration-test2-1.0/debian/conffiles +1 -1
  201. data/spec/data/apt/chef-integration-test2-1.0/debian/control +13 -13
  202. data/spec/data/apt/chef-integration-test2-1.0/debian/copyright +34 -34
  203. data/spec/data/apt/chef-integration-test2-1.0/debian/files +1 -1
  204. data/spec/data/apt/chef-integration-test2-1.0/debian/rules +13 -13
  205. data/spec/data/apt/chef-integration-test2-1.0/debian/source/format +1 -1
  206. data/spec/data/apt/chef-integration-test2_1.0-1.dsc +18 -18
  207. data/spec/data/apt/chef-integration-test2_1.0-1_amd64.build +91 -91
  208. data/spec/data/apt/chef-integration-test2_1.0-1_amd64.changes +31 -31
  209. data/spec/data/apt/chef-integration-test_1.0-1_amd64.changes +22 -22
  210. data/spec/data/apt/chef-integration-test_1.1-1_amd64.changes +22 -22
  211. data/spec/data/apt/var/www/apt/conf/distributions +7 -7
  212. data/spec/data/apt/var/www/apt/conf/incoming +4 -4
  213. data/spec/data/apt/var/www/apt/conf/pulls +3 -3
  214. data/spec/data/apt/var/www/apt/db/version +4 -4
  215. data/spec/data/apt/var/www/apt/dists/sid/Release +19 -19
  216. data/spec/data/apt/var/www/apt/dists/sid/main/binary-amd64/Packages +16 -16
  217. data/spec/data/apt/var/www/apt/dists/sid/main/binary-amd64/Release +5 -5
  218. data/spec/data/bootstrap/encrypted_data_bag_secret +1 -1
  219. data/spec/data/bootstrap/no_proxy.erb +2 -2
  220. data/spec/data/bootstrap/secret.erb +9 -9
  221. data/spec/data/bootstrap/test-hints.erb +12 -12
  222. data/spec/data/bootstrap/test.erb +1 -1
  223. data/spec/data/cb_version_cookbooks/tatft/README.rdoc +2 -2
  224. data/spec/data/cb_version_cookbooks/tatft/attributes/default.rb +1 -1
  225. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-600hhz-0 +1 -1
  226. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-ahd2gq-0 +1 -1
  227. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-api8ux-0 +1 -1
  228. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-b0r1m1-0 +1 -1
  229. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-bfygsi-0 +1 -1
  230. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-el14l6-0 +1 -1
  231. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-ivrl3y-0 +1 -1
  232. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-kkbs85-0 +1 -1
  233. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-ory1ux-0 +1 -1
  234. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-pgsq76-0 +1 -1
  235. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-ra8uim-0 +1 -1
  236. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-t7k1g-0 +1 -1
  237. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-t8g0sv-0 +1 -1
  238. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-ufy6g3-0 +1 -1
  239. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-x2d6j9-0 +1 -1
  240. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-xi0l6h-0 +1 -1
  241. data/spec/data/client.d_00/00-foo.rb +2 -2
  242. data/spec/data/client.d_00/01-bar.rb +1 -1
  243. data/spec/data/client.d_00/02-strings.rb +2 -2
  244. data/spec/data/client.d_00/bar +1 -1
  245. data/spec/data/client.d_01/foo/bar.rb +1 -1
  246. data/spec/data/client.d_02/foo.rb/foo.txt +1 -1
  247. data/spec/data/config.rb +6 -6
  248. data/spec/data/cookbooks/angrybash/metadata.rb +2 -2
  249. data/spec/data/cookbooks/angrybash/recipes/default.rb +8 -8
  250. data/spec/data/cookbooks/apache2/files/default/apache2_module_conf_generate.pl +2 -2
  251. data/spec/data/cookbooks/apache2/metadata.json +33 -33
  252. data/spec/data/cookbooks/apache2/metadata.rb +2 -2
  253. data/spec/data/cookbooks/apache2/recipes/default.rb +2 -2
  254. data/spec/data/cookbooks/borken/metadata.rb +2 -2
  255. data/spec/data/cookbooks/borken/recipes/default.rb +1 -1
  256. data/spec/data/cookbooks/borken/templates/default/borken.erb +1 -1
  257. data/spec/data/cookbooks/chefignore +8 -8
  258. data/spec/data/cookbooks/ignorken/files/default/not_me.rb +2 -2
  259. data/spec/data/cookbooks/ignorken/metadata.rb +2 -2
  260. data/spec/data/cookbooks/ignorken/recipes/ignoreme.rb +1 -1
  261. data/spec/data/cookbooks/ignorken/templates/ubuntu-12.10/not_me.rb +2 -2
  262. data/spec/data/cookbooks/irssi/files/default/irssi.response +2 -2
  263. data/spec/data/cookbooks/java/files/default/java.response +1 -1
  264. data/spec/data/cookbooks/java/metadata.json +33 -33
  265. data/spec/data/cookbooks/java/metadata.rb +2 -2
  266. data/spec/data/cookbooks/name-mismatch-versionnumber/README.md +4 -4
  267. data/spec/data/cookbooks/name-mismatch-versionnumber/metadata.rb +8 -8
  268. data/spec/data/cookbooks/name-mismatch-versionnumber/recipes/default.rb +8 -8
  269. data/spec/data/cookbooks/openldap/attributes/default.rb +16 -16
  270. data/spec/data/cookbooks/openldap/attributes/smokey.rb +1 -1
  271. data/spec/data/cookbooks/openldap/definitions/client.rb +5 -5
  272. data/spec/data/cookbooks/openldap/definitions/server.rb +5 -5
  273. data/spec/data/cookbooks/openldap/files/default/.dotfile +1 -1
  274. data/spec/data/cookbooks/openldap/files/default/remotedir/.a_dotdir/.a_dotfile_in_a_dotdir +1 -1
  275. data/spec/data/cookbooks/openldap/files/default/remotedir/not_a_template.erb +2 -2
  276. data/spec/data/cookbooks/openldap/files/default/remotedir/remote_dir_file1.txt +2 -2
  277. data/spec/data/cookbooks/openldap/files/default/remotedir/remote_dir_file2.txt +2 -2
  278. data/spec/data/cookbooks/openldap/files/default/remotedir/remotesubdir/.a_dotfile +1 -1
  279. data/spec/data/cookbooks/openldap/files/default/remotedir/remotesubdir/remote_subdir_file1.txt +2 -2
  280. data/spec/data/cookbooks/openldap/files/default/remotedir/remotesubdir/remote_subdir_file2.txt +2 -2
  281. data/spec/data/cookbooks/openldap/files/default/remotedir/subdir_with_no_file_just_a_subsubdir/the_subsubdir/some_file.txt +3 -3
  282. data/spec/data/cookbooks/openldap/libraries/openldap/version.rb +3 -3
  283. data/spec/data/cookbooks/openldap/libraries/openldap.rb +4 -4
  284. data/spec/data/cookbooks/openldap/metadata.rb +8 -8
  285. data/spec/data/cookbooks/openldap/recipes/default.rb +4 -4
  286. data/spec/data/cookbooks/openldap/recipes/gigantor.rb +3 -3
  287. data/spec/data/cookbooks/openldap/recipes/one.rb +15 -15
  288. data/spec/data/cookbooks/openldap/recipes/return.rb +2 -2
  289. data/spec/data/cookbooks/openldap/templates/default/helper_test.erb +1 -1
  290. data/spec/data/cookbooks/openldap/templates/default/helpers.erb +14 -14
  291. data/spec/data/cookbooks/openldap/templates/default/helpers_via_partial_test.erb +1 -1
  292. data/spec/data/cookbooks/openldap/templates/default/nested_openldap_partials.erb +1 -1
  293. data/spec/data/cookbooks/openldap/templates/default/nested_partial.erb +1 -1
  294. data/spec/data/cookbooks/openldap/templates/default/no_windows_line_endings.erb +4 -4
  295. data/spec/data/cookbooks/openldap/templates/default/openldap_nested_variable_stuff.erb +1 -1
  296. data/spec/data/cookbooks/openldap/templates/default/openldap_variable_stuff.conf.erb +1 -1
  297. data/spec/data/cookbooks/openldap/templates/default/test.erb +1 -1
  298. data/spec/data/cookbooks/preseed/files/default/preseed-file.seed +1 -1
  299. data/spec/data/cookbooks/preseed/files/default/preseed-template.seed +4 -4
  300. data/spec/data/cookbooks/preseed/metadata.rb +2 -2
  301. data/spec/data/cookbooks/preseed/templates/default/preseed-template-variables.seed +1 -1
  302. data/spec/data/cookbooks/preseed/templates/default/preseed-template.seed +1 -1
  303. data/spec/data/cookbooks/starter/chefignore +8 -8
  304. data/spec/data/cookbooks/starter/metadata.rb +2 -2
  305. data/spec/data/cookbooks/starter/recipes/default.rb +4 -4
  306. data/spec/data/cookbooks/supports-platform-constraints/metadata.rb +5 -5
  307. data/spec/data/cookbooks/wget/files/default/wget.response +2 -2
  308. data/spec/data/definitions/test.rb +4 -4
  309. data/spec/data/environment-config.rb +4 -4
  310. data/spec/data/file-providers-method-snapshot-chef-11-4.json +127 -127
  311. data/spec/data/fileedit/hosts +4 -4
  312. data/spec/data/incomplete-metadata-chef-repo/incomplete-metadata/README.md +4 -4
  313. data/spec/data/incomplete-metadata-chef-repo/incomplete-metadata/metadata.rb +13 -13
  314. data/spec/data/incomplete-metadata-chef-repo/incomplete-metadata/recipes/default.rb +8 -8
  315. data/spec/data/invalid-metadata-chef-repo/invalid-metadata/README.md +4 -4
  316. data/spec/data/invalid-metadata-chef-repo/invalid-metadata/metadata.rb +9 -9
  317. data/spec/data/invalid-metadata-chef-repo/invalid-metadata/recipes/default.rb +8 -8
  318. data/spec/data/kitchen/chefignore +6 -6
  319. data/spec/data/kitchen/openldap/attributes/default.rb +3 -3
  320. data/spec/data/kitchen/openldap/attributes/robinson.rb +2 -2
  321. data/spec/data/kitchen/openldap/definitions/client.rb +3 -3
  322. data/spec/data/kitchen/openldap/definitions/drewbarrymore.rb +2 -2
  323. data/spec/data/kitchen/openldap/recipes/gigantor.rb +2 -2
  324. data/spec/data/kitchen/openldap/recipes/ignoreme.rb +2 -2
  325. data/spec/data/kitchen/openldap/recipes/woot.rb +3 -3
  326. data/spec/data/knife_subcommand/test_explicit_category.rb +6 -6
  327. data/spec/data/knife_subcommand/test_name_mapping.rb +4 -4
  328. data/spec/data/knife_subcommand/test_yourself.rb +21 -21
  329. data/spec/data/lwrp/providers/buck_passer.rb +28 -28
  330. data/spec/data/lwrp/providers/buck_passer_2.rb +26 -26
  331. data/spec/data/lwrp/providers/embedded_resource_accesses_providers_scope.rb +28 -28
  332. data/spec/data/lwrp/providers/inline_compiler.rb +24 -24
  333. data/spec/data/lwrp/providers/monkey_name_printer.rb +5 -5
  334. data/spec/data/lwrp/providers/paint_drying_watcher.rb +7 -7
  335. data/spec/data/lwrp/providers/thumb_twiddler.rb +7 -7
  336. data/spec/data/lwrp/resources/bar.rb +4 -4
  337. data/spec/data/lwrp/resources/buck_passer.rb +6 -6
  338. data/spec/data/lwrp/resources/buck_passer_2.rb +4 -4
  339. data/spec/data/lwrp/resources/embedded_resource_accesses_providers_scope.rb +4 -4
  340. data/spec/data/lwrp/resources/foo.rb +6 -6
  341. data/spec/data/lwrp/resources/inline_compiler.rb +4 -4
  342. data/spec/data/lwrp/resources/monkey_name_printer.rb +6 -6
  343. data/spec/data/lwrp/resources/paint_drying_watcher.rb +4 -4
  344. data/spec/data/lwrp/resources/thumb_twiddler.rb +4 -4
  345. data/spec/data/lwrp/resources_with_default_attributes/nodeattr.rb +3 -3
  346. data/spec/data/lwrp_const_scoping/resources/conflict.rb +1 -1
  347. data/spec/data/lwrp_override/providers/buck_passer.rb +5 -5
  348. data/spec/data/lwrp_override/resources/foo.rb +11 -11
  349. data/spec/data/mac_users/10.9.plist.xml +560 -560
  350. data/spec/data/mac_users/10.9.shadow.xml +21 -21
  351. data/spec/data/metadata/quick_start/metadata.rb +14 -14
  352. data/spec/data/mixin/invalid_data.rb +3 -3
  353. data/spec/data/mixin/real_data.rb +2 -2
  354. data/spec/data/nested.json +2 -2
  355. data/spec/data/nodes/default.rb +15 -15
  356. data/spec/data/nodes/test.example.com.rb +17 -17
  357. data/spec/data/nodes/test.rb +15 -15
  358. data/spec/data/null_config.rb +1 -1
  359. data/spec/data/object_loader/environments/test.json +7 -7
  360. data/spec/data/object_loader/environments/test.rb +2 -2
  361. data/spec/data/object_loader/environments/test_json_class.json +8 -8
  362. data/spec/data/object_loader/nodes/test.json +7 -7
  363. data/spec/data/object_loader/nodes/test.rb +2 -2
  364. data/spec/data/object_loader/nodes/test_json_class.json +8 -8
  365. data/spec/data/object_loader/roles/test.json +7 -7
  366. data/spec/data/object_loader/roles/test.rb +2 -2
  367. data/spec/data/object_loader/roles/test_json_class.json +8 -8
  368. data/spec/data/partial_one.erb +1 -1
  369. data/spec/data/prefer_metadata_json/metadata.json +51 -51
  370. data/spec/data/prefer_metadata_json/metadata.rb +6 -6
  371. data/spec/data/recipes/test.rb +7 -7
  372. data/spec/data/root_alias_cookbooks/dup_attr/attributes/default.rb +1 -1
  373. data/spec/data/root_alias_cookbooks/dup_attr/attributes.rb +1 -1
  374. data/spec/data/root_alias_cookbooks/dup_attr/metadata.rb +2 -2
  375. data/spec/data/root_alias_cookbooks/dup_attr/recipe.rb +3 -3
  376. data/spec/data/root_alias_cookbooks/dup_recipe/attributes.rb +1 -1
  377. data/spec/data/root_alias_cookbooks/dup_recipe/metadata.rb +2 -2
  378. data/spec/data/root_alias_cookbooks/dup_recipe/recipe.rb +3 -3
  379. data/spec/data/root_alias_cookbooks/dup_recipe/recipes/default.rb +3 -3
  380. data/spec/data/root_alias_cookbooks/simple/attributes.rb +1 -1
  381. data/spec/data/root_alias_cookbooks/simple/metadata.rb +2 -2
  382. data/spec/data/root_alias_cookbooks/simple/recipe.rb +3 -3
  383. data/spec/data/rubygems.org/sexp_processor-info +49 -49
  384. data/spec/data/run_context/cookbooks/circular-dep1/attributes/default.rb +2 -2
  385. data/spec/data/run_context/cookbooks/circular-dep1/definitions/circular_dep1_res.rb +1 -1
  386. data/spec/data/run_context/cookbooks/circular-dep1/libraries/lib.rb +2 -2
  387. data/spec/data/run_context/cookbooks/circular-dep1/metadata.rb +2 -2
  388. data/spec/data/run_context/cookbooks/circular-dep1/providers/provider.rb +1 -1
  389. data/spec/data/run_context/cookbooks/circular-dep1/resources/resource.rb +2 -2
  390. data/spec/data/run_context/cookbooks/circular-dep2/attributes/default.rb +2 -2
  391. data/spec/data/run_context/cookbooks/circular-dep2/definitions/circular_dep2_res.rb +1 -1
  392. data/spec/data/run_context/cookbooks/circular-dep2/libraries/lib.rb +2 -2
  393. data/spec/data/run_context/cookbooks/circular-dep2/metadata.rb +2 -2
  394. data/spec/data/run_context/cookbooks/circular-dep2/providers/provider.rb +1 -1
  395. data/spec/data/run_context/cookbooks/circular-dep2/resources/resource.rb +2 -2
  396. data/spec/data/run_context/cookbooks/dependency1/attributes/aa_first.rb +2 -2
  397. data/spec/data/run_context/cookbooks/dependency1/attributes/default.rb +2 -2
  398. data/spec/data/run_context/cookbooks/dependency1/attributes/unparsed_file +1 -1
  399. data/spec/data/run_context/cookbooks/dependency1/attributes/zz_last.rb +2 -2
  400. data/spec/data/run_context/cookbooks/dependency1/definitions/dependency1_res.rb +1 -1
  401. data/spec/data/run_context/cookbooks/dependency1/definitions/unparsed_file +1 -1
  402. data/spec/data/run_context/cookbooks/dependency1/libraries/lib.rb +2 -2
  403. data/spec/data/run_context/cookbooks/dependency1/libraries/unparsed_file +1 -1
  404. data/spec/data/run_context/cookbooks/dependency1/providers/provider.rb +1 -1
  405. data/spec/data/run_context/cookbooks/dependency1/providers/unparsed_file +1 -1
  406. data/spec/data/run_context/cookbooks/dependency1/recipes/unparsed_file +1 -1
  407. data/spec/data/run_context/cookbooks/dependency1/resources/resource.rb +2 -2
  408. data/spec/data/run_context/cookbooks/dependency1/resources/unparsed_file +1 -1
  409. data/spec/data/run_context/cookbooks/dependency2/attributes/default.rb +2 -2
  410. data/spec/data/run_context/cookbooks/dependency2/definitions/dependency2_res.rb +1 -1
  411. data/spec/data/run_context/cookbooks/dependency2/libraries/lib.rb +2 -2
  412. data/spec/data/run_context/cookbooks/dependency2/providers/provider.rb +1 -1
  413. data/spec/data/run_context/cookbooks/dependency2/resources/resource.rb +2 -2
  414. data/spec/data/run_context/cookbooks/include/recipes/default.rb +24 -24
  415. data/spec/data/run_context/cookbooks/include/recipes/includee.rb +3 -3
  416. data/spec/data/run_context/cookbooks/no-default-attr/attributes/server.rb +2 -2
  417. data/spec/data/run_context/cookbooks/no-default-attr/definitions/no_default-attr_res.rb +1 -1
  418. data/spec/data/run_context/cookbooks/no-default-attr/providers/provider.rb +1 -1
  419. data/spec/data/run_context/cookbooks/no-default-attr/resources/resource.rb +2 -2
  420. data/spec/data/run_context/cookbooks/test/attributes/george.rb +1 -1
  421. data/spec/data/run_context/cookbooks/test/definitions/new_animals.rb +9 -9
  422. data/spec/data/run_context/cookbooks/test/definitions/new_cat.rb +5 -5
  423. data/spec/data/run_context/cookbooks/test/definitions/test_res.rb +1 -1
  424. data/spec/data/run_context/cookbooks/test/providers/provider.rb +1 -1
  425. data/spec/data/run_context/cookbooks/test/recipes/default.rb +5 -5
  426. data/spec/data/run_context/cookbooks/test/recipes/one.rb +7 -7
  427. data/spec/data/run_context/cookbooks/test/recipes/two.rb +7 -7
  428. data/spec/data/run_context/cookbooks/test/resources/resource.rb +3 -3
  429. data/spec/data/run_context/cookbooks/test-with-circular-deps/attributes/default.rb +2 -2
  430. data/spec/data/run_context/cookbooks/test-with-circular-deps/definitions/test_with-circular-deps_res.rb +1 -1
  431. data/spec/data/run_context/cookbooks/test-with-circular-deps/libraries/lib.rb +2 -2
  432. data/spec/data/run_context/cookbooks/test-with-circular-deps/metadata.rb +2 -2
  433. data/spec/data/run_context/cookbooks/test-with-circular-deps/providers/provider.rb +1 -1
  434. data/spec/data/run_context/cookbooks/test-with-circular-deps/resources/resource.rb +3 -3
  435. data/spec/data/run_context/cookbooks/test-with-deps/attributes/default.rb +2 -2
  436. data/spec/data/run_context/cookbooks/test-with-deps/definitions/test_with-deps_res.rb +1 -1
  437. data/spec/data/run_context/cookbooks/test-with-deps/libraries/lib.rb +1 -1
  438. data/spec/data/run_context/cookbooks/test-with-deps/metadata.rb +3 -3
  439. data/spec/data/run_context/cookbooks/test-with-deps/providers/provider.rb +1 -1
  440. data/spec/data/run_context/cookbooks/test-with-deps/resources/resource.rb +2 -2
  441. data/spec/data/run_context/nodes/run_context.rb +5 -5
  442. data/spec/data/sample_msu1.xml +10 -10
  443. data/spec/data/sample_msu2.xml +14 -14
  444. data/spec/data/sample_msu3.xml +16 -16
  445. data/spec/data/search_queries_to_transform.txt +98 -98
  446. data/spec/data/shef-config.rb +11 -11
  447. data/spec/data/snap_package/async_result_success.json +6 -6
  448. data/spec/data/snap_package/change_id_result.json +175 -175
  449. data/spec/data/snap_package/find_result_failure.json +10 -10
  450. data/spec/data/snap_package/find_result_success.json +70 -70
  451. data/spec/data/snap_package/get_by_name_result_failure.json +10 -10
  452. data/spec/data/snap_package/get_by_name_result_success.json +38 -38
  453. data/spec/data/snap_package/get_conf_success.json +10 -10
  454. data/spec/data/snap_package/result_failure.json +9 -9
  455. data/spec/data/ssl/5e707473.0 +18 -18
  456. data/spec/data/ssl/chef-rspec.cert +27 -27
  457. data/spec/data/ssl/chef-rspec.key +27 -27
  458. data/spec/data/ssl/key.pem +15 -15
  459. data/spec/data/ssl/private_key.pem +27 -27
  460. data/spec/data/ssl/private_key_with_whitespace.pem +32 -32
  461. data/spec/data/standalone_cookbook/chefignore +9 -9
  462. data/spec/data/standalone_cookbook/recipes/default.rb +2 -2
  463. data/spec/data/templates/failed.erb +5 -5
  464. data/spec/data/trusted_certs/example.crt +22 -22
  465. data/spec/data/trusted_certs/example_no_cn.crt +36 -36
  466. data/spec/data/trusted_certs/intermediate.pem +27 -27
  467. data/spec/data/trusted_certs/opscode.pem +57 -57
  468. data/spec/data/trusted_certs/root.pem +22 -22
  469. data/spec/data/windows_certificates/base64_test.cer +20 -20
  470. data/spec/data/windows_certificates/othertest.cer +20 -20
  471. data/spec/data/windows_certificates/test.cer +20 -20
  472. data/spec/data/windows_certificates/test.pem +20 -20
  473. data/spec/functional/configure_spec.rb +33 -33
  474. data/spec/functional/cookbook_delete_spec.rb +156 -156
  475. data/spec/functional/exec_spec.rb +55 -55
  476. data/spec/functional/rehash_spec.rb +39 -39
  477. data/spec/functional/smoke_test.rb +42 -42
  478. data/spec/functional/ssh_spec.rb +352 -352
  479. data/spec/functional/version_spec.rb +26 -26
  480. data/spec/integration/chef_fs_data_store_spec.rb +557 -557
  481. data/spec/integration/chef_repo_path_spec.rb +962 -962
  482. data/spec/integration/chef_repository_file_system_spec.rb +200 -200
  483. data/spec/integration/chefignore_spec.rb +301 -301
  484. data/spec/integration/client_bulk_delete_spec.rb +131 -131
  485. data/spec/integration/client_create_spec.rb +70 -70
  486. data/spec/integration/client_delete_spec.rb +64 -64
  487. data/spec/integration/client_key_create_spec.rb +66 -66
  488. data/spec/integration/client_key_delete_spec.rb +43 -43
  489. data/spec/integration/client_key_list_spec.rb +61 -61
  490. data/spec/integration/client_key_show_spec.rb +45 -45
  491. data/spec/integration/client_list_spec.rb +49 -49
  492. data/spec/integration/client_show_spec.rb +37 -37
  493. data/spec/integration/commands_spec.rb +55 -55
  494. data/spec/integration/common_options_spec.rb +174 -174
  495. data/spec/integration/config_list_spec.rb +220 -220
  496. data/spec/integration/config_show_spec.rb +192 -192
  497. data/spec/integration/config_use_spec.rb +198 -198
  498. data/spec/integration/cookbook_api_ipv6_spec.rb +113 -113
  499. data/spec/integration/cookbook_bulk_delete_spec.rb +65 -65
  500. data/spec/integration/cookbook_download_spec.rb +72 -72
  501. data/spec/integration/cookbook_list_spec.rb +55 -55
  502. data/spec/integration/cookbook_show_spec.rb +149 -149
  503. data/spec/integration/cookbook_upload_spec.rb +128 -128
  504. data/spec/integration/data_bag_create_spec.rb +125 -125
  505. data/spec/integration/data_bag_delete_spec.rb +59 -59
  506. data/spec/integration/data_bag_edit_spec.rb +105 -105
  507. data/spec/integration/data_bag_from_file_spec.rb +116 -116
  508. data/spec/integration/data_bag_list_spec.rb +44 -44
  509. data/spec/integration/data_bag_show_spec.rb +95 -95
  510. data/spec/integration/delete_spec.rb +1018 -1018
  511. data/spec/integration/deps_spec.rb +703 -703
  512. data/spec/integration/diff_spec.rb +605 -605
  513. data/spec/integration/download_spec.rb +1336 -1336
  514. data/spec/integration/environment_compare_spec.rb +75 -75
  515. data/spec/integration/environment_create_spec.rb +41 -41
  516. data/spec/integration/environment_delete_spec.rb +37 -37
  517. data/spec/integration/environment_from_file_spec.rb +116 -116
  518. data/spec/integration/environment_list_spec.rb +42 -42
  519. data/spec/integration/environment_show_spec.rb +77 -77
  520. data/spec/integration/list_spec.rb +1060 -1060
  521. data/spec/integration/node_bulk_delete_spec.rb +52 -52
  522. data/spec/integration/node_create_spec.rb +47 -47
  523. data/spec/integration/node_delete_spec.rb +48 -48
  524. data/spec/integration/node_environment_set_spec.rb +46 -46
  525. data/spec/integration/node_from_file_spec.rb +59 -59
  526. data/spec/integration/node_list_spec.rb +45 -45
  527. data/spec/integration/node_run_list_add_spec.rb +54 -54
  528. data/spec/integration/node_run_list_remove_spec.rb +36 -36
  529. data/spec/integration/node_run_list_set_spec.rb +41 -41
  530. data/spec/integration/node_show_spec.rb +36 -36
  531. data/spec/integration/raw_spec.rb +297 -297
  532. data/spec/integration/redirection_spec.rb +64 -64
  533. data/spec/integration/role_bulk_delete_spec.rb +52 -52
  534. data/spec/integration/role_create_spec.rb +41 -41
  535. data/spec/integration/role_delete_spec.rb +48 -48
  536. data/spec/integration/role_from_file_spec.rb +96 -96
  537. data/spec/integration/role_list_spec.rb +45 -45
  538. data/spec/integration/role_show_spec.rb +51 -51
  539. data/spec/integration/search_node_spec.rb +40 -40
  540. data/spec/integration/serve_spec.rb +92 -92
  541. data/spec/integration/show_spec.rb +197 -197
  542. data/spec/integration/upload_spec.rb +1616 -1616
  543. data/spec/knife_spec_helper.rb +241 -241
  544. data/spec/support/chef_helpers.rb +79 -79
  545. data/spec/support/key_helpers.rb +102 -102
  546. data/spec/support/platform_helpers.rb +255 -255
  547. data/spec/support/platforms/prof/gc.rb +51 -51
  548. data/spec/support/platforms/prof/win32.rb +45 -45
  549. data/spec/support/platforms/win32/spec_service.rb +57 -57
  550. data/spec/support/recipe_dsl_helper.rb +83 -83
  551. data/spec/support/shared/context/config.rb +18 -18
  552. data/spec/support/shared/functional/knife.rb +37 -37
  553. data/spec/support/shared/integration/integration_helper.rb +122 -122
  554. data/spec/support/shared/integration/knife_support.rb +192 -192
  555. data/spec/support/shared/matchers/exit_with_code.rb +32 -32
  556. data/spec/support/shared/matchers/match_environment_variable.rb +17 -17
  557. data/spec/support/shared/unit/knife_shared.rb +39 -39
  558. data/spec/support/shared/unit/mock_shellout.rb +49 -49
  559. data/spec/tiny_server.rb +190 -190
  560. data/spec/unit/application/knife_spec.rb +241 -241
  561. data/spec/unit/knife/bootstrap/chef_vault_handler_spec.rb +152 -152
  562. data/spec/unit/knife/bootstrap/client_builder_spec.rb +207 -207
  563. data/spec/unit/knife/bootstrap/train_connector_spec.rb +244 -244
  564. data/spec/unit/knife/bootstrap_spec.rb +2233 -2233
  565. data/spec/unit/knife/client_bulk_delete_spec.rb +166 -166
  566. data/spec/unit/knife/client_create_spec.rb +232 -232
  567. data/spec/unit/knife/client_delete_spec.rb +99 -99
  568. data/spec/unit/knife/client_edit_spec.rb +53 -53
  569. data/spec/unit/knife/client_list_spec.rb +34 -34
  570. data/spec/unit/knife/client_reregister_spec.rb +62 -62
  571. data/spec/unit/knife/client_show_spec.rb +52 -52
  572. data/spec/unit/knife/configure_client_spec.rb +81 -81
  573. data/spec/unit/knife/configure_spec.rb +190 -190
  574. data/spec/unit/knife/cookbook_bulk_delete_spec.rb +87 -87
  575. data/spec/unit/knife/cookbook_delete_spec.rb +239 -239
  576. data/spec/unit/knife/cookbook_download_spec.rb +255 -255
  577. data/spec/unit/knife/cookbook_list_spec.rb +88 -88
  578. data/spec/unit/knife/cookbook_metadata_from_file_spec.rb +72 -72
  579. data/spec/unit/knife/cookbook_metadata_spec.rb +182 -182
  580. data/spec/unit/knife/cookbook_show_spec.rb +253 -253
  581. data/spec/unit/knife/cookbook_upload_spec.rb +426 -426
  582. data/spec/unit/knife/core/bootstrap_context_spec.rb +287 -287
  583. data/spec/unit/knife/core/cookbook_scm_repo_spec.rb +187 -187
  584. data/spec/unit/knife/core/cookbook_site_streaming_uploader_spec.rb +198 -198
  585. data/spec/unit/knife/core/gem_glob_loader_spec.rb +242 -242
  586. data/spec/unit/knife/core/hashed_command_loader_spec.rb +112 -112
  587. data/spec/unit/knife/core/node_editor_spec.rb +211 -211
  588. data/spec/unit/knife/core/object_loader_spec.rb +81 -81
  589. data/spec/unit/knife/core/status_presenter_spec.rb +54 -54
  590. data/spec/unit/knife/core/subcommand_loader_spec.rb +64 -64
  591. data/spec/unit/knife/core/ui_spec.rb +656 -656
  592. data/spec/unit/knife/core/windows_bootstrap_context_spec.rb +238 -238
  593. data/spec/unit/knife/data_bag_create_spec.rb +175 -175
  594. data/spec/unit/knife/data_bag_edit_spec.rb +126 -126
  595. data/spec/unit/knife/data_bag_from_file_spec.rb +174 -174
  596. data/spec/unit/knife/data_bag_secret_options_spec.rb +173 -173
  597. data/spec/unit/knife/data_bag_show_spec.rb +139 -139
  598. data/spec/unit/knife/environment_compare_spec.rb +112 -112
  599. data/spec/unit/knife/environment_create_spec.rb +91 -91
  600. data/spec/unit/knife/environment_delete_spec.rb +71 -71
  601. data/spec/unit/knife/environment_edit_spec.rb +79 -79
  602. data/spec/unit/knife/environment_from_file_spec.rb +90 -90
  603. data/spec/unit/knife/environment_list_spec.rb +54 -54
  604. data/spec/unit/knife/environment_show_spec.rb +52 -52
  605. data/spec/unit/knife/key_create_spec.rb +223 -223
  606. data/spec/unit/knife/key_delete_spec.rb +133 -133
  607. data/spec/unit/knife/key_edit_spec.rb +264 -264
  608. data/spec/unit/knife/key_helper.rb +74 -74
  609. data/spec/unit/knife/key_list_spec.rb +216 -216
  610. data/spec/unit/knife/key_show_spec.rb +126 -126
  611. data/spec/unit/knife/node_bulk_delete_spec.rb +94 -94
  612. data/spec/unit/knife/node_delete_spec.rb +77 -77
  613. data/spec/unit/knife/node_edit_spec.rb +116 -116
  614. data/spec/unit/knife/node_environment_set_spec.rb +61 -61
  615. data/spec/unit/knife/node_from_file_spec.rb +59 -59
  616. data/spec/unit/knife/node_list_spec.rb +62 -62
  617. data/spec/unit/knife/node_policy_set_spec.rb +122 -122
  618. data/spec/unit/knife/node_run_list_add_spec.rb +145 -145
  619. data/spec/unit/knife/node_run_list_remove_spec.rb +106 -106
  620. data/spec/unit/knife/node_run_list_set_spec.rb +115 -115
  621. data/spec/unit/knife/node_show_spec.rb +65 -65
  622. data/spec/unit/knife/org_create_spec.rb +76 -76
  623. data/spec/unit/knife/org_delete_spec.rb +41 -41
  624. data/spec/unit/knife/org_edit_spec.rb +49 -49
  625. data/spec/unit/knife/org_list_spec.rb +58 -58
  626. data/spec/unit/knife/org_show_spec.rb +45 -45
  627. data/spec/unit/knife/org_user_add_spec.rb +39 -39
  628. data/spec/unit/knife/raw_spec.rb +43 -43
  629. data/spec/unit/knife/role_bulk_delete_spec.rb +80 -80
  630. data/spec/unit/knife/role_create_spec.rb +80 -80
  631. data/spec/unit/knife/role_delete_spec.rb +67 -67
  632. data/spec/unit/knife/role_edit_spec.rb +77 -77
  633. data/spec/unit/knife/role_env_run_list_add_spec.rb +217 -217
  634. data/spec/unit/knife/role_env_run_list_clear_spec.rb +94 -94
  635. data/spec/unit/knife/role_env_run_list_remove_spec.rb +102 -102
  636. data/spec/unit/knife/role_env_run_list_replace_spec.rb +105 -105
  637. data/spec/unit/knife/role_env_run_list_set_spec.rb +99 -99
  638. data/spec/unit/knife/role_from_file_spec.rb +69 -69
  639. data/spec/unit/knife/role_list_spec.rb +54 -54
  640. data/spec/unit/knife/role_run_list_add_spec.rb +179 -179
  641. data/spec/unit/knife/role_run_list_clear_spec.rb +84 -84
  642. data/spec/unit/knife/role_run_list_remove_spec.rb +92 -92
  643. data/spec/unit/knife/role_run_list_replace_spec.rb +98 -98
  644. data/spec/unit/knife/role_run_list_set_spec.rb +89 -89
  645. data/spec/unit/knife/role_show_spec.rb +59 -59
  646. data/spec/unit/knife/ssh_spec.rb +403 -403
  647. data/spec/unit/knife/ssl_check_spec.rb +256 -256
  648. data/spec/unit/knife/ssl_fetch_spec.rb +222 -222
  649. data/spec/unit/knife/status_spec.rb +112 -112
  650. data/spec/unit/knife/supermarket_download_spec.rb +152 -152
  651. data/spec/unit/knife/supermarket_install_spec.rb +203 -203
  652. data/spec/unit/knife/supermarket_list_spec.rb +70 -70
  653. data/spec/unit/knife/supermarket_search_spec.rb +85 -85
  654. data/spec/unit/knife/supermarket_share_spec.rb +208 -208
  655. data/spec/unit/knife/supermarket_unshare_spec.rb +78 -78
  656. data/spec/unit/knife/tag_create_spec.rb +23 -23
  657. data/spec/unit/knife/tag_delete_spec.rb +25 -25
  658. data/spec/unit/knife/tag_list_spec.rb +23 -23
  659. data/spec/unit/knife/user_create_spec.rb +282 -282
  660. data/spec/unit/knife/user_delete_spec.rb +171 -171
  661. data/spec/unit/knife/user_edit_spec.rb +54 -54
  662. data/spec/unit/knife/user_list_spec.rb +73 -73
  663. data/spec/unit/knife/user_password_spec.rb +64 -64
  664. data/spec/unit/knife/user_reregister_spec.rb +56 -56
  665. data/spec/unit/knife/user_show_spec.rb +91 -91
  666. data/spec/unit/knife_spec.rb +634 -634
  667. metadata +3 -3
@@ -1,2233 +1,2233 @@
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_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_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_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_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
- before do
1407
- allow(knife).to receive(:connection_protocol).and_return connection_protocol
1408
- end
1409
-
1410
- context "for winrm" do
1411
- let(:connection_protocol) { "winrm" }
1412
- it "returns an empty hash" do
1413
- expect(knife.sudo_opts).to eq({})
1414
- end
1415
- end
1416
-
1417
- context "for ssh" do
1418
- let(:connection_protocol) { "ssh" }
1419
- context "when use_sudo is set" do
1420
- before do
1421
- knife.config[:use_sudo] = true
1422
- end
1423
-
1424
- it "returns a config that enables sudo" do
1425
- expect(knife.sudo_opts).to eq( { sudo: true } )
1426
- end
1427
-
1428
- context "when use_sudo_password is also set" do
1429
- before do
1430
- knife.config[:use_sudo_password] = true
1431
- knife.config[:connection_password] = "opscode"
1432
- end
1433
- it "includes :connection_password value in a sudo-enabled configuration" do
1434
- expect(knife.sudo_opts).to eq({
1435
- sudo: true,
1436
- sudo_password: "opscode",
1437
- })
1438
- end
1439
- end
1440
-
1441
- context "when preserve_home is set" do
1442
- before do
1443
- knife.config[:preserve_home] = true
1444
- end
1445
- it "enables sudo with sudo_option to preserve home" do
1446
- expect(knife.sudo_opts).to eq({
1447
- sudo_options: "-H",
1448
- sudo: true,
1449
- })
1450
- end
1451
- end
1452
- end
1453
-
1454
- context "when use_sudo is not set" do
1455
- before do
1456
- knife.config[:use_sudo_password] = true
1457
- knife.config[:preserve_home] = true
1458
- end
1459
- it "returns configuration for sudo off, ignoring other related options" do
1460
- expect(knife.sudo_opts).to eq( { sudo: false } )
1461
- end
1462
- end
1463
- end
1464
- end
1465
-
1466
- context "#ssh_opts" do
1467
- let(:connection_protocol) { nil }
1468
- before do
1469
- allow(knife).to receive(:connection_protocol).and_return connection_protocol
1470
- end
1471
-
1472
- context "for ssh" do
1473
- let(:connection_protocol) { "ssh" }
1474
- let(:default_opts) do
1475
- {
1476
- non_interactive: true,
1477
- forward_agent: false,
1478
- connection_timeout: 60,
1479
- }
1480
- end
1481
-
1482
- context "by default" do
1483
- it "returns a configuration hash with appropriate defaults" do
1484
- expect(knife.ssh_opts).to eq default_opts
1485
- end
1486
- end
1487
-
1488
- context "when ssh_forward_agent has a value" do
1489
- before do
1490
- knife.config[:ssh_forward_agent] = true
1491
- end
1492
- it "returns a default configuration hash with forward_agent set to true" do
1493
- expect(knife.ssh_opts).to eq(default_opts.merge(forward_agent: true))
1494
- end
1495
- end
1496
- context "when session_timeout has a value" do
1497
- before do
1498
- knife.config[:session_timeout] = 120
1499
- end
1500
- it "returns a default configuration hash with updated timeout value." do
1501
- expect(knife.ssh_opts).to eq(default_opts.merge(connection_timeout: 120))
1502
- end
1503
- end
1504
-
1505
- end
1506
-
1507
- context "for winrm" do
1508
- let(:connection_protocol) { "winrm" }
1509
- it "returns an empty has because ssh is not winrm" do
1510
- expect(knife.ssh_opts).to eq({})
1511
- end
1512
- end
1513
-
1514
- end
1515
-
1516
- context "#winrm_opts" do
1517
- let(:connection_protocol) { nil }
1518
- before do
1519
- allow(knife).to receive(:connection_protocol).and_return connection_protocol
1520
- end
1521
-
1522
- context "for winrm" do
1523
- let(:connection_protocol) { "winrm" }
1524
- let(:expected) do
1525
- {
1526
- winrm_transport: "negotiate",
1527
- winrm_basic_auth_only: false,
1528
- ssl: false,
1529
- ssl_peer_fingerprint: nil,
1530
- operation_timeout: 60,
1531
- }
1532
- end
1533
-
1534
- it "generates a correct configuration hash with expected defaults" do
1535
- expect(knife.winrm_opts).to eq expected
1536
- end
1537
-
1538
- context "with ssl_peer_fingerprint" do
1539
- let(:ssl_peer_fingerprint_expected) do
1540
- expected.merge({ ssl_peer_fingerprint: "ABCD" })
1541
- end
1542
-
1543
- before do
1544
- knife.config[:winrm_ssl_peer_fingerprint] = "ABCD"
1545
- end
1546
-
1547
- it "generates a correct options hash with ssl_peer_fingerprint from the config provided" do
1548
- expect(knife.winrm_opts).to eq ssl_peer_fingerprint_expected
1549
- end
1550
- end
1551
-
1552
- context "with winrm_ssl" do
1553
- let(:ssl_expected) do
1554
- expected.merge({ ssl: true })
1555
- end
1556
- before do
1557
- knife.config[:winrm_ssl] = true
1558
- end
1559
-
1560
- it "generates a correct options hash with ssl from the config provided" do
1561
- expect(knife.winrm_opts).to eq ssl_expected
1562
- end
1563
- end
1564
-
1565
- context "with winrm_auth_method" do
1566
- let(:winrm_auth_method_expected) do
1567
- expected.merge({ winrm_transport: "freeaccess" })
1568
- end
1569
-
1570
- before do
1571
- knife.config[:winrm_auth_method] = "freeaccess"
1572
- end
1573
-
1574
- it "generates a correct options hash with winrm_transport from the config provided" do
1575
- expect(knife.winrm_opts).to eq winrm_auth_method_expected
1576
- end
1577
- end
1578
-
1579
- context "with ca_trust_file" do
1580
- let(:ca_trust_expected) do
1581
- expected.merge({ ca_trust_path: "/trust.me" })
1582
- end
1583
- before do
1584
- knife.config[:ca_trust_file] = "/trust.me"
1585
- end
1586
-
1587
- it "generates a correct options hash with ca_trust_file from the config provided" do
1588
- expect(knife.winrm_opts).to eq ca_trust_expected
1589
- end
1590
- end
1591
-
1592
- context "with kerberos auth" do
1593
- let(:kerberos_expected) do
1594
- expected.merge({
1595
- kerberos_service: "testsvc",
1596
- kerberos_realm: "TESTREALM",
1597
- winrm_transport: "kerberos",
1598
- })
1599
- end
1600
-
1601
- before do
1602
- knife.config[:winrm_auth_method] = "kerberos"
1603
- knife.config[:kerberos_service] = "testsvc"
1604
- knife.config[:kerberos_realm] = "TESTREALM"
1605
- end
1606
-
1607
- it "generates a correct options hash containing kerberos auth configuration from the config provided" do
1608
- expect(knife.winrm_opts).to eq kerberos_expected
1609
- end
1610
- end
1611
-
1612
- context "with winrm_basic_auth_only" do
1613
- before do
1614
- knife.config[:winrm_basic_auth_only] = true
1615
- end
1616
- let(:basic_auth_expected) do
1617
- expected.merge( { winrm_basic_auth_only: true } )
1618
- end
1619
- it "generates a correct options hash containing winrm_basic_auth_only from the config provided" do
1620
- expect(knife.winrm_opts).to eq basic_auth_expected
1621
- end
1622
- end
1623
- end
1624
-
1625
- context "for ssh" do
1626
- let(:connection_protocol) { "ssh" }
1627
- it "returns an empty hash because ssh is not winrm" do
1628
- expect(knife.winrm_opts).to eq({})
1629
- end
1630
- end
1631
- end
1632
- describe "#run" do
1633
- it "performs the steps we expect to run a bootstrap" do
1634
- expect(knife).to receive(:check_license)
1635
- expect(knife).to receive(:validate_name_args!).ordered
1636
- expect(knife).to receive(:validate_protocol!).ordered
1637
- expect(knife).to receive(:validate_first_boot_attributes!).ordered
1638
- expect(knife).to receive(:validate_winrm_transport_opts!).ordered
1639
- expect(knife).to receive(:validate_policy_options!).ordered
1640
- expect(knife).to receive(:winrm_warn_no_ssl_verification).ordered
1641
- expect(knife).to receive(:warn_on_short_session_timeout).ordered
1642
- expect(knife).to receive(:connect!).ordered
1643
- expect(knife).to receive(:register_client).ordered
1644
- expect(knife).to receive(:render_template).and_return "content"
1645
- expect(knife).to receive(:upload_bootstrap).with("content").and_return "/remote/path.sh"
1646
- expect(knife).to receive(:perform_bootstrap).with("/remote/path.sh")
1647
- expect(connection).to receive(:del_file!) # Make sure cleanup happens
1648
-
1649
- knife.run
1650
-
1651
- # Post-run verify expected state changes (not many directly in #run)
1652
- expect($stdout.sync).to eq true
1653
- end
1654
- end
1655
-
1656
- describe "#register_client" do
1657
- let(:vault_handler_mock) { double("ChefVaultHandler") }
1658
- let(:client_builder_mock) { double("ClientBuilder") }
1659
- let(:node_name) { nil }
1660
- before do
1661
- allow(knife).to receive(:chef_vault_handler).and_return vault_handler_mock
1662
- allow(knife).to receive(:client_builder).and_return client_builder_mock
1663
- knife.config[:chef_node_name] = node_name
1664
- end
1665
-
1666
- shared_examples_for "creating the client locally" do
1667
- context "when a valid node name is present" do
1668
- let(:node_name) { "test" }
1669
- before do
1670
- allow(client_builder_mock).to receive(:client).and_return "client"
1671
- allow(client_builder_mock).to receive(:client_path).and_return "/key.pem"
1672
- end
1673
-
1674
- it "runs client_builder and vault_handler" do
1675
- expect(client_builder_mock).to receive(:run)
1676
- expect(vault_handler_mock).to receive(:run).with("client")
1677
- knife.register_client
1678
- end
1679
-
1680
- it "sets the path to the client key in the bootstrap context" do
1681
- allow(client_builder_mock).to receive(:run)
1682
- allow(vault_handler_mock).to receive(:run).with("client")
1683
- knife.register_client
1684
- expect(knife.bootstrap_context.client_pem).to eq "/key.pem"
1685
- end
1686
- end
1687
-
1688
- context "when no valid node name is present" do
1689
- let(:node_name) { nil }
1690
- it "shows an error and exits" do
1691
- expect(knife.ui).to receive(:error)
1692
- expect { knife.register_client }.to raise_error(SystemExit)
1693
- end
1694
- end
1695
- end
1696
- context "when chef_vault_handler says we're using vault" do
1697
- let(:vault_handler_mock) { double("ChefVaultHandler") }
1698
- before do
1699
- allow(vault_handler_mock).to receive(:doing_chef_vault?).and_return true
1700
- end
1701
- it_behaves_like "creating the client locally"
1702
- end
1703
-
1704
- context "when an non-existant validation key is specified in chef config" do
1705
- before do
1706
- Chef::Config[:validation_key] = "/blah"
1707
- allow(vault_handler_mock).to receive(:doing_chef_vault?).and_return false
1708
- allow(File).to receive(:exist?).with(%r{/blah}).and_return false
1709
- end
1710
- it_behaves_like "creating the client locally"
1711
- end
1712
-
1713
- context "when a valid validation key is given and we're doing old-style client creation" do
1714
- before do
1715
- Chef::Config[:validation_key] = "/blah"
1716
- allow(File).to receive(:exist?).with(%r{/blah}).and_return true
1717
- allow(vault_handler_mock).to receive(:doing_chef_vault?).and_return false
1718
- end
1719
-
1720
- it "shows a warning message" do
1721
- expect(knife.ui).to receive(:warn).twice
1722
- knife.register_client
1723
- end
1724
- end
1725
- end
1726
-
1727
- describe "#perform_bootstrap" do
1728
- let(:exit_status) { 0 }
1729
- let(:stdout) { "" }
1730
- let(:result_mock) { double("result", exit_status: exit_status, stderr: "A message", stdout: stdout) }
1731
-
1732
- before do
1733
- allow(connection).to receive(:hostname).and_return "testhost"
1734
- end
1735
- it "runs the remote script and logs the output" do
1736
- expect(knife.ui).to receive(:info).with(/Bootstrapping.*/)
1737
- expect(knife).to receive(:bootstrap_command)
1738
- .with("/path.sh")
1739
- .and_return("sh /path.sh")
1740
- expect(connection)
1741
- .to receive(:run_command)
1742
- .with("sh /path.sh")
1743
- .and_yield("output here", nil)
1744
- .and_return result_mock
1745
-
1746
- expect(knife.ui).to receive(:msg).with(/testhost/)
1747
- knife.perform_bootstrap("/path.sh")
1748
- end
1749
-
1750
- context "when the remote command fails" do
1751
- let(:exit_status) { 1 }
1752
- it "shows an error and exits" do
1753
- expect(knife.ui).to receive(:info).with(/Bootstrapping.*/)
1754
- expect(knife).to receive(:bootstrap_command)
1755
- .with("/path.sh")
1756
- .and_return("sh /path.sh")
1757
- expect(connection).to receive(:run_command).with("sh /path.sh").and_return result_mock
1758
- expect { knife.perform_bootstrap("/path.sh") }.to raise_error(SystemExit)
1759
- end
1760
- end
1761
-
1762
- context "when the remote command failed due to su auth error" do
1763
- let(:exit_status) { 1 }
1764
- let(:stdout) { "su: Authentication failure" }
1765
- let(:connection_obj) { double("connection", transport_options: {}) }
1766
- it "shows an error and exits" do
1767
- allow(connection).to receive(:connection).and_return(connection_obj)
1768
- expect(knife.ui).to receive(:info).with(/Bootstrapping.*/)
1769
- expect(knife).to receive(:bootstrap_command)
1770
- .with("/path.sh")
1771
- .and_return("su - USER -c 'sh /path.sh'")
1772
- expect(connection)
1773
- .to receive(:run_command)
1774
- .with("su - USER -c 'sh /path.sh'")
1775
- .and_yield("output here", nil)
1776
- .and_raise(Train::UserError)
1777
- expect { knife.perform_bootstrap("/path.sh") }.to raise_error(Train::UserError)
1778
- end
1779
- end
1780
- end
1781
-
1782
- describe "#connect!" do
1783
- before do
1784
- # These are not required at run-time because train will handle its own
1785
- # protocol loading. In this case, we're simulating train failures and have to load
1786
- # them ourselves.
1787
- require "net/ssh"
1788
- require "train/transports/ssh"
1789
- end
1790
-
1791
- context "in the normal case" do
1792
- it "connects using the connection_opts and notifies the operator of progress" do
1793
- expect(knife.ui).to receive(:info).with(/Connecting to.*/)
1794
- expect(knife).to receive(:connection_opts).and_return( { opts: "here" })
1795
- expect(knife).to receive(:do_connect).with( { opts: "here" } )
1796
- knife.connect!
1797
- end
1798
- end
1799
-
1800
- context "when a general non-auth-failure occurs" do
1801
- let(:expected_error) { RuntimeError.new }
1802
- before do
1803
- allow(knife).to receive(:do_connect).and_raise(expected_error)
1804
- end
1805
- it "re-raises the exception" do
1806
- expect { knife.connect! }.to raise_error(expected_error)
1807
- end
1808
- end
1809
-
1810
- context "when ssh fingerprint is invalid" do
1811
- let(:expected_error) { Train::Error.new("fingerprint AA:BB is unknown for \"blah,127.0.0.1\"") }
1812
- before do
1813
- allow(knife).to receive(:do_connect).and_raise(expected_error)
1814
- end
1815
- it "warns, prompts to accept, then connects with verify_host_key of accept_new" do
1816
- expect(knife).to receive(:do_connect).and_raise(expected_error)
1817
- expect(knife.ui).to receive(:confirm)
1818
- .with(/.*host 'blah \(127.0.0.1\)'.*AA:BB.*Are you sure you want to continue.*/m)
1819
- .and_return(true)
1820
- expect(knife).to receive(:do_connect) do |opts|
1821
- expect(opts[:verify_host_key]).to eq :accept_new
1822
- end
1823
- knife.connect!
1824
- end
1825
- end
1826
-
1827
- context "when an auth failure occurs" do
1828
- let(:expected_error) do
1829
- e = Train::Error.new
1830
- actual = Net::SSH::AuthenticationFailed.new
1831
- # Simulate train's nested error - they wrap
1832
- # ssh/network errors in a TrainError.
1833
- allow(e).to receive(:cause).and_return(actual)
1834
- e
1835
- end
1836
-
1837
- let(:expected_error_password_prompt) do
1838
- e = Train::ClientError.new
1839
- reason = :no_ssh_password_or_key_available
1840
- allow(e).to receive(:reason).and_return(reason)
1841
- e
1842
- end
1843
-
1844
- let(:expected_error_password_prompt_winrm) do
1845
- e = RuntimeError.new
1846
- message = "password is a required option"
1847
- allow(e).to receive(:message).and_return(message)
1848
- e
1849
- end
1850
-
1851
- context "and password auth was used" do
1852
- before do
1853
- allow(connection).to receive(:password_auth?).and_return true
1854
- end
1855
-
1856
- it "re-raises the error so as not to resubmit the same failing password" do
1857
- expect(knife).to receive(:do_connect).and_raise(expected_error)
1858
- expect { knife.connect! }.to raise_error(expected_error)
1859
- end
1860
- end
1861
-
1862
- context "and password auth was not used" do
1863
- before do
1864
- allow(connection).to receive(:password_auth?).and_return false
1865
- allow(connection).to receive(:user).and_return "testuser"
1866
- allow(knife).to receive(:connection_protocol).and_return connection_protocol
1867
- end
1868
-
1869
- context "when using ssh" do
1870
- let(:connection_protocol) { "ssh" }
1871
-
1872
- it "warns, prompts for password, then reconnects with a password-enabled configuration using the new password" do
1873
- expect(knife).to receive(:do_connect).and_raise(expected_error_password_prompt)
1874
- expect(knife.ui).to receive(:warn).with(/Failed to auth.*/)
1875
- expect(knife.ui).to receive(:ask).and_return("newpassword")
1876
- # Ensure that we set echo off to prevent showing password on the screen
1877
- expect(knife).to receive(:do_connect) do |opts|
1878
- expect(opts[:password]).to eq "newpassword"
1879
- end
1880
- knife.connect!
1881
- end
1882
- end
1883
-
1884
- context "when using winrm" do
1885
- let(:connection_protocol) { "winrm" }
1886
-
1887
- it "warns, prompts for password, then reconnects with a password-enabled configuration using the new password for" do
1888
- expect(knife).to receive(:do_connect).and_raise(expected_error_password_prompt_winrm)
1889
- expect(knife.ui).to receive(:warn).with(/Failed to auth.*/)
1890
- expect(knife.ui).to receive(:ask).and_return("newpassword")
1891
- # Ensure that we set echo off to prevent showing password on the screen
1892
- expect(knife).to receive(:do_connect) do |opts|
1893
- expect(opts[:password]).to eq "newpassword"
1894
- end
1895
- knife.connect!
1896
- end
1897
- end
1898
- end
1899
- end
1900
- end
1901
-
1902
- it "verifies that a server to bootstrap was given as a command line arg" do
1903
- knife.name_args = nil
1904
- expect(knife).to receive(:check_license)
1905
- expect { knife.run }.to raise_error(SystemExit)
1906
- expect(stderr.string).to match(/ERROR:.+FQDN or ip/)
1907
- end
1908
-
1909
- describe "#bootstrap_context" do
1910
- context "under Windows" do
1911
- let(:windows_test) { true }
1912
- it "creates a WindowsBootstrapContext" do
1913
- require "chef/knife/core/windows_bootstrap_context"
1914
- expect(knife.bootstrap_context.class).to eq Chef::Knife::Core::WindowsBootstrapContext
1915
- end
1916
- end
1917
-
1918
- context "under linux" do
1919
- let(:linux_test) { true }
1920
- it "creates a BootstrapContext" do
1921
- require "chef/knife/core/bootstrap_context"
1922
- expect(knife.bootstrap_context.class).to eq Chef::Knife::Core::BootstrapContext
1923
- end
1924
- end
1925
- end
1926
-
1927
- describe "#config_value" do
1928
- before do
1929
- knife.config[:test_key_a] = "a from cli"
1930
- knife.config[:test_key_b] = "b from cli"
1931
- Chef::Config[:knife][:test_key_a] = "a from Chef::Config"
1932
- Chef::Config[:knife][:test_key_c] = "c from Chef::Config"
1933
- Chef::Config[:knife][:alt_test_key_c] = "alt c from Chef::Config"
1934
- knife.merge_configs
1935
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
1936
- end
1937
-
1938
- it "returns the Chef::Config value from the cli when the CLI key is set" do
1939
- expect(knife.config_value(:test_key_a, :alt_test_key_c)).to eq "a from cli"
1940
- end
1941
-
1942
- it "returns the Chef::Config value from the alternative key when the CLI key is not set" do
1943
- expect(knife.config_value(:test_key_d, :alt_test_key_c)).to eq "alt c from Chef::Config"
1944
- end
1945
-
1946
- it "returns the default value when the key is not provided by CLI or Chef::Config" do
1947
- expect(knife.config_value(:missing_key, :missing_key, "found")).to eq "found"
1948
- end
1949
- end
1950
-
1951
- describe "#upload_bootstrap" do
1952
- before do
1953
- allow(connection).to receive(:temp_dir).and_return(temp_dir)
1954
- allow(connection).to receive(:normalize_path) { |a| a }
1955
- end
1956
-
1957
- let(:content) { "bootstrap script content" }
1958
- context "under Windows" do
1959
- let(:windows_test) { true }
1960
- let(:temp_dir) { "C:/Temp/bootstrap" }
1961
- it "creates a bat file in the temp dir provided by connection, using given content" do
1962
- expect(connection).to receive(:upload_file_content!).with(content, "C:/Temp/bootstrap/bootstrap.bat")
1963
- expect(knife.upload_bootstrap(content)).to eq "C:/Temp/bootstrap/bootstrap.bat"
1964
- end
1965
- end
1966
-
1967
- context "under Linux" do
1968
- let(:linux_test) { true }
1969
- let(:temp_dir) { "/tmp/bootstrap" }
1970
- it "creates a 'sh file in the temp dir provided by connection, using given content" do
1971
- expect(connection).to receive(:upload_file_content!).with(content, "/tmp/bootstrap/bootstrap.sh")
1972
- expect(knife.upload_bootstrap(content)).to eq "/tmp/bootstrap/bootstrap.sh"
1973
- end
1974
- end
1975
- end
1976
-
1977
- describe "#bootstrap_command" do
1978
- context "under Windows" do
1979
- let(:windows_test) { true }
1980
- it "prefixes the command to run under cmd.exe" do
1981
- expect(knife.bootstrap_command("autoexec.bat")).to eq "cmd.exe /C autoexec.bat"
1982
- end
1983
-
1984
- end
1985
- context "under Linux" do
1986
- let(:linux_test) { true }
1987
- it "prefixes the command to run under sh" do
1988
- expect(knife.bootstrap_command("bootstrap.sh")).to eq "sh bootstrap.sh"
1989
- end
1990
-
1991
- context "with --su-user option" do
1992
- let(:connection_obj) { double("connection", transport_options: {}) }
1993
- before do
1994
- knife.config[:su_user] = "root"
1995
- allow(connection).to receive(:connection).and_return(connection_obj)
1996
- end
1997
- it "prefixes the command to run using su -USER -c" do
1998
- expect(knife.bootstrap_command("bootstrap.sh")).to eq "su - #{knife.config[:su_user]} -c 'sh bootstrap.sh'"
1999
- expect(connection_obj.transport_options.key?(:pty)).to eq true
2000
- end
2001
-
2002
- it "sudo appended if --sudo option enabled" do
2003
- knife.config[:use_sudo] = true
2004
- expect(knife.bootstrap_command("bootstrap.sh")).to eq "sudo su - #{knife.config[:su_user]} -c 'sh bootstrap.sh'"
2005
- expect(connection_obj.transport_options.key?(:pty)).to eq true
2006
- end
2007
- end
2008
- end
2009
- end
2010
-
2011
- describe "#default_bootstrap_template" do
2012
- context "under Windows" do
2013
- let(:windows_test) { true }
2014
- it "is windows-chef-client-msi" do
2015
- expect(knife.default_bootstrap_template).to eq "windows-chef-client-msi"
2016
- end
2017
-
2018
- end
2019
- context "under Linux" do
2020
- let(:linux_test) { true }
2021
- it "is chef-full" do
2022
- expect(knife.default_bootstrap_template).to eq "chef-full"
2023
- end
2024
- end
2025
- end
2026
-
2027
- describe "#do_connect" do
2028
- let(:host_descriptor) { "example.com" }
2029
- let(:connection) { double("TrainConnector") }
2030
- let(:connector_mock) { double("TargetResolver", targets: [ connection ]) }
2031
- before do
2032
- allow(knife).to receive(:host_descriptor).and_return host_descriptor
2033
- end
2034
-
2035
- it "creates a TrainConnector and connects it" do
2036
- expect(Chef::Knife::Bootstrap::TrainConnector).to receive(:new).and_return connection
2037
- expect(connection).to receive(:connect!)
2038
- knife.do_connect({})
2039
- end
2040
-
2041
- context "when sshd configured with requiretty" do
2042
- let(:pty_err_msg) { "Sudo requires a TTY. Please see the README on how to configure sudo to allow for non-interactive usage." }
2043
- let(:expected_error) { Train::UserError.new(pty_err_msg, :sudo_no_tty) }
2044
- before do
2045
- allow(connection).to receive(:connect!).and_raise(expected_error)
2046
- end
2047
- it "retry with pty true request option" do
2048
- expect(Chef::Knife::Bootstrap::TrainConnector).to receive(:new).and_return(connection).exactly(2).times
2049
- expect(knife.ui).to receive(:warn).with("#{pty_err_msg} - trying with pty request")
2050
- expect { knife.do_connect({}) }.to raise_error(expected_error)
2051
- end
2052
- end
2053
-
2054
- context "when a train sudo error is thrown for missing terminal" do
2055
- let(:ui_error_msg) { "Sudo password is required for this operation. Please enter password using -P or --ssh-password option" }
2056
- let(:expected_error) { Train::UserError.new(ui_error_msg, :sudo_missing_terminal) }
2057
- before do
2058
- allow(connection).to receive(:connect!).and_raise(expected_error)
2059
- end
2060
- it "outputs user friendly error message" do
2061
- expect { knife.do_connect({}) }.not_to raise_error
2062
- expect(stderr.string).to include(ui_error_msg)
2063
- end
2064
- end
2065
-
2066
- end
2067
-
2068
- describe "validate_winrm_transport_opts!" do
2069
- before do
2070
- allow(knife).to receive(:connection_protocol).and_return connection_protocol
2071
- end
2072
-
2073
- context "when using ssh" do
2074
- let(:connection_protocol) { "ssh" }
2075
- it "returns true" do
2076
- expect(knife.validate_winrm_transport_opts!).to eq true
2077
- end
2078
- end
2079
- context "when using winrm" do
2080
- let(:connection_protocol) { "winrm" }
2081
- context "with plaintext auth" do
2082
- before do
2083
- knife.config[:winrm_auth_method] = "plaintext"
2084
- end
2085
- context "with ssl" do
2086
- before do
2087
- knife.config[:winrm_ssl] = true
2088
- end
2089
- it "will not error because we won't send anything in plaintext regardless" do
2090
- expect(knife.validate_winrm_transport_opts!).to eq true
2091
- end
2092
- end
2093
- context "without ssl" do
2094
- before do
2095
- knife.config[:winrm_ssl] = false
2096
- end
2097
- context "and no validation key exists" do
2098
- before do
2099
- Chef::Config[:validation_key] = "validation_key.pem"
2100
- allow(File).to receive(:exist?).with(/.*validation_key.pem/).and_return false
2101
- end
2102
-
2103
- it "will error because we will generate and send a client key over the wire in plaintext" do
2104
- expect { knife.validate_winrm_transport_opts! }.to raise_error(SystemExit)
2105
- end
2106
-
2107
- end
2108
- context "and a validation key exists" do
2109
- before do
2110
- Chef::Config[:validation_key] = "validation_key.pem"
2111
- allow(File).to receive(:exist?).with(/.*validation_key.pem/).and_return true
2112
- end
2113
- # TODO - don't we still send validation key?
2114
- it "will not error because we don not send client key over the wire" do
2115
- expect(knife.validate_winrm_transport_opts!).to eq true
2116
- end
2117
- end
2118
- end
2119
- end
2120
-
2121
- context "with other auth" do
2122
- before do
2123
- knife.config[:winrm_auth_method] = "kerberos"
2124
- end
2125
-
2126
- context "and no validation key exists" do
2127
- before do
2128
-
2129
- Chef::Config[:validation_key] = "validation_key.pem"
2130
- allow(File).to receive(:exist?).with(/.*validation_key.pem/).and_return false
2131
- end
2132
-
2133
- it "will not error because we're not using plaintext auth" do
2134
- expect(knife.validate_winrm_transport_opts!).to eq true
2135
- end
2136
- end
2137
- context "and a validation key exists" do
2138
- before do
2139
- Chef::Config[:validation_key] = "validation_key.pem"
2140
- allow(File).to receive(:exist?).with(/.*validation_key.pem/).and_return true
2141
- end
2142
-
2143
- it "will not error because a client key won't be sent over the wire in plaintext when a validation key is present" do
2144
- expect(knife.validate_winrm_transport_opts!).to eq true
2145
- end
2146
- end
2147
-
2148
- end
2149
-
2150
- end
2151
-
2152
- end
2153
-
2154
- describe "#winrm_warn_no_ssl_verification" do
2155
- before do
2156
- allow(knife).to receive(:connection_protocol).and_return connection_protocol
2157
- end
2158
-
2159
- context "when using ssh" do
2160
- let(:connection_protocol) { "ssh" }
2161
- it "does not issue a warning" do
2162
- expect(knife.ui).to_not receive(:warn)
2163
- knife.winrm_warn_no_ssl_verification
2164
- end
2165
- end
2166
- context "when using winrm" do
2167
- let(:connection_protocol) { "winrm" }
2168
- context "winrm_no_verify_cert is set" do
2169
- before do
2170
- knife.config[:winrm_no_verify_cert] = true
2171
- end
2172
-
2173
- context "and ca_trust_file is present" do
2174
- before do
2175
- knife.config[:ca_trust_file] = "file"
2176
- end
2177
-
2178
- it "does not issue a warning" do
2179
- expect(knife.ui).to_not receive(:warn)
2180
- knife.winrm_warn_no_ssl_verification
2181
- end
2182
- end
2183
-
2184
- context "and winrm_ssl_peer_fingerprint is present" do
2185
- before do
2186
- knife.config[:winrm_ssl_peer_fingerprint] = "ABCD"
2187
- end
2188
- it "does not issue a warning" do
2189
- expect(knife.ui).to_not receive(:warn)
2190
- knife.winrm_warn_no_ssl_verification
2191
- end
2192
- end
2193
- context "and neither ca_trust_file nor winrm_ssl_peer_fingerprint is present" do
2194
- it "issues a warning" do
2195
- expect(knife.ui).to receive(:warn)
2196
- knife.winrm_warn_no_ssl_verification
2197
- end
2198
- end
2199
- end
2200
- end
2201
- end
2202
-
2203
- describe "#warn_on_short_session_timeout" do
2204
- let(:session_timeout) { 60 }
2205
-
2206
- before do
2207
- allow(knife).to receive(:session_timeout).and_return(session_timeout)
2208
- end
2209
-
2210
- context "timeout is not set at all" do
2211
- let(:session_timeout) { nil }
2212
- it "does not issue a warning" do
2213
- expect(knife.ui).to_not receive(:warn)
2214
- knife.warn_on_short_session_timeout
2215
- end
2216
- end
2217
-
2218
- context "timeout is more than 15" do
2219
- let(:session_timeout) { 16 }
2220
- it "does not issue a warning" do
2221
- expect(knife.ui).to_not receive(:warn)
2222
- knife.warn_on_short_session_timeout
2223
- end
2224
- end
2225
- context "timeout is 15 or less" do
2226
- let(:session_timeout) { 15 }
2227
- it "issues a warning" do
2228
- expect(knife.ui).to receive(:warn)
2229
- knife.warn_on_short_session_timeout
2230
- end
2231
- end
2232
- end
2233
- 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_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_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_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_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
+ before do
1407
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
1408
+ end
1409
+
1410
+ context "for winrm" do
1411
+ let(:connection_protocol) { "winrm" }
1412
+ it "returns an empty hash" do
1413
+ expect(knife.sudo_opts).to eq({})
1414
+ end
1415
+ end
1416
+
1417
+ context "for ssh" do
1418
+ let(:connection_protocol) { "ssh" }
1419
+ context "when use_sudo is set" do
1420
+ before do
1421
+ knife.config[:use_sudo] = true
1422
+ end
1423
+
1424
+ it "returns a config that enables sudo" do
1425
+ expect(knife.sudo_opts).to eq( { sudo: true } )
1426
+ end
1427
+
1428
+ context "when use_sudo_password is also set" do
1429
+ before do
1430
+ knife.config[:use_sudo_password] = true
1431
+ knife.config[:connection_password] = "opscode"
1432
+ end
1433
+ it "includes :connection_password value in a sudo-enabled configuration" do
1434
+ expect(knife.sudo_opts).to eq({
1435
+ sudo: true,
1436
+ sudo_password: "opscode",
1437
+ })
1438
+ end
1439
+ end
1440
+
1441
+ context "when preserve_home is set" do
1442
+ before do
1443
+ knife.config[:preserve_home] = true
1444
+ end
1445
+ it "enables sudo with sudo_option to preserve home" do
1446
+ expect(knife.sudo_opts).to eq({
1447
+ sudo_options: "-H",
1448
+ sudo: true,
1449
+ })
1450
+ end
1451
+ end
1452
+ end
1453
+
1454
+ context "when use_sudo is not set" do
1455
+ before do
1456
+ knife.config[:use_sudo_password] = true
1457
+ knife.config[:preserve_home] = true
1458
+ end
1459
+ it "returns configuration for sudo off, ignoring other related options" do
1460
+ expect(knife.sudo_opts).to eq( { sudo: false } )
1461
+ end
1462
+ end
1463
+ end
1464
+ end
1465
+
1466
+ context "#ssh_opts" do
1467
+ let(:connection_protocol) { nil }
1468
+ before do
1469
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
1470
+ end
1471
+
1472
+ context "for ssh" do
1473
+ let(:connection_protocol) { "ssh" }
1474
+ let(:default_opts) do
1475
+ {
1476
+ non_interactive: true,
1477
+ forward_agent: false,
1478
+ connection_timeout: 60,
1479
+ }
1480
+ end
1481
+
1482
+ context "by default" do
1483
+ it "returns a configuration hash with appropriate defaults" do
1484
+ expect(knife.ssh_opts).to eq default_opts
1485
+ end
1486
+ end
1487
+
1488
+ context "when ssh_forward_agent has a value" do
1489
+ before do
1490
+ knife.config[:ssh_forward_agent] = true
1491
+ end
1492
+ it "returns a default configuration hash with forward_agent set to true" do
1493
+ expect(knife.ssh_opts).to eq(default_opts.merge(forward_agent: true))
1494
+ end
1495
+ end
1496
+ context "when session_timeout has a value" do
1497
+ before do
1498
+ knife.config[:session_timeout] = 120
1499
+ end
1500
+ it "returns a default configuration hash with updated timeout value." do
1501
+ expect(knife.ssh_opts).to eq(default_opts.merge(connection_timeout: 120))
1502
+ end
1503
+ end
1504
+
1505
+ end
1506
+
1507
+ context "for winrm" do
1508
+ let(:connection_protocol) { "winrm" }
1509
+ it "returns an empty has because ssh is not winrm" do
1510
+ expect(knife.ssh_opts).to eq({})
1511
+ end
1512
+ end
1513
+
1514
+ end
1515
+
1516
+ context "#winrm_opts" do
1517
+ let(:connection_protocol) { nil }
1518
+ before do
1519
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
1520
+ end
1521
+
1522
+ context "for winrm" do
1523
+ let(:connection_protocol) { "winrm" }
1524
+ let(:expected) do
1525
+ {
1526
+ winrm_transport: "negotiate",
1527
+ winrm_basic_auth_only: false,
1528
+ ssl: false,
1529
+ ssl_peer_fingerprint: nil,
1530
+ operation_timeout: 60,
1531
+ }
1532
+ end
1533
+
1534
+ it "generates a correct configuration hash with expected defaults" do
1535
+ expect(knife.winrm_opts).to eq expected
1536
+ end
1537
+
1538
+ context "with ssl_peer_fingerprint" do
1539
+ let(:ssl_peer_fingerprint_expected) do
1540
+ expected.merge({ ssl_peer_fingerprint: "ABCD" })
1541
+ end
1542
+
1543
+ before do
1544
+ knife.config[:winrm_ssl_peer_fingerprint] = "ABCD"
1545
+ end
1546
+
1547
+ it "generates a correct options hash with ssl_peer_fingerprint from the config provided" do
1548
+ expect(knife.winrm_opts).to eq ssl_peer_fingerprint_expected
1549
+ end
1550
+ end
1551
+
1552
+ context "with winrm_ssl" do
1553
+ let(:ssl_expected) do
1554
+ expected.merge({ ssl: true })
1555
+ end
1556
+ before do
1557
+ knife.config[:winrm_ssl] = true
1558
+ end
1559
+
1560
+ it "generates a correct options hash with ssl from the config provided" do
1561
+ expect(knife.winrm_opts).to eq ssl_expected
1562
+ end
1563
+ end
1564
+
1565
+ context "with winrm_auth_method" do
1566
+ let(:winrm_auth_method_expected) do
1567
+ expected.merge({ winrm_transport: "freeaccess" })
1568
+ end
1569
+
1570
+ before do
1571
+ knife.config[:winrm_auth_method] = "freeaccess"
1572
+ end
1573
+
1574
+ it "generates a correct options hash with winrm_transport from the config provided" do
1575
+ expect(knife.winrm_opts).to eq winrm_auth_method_expected
1576
+ end
1577
+ end
1578
+
1579
+ context "with ca_trust_file" do
1580
+ let(:ca_trust_expected) do
1581
+ expected.merge({ ca_trust_path: "/trust.me" })
1582
+ end
1583
+ before do
1584
+ knife.config[:ca_trust_file] = "/trust.me"
1585
+ end
1586
+
1587
+ it "generates a correct options hash with ca_trust_file from the config provided" do
1588
+ expect(knife.winrm_opts).to eq ca_trust_expected
1589
+ end
1590
+ end
1591
+
1592
+ context "with kerberos auth" do
1593
+ let(:kerberos_expected) do
1594
+ expected.merge({
1595
+ kerberos_service: "testsvc",
1596
+ kerberos_realm: "TESTREALM",
1597
+ winrm_transport: "kerberos",
1598
+ })
1599
+ end
1600
+
1601
+ before do
1602
+ knife.config[:winrm_auth_method] = "kerberos"
1603
+ knife.config[:kerberos_service] = "testsvc"
1604
+ knife.config[:kerberos_realm] = "TESTREALM"
1605
+ end
1606
+
1607
+ it "generates a correct options hash containing kerberos auth configuration from the config provided" do
1608
+ expect(knife.winrm_opts).to eq kerberos_expected
1609
+ end
1610
+ end
1611
+
1612
+ context "with winrm_basic_auth_only" do
1613
+ before do
1614
+ knife.config[:winrm_basic_auth_only] = true
1615
+ end
1616
+ let(:basic_auth_expected) do
1617
+ expected.merge( { winrm_basic_auth_only: true } )
1618
+ end
1619
+ it "generates a correct options hash containing winrm_basic_auth_only from the config provided" do
1620
+ expect(knife.winrm_opts).to eq basic_auth_expected
1621
+ end
1622
+ end
1623
+ end
1624
+
1625
+ context "for ssh" do
1626
+ let(:connection_protocol) { "ssh" }
1627
+ it "returns an empty hash because ssh is not winrm" do
1628
+ expect(knife.winrm_opts).to eq({})
1629
+ end
1630
+ end
1631
+ end
1632
+ describe "#run" do
1633
+ it "performs the steps we expect to run a bootstrap" do
1634
+ expect(knife).to receive(:check_license)
1635
+ expect(knife).to receive(:validate_name_args!).ordered
1636
+ expect(knife).to receive(:validate_protocol!).ordered
1637
+ expect(knife).to receive(:validate_first_boot_attributes!).ordered
1638
+ expect(knife).to receive(:validate_winrm_transport_opts!).ordered
1639
+ expect(knife).to receive(:validate_policy_options!).ordered
1640
+ expect(knife).to receive(:winrm_warn_no_ssl_verification).ordered
1641
+ expect(knife).to receive(:warn_on_short_session_timeout).ordered
1642
+ expect(knife).to receive(:connect!).ordered
1643
+ expect(knife).to receive(:register_client).ordered
1644
+ expect(knife).to receive(:render_template).and_return "content"
1645
+ expect(knife).to receive(:upload_bootstrap).with("content").and_return "/remote/path.sh"
1646
+ expect(knife).to receive(:perform_bootstrap).with("/remote/path.sh")
1647
+ expect(connection).to receive(:del_file!) # Make sure cleanup happens
1648
+
1649
+ knife.run
1650
+
1651
+ # Post-run verify expected state changes (not many directly in #run)
1652
+ expect($stdout.sync).to eq true
1653
+ end
1654
+ end
1655
+
1656
+ describe "#register_client" do
1657
+ let(:vault_handler_mock) { double("ChefVaultHandler") }
1658
+ let(:client_builder_mock) { double("ClientBuilder") }
1659
+ let(:node_name) { nil }
1660
+ before do
1661
+ allow(knife).to receive(:chef_vault_handler).and_return vault_handler_mock
1662
+ allow(knife).to receive(:client_builder).and_return client_builder_mock
1663
+ knife.config[:chef_node_name] = node_name
1664
+ end
1665
+
1666
+ shared_examples_for "creating the client locally" do
1667
+ context "when a valid node name is present" do
1668
+ let(:node_name) { "test" }
1669
+ before do
1670
+ allow(client_builder_mock).to receive(:client).and_return "client"
1671
+ allow(client_builder_mock).to receive(:client_path).and_return "/key.pem"
1672
+ end
1673
+
1674
+ it "runs client_builder and vault_handler" do
1675
+ expect(client_builder_mock).to receive(:run)
1676
+ expect(vault_handler_mock).to receive(:run).with("client")
1677
+ knife.register_client
1678
+ end
1679
+
1680
+ it "sets the path to the client key in the bootstrap context" do
1681
+ allow(client_builder_mock).to receive(:run)
1682
+ allow(vault_handler_mock).to receive(:run).with("client")
1683
+ knife.register_client
1684
+ expect(knife.bootstrap_context.client_pem).to eq "/key.pem"
1685
+ end
1686
+ end
1687
+
1688
+ context "when no valid node name is present" do
1689
+ let(:node_name) { nil }
1690
+ it "shows an error and exits" do
1691
+ expect(knife.ui).to receive(:error)
1692
+ expect { knife.register_client }.to raise_error(SystemExit)
1693
+ end
1694
+ end
1695
+ end
1696
+ context "when chef_vault_handler says we're using vault" do
1697
+ let(:vault_handler_mock) { double("ChefVaultHandler") }
1698
+ before do
1699
+ allow(vault_handler_mock).to receive(:doing_chef_vault?).and_return true
1700
+ end
1701
+ it_behaves_like "creating the client locally"
1702
+ end
1703
+
1704
+ context "when an non-existant validation key is specified in chef config" do
1705
+ before do
1706
+ Chef::Config[:validation_key] = "/blah"
1707
+ allow(vault_handler_mock).to receive(:doing_chef_vault?).and_return false
1708
+ allow(File).to receive(:exist?).with(%r{/blah}).and_return false
1709
+ end
1710
+ it_behaves_like "creating the client locally"
1711
+ end
1712
+
1713
+ context "when a valid validation key is given and we're doing old-style client creation" do
1714
+ before do
1715
+ Chef::Config[:validation_key] = "/blah"
1716
+ allow(File).to receive(:exist?).with(%r{/blah}).and_return true
1717
+ allow(vault_handler_mock).to receive(:doing_chef_vault?).and_return false
1718
+ end
1719
+
1720
+ it "shows a warning message" do
1721
+ expect(knife.ui).to receive(:warn).twice
1722
+ knife.register_client
1723
+ end
1724
+ end
1725
+ end
1726
+
1727
+ describe "#perform_bootstrap" do
1728
+ let(:exit_status) { 0 }
1729
+ let(:stdout) { "" }
1730
+ let(:result_mock) { double("result", exit_status: exit_status, stderr: "A message", stdout: stdout) }
1731
+
1732
+ before do
1733
+ allow(connection).to receive(:hostname).and_return "testhost"
1734
+ end
1735
+ it "runs the remote script and logs the output" do
1736
+ expect(knife.ui).to receive(:info).with(/Bootstrapping.*/)
1737
+ expect(knife).to receive(:bootstrap_command)
1738
+ .with("/path.sh")
1739
+ .and_return("sh /path.sh")
1740
+ expect(connection)
1741
+ .to receive(:run_command)
1742
+ .with("sh /path.sh")
1743
+ .and_yield("output here", nil)
1744
+ .and_return result_mock
1745
+
1746
+ expect(knife.ui).to receive(:msg).with(/testhost/)
1747
+ knife.perform_bootstrap("/path.sh")
1748
+ end
1749
+
1750
+ context "when the remote command fails" do
1751
+ let(:exit_status) { 1 }
1752
+ it "shows an error and exits" do
1753
+ expect(knife.ui).to receive(:info).with(/Bootstrapping.*/)
1754
+ expect(knife).to receive(:bootstrap_command)
1755
+ .with("/path.sh")
1756
+ .and_return("sh /path.sh")
1757
+ expect(connection).to receive(:run_command).with("sh /path.sh").and_return result_mock
1758
+ expect { knife.perform_bootstrap("/path.sh") }.to raise_error(SystemExit)
1759
+ end
1760
+ end
1761
+
1762
+ context "when the remote command failed due to su auth error" do
1763
+ let(:exit_status) { 1 }
1764
+ let(:stdout) { "su: Authentication failure" }
1765
+ let(:connection_obj) { double("connection", transport_options: {}) }
1766
+ it "shows an error and exits" do
1767
+ allow(connection).to receive(:connection).and_return(connection_obj)
1768
+ expect(knife.ui).to receive(:info).with(/Bootstrapping.*/)
1769
+ expect(knife).to receive(:bootstrap_command)
1770
+ .with("/path.sh")
1771
+ .and_return("su - USER -c 'sh /path.sh'")
1772
+ expect(connection)
1773
+ .to receive(:run_command)
1774
+ .with("su - USER -c 'sh /path.sh'")
1775
+ .and_yield("output here", nil)
1776
+ .and_raise(Train::UserError)
1777
+ expect { knife.perform_bootstrap("/path.sh") }.to raise_error(Train::UserError)
1778
+ end
1779
+ end
1780
+ end
1781
+
1782
+ describe "#connect!" do
1783
+ before do
1784
+ # These are not required at run-time because train will handle its own
1785
+ # protocol loading. In this case, we're simulating train failures and have to load
1786
+ # them ourselves.
1787
+ require "net/ssh"
1788
+ require "train/transports/ssh"
1789
+ end
1790
+
1791
+ context "in the normal case" do
1792
+ it "connects using the connection_opts and notifies the operator of progress" do
1793
+ expect(knife.ui).to receive(:info).with(/Connecting to.*/)
1794
+ expect(knife).to receive(:connection_opts).and_return( { opts: "here" })
1795
+ expect(knife).to receive(:do_connect).with( { opts: "here" } )
1796
+ knife.connect!
1797
+ end
1798
+ end
1799
+
1800
+ context "when a general non-auth-failure occurs" do
1801
+ let(:expected_error) { RuntimeError.new }
1802
+ before do
1803
+ allow(knife).to receive(:do_connect).and_raise(expected_error)
1804
+ end
1805
+ it "re-raises the exception" do
1806
+ expect { knife.connect! }.to raise_error(expected_error)
1807
+ end
1808
+ end
1809
+
1810
+ context "when ssh fingerprint is invalid" do
1811
+ let(:expected_error) { Train::Error.new("fingerprint AA:BB is unknown for \"blah,127.0.0.1\"") }
1812
+ before do
1813
+ allow(knife).to receive(:do_connect).and_raise(expected_error)
1814
+ end
1815
+ it "warns, prompts to accept, then connects with verify_host_key of accept_new" do
1816
+ expect(knife).to receive(:do_connect).and_raise(expected_error)
1817
+ expect(knife.ui).to receive(:confirm)
1818
+ .with(/.*host 'blah \(127.0.0.1\)'.*AA:BB.*Are you sure you want to continue.*/m)
1819
+ .and_return(true)
1820
+ expect(knife).to receive(:do_connect) do |opts|
1821
+ expect(opts[:verify_host_key]).to eq :accept_new
1822
+ end
1823
+ knife.connect!
1824
+ end
1825
+ end
1826
+
1827
+ context "when an auth failure occurs" do
1828
+ let(:expected_error) do
1829
+ e = Train::Error.new
1830
+ actual = Net::SSH::AuthenticationFailed.new
1831
+ # Simulate train's nested error - they wrap
1832
+ # ssh/network errors in a TrainError.
1833
+ allow(e).to receive(:cause).and_return(actual)
1834
+ e
1835
+ end
1836
+
1837
+ let(:expected_error_password_prompt) do
1838
+ e = Train::ClientError.new
1839
+ reason = :no_ssh_password_or_key_available
1840
+ allow(e).to receive(:reason).and_return(reason)
1841
+ e
1842
+ end
1843
+
1844
+ let(:expected_error_password_prompt_winrm) do
1845
+ e = RuntimeError.new
1846
+ message = "password is a required option"
1847
+ allow(e).to receive(:message).and_return(message)
1848
+ e
1849
+ end
1850
+
1851
+ context "and password auth was used" do
1852
+ before do
1853
+ allow(connection).to receive(:password_auth?).and_return true
1854
+ end
1855
+
1856
+ it "re-raises the error so as not to resubmit the same failing password" do
1857
+ expect(knife).to receive(:do_connect).and_raise(expected_error)
1858
+ expect { knife.connect! }.to raise_error(expected_error)
1859
+ end
1860
+ end
1861
+
1862
+ context "and password auth was not used" do
1863
+ before do
1864
+ allow(connection).to receive(:password_auth?).and_return false
1865
+ allow(connection).to receive(:user).and_return "testuser"
1866
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
1867
+ end
1868
+
1869
+ context "when using ssh" do
1870
+ let(:connection_protocol) { "ssh" }
1871
+
1872
+ it "warns, prompts for password, then reconnects with a password-enabled configuration using the new password" do
1873
+ expect(knife).to receive(:do_connect).and_raise(expected_error_password_prompt)
1874
+ expect(knife.ui).to receive(:warn).with(/Failed to auth.*/)
1875
+ expect(knife.ui).to receive(:ask).and_return("newpassword")
1876
+ # Ensure that we set echo off to prevent showing password on the screen
1877
+ expect(knife).to receive(:do_connect) do |opts|
1878
+ expect(opts[:password]).to eq "newpassword"
1879
+ end
1880
+ knife.connect!
1881
+ end
1882
+ end
1883
+
1884
+ context "when using winrm" do
1885
+ let(:connection_protocol) { "winrm" }
1886
+
1887
+ it "warns, prompts for password, then reconnects with a password-enabled configuration using the new password for" do
1888
+ expect(knife).to receive(:do_connect).and_raise(expected_error_password_prompt_winrm)
1889
+ expect(knife.ui).to receive(:warn).with(/Failed to auth.*/)
1890
+ expect(knife.ui).to receive(:ask).and_return("newpassword")
1891
+ # Ensure that we set echo off to prevent showing password on the screen
1892
+ expect(knife).to receive(:do_connect) do |opts|
1893
+ expect(opts[:password]).to eq "newpassword"
1894
+ end
1895
+ knife.connect!
1896
+ end
1897
+ end
1898
+ end
1899
+ end
1900
+ end
1901
+
1902
+ it "verifies that a server to bootstrap was given as a command line arg" do
1903
+ knife.name_args = nil
1904
+ expect(knife).to receive(:check_license)
1905
+ expect { knife.run }.to raise_error(SystemExit)
1906
+ expect(stderr.string).to match(/ERROR:.+FQDN or ip/)
1907
+ end
1908
+
1909
+ describe "#bootstrap_context" do
1910
+ context "under Windows" do
1911
+ let(:windows_test) { true }
1912
+ it "creates a WindowsBootstrapContext" do
1913
+ require "chef/knife/core/windows_bootstrap_context"
1914
+ expect(knife.bootstrap_context.class).to eq Chef::Knife::Core::WindowsBootstrapContext
1915
+ end
1916
+ end
1917
+
1918
+ context "under linux" do
1919
+ let(:linux_test) { true }
1920
+ it "creates a BootstrapContext" do
1921
+ require "chef/knife/core/bootstrap_context"
1922
+ expect(knife.bootstrap_context.class).to eq Chef::Knife::Core::BootstrapContext
1923
+ end
1924
+ end
1925
+ end
1926
+
1927
+ describe "#config_value" do
1928
+ before do
1929
+ knife.config[:test_key_a] = "a from cli"
1930
+ knife.config[:test_key_b] = "b from cli"
1931
+ Chef::Config[:knife][:test_key_a] = "a from Chef::Config"
1932
+ Chef::Config[:knife][:test_key_c] = "c from Chef::Config"
1933
+ Chef::Config[:knife][:alt_test_key_c] = "alt c from Chef::Config"
1934
+ knife.merge_configs
1935
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
1936
+ end
1937
+
1938
+ it "returns the Chef::Config value from the cli when the CLI key is set" do
1939
+ expect(knife.config_value(:test_key_a, :alt_test_key_c)).to eq "a from cli"
1940
+ end
1941
+
1942
+ it "returns the Chef::Config value from the alternative key when the CLI key is not set" do
1943
+ expect(knife.config_value(:test_key_d, :alt_test_key_c)).to eq "alt c from Chef::Config"
1944
+ end
1945
+
1946
+ it "returns the default value when the key is not provided by CLI or Chef::Config" do
1947
+ expect(knife.config_value(:missing_key, :missing_key, "found")).to eq "found"
1948
+ end
1949
+ end
1950
+
1951
+ describe "#upload_bootstrap" do
1952
+ before do
1953
+ allow(connection).to receive(:temp_dir).and_return(temp_dir)
1954
+ allow(connection).to receive(:normalize_path) { |a| a }
1955
+ end
1956
+
1957
+ let(:content) { "bootstrap script content" }
1958
+ context "under Windows" do
1959
+ let(:windows_test) { true }
1960
+ let(:temp_dir) { "C:/Temp/bootstrap" }
1961
+ it "creates a bat file in the temp dir provided by connection, using given content" do
1962
+ expect(connection).to receive(:upload_file_content!).with(content, "C:/Temp/bootstrap/bootstrap.bat")
1963
+ expect(knife.upload_bootstrap(content)).to eq "C:/Temp/bootstrap/bootstrap.bat"
1964
+ end
1965
+ end
1966
+
1967
+ context "under Linux" do
1968
+ let(:linux_test) { true }
1969
+ let(:temp_dir) { "/tmp/bootstrap" }
1970
+ it "creates a 'sh file in the temp dir provided by connection, using given content" do
1971
+ expect(connection).to receive(:upload_file_content!).with(content, "/tmp/bootstrap/bootstrap.sh")
1972
+ expect(knife.upload_bootstrap(content)).to eq "/tmp/bootstrap/bootstrap.sh"
1973
+ end
1974
+ end
1975
+ end
1976
+
1977
+ describe "#bootstrap_command" do
1978
+ context "under Windows" do
1979
+ let(:windows_test) { true }
1980
+ it "prefixes the command to run under cmd.exe" do
1981
+ expect(knife.bootstrap_command("autoexec.bat")).to eq "cmd.exe /C autoexec.bat"
1982
+ end
1983
+
1984
+ end
1985
+ context "under Linux" do
1986
+ let(:linux_test) { true }
1987
+ it "prefixes the command to run under sh" do
1988
+ expect(knife.bootstrap_command("bootstrap.sh")).to eq "sh bootstrap.sh"
1989
+ end
1990
+
1991
+ context "with --su-user option" do
1992
+ let(:connection_obj) { double("connection", transport_options: {}) }
1993
+ before do
1994
+ knife.config[:su_user] = "root"
1995
+ allow(connection).to receive(:connection).and_return(connection_obj)
1996
+ end
1997
+ it "prefixes the command to run using su -USER -c" do
1998
+ expect(knife.bootstrap_command("bootstrap.sh")).to eq "su - #{knife.config[:su_user]} -c 'sh bootstrap.sh'"
1999
+ expect(connection_obj.transport_options.key?(:pty)).to eq true
2000
+ end
2001
+
2002
+ it "sudo appended if --sudo option enabled" do
2003
+ knife.config[:use_sudo] = true
2004
+ expect(knife.bootstrap_command("bootstrap.sh")).to eq "sudo su - #{knife.config[:su_user]} -c 'sh bootstrap.sh'"
2005
+ expect(connection_obj.transport_options.key?(:pty)).to eq true
2006
+ end
2007
+ end
2008
+ end
2009
+ end
2010
+
2011
+ describe "#default_bootstrap_template" do
2012
+ context "under Windows" do
2013
+ let(:windows_test) { true }
2014
+ it "is windows-chef-client-msi" do
2015
+ expect(knife.default_bootstrap_template).to eq "windows-chef-client-msi"
2016
+ end
2017
+
2018
+ end
2019
+ context "under Linux" do
2020
+ let(:linux_test) { true }
2021
+ it "is chef-full" do
2022
+ expect(knife.default_bootstrap_template).to eq "chef-full"
2023
+ end
2024
+ end
2025
+ end
2026
+
2027
+ describe "#do_connect" do
2028
+ let(:host_descriptor) { "example.com" }
2029
+ let(:connection) { double("TrainConnector") }
2030
+ let(:connector_mock) { double("TargetResolver", targets: [ connection ]) }
2031
+ before do
2032
+ allow(knife).to receive(:host_descriptor).and_return host_descriptor
2033
+ end
2034
+
2035
+ it "creates a TrainConnector and connects it" do
2036
+ expect(Chef::Knife::Bootstrap::TrainConnector).to receive(:new).and_return connection
2037
+ expect(connection).to receive(:connect!)
2038
+ knife.do_connect({})
2039
+ end
2040
+
2041
+ context "when sshd configured with requiretty" do
2042
+ let(:pty_err_msg) { "Sudo requires a TTY. Please see the README on how to configure sudo to allow for non-interactive usage." }
2043
+ let(:expected_error) { Train::UserError.new(pty_err_msg, :sudo_no_tty) }
2044
+ before do
2045
+ allow(connection).to receive(:connect!).and_raise(expected_error)
2046
+ end
2047
+ it "retry with pty true request option" do
2048
+ expect(Chef::Knife::Bootstrap::TrainConnector).to receive(:new).and_return(connection).exactly(2).times
2049
+ expect(knife.ui).to receive(:warn).with("#{pty_err_msg} - trying with pty request")
2050
+ expect { knife.do_connect({}) }.to raise_error(expected_error)
2051
+ end
2052
+ end
2053
+
2054
+ context "when a train sudo error is thrown for missing terminal" do
2055
+ let(:ui_error_msg) { "Sudo password is required for this operation. Please enter password using -P or --ssh-password option" }
2056
+ let(:expected_error) { Train::UserError.new(ui_error_msg, :sudo_missing_terminal) }
2057
+ before do
2058
+ allow(connection).to receive(:connect!).and_raise(expected_error)
2059
+ end
2060
+ it "outputs user friendly error message" do
2061
+ expect { knife.do_connect({}) }.not_to raise_error
2062
+ expect(stderr.string).to include(ui_error_msg)
2063
+ end
2064
+ end
2065
+
2066
+ end
2067
+
2068
+ describe "validate_winrm_transport_opts!" do
2069
+ before do
2070
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
2071
+ end
2072
+
2073
+ context "when using ssh" do
2074
+ let(:connection_protocol) { "ssh" }
2075
+ it "returns true" do
2076
+ expect(knife.validate_winrm_transport_opts!).to eq true
2077
+ end
2078
+ end
2079
+ context "when using winrm" do
2080
+ let(:connection_protocol) { "winrm" }
2081
+ context "with plaintext auth" do
2082
+ before do
2083
+ knife.config[:winrm_auth_method] = "plaintext"
2084
+ end
2085
+ context "with ssl" do
2086
+ before do
2087
+ knife.config[:winrm_ssl] = true
2088
+ end
2089
+ it "will not error because we won't send anything in plaintext regardless" do
2090
+ expect(knife.validate_winrm_transport_opts!).to eq true
2091
+ end
2092
+ end
2093
+ context "without ssl" do
2094
+ before do
2095
+ knife.config[:winrm_ssl] = false
2096
+ end
2097
+ context "and no validation key exists" do
2098
+ before do
2099
+ Chef::Config[:validation_key] = "validation_key.pem"
2100
+ allow(File).to receive(:exist?).with(/.*validation_key.pem/).and_return false
2101
+ end
2102
+
2103
+ it "will error because we will generate and send a client key over the wire in plaintext" do
2104
+ expect { knife.validate_winrm_transport_opts! }.to raise_error(SystemExit)
2105
+ end
2106
+
2107
+ end
2108
+ context "and a validation key exists" do
2109
+ before do
2110
+ Chef::Config[:validation_key] = "validation_key.pem"
2111
+ allow(File).to receive(:exist?).with(/.*validation_key.pem/).and_return true
2112
+ end
2113
+ # TODO - don't we still send validation key?
2114
+ it "will not error because we don not send client key over the wire" do
2115
+ expect(knife.validate_winrm_transport_opts!).to eq true
2116
+ end
2117
+ end
2118
+ end
2119
+ end
2120
+
2121
+ context "with other auth" do
2122
+ before do
2123
+ knife.config[:winrm_auth_method] = "kerberos"
2124
+ end
2125
+
2126
+ context "and no validation key exists" do
2127
+ before do
2128
+
2129
+ Chef::Config[:validation_key] = "validation_key.pem"
2130
+ allow(File).to receive(:exist?).with(/.*validation_key.pem/).and_return false
2131
+ end
2132
+
2133
+ it "will not error because we're not using plaintext auth" do
2134
+ expect(knife.validate_winrm_transport_opts!).to eq true
2135
+ end
2136
+ end
2137
+ context "and a validation key exists" do
2138
+ before do
2139
+ Chef::Config[:validation_key] = "validation_key.pem"
2140
+ allow(File).to receive(:exist?).with(/.*validation_key.pem/).and_return true
2141
+ end
2142
+
2143
+ it "will not error because a client key won't be sent over the wire in plaintext when a validation key is present" do
2144
+ expect(knife.validate_winrm_transport_opts!).to eq true
2145
+ end
2146
+ end
2147
+
2148
+ end
2149
+
2150
+ end
2151
+
2152
+ end
2153
+
2154
+ describe "#winrm_warn_no_ssl_verification" do
2155
+ before do
2156
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
2157
+ end
2158
+
2159
+ context "when using ssh" do
2160
+ let(:connection_protocol) { "ssh" }
2161
+ it "does not issue a warning" do
2162
+ expect(knife.ui).to_not receive(:warn)
2163
+ knife.winrm_warn_no_ssl_verification
2164
+ end
2165
+ end
2166
+ context "when using winrm" do
2167
+ let(:connection_protocol) { "winrm" }
2168
+ context "winrm_no_verify_cert is set" do
2169
+ before do
2170
+ knife.config[:winrm_no_verify_cert] = true
2171
+ end
2172
+
2173
+ context "and ca_trust_file is present" do
2174
+ before do
2175
+ knife.config[:ca_trust_file] = "file"
2176
+ end
2177
+
2178
+ it "does not issue a warning" do
2179
+ expect(knife.ui).to_not receive(:warn)
2180
+ knife.winrm_warn_no_ssl_verification
2181
+ end
2182
+ end
2183
+
2184
+ context "and winrm_ssl_peer_fingerprint is present" do
2185
+ before do
2186
+ knife.config[:winrm_ssl_peer_fingerprint] = "ABCD"
2187
+ end
2188
+ it "does not issue a warning" do
2189
+ expect(knife.ui).to_not receive(:warn)
2190
+ knife.winrm_warn_no_ssl_verification
2191
+ end
2192
+ end
2193
+ context "and neither ca_trust_file nor winrm_ssl_peer_fingerprint is present" do
2194
+ it "issues a warning" do
2195
+ expect(knife.ui).to receive(:warn)
2196
+ knife.winrm_warn_no_ssl_verification
2197
+ end
2198
+ end
2199
+ end
2200
+ end
2201
+ end
2202
+
2203
+ describe "#warn_on_short_session_timeout" do
2204
+ let(:session_timeout) { 60 }
2205
+
2206
+ before do
2207
+ allow(knife).to receive(:session_timeout).and_return(session_timeout)
2208
+ end
2209
+
2210
+ context "timeout is not set at all" do
2211
+ let(:session_timeout) { nil }
2212
+ it "does not issue a warning" do
2213
+ expect(knife.ui).to_not receive(:warn)
2214
+ knife.warn_on_short_session_timeout
2215
+ end
2216
+ end
2217
+
2218
+ context "timeout is more than 15" do
2219
+ let(:session_timeout) { 16 }
2220
+ it "does not issue a warning" do
2221
+ expect(knife.ui).to_not receive(:warn)
2222
+ knife.warn_on_short_session_timeout
2223
+ end
2224
+ end
2225
+ context "timeout is 15 or less" do
2226
+ let(:session_timeout) { 15 }
2227
+ it "issues a warning" do
2228
+ expect(knife.ui).to receive(:warn)
2229
+ knife.warn_on_short_session_timeout
2230
+ end
2231
+ end
2232
+ end
2233
+ end