knife 18.7.9 → 18.8.13

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