knife 18.2.7 → 18.3.0

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 (667) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +26 -26
  3. data/LICENSE +201 -201
  4. data/Rakefile +33 -33
  5. data/bin/knife +24 -24
  6. data/knife.gemspec +62 -62
  7. data/lib/chef/application/knife.rb +234 -234
  8. data/lib/chef/chef_fs/knife.rb +162 -162
  9. data/lib/chef/knife/acl_add.rb +57 -57
  10. data/lib/chef/knife/acl_base.rb +183 -183
  11. data/lib/chef/knife/acl_bulk_add.rb +78 -78
  12. data/lib/chef/knife/acl_bulk_remove.rb +83 -83
  13. data/lib/chef/knife/acl_remove.rb +62 -62
  14. data/lib/chef/knife/acl_show.rb +56 -56
  15. data/lib/chef/knife/bootstrap/chef_vault_handler.rb +160 -160
  16. data/lib/chef/knife/bootstrap/client_builder.rb +212 -212
  17. data/lib/chef/knife/bootstrap/templates/README.md +11 -11
  18. data/lib/chef/knife/bootstrap/templates/chef-full.erb +242 -242
  19. data/lib/chef/knife/bootstrap/templates/windows-chef-client-msi.erb +278 -278
  20. data/lib/chef/knife/bootstrap/train_connector.rb +334 -334
  21. data/lib/chef/knife/bootstrap.rb +1193 -1193
  22. data/lib/chef/knife/client_bulk_delete.rb +104 -104
  23. data/lib/chef/knife/client_create.rb +140 -140
  24. data/lib/chef/knife/client_delete.rb +62 -62
  25. data/lib/chef/knife/client_edit.rb +52 -52
  26. data/lib/chef/knife/client_key_create.rb +73 -73
  27. data/lib/chef/knife/client_key_delete.rb +80 -80
  28. data/lib/chef/knife/client_key_edit.rb +83 -83
  29. data/lib/chef/knife/client_key_list.rb +73 -73
  30. data/lib/chef/knife/client_key_show.rb +80 -80
  31. data/lib/chef/knife/client_list.rb +41 -41
  32. data/lib/chef/knife/client_reregister.rb +58 -58
  33. data/lib/chef/knife/client_show.rb +48 -48
  34. data/lib/chef/knife/config_get.rb +39 -39
  35. data/lib/chef/knife/config_get_profile.rb +37 -37
  36. data/lib/chef/knife/config_list.rb +139 -139
  37. data/lib/chef/knife/config_list_profiles.rb +37 -37
  38. data/lib/chef/knife/config_show.rb +127 -127
  39. data/lib/chef/knife/config_use.rb +61 -61
  40. data/lib/chef/knife/config_use_profile.rb +47 -47
  41. data/lib/chef/knife/configure.rb +150 -150
  42. data/lib/chef/knife/configure_client.rb +48 -48
  43. data/lib/chef/knife/cookbook_bulk_delete.rb +71 -71
  44. data/lib/chef/knife/cookbook_delete.rb +151 -151
  45. data/lib/chef/knife/cookbook_download.rb +142 -142
  46. data/lib/chef/knife/cookbook_list.rb +47 -47
  47. data/lib/chef/knife/cookbook_metadata.rb +106 -106
  48. data/lib/chef/knife/cookbook_metadata_from_file.rb +49 -49
  49. data/lib/chef/knife/cookbook_show.rb +98 -98
  50. data/lib/chef/knife/cookbook_upload.rb +313 -313
  51. data/lib/chef/knife/core/bootstrap_context.rb +264 -264
  52. data/lib/chef/knife/core/cookbook_scm_repo.rb +159 -159
  53. data/lib/chef/knife/core/cookbook_site_streaming_uploader.rb +249 -249
  54. data/lib/chef/knife/core/formatting_options.rb +49 -49
  55. data/lib/chef/knife/core/gem_glob_loader.rb +134 -134
  56. data/lib/chef/knife/core/generic_presenter.rb +238 -232
  57. data/lib/chef/knife/core/hashed_command_loader.rb +100 -100
  58. data/lib/chef/knife/core/node_editor.rb +130 -130
  59. data/lib/chef/knife/core/node_presenter.rb +133 -133
  60. data/lib/chef/knife/core/object_loader.rb +115 -115
  61. data/lib/chef/knife/core/status_presenter.rb +147 -147
  62. data/lib/chef/knife/core/subcommand_loader.rb +208 -208
  63. data/lib/chef/knife/core/text_formatter.rb +85 -85
  64. data/lib/chef/knife/core/ui.rb +338 -338
  65. data/lib/chef/knife/core/windows_bootstrap_context.rb +405 -405
  66. data/lib/chef/knife/data_bag_create.rb +81 -81
  67. data/lib/chef/knife/data_bag_delete.rb +49 -49
  68. data/lib/chef/knife/data_bag_edit.rb +74 -74
  69. data/lib/chef/knife/data_bag_from_file.rb +113 -113
  70. data/lib/chef/knife/data_bag_list.rb +42 -42
  71. data/lib/chef/knife/data_bag_secret_options.rb +122 -122
  72. data/lib/chef/knife/data_bag_show.rb +69 -69
  73. data/lib/chef/knife/delete.rb +125 -125
  74. data/lib/chef/knife/deps.rb +156 -156
  75. data/lib/chef/knife/diff.rb +83 -83
  76. data/lib/chef/knife/download.rb +85 -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/list.rb +177 -177
  101. data/lib/chef/knife/node_bulk_delete.rb +75 -75
  102. data/lib/chef/knife/node_create.rb +47 -47
  103. data/lib/chef/knife/node_delete.rb +46 -46
  104. data/lib/chef/knife/node_edit.rb +70 -70
  105. data/lib/chef/knife/node_environment_set.rb +53 -53
  106. data/lib/chef/knife/node_from_file.rb +51 -51
  107. data/lib/chef/knife/node_list.rb +44 -44
  108. data/lib/chef/knife/node_policy_set.rb +79 -79
  109. data/lib/chef/knife/node_run_list_add.rb +104 -104
  110. data/lib/chef/knife/node_run_list_remove.rb +67 -67
  111. data/lib/chef/knife/node_run_list_set.rb +66 -66
  112. data/lib/chef/knife/node_show.rb +63 -63
  113. data/lib/chef/knife/null.rb +12 -12
  114. data/lib/chef/knife/org_create.rb +70 -70
  115. data/lib/chef/knife/org_delete.rb +32 -32
  116. data/lib/chef/knife/org_edit.rb +48 -48
  117. data/lib/chef/knife/org_list.rb +44 -44
  118. data/lib/chef/knife/org_show.rb +31 -31
  119. data/lib/chef/knife/org_user_add.rb +62 -62
  120. data/lib/chef/knife/org_user_remove.rb +103 -103
  121. data/lib/chef/knife/raw.rb +123 -123
  122. data/lib/chef/knife/recipe_list.rb +32 -32
  123. data/lib/chef/knife/rehash.rb +50 -50
  124. data/lib/chef/knife/role_bulk_delete.rb +66 -66
  125. data/lib/chef/knife/role_create.rb +53 -53
  126. data/lib/chef/knife/role_delete.rb +46 -46
  127. data/lib/chef/knife/role_edit.rb +45 -45
  128. data/lib/chef/knife/role_env_run_list_add.rb +87 -87
  129. data/lib/chef/knife/role_env_run_list_clear.rb +55 -55
  130. data/lib/chef/knife/role_env_run_list_remove.rb +57 -57
  131. data/lib/chef/knife/role_env_run_list_replace.rb +60 -60
  132. data/lib/chef/knife/role_env_run_list_set.rb +70 -70
  133. data/lib/chef/knife/role_from_file.rb +51 -51
  134. data/lib/chef/knife/role_list.rb +42 -42
  135. data/lib/chef/knife/role_run_list_add.rb +87 -87
  136. data/lib/chef/knife/role_run_list_clear.rb +55 -55
  137. data/lib/chef/knife/role_run_list_remove.rb +56 -56
  138. data/lib/chef/knife/role_run_list_replace.rb +60 -60
  139. data/lib/chef/knife/role_run_list_set.rb +69 -69
  140. data/lib/chef/knife/role_show.rb +48 -48
  141. data/lib/chef/knife/search.rb +194 -194
  142. data/lib/chef/knife/serve.rb +65 -65
  143. data/lib/chef/knife/show.rb +72 -72
  144. data/lib/chef/knife/ssh.rb +657 -657
  145. data/lib/chef/knife/ssl_check.rb +284 -284
  146. data/lib/chef/knife/ssl_fetch.rb +162 -162
  147. data/lib/chef/knife/status.rb +95 -95
  148. data/lib/chef/knife/supermarket_download.rb +119 -119
  149. data/lib/chef/knife/supermarket_install.rb +192 -192
  150. data/lib/chef/knife/supermarket_list.rb +76 -76
  151. data/lib/chef/knife/supermarket_search.rb +53 -53
  152. data/lib/chef/knife/supermarket_share.rb +166 -166
  153. data/lib/chef/knife/supermarket_show.rb +66 -66
  154. data/lib/chef/knife/supermarket_unshare.rb +62 -62
  155. data/lib/chef/knife/tag_create.rb +52 -52
  156. data/lib/chef/knife/tag_delete.rb +60 -60
  157. data/lib/chef/knife/tag_list.rb +47 -47
  158. data/lib/chef/knife/upload.rb +87 -87
  159. data/lib/chef/knife/user_create.rb +180 -180
  160. data/lib/chef/knife/user_delete.rb +151 -151
  161. data/lib/chef/knife/user_dissociate.rb +42 -42
  162. data/lib/chef/knife/user_edit.rb +94 -94
  163. data/lib/chef/knife/user_invite_add.rb +43 -43
  164. data/lib/chef/knife/user_invite_list.rb +34 -34
  165. data/lib/chef/knife/user_invite_rescind.rb +63 -63
  166. data/lib/chef/knife/user_key_create.rb +73 -73
  167. data/lib/chef/knife/user_key_delete.rb +80 -80
  168. data/lib/chef/knife/user_key_edit.rb +83 -83
  169. data/lib/chef/knife/user_key_list.rb +73 -73
  170. data/lib/chef/knife/user_key_show.rb +80 -80
  171. data/lib/chef/knife/user_list.rb +43 -43
  172. data/lib/chef/knife/user_password.rb +70 -70
  173. data/lib/chef/knife/user_reregister.rb +59 -59
  174. data/lib/chef/knife/user_show.rb +52 -52
  175. data/lib/chef/knife/version.rb +24 -24
  176. data/lib/chef/knife/xargs.rb +282 -282
  177. data/lib/chef/knife/yaml_convert.rb +91 -91
  178. data/lib/chef/knife.rb +673 -673
  179. data/spec/data/apt/chef-integration-test-1.0/debian/changelog +5 -5
  180. data/spec/data/apt/chef-integration-test-1.0/debian/compat +1 -1
  181. data/spec/data/apt/chef-integration-test-1.0/debian/control +13 -13
  182. data/spec/data/apt/chef-integration-test-1.0/debian/copyright +34 -34
  183. data/spec/data/apt/chef-integration-test-1.0/debian/files +1 -1
  184. data/spec/data/apt/chef-integration-test-1.0/debian/rules +13 -13
  185. data/spec/data/apt/chef-integration-test-1.0/debian/source/format +1 -1
  186. data/spec/data/apt/chef-integration-test-1.1/debian/changelog +11 -11
  187. data/spec/data/apt/chef-integration-test-1.1/debian/compat +1 -1
  188. data/spec/data/apt/chef-integration-test-1.1/debian/control +13 -13
  189. data/spec/data/apt/chef-integration-test-1.1/debian/copyright +34 -34
  190. data/spec/data/apt/chef-integration-test-1.1/debian/files +1 -1
  191. data/spec/data/apt/chef-integration-test-1.1/debian/rules +13 -13
  192. data/spec/data/apt/chef-integration-test-1.1/debian/source/format +1 -1
  193. data/spec/data/apt/chef-integration-test2-1.0/debian/changelog +5 -5
  194. data/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2/DEBIAN/conffiles +1 -1
  195. data/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2/DEBIAN/control +10 -10
  196. data/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2/DEBIAN/md5sums +1 -1
  197. data/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2.debhelper.log +45 -45
  198. data/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2.substvars +1 -1
  199. data/spec/data/apt/chef-integration-test2-1.0/debian/compat +1 -1
  200. data/spec/data/apt/chef-integration-test2-1.0/debian/conffiles +1 -1
  201. data/spec/data/apt/chef-integration-test2-1.0/debian/control +13 -13
  202. data/spec/data/apt/chef-integration-test2-1.0/debian/copyright +34 -34
  203. data/spec/data/apt/chef-integration-test2-1.0/debian/files +1 -1
  204. data/spec/data/apt/chef-integration-test2-1.0/debian/rules +13 -13
  205. data/spec/data/apt/chef-integration-test2-1.0/debian/source/format +1 -1
  206. data/spec/data/apt/chef-integration-test2_1.0-1.dsc +18 -18
  207. data/spec/data/apt/chef-integration-test2_1.0-1_amd64.build +91 -91
  208. data/spec/data/apt/chef-integration-test2_1.0-1_amd64.changes +31 -31
  209. data/spec/data/apt/chef-integration-test_1.0-1_amd64.changes +22 -22
  210. data/spec/data/apt/chef-integration-test_1.1-1_amd64.changes +22 -22
  211. data/spec/data/apt/var/www/apt/conf/distributions +7 -7
  212. data/spec/data/apt/var/www/apt/conf/incoming +4 -4
  213. data/spec/data/apt/var/www/apt/conf/pulls +3 -3
  214. data/spec/data/apt/var/www/apt/db/version +4 -4
  215. data/spec/data/apt/var/www/apt/dists/sid/Release +19 -19
  216. data/spec/data/apt/var/www/apt/dists/sid/main/binary-amd64/Packages +16 -16
  217. data/spec/data/apt/var/www/apt/dists/sid/main/binary-amd64/Release +5 -5
  218. data/spec/data/bootstrap/encrypted_data_bag_secret +1 -1
  219. data/spec/data/bootstrap/no_proxy.erb +2 -2
  220. data/spec/data/bootstrap/secret.erb +9 -9
  221. data/spec/data/bootstrap/test-hints.erb +12 -12
  222. data/spec/data/bootstrap/test.erb +1 -1
  223. data/spec/data/cb_version_cookbooks/tatft/README.rdoc +2 -2
  224. data/spec/data/cb_version_cookbooks/tatft/attributes/default.rb +1 -1
  225. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-600hhz-0 +1 -1
  226. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-ahd2gq-0 +1 -1
  227. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-api8ux-0 +1 -1
  228. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-b0r1m1-0 +1 -1
  229. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-bfygsi-0 +1 -1
  230. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-el14l6-0 +1 -1
  231. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-ivrl3y-0 +1 -1
  232. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-kkbs85-0 +1 -1
  233. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-ory1ux-0 +1 -1
  234. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-pgsq76-0 +1 -1
  235. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-ra8uim-0 +1 -1
  236. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-t7k1g-0 +1 -1
  237. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-t8g0sv-0 +1 -1
  238. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-ufy6g3-0 +1 -1
  239. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-x2d6j9-0 +1 -1
  240. data/spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-xi0l6h-0 +1 -1
  241. data/spec/data/client.d_00/00-foo.rb +2 -2
  242. data/spec/data/client.d_00/01-bar.rb +1 -1
  243. data/spec/data/client.d_00/02-strings.rb +2 -2
  244. data/spec/data/client.d_00/bar +1 -1
  245. data/spec/data/client.d_01/foo/bar.rb +1 -1
  246. data/spec/data/client.d_02/foo.rb/foo.txt +1 -1
  247. data/spec/data/config.rb +6 -6
  248. data/spec/data/cookbooks/angrybash/metadata.rb +2 -2
  249. data/spec/data/cookbooks/angrybash/recipes/default.rb +8 -8
  250. data/spec/data/cookbooks/apache2/files/default/apache2_module_conf_generate.pl +2 -2
  251. data/spec/data/cookbooks/apache2/metadata.json +33 -33
  252. data/spec/data/cookbooks/apache2/metadata.rb +2 -2
  253. data/spec/data/cookbooks/apache2/recipes/default.rb +2 -2
  254. data/spec/data/cookbooks/borken/metadata.rb +2 -2
  255. data/spec/data/cookbooks/borken/recipes/default.rb +1 -1
  256. data/spec/data/cookbooks/borken/templates/default/borken.erb +1 -1
  257. data/spec/data/cookbooks/chefignore +8 -8
  258. data/spec/data/cookbooks/ignorken/files/default/not_me.rb +2 -2
  259. data/spec/data/cookbooks/ignorken/metadata.rb +2 -2
  260. data/spec/data/cookbooks/ignorken/recipes/ignoreme.rb +1 -1
  261. data/spec/data/cookbooks/ignorken/templates/ubuntu-12.10/not_me.rb +2 -2
  262. data/spec/data/cookbooks/irssi/files/default/irssi.response +2 -2
  263. data/spec/data/cookbooks/java/files/default/java.response +1 -1
  264. data/spec/data/cookbooks/java/metadata.json +33 -33
  265. data/spec/data/cookbooks/java/metadata.rb +2 -2
  266. data/spec/data/cookbooks/name-mismatch-versionnumber/README.md +4 -4
  267. data/spec/data/cookbooks/name-mismatch-versionnumber/metadata.rb +8 -8
  268. data/spec/data/cookbooks/name-mismatch-versionnumber/recipes/default.rb +8 -8
  269. data/spec/data/cookbooks/openldap/attributes/default.rb +16 -16
  270. data/spec/data/cookbooks/openldap/attributes/smokey.rb +1 -1
  271. data/spec/data/cookbooks/openldap/definitions/client.rb +5 -5
  272. data/spec/data/cookbooks/openldap/definitions/server.rb +5 -5
  273. data/spec/data/cookbooks/openldap/files/default/.dotfile +1 -1
  274. data/spec/data/cookbooks/openldap/files/default/remotedir/.a_dotdir/.a_dotfile_in_a_dotdir +1 -1
  275. data/spec/data/cookbooks/openldap/files/default/remotedir/not_a_template.erb +2 -2
  276. data/spec/data/cookbooks/openldap/files/default/remotedir/remote_dir_file1.txt +2 -2
  277. data/spec/data/cookbooks/openldap/files/default/remotedir/remote_dir_file2.txt +2 -2
  278. data/spec/data/cookbooks/openldap/files/default/remotedir/remotesubdir/.a_dotfile +1 -1
  279. data/spec/data/cookbooks/openldap/files/default/remotedir/remotesubdir/remote_subdir_file1.txt +2 -2
  280. data/spec/data/cookbooks/openldap/files/default/remotedir/remotesubdir/remote_subdir_file2.txt +2 -2
  281. data/spec/data/cookbooks/openldap/files/default/remotedir/subdir_with_no_file_just_a_subsubdir/the_subsubdir/some_file.txt +3 -3
  282. data/spec/data/cookbooks/openldap/libraries/openldap/version.rb +3 -3
  283. data/spec/data/cookbooks/openldap/libraries/openldap.rb +4 -4
  284. data/spec/data/cookbooks/openldap/metadata.rb +8 -8
  285. data/spec/data/cookbooks/openldap/recipes/default.rb +4 -4
  286. data/spec/data/cookbooks/openldap/recipes/gigantor.rb +3 -3
  287. data/spec/data/cookbooks/openldap/recipes/one.rb +15 -15
  288. data/spec/data/cookbooks/openldap/recipes/return.rb +2 -2
  289. data/spec/data/cookbooks/openldap/templates/default/helper_test.erb +1 -1
  290. data/spec/data/cookbooks/openldap/templates/default/helpers.erb +14 -14
  291. data/spec/data/cookbooks/openldap/templates/default/helpers_via_partial_test.erb +1 -1
  292. data/spec/data/cookbooks/openldap/templates/default/nested_openldap_partials.erb +1 -1
  293. data/spec/data/cookbooks/openldap/templates/default/nested_partial.erb +1 -1
  294. data/spec/data/cookbooks/openldap/templates/default/no_windows_line_endings.erb +4 -4
  295. data/spec/data/cookbooks/openldap/templates/default/openldap_nested_variable_stuff.erb +1 -1
  296. data/spec/data/cookbooks/openldap/templates/default/openldap_variable_stuff.conf.erb +1 -1
  297. data/spec/data/cookbooks/openldap/templates/default/test.erb +1 -1
  298. data/spec/data/cookbooks/preseed/files/default/preseed-file.seed +1 -1
  299. data/spec/data/cookbooks/preseed/files/default/preseed-template.seed +4 -4
  300. data/spec/data/cookbooks/preseed/metadata.rb +2 -2
  301. data/spec/data/cookbooks/preseed/templates/default/preseed-template-variables.seed +1 -1
  302. data/spec/data/cookbooks/preseed/templates/default/preseed-template.seed +1 -1
  303. data/spec/data/cookbooks/starter/chefignore +8 -8
  304. data/spec/data/cookbooks/starter/metadata.rb +2 -2
  305. data/spec/data/cookbooks/starter/recipes/default.rb +4 -4
  306. data/spec/data/cookbooks/supports-platform-constraints/metadata.rb +5 -5
  307. data/spec/data/cookbooks/wget/files/default/wget.response +2 -2
  308. data/spec/data/definitions/test.rb +4 -4
  309. data/spec/data/environment-config.rb +4 -4
  310. data/spec/data/file-providers-method-snapshot-chef-11-4.json +127 -127
  311. data/spec/data/fileedit/hosts +4 -4
  312. data/spec/data/incomplete-metadata-chef-repo/incomplete-metadata/README.md +4 -4
  313. data/spec/data/incomplete-metadata-chef-repo/incomplete-metadata/metadata.rb +13 -13
  314. data/spec/data/incomplete-metadata-chef-repo/incomplete-metadata/recipes/default.rb +8 -8
  315. data/spec/data/invalid-metadata-chef-repo/invalid-metadata/README.md +4 -4
  316. data/spec/data/invalid-metadata-chef-repo/invalid-metadata/metadata.rb +9 -9
  317. data/spec/data/invalid-metadata-chef-repo/invalid-metadata/recipes/default.rb +8 -8
  318. data/spec/data/kitchen/chefignore +6 -6
  319. data/spec/data/kitchen/openldap/attributes/default.rb +3 -3
  320. data/spec/data/kitchen/openldap/attributes/robinson.rb +2 -2
  321. data/spec/data/kitchen/openldap/definitions/client.rb +3 -3
  322. data/spec/data/kitchen/openldap/definitions/drewbarrymore.rb +2 -2
  323. data/spec/data/kitchen/openldap/recipes/gigantor.rb +2 -2
  324. data/spec/data/kitchen/openldap/recipes/ignoreme.rb +2 -2
  325. data/spec/data/kitchen/openldap/recipes/woot.rb +3 -3
  326. data/spec/data/knife_subcommand/test_explicit_category.rb +6 -6
  327. data/spec/data/knife_subcommand/test_name_mapping.rb +4 -4
  328. data/spec/data/knife_subcommand/test_yourself.rb +21 -21
  329. data/spec/data/lwrp/providers/buck_passer.rb +28 -28
  330. data/spec/data/lwrp/providers/buck_passer_2.rb +26 -26
  331. data/spec/data/lwrp/providers/embedded_resource_accesses_providers_scope.rb +28 -28
  332. data/spec/data/lwrp/providers/inline_compiler.rb +24 -24
  333. data/spec/data/lwrp/providers/monkey_name_printer.rb +5 -5
  334. data/spec/data/lwrp/providers/paint_drying_watcher.rb +7 -7
  335. data/spec/data/lwrp/providers/thumb_twiddler.rb +7 -7
  336. data/spec/data/lwrp/resources/bar.rb +4 -4
  337. data/spec/data/lwrp/resources/buck_passer.rb +6 -6
  338. data/spec/data/lwrp/resources/buck_passer_2.rb +4 -4
  339. data/spec/data/lwrp/resources/embedded_resource_accesses_providers_scope.rb +4 -4
  340. data/spec/data/lwrp/resources/foo.rb +6 -6
  341. data/spec/data/lwrp/resources/inline_compiler.rb +4 -4
  342. data/spec/data/lwrp/resources/monkey_name_printer.rb +6 -6
  343. data/spec/data/lwrp/resources/paint_drying_watcher.rb +4 -4
  344. data/spec/data/lwrp/resources/thumb_twiddler.rb +4 -4
  345. data/spec/data/lwrp/resources_with_default_attributes/nodeattr.rb +3 -3
  346. data/spec/data/lwrp_const_scoping/resources/conflict.rb +1 -1
  347. data/spec/data/lwrp_override/providers/buck_passer.rb +5 -5
  348. data/spec/data/lwrp_override/resources/foo.rb +11 -11
  349. data/spec/data/mac_users/10.9.plist.xml +560 -560
  350. data/spec/data/mac_users/10.9.shadow.xml +21 -21
  351. data/spec/data/metadata/quick_start/metadata.rb +14 -14
  352. data/spec/data/mixin/invalid_data.rb +3 -3
  353. data/spec/data/mixin/real_data.rb +2 -2
  354. data/spec/data/nested.json +2 -2
  355. data/spec/data/nodes/default.rb +15 -15
  356. data/spec/data/nodes/test.example.com.rb +17 -17
  357. data/spec/data/nodes/test.rb +15 -15
  358. data/spec/data/null_config.rb +1 -1
  359. data/spec/data/object_loader/environments/test.json +7 -7
  360. data/spec/data/object_loader/environments/test.rb +2 -2
  361. data/spec/data/object_loader/environments/test_json_class.json +8 -8
  362. data/spec/data/object_loader/nodes/test.json +7 -7
  363. data/spec/data/object_loader/nodes/test.rb +2 -2
  364. data/spec/data/object_loader/nodes/test_json_class.json +8 -8
  365. data/spec/data/object_loader/roles/test.json +7 -7
  366. data/spec/data/object_loader/roles/test.rb +2 -2
  367. data/spec/data/object_loader/roles/test_json_class.json +8 -8
  368. data/spec/data/partial_one.erb +1 -1
  369. data/spec/data/prefer_metadata_json/metadata.json +51 -51
  370. data/spec/data/prefer_metadata_json/metadata.rb +6 -6
  371. data/spec/data/recipes/test.rb +7 -7
  372. data/spec/data/root_alias_cookbooks/dup_attr/attributes/default.rb +1 -1
  373. data/spec/data/root_alias_cookbooks/dup_attr/attributes.rb +1 -1
  374. data/spec/data/root_alias_cookbooks/dup_attr/metadata.rb +2 -2
  375. data/spec/data/root_alias_cookbooks/dup_attr/recipe.rb +3 -3
  376. data/spec/data/root_alias_cookbooks/dup_recipe/attributes.rb +1 -1
  377. data/spec/data/root_alias_cookbooks/dup_recipe/metadata.rb +2 -2
  378. data/spec/data/root_alias_cookbooks/dup_recipe/recipe.rb +3 -3
  379. data/spec/data/root_alias_cookbooks/dup_recipe/recipes/default.rb +3 -3
  380. data/spec/data/root_alias_cookbooks/simple/attributes.rb +1 -1
  381. data/spec/data/root_alias_cookbooks/simple/metadata.rb +2 -2
  382. data/spec/data/root_alias_cookbooks/simple/recipe.rb +3 -3
  383. data/spec/data/rubygems.org/sexp_processor-info +49 -49
  384. data/spec/data/run_context/cookbooks/circular-dep1/attributes/default.rb +2 -2
  385. data/spec/data/run_context/cookbooks/circular-dep1/definitions/circular_dep1_res.rb +1 -1
  386. data/spec/data/run_context/cookbooks/circular-dep1/libraries/lib.rb +2 -2
  387. data/spec/data/run_context/cookbooks/circular-dep1/metadata.rb +2 -2
  388. data/spec/data/run_context/cookbooks/circular-dep1/providers/provider.rb +1 -1
  389. data/spec/data/run_context/cookbooks/circular-dep1/resources/resource.rb +2 -2
  390. data/spec/data/run_context/cookbooks/circular-dep2/attributes/default.rb +2 -2
  391. data/spec/data/run_context/cookbooks/circular-dep2/definitions/circular_dep2_res.rb +1 -1
  392. data/spec/data/run_context/cookbooks/circular-dep2/libraries/lib.rb +2 -2
  393. data/spec/data/run_context/cookbooks/circular-dep2/metadata.rb +2 -2
  394. data/spec/data/run_context/cookbooks/circular-dep2/providers/provider.rb +1 -1
  395. data/spec/data/run_context/cookbooks/circular-dep2/resources/resource.rb +2 -2
  396. data/spec/data/run_context/cookbooks/dependency1/attributes/aa_first.rb +2 -2
  397. data/spec/data/run_context/cookbooks/dependency1/attributes/default.rb +2 -2
  398. data/spec/data/run_context/cookbooks/dependency1/attributes/unparsed_file +1 -1
  399. data/spec/data/run_context/cookbooks/dependency1/attributes/zz_last.rb +2 -2
  400. data/spec/data/run_context/cookbooks/dependency1/definitions/dependency1_res.rb +1 -1
  401. data/spec/data/run_context/cookbooks/dependency1/definitions/unparsed_file +1 -1
  402. data/spec/data/run_context/cookbooks/dependency1/libraries/lib.rb +2 -2
  403. data/spec/data/run_context/cookbooks/dependency1/libraries/unparsed_file +1 -1
  404. data/spec/data/run_context/cookbooks/dependency1/providers/provider.rb +1 -1
  405. data/spec/data/run_context/cookbooks/dependency1/providers/unparsed_file +1 -1
  406. data/spec/data/run_context/cookbooks/dependency1/recipes/unparsed_file +1 -1
  407. data/spec/data/run_context/cookbooks/dependency1/resources/resource.rb +2 -2
  408. data/spec/data/run_context/cookbooks/dependency1/resources/unparsed_file +1 -1
  409. data/spec/data/run_context/cookbooks/dependency2/attributes/default.rb +2 -2
  410. data/spec/data/run_context/cookbooks/dependency2/definitions/dependency2_res.rb +1 -1
  411. data/spec/data/run_context/cookbooks/dependency2/libraries/lib.rb +2 -2
  412. data/spec/data/run_context/cookbooks/dependency2/providers/provider.rb +1 -1
  413. data/spec/data/run_context/cookbooks/dependency2/resources/resource.rb +2 -2
  414. data/spec/data/run_context/cookbooks/include/recipes/default.rb +24 -24
  415. data/spec/data/run_context/cookbooks/include/recipes/includee.rb +3 -3
  416. data/spec/data/run_context/cookbooks/no-default-attr/attributes/server.rb +2 -2
  417. data/spec/data/run_context/cookbooks/no-default-attr/definitions/no_default-attr_res.rb +1 -1
  418. data/spec/data/run_context/cookbooks/no-default-attr/providers/provider.rb +1 -1
  419. data/spec/data/run_context/cookbooks/no-default-attr/resources/resource.rb +2 -2
  420. data/spec/data/run_context/cookbooks/test/attributes/george.rb +1 -1
  421. data/spec/data/run_context/cookbooks/test/definitions/new_animals.rb +9 -9
  422. data/spec/data/run_context/cookbooks/test/definitions/new_cat.rb +5 -5
  423. data/spec/data/run_context/cookbooks/test/definitions/test_res.rb +1 -1
  424. data/spec/data/run_context/cookbooks/test/providers/provider.rb +1 -1
  425. data/spec/data/run_context/cookbooks/test/recipes/default.rb +5 -5
  426. data/spec/data/run_context/cookbooks/test/recipes/one.rb +7 -7
  427. data/spec/data/run_context/cookbooks/test/recipes/two.rb +7 -7
  428. data/spec/data/run_context/cookbooks/test/resources/resource.rb +3 -3
  429. data/spec/data/run_context/cookbooks/test-with-circular-deps/attributes/default.rb +2 -2
  430. data/spec/data/run_context/cookbooks/test-with-circular-deps/definitions/test_with-circular-deps_res.rb +1 -1
  431. data/spec/data/run_context/cookbooks/test-with-circular-deps/libraries/lib.rb +2 -2
  432. data/spec/data/run_context/cookbooks/test-with-circular-deps/metadata.rb +2 -2
  433. data/spec/data/run_context/cookbooks/test-with-circular-deps/providers/provider.rb +1 -1
  434. data/spec/data/run_context/cookbooks/test-with-circular-deps/resources/resource.rb +3 -3
  435. data/spec/data/run_context/cookbooks/test-with-deps/attributes/default.rb +2 -2
  436. data/spec/data/run_context/cookbooks/test-with-deps/definitions/test_with-deps_res.rb +1 -1
  437. data/spec/data/run_context/cookbooks/test-with-deps/libraries/lib.rb +1 -1
  438. data/spec/data/run_context/cookbooks/test-with-deps/metadata.rb +3 -3
  439. data/spec/data/run_context/cookbooks/test-with-deps/providers/provider.rb +1 -1
  440. data/spec/data/run_context/cookbooks/test-with-deps/resources/resource.rb +2 -2
  441. data/spec/data/run_context/nodes/run_context.rb +5 -5
  442. data/spec/data/sample_msu1.xml +10 -10
  443. data/spec/data/sample_msu2.xml +14 -14
  444. data/spec/data/sample_msu3.xml +16 -16
  445. data/spec/data/search_queries_to_transform.txt +98 -98
  446. data/spec/data/shef-config.rb +11 -11
  447. data/spec/data/snap_package/async_result_success.json +6 -6
  448. data/spec/data/snap_package/change_id_result.json +175 -175
  449. data/spec/data/snap_package/find_result_failure.json +10 -10
  450. data/spec/data/snap_package/find_result_success.json +70 -70
  451. data/spec/data/snap_package/get_by_name_result_failure.json +10 -10
  452. data/spec/data/snap_package/get_by_name_result_success.json +38 -38
  453. data/spec/data/snap_package/get_conf_success.json +10 -10
  454. data/spec/data/snap_package/result_failure.json +9 -9
  455. data/spec/data/ssl/5e707473.0 +18 -18
  456. data/spec/data/ssl/chef-rspec.cert +27 -27
  457. data/spec/data/ssl/chef-rspec.key +27 -27
  458. data/spec/data/ssl/key.pem +15 -15
  459. data/spec/data/ssl/private_key.pem +27 -27
  460. data/spec/data/ssl/private_key_with_whitespace.pem +32 -32
  461. data/spec/data/standalone_cookbook/chefignore +9 -9
  462. data/spec/data/standalone_cookbook/recipes/default.rb +2 -2
  463. data/spec/data/templates/failed.erb +5 -5
  464. data/spec/data/trusted_certs/example.crt +22 -22
  465. data/spec/data/trusted_certs/example_no_cn.crt +36 -36
  466. data/spec/data/trusted_certs/intermediate.pem +27 -27
  467. data/spec/data/trusted_certs/opscode.pem +57 -57
  468. data/spec/data/trusted_certs/root.pem +22 -22
  469. data/spec/data/windows_certificates/base64_test.cer +20 -20
  470. data/spec/data/windows_certificates/othertest.cer +20 -20
  471. data/spec/data/windows_certificates/test.cer +20 -20
  472. data/spec/data/windows_certificates/test.pem +20 -20
  473. data/spec/functional/configure_spec.rb +33 -33
  474. data/spec/functional/cookbook_delete_spec.rb +156 -156
  475. data/spec/functional/exec_spec.rb +55 -55
  476. data/spec/functional/rehash_spec.rb +39 -39
  477. data/spec/functional/smoke_test.rb +42 -42
  478. data/spec/functional/ssh_spec.rb +352 -352
  479. data/spec/functional/version_spec.rb +26 -26
  480. data/spec/integration/chef_fs_data_store_spec.rb +557 -557
  481. data/spec/integration/chef_repo_path_spec.rb +962 -962
  482. data/spec/integration/chef_repository_file_system_spec.rb +200 -200
  483. data/spec/integration/chefignore_spec.rb +301 -301
  484. data/spec/integration/client_bulk_delete_spec.rb +131 -131
  485. data/spec/integration/client_create_spec.rb +70 -70
  486. data/spec/integration/client_delete_spec.rb +64 -64
  487. data/spec/integration/client_key_create_spec.rb +66 -66
  488. data/spec/integration/client_key_delete_spec.rb +43 -43
  489. data/spec/integration/client_key_list_spec.rb +61 -61
  490. data/spec/integration/client_key_show_spec.rb +45 -45
  491. data/spec/integration/client_list_spec.rb +49 -49
  492. data/spec/integration/client_show_spec.rb +37 -37
  493. data/spec/integration/commands_spec.rb +55 -55
  494. data/spec/integration/common_options_spec.rb +174 -174
  495. data/spec/integration/config_list_spec.rb +220 -220
  496. data/spec/integration/config_show_spec.rb +192 -192
  497. data/spec/integration/config_use_spec.rb +198 -198
  498. data/spec/integration/cookbook_api_ipv6_spec.rb +113 -113
  499. data/spec/integration/cookbook_bulk_delete_spec.rb +65 -65
  500. data/spec/integration/cookbook_download_spec.rb +72 -72
  501. data/spec/integration/cookbook_list_spec.rb +55 -55
  502. data/spec/integration/cookbook_show_spec.rb +149 -149
  503. data/spec/integration/cookbook_upload_spec.rb +128 -128
  504. data/spec/integration/data_bag_create_spec.rb +125 -125
  505. data/spec/integration/data_bag_delete_spec.rb +59 -59
  506. data/spec/integration/data_bag_edit_spec.rb +105 -105
  507. data/spec/integration/data_bag_from_file_spec.rb +116 -116
  508. data/spec/integration/data_bag_list_spec.rb +44 -44
  509. data/spec/integration/data_bag_show_spec.rb +95 -95
  510. data/spec/integration/delete_spec.rb +1018 -1018
  511. data/spec/integration/deps_spec.rb +703 -703
  512. data/spec/integration/diff_spec.rb +605 -605
  513. data/spec/integration/download_spec.rb +1336 -1336
  514. data/spec/integration/environment_compare_spec.rb +75 -75
  515. data/spec/integration/environment_create_spec.rb +41 -41
  516. data/spec/integration/environment_delete_spec.rb +37 -37
  517. data/spec/integration/environment_from_file_spec.rb +116 -116
  518. data/spec/integration/environment_list_spec.rb +42 -42
  519. data/spec/integration/environment_show_spec.rb +77 -77
  520. data/spec/integration/list_spec.rb +1060 -1060
  521. data/spec/integration/node_bulk_delete_spec.rb +52 -52
  522. data/spec/integration/node_create_spec.rb +47 -47
  523. data/spec/integration/node_delete_spec.rb +48 -48
  524. data/spec/integration/node_environment_set_spec.rb +46 -46
  525. data/spec/integration/node_from_file_spec.rb +59 -59
  526. data/spec/integration/node_list_spec.rb +45 -45
  527. data/spec/integration/node_run_list_add_spec.rb +54 -54
  528. data/spec/integration/node_run_list_remove_spec.rb +36 -36
  529. data/spec/integration/node_run_list_set_spec.rb +41 -41
  530. data/spec/integration/node_show_spec.rb +36 -36
  531. data/spec/integration/raw_spec.rb +297 -297
  532. data/spec/integration/redirection_spec.rb +64 -64
  533. data/spec/integration/role_bulk_delete_spec.rb +52 -52
  534. data/spec/integration/role_create_spec.rb +41 -41
  535. data/spec/integration/role_delete_spec.rb +48 -48
  536. data/spec/integration/role_from_file_spec.rb +96 -96
  537. data/spec/integration/role_list_spec.rb +45 -45
  538. data/spec/integration/role_show_spec.rb +51 -51
  539. data/spec/integration/search_node_spec.rb +40 -40
  540. data/spec/integration/serve_spec.rb +92 -92
  541. data/spec/integration/show_spec.rb +197 -197
  542. data/spec/integration/upload_spec.rb +1616 -1616
  543. data/spec/knife_spec_helper.rb +241 -241
  544. data/spec/support/chef_helpers.rb +79 -79
  545. data/spec/support/key_helpers.rb +102 -102
  546. data/spec/support/platform_helpers.rb +255 -255
  547. data/spec/support/platforms/prof/gc.rb +51 -51
  548. data/spec/support/platforms/prof/win32.rb +45 -45
  549. data/spec/support/platforms/win32/spec_service.rb +57 -57
  550. data/spec/support/recipe_dsl_helper.rb +83 -83
  551. data/spec/support/shared/context/config.rb +18 -18
  552. data/spec/support/shared/functional/knife.rb +37 -37
  553. data/spec/support/shared/integration/integration_helper.rb +122 -122
  554. data/spec/support/shared/integration/knife_support.rb +192 -192
  555. data/spec/support/shared/matchers/exit_with_code.rb +32 -32
  556. data/spec/support/shared/matchers/match_environment_variable.rb +17 -17
  557. data/spec/support/shared/unit/knife_shared.rb +39 -39
  558. data/spec/support/shared/unit/mock_shellout.rb +49 -49
  559. data/spec/tiny_server.rb +190 -190
  560. data/spec/unit/application/knife_spec.rb +241 -241
  561. data/spec/unit/knife/bootstrap/chef_vault_handler_spec.rb +152 -152
  562. data/spec/unit/knife/bootstrap/client_builder_spec.rb +207 -207
  563. data/spec/unit/knife/bootstrap/train_connector_spec.rb +244 -244
  564. data/spec/unit/knife/bootstrap_spec.rb +2288 -2233
  565. data/spec/unit/knife/client_bulk_delete_spec.rb +166 -166
  566. data/spec/unit/knife/client_create_spec.rb +232 -232
  567. data/spec/unit/knife/client_delete_spec.rb +99 -99
  568. data/spec/unit/knife/client_edit_spec.rb +53 -53
  569. data/spec/unit/knife/client_list_spec.rb +34 -34
  570. data/spec/unit/knife/client_reregister_spec.rb +62 -62
  571. data/spec/unit/knife/client_show_spec.rb +52 -52
  572. data/spec/unit/knife/configure_client_spec.rb +81 -81
  573. data/spec/unit/knife/configure_spec.rb +190 -190
  574. data/spec/unit/knife/cookbook_bulk_delete_spec.rb +87 -87
  575. data/spec/unit/knife/cookbook_delete_spec.rb +239 -239
  576. data/spec/unit/knife/cookbook_download_spec.rb +255 -255
  577. data/spec/unit/knife/cookbook_list_spec.rb +88 -88
  578. data/spec/unit/knife/cookbook_metadata_from_file_spec.rb +72 -72
  579. data/spec/unit/knife/cookbook_metadata_spec.rb +182 -182
  580. data/spec/unit/knife/cookbook_show_spec.rb +253 -253
  581. data/spec/unit/knife/cookbook_upload_spec.rb +426 -426
  582. data/spec/unit/knife/core/bootstrap_context_spec.rb +287 -287
  583. data/spec/unit/knife/core/cookbook_scm_repo_spec.rb +187 -187
  584. data/spec/unit/knife/core/cookbook_site_streaming_uploader_spec.rb +198 -198
  585. data/spec/unit/knife/core/gem_glob_loader_spec.rb +242 -242
  586. data/spec/unit/knife/core/hashed_command_loader_spec.rb +112 -112
  587. data/spec/unit/knife/core/node_editor_spec.rb +211 -211
  588. data/spec/unit/knife/core/object_loader_spec.rb +81 -81
  589. data/spec/unit/knife/core/status_presenter_spec.rb +54 -54
  590. data/spec/unit/knife/core/subcommand_loader_spec.rb +64 -64
  591. data/spec/unit/knife/core/ui_spec.rb +656 -656
  592. data/spec/unit/knife/core/windows_bootstrap_context_spec.rb +238 -238
  593. data/spec/unit/knife/data_bag_create_spec.rb +175 -175
  594. data/spec/unit/knife/data_bag_edit_spec.rb +126 -126
  595. data/spec/unit/knife/data_bag_from_file_spec.rb +174 -174
  596. data/spec/unit/knife/data_bag_secret_options_spec.rb +173 -173
  597. data/spec/unit/knife/data_bag_show_spec.rb +139 -139
  598. data/spec/unit/knife/environment_compare_spec.rb +112 -112
  599. data/spec/unit/knife/environment_create_spec.rb +91 -91
  600. data/spec/unit/knife/environment_delete_spec.rb +71 -71
  601. data/spec/unit/knife/environment_edit_spec.rb +79 -79
  602. data/spec/unit/knife/environment_from_file_spec.rb +90 -90
  603. data/spec/unit/knife/environment_list_spec.rb +54 -54
  604. data/spec/unit/knife/environment_show_spec.rb +52 -52
  605. data/spec/unit/knife/key_create_spec.rb +223 -223
  606. data/spec/unit/knife/key_delete_spec.rb +133 -133
  607. data/spec/unit/knife/key_edit_spec.rb +264 -264
  608. data/spec/unit/knife/key_helper.rb +74 -74
  609. data/spec/unit/knife/key_list_spec.rb +216 -216
  610. data/spec/unit/knife/key_show_spec.rb +126 -126
  611. data/spec/unit/knife/node_bulk_delete_spec.rb +94 -94
  612. data/spec/unit/knife/node_delete_spec.rb +77 -77
  613. data/spec/unit/knife/node_edit_spec.rb +116 -116
  614. data/spec/unit/knife/node_environment_set_spec.rb +61 -61
  615. data/spec/unit/knife/node_from_file_spec.rb +59 -59
  616. data/spec/unit/knife/node_list_spec.rb +62 -62
  617. data/spec/unit/knife/node_policy_set_spec.rb +122 -122
  618. data/spec/unit/knife/node_run_list_add_spec.rb +145 -145
  619. data/spec/unit/knife/node_run_list_remove_spec.rb +106 -106
  620. data/spec/unit/knife/node_run_list_set_spec.rb +115 -115
  621. data/spec/unit/knife/node_show_spec.rb +65 -65
  622. data/spec/unit/knife/org_create_spec.rb +76 -76
  623. data/spec/unit/knife/org_delete_spec.rb +41 -41
  624. data/spec/unit/knife/org_edit_spec.rb +49 -49
  625. data/spec/unit/knife/org_list_spec.rb +58 -58
  626. data/spec/unit/knife/org_show_spec.rb +45 -45
  627. data/spec/unit/knife/org_user_add_spec.rb +39 -39
  628. data/spec/unit/knife/raw_spec.rb +43 -43
  629. data/spec/unit/knife/role_bulk_delete_spec.rb +80 -80
  630. data/spec/unit/knife/role_create_spec.rb +80 -80
  631. data/spec/unit/knife/role_delete_spec.rb +67 -67
  632. data/spec/unit/knife/role_edit_spec.rb +77 -77
  633. data/spec/unit/knife/role_env_run_list_add_spec.rb +217 -217
  634. data/spec/unit/knife/role_env_run_list_clear_spec.rb +94 -94
  635. data/spec/unit/knife/role_env_run_list_remove_spec.rb +102 -102
  636. data/spec/unit/knife/role_env_run_list_replace_spec.rb +105 -105
  637. data/spec/unit/knife/role_env_run_list_set_spec.rb +99 -99
  638. data/spec/unit/knife/role_from_file_spec.rb +69 -69
  639. data/spec/unit/knife/role_list_spec.rb +54 -54
  640. data/spec/unit/knife/role_run_list_add_spec.rb +179 -179
  641. data/spec/unit/knife/role_run_list_clear_spec.rb +84 -84
  642. data/spec/unit/knife/role_run_list_remove_spec.rb +92 -92
  643. data/spec/unit/knife/role_run_list_replace_spec.rb +98 -98
  644. data/spec/unit/knife/role_run_list_set_spec.rb +89 -89
  645. data/spec/unit/knife/role_show_spec.rb +59 -59
  646. data/spec/unit/knife/ssh_spec.rb +403 -403
  647. data/spec/unit/knife/ssl_check_spec.rb +256 -256
  648. data/spec/unit/knife/ssl_fetch_spec.rb +222 -222
  649. data/spec/unit/knife/status_spec.rb +112 -112
  650. data/spec/unit/knife/supermarket_download_spec.rb +152 -152
  651. data/spec/unit/knife/supermarket_install_spec.rb +203 -203
  652. data/spec/unit/knife/supermarket_list_spec.rb +70 -70
  653. data/spec/unit/knife/supermarket_search_spec.rb +85 -85
  654. data/spec/unit/knife/supermarket_share_spec.rb +208 -208
  655. data/spec/unit/knife/supermarket_unshare_spec.rb +78 -78
  656. data/spec/unit/knife/tag_create_spec.rb +23 -23
  657. data/spec/unit/knife/tag_delete_spec.rb +25 -25
  658. data/spec/unit/knife/tag_list_spec.rb +23 -23
  659. data/spec/unit/knife/user_create_spec.rb +282 -282
  660. data/spec/unit/knife/user_delete_spec.rb +171 -171
  661. data/spec/unit/knife/user_edit_spec.rb +54 -54
  662. data/spec/unit/knife/user_list_spec.rb +73 -73
  663. data/spec/unit/knife/user_password_spec.rb +64 -64
  664. data/spec/unit/knife/user_reregister_spec.rb +56 -56
  665. data/spec/unit/knife/user_show_spec.rb +91 -91
  666. data/spec/unit/knife_spec.rb +634 -634
  667. metadata +5 -5
@@ -1,1193 +1,1193 @@
1
- #
2
- # Author:: Adam Jacob (<adam@chef.io>)
3
- # Copyright:: Copyright (c) Chef Software Inc.
4
- # License:: Apache License, Version 2.0
5
- #
6
- # Licensed under the Apache License, Version 2.0 (the "License");
7
- # you may not use this file except in compliance with the License.
8
- # You may obtain a copy of the License at
9
- #
10
- # http://www.apache.org/licenses/LICENSE-2.0
11
- #
12
- # Unless required by applicable law or agreed to in writing, software
13
- # distributed under the License is distributed on an "AS IS" BASIS,
14
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- # See the License for the specific language governing permissions and
16
- # limitations under the License.
17
- #
18
-
19
- require_relative "../knife"
20
- require_relative "data_bag_secret_options"
21
- require "chef-utils/dist" unless defined?(ChefUtils::Dist)
22
- require "license_acceptance/cli_flags/mixlib_cli"
23
- require "chef/json_compat" unless defined?(Chef::JSONCompat) # can't be lazy loaded since it's used in options
24
-
25
- module LicenseAcceptance
26
- autoload :Acceptor, "license_acceptance/acceptor"
27
- end
28
-
29
- class Chef
30
- class Knife
31
- class Bootstrap < Knife
32
- include DataBagSecretOptions
33
- include LicenseAcceptance::CLIFlags::MixlibCLI
34
-
35
- SUPPORTED_CONNECTION_PROTOCOLS ||= %w{ssh winrm}.freeze
36
- WINRM_AUTH_PROTOCOL_LIST ||= %w{plaintext kerberos ssl negotiate}.freeze
37
-
38
- # Common connectivity options
39
- option :connection_user,
40
- short: "-U USERNAME",
41
- long: "--connection-user USERNAME",
42
- description: "Authenticate to the target host with this user account."
43
-
44
- option :connection_password,
45
- short: "-P PASSWORD",
46
- long: "--connection-password PASSWORD",
47
- description: "Authenticate to the target host with this password."
48
-
49
- option :connection_port,
50
- short: "-p PORT",
51
- long: "--connection-port PORT",
52
- description: "The port on the target node to connect to."
53
-
54
- option :connection_protocol,
55
- short: "-o PROTOCOL",
56
- long: "--connection-protocol PROTOCOL",
57
- description: "The protocol to use to connect to the target node.",
58
- in: SUPPORTED_CONNECTION_PROTOCOLS
59
-
60
- option :max_wait,
61
- short: "-W SECONDS",
62
- long: "--max-wait SECONDS",
63
- description: "The maximum time to wait for the initial connection to be established."
64
-
65
- option :session_timeout,
66
- long: "--session-timeout SECONDS",
67
- description: "The number of seconds to wait for each connection operation to be acknowledged while running bootstrap.",
68
- default: 60
69
-
70
- # WinRM Authentication
71
- option :winrm_ssl_peer_fingerprint,
72
- long: "--winrm-ssl-peer-fingerprint FINGERPRINT",
73
- description: "SSL certificate fingerprint expected from the target."
74
-
75
- option :ca_trust_file,
76
- short: "-f CA_TRUST_PATH",
77
- long: "--ca-trust-file CA_TRUST_PATH",
78
- description: "The Certificate Authority (CA) trust file used for SSL transport."
79
-
80
- option :winrm_no_verify_cert,
81
- long: "--winrm-no-verify-cert",
82
- description: "Do not verify the SSL certificate of the target node for WinRM.",
83
- boolean: true
84
-
85
- option :winrm_ssl,
86
- long: "--winrm-ssl",
87
- description: "Use SSL in the WinRM connection."
88
-
89
- option :winrm_auth_method,
90
- short: "-w AUTH-METHOD",
91
- long: "--winrm-auth-method AUTH-METHOD",
92
- description: "The WinRM authentication method to use.",
93
- in: WINRM_AUTH_PROTOCOL_LIST
94
-
95
- option :winrm_basic_auth_only,
96
- long: "--winrm-basic-auth-only",
97
- description: "For WinRM basic authentication when using the 'ssl' auth method.",
98
- boolean: true
99
-
100
- # This option was provided in knife bootstrap windows winrm,
101
- # but it is ignored in knife-windows/WinrmSession, and so remains unimplemented here.
102
- # option :kerberos_keytab_file,
103
- # :short => "-T KEYTAB_FILE",
104
- # :long => "--keytab-file KEYTAB_FILE",
105
- # :description => "The Kerberos keytab file used for authentication"
106
-
107
- option :kerberos_realm,
108
- short: "-R KERBEROS_REALM",
109
- long: "--kerberos-realm KERBEROS_REALM",
110
- description: "The Kerberos realm used for authentication."
111
-
112
- option :kerberos_service,
113
- short: "-S KERBEROS_SERVICE",
114
- long: "--kerberos-service KERBEROS_SERVICE",
115
- description: "The Kerberos service used for authentication."
116
-
117
- ## SSH Authentication
118
- option :ssh_gateway,
119
- short: "-G GATEWAY",
120
- long: "--ssh-gateway GATEWAY",
121
- description: "The SSH gateway."
122
-
123
- option :ssh_gateway_identity,
124
- long: "--ssh-gateway-identity SSH_GATEWAY_IDENTITY",
125
- description: "The SSH identity file used for gateway authentication."
126
-
127
- option :ssh_forward_agent,
128
- short: "-A",
129
- long: "--ssh-forward-agent",
130
- description: "Enable SSH agent forwarding.",
131
- boolean: true
132
-
133
- option :ssh_identity_file,
134
- short: "-i IDENTITY_FILE",
135
- long: "--ssh-identity-file IDENTITY_FILE",
136
- description: "The SSH identity file used for authentication."
137
-
138
- option :ssh_verify_host_key,
139
- long: "--ssh-verify-host-key VALUE",
140
- description: "Verify host key. Default is 'always'.",
141
- in: %w{always accept_new accept_new_or_local_tunnel never},
142
- default: "always"
143
-
144
- #
145
- # bootstrap options
146
- #
147
-
148
- # client.rb content via chef-full/bootstrap_context
149
- option :bootstrap_version,
150
- long: "--bootstrap-version VERSION",
151
- description: "The version of #{ChefUtils::Dist::Infra::PRODUCT} to install."
152
-
153
- option :channel,
154
- long: "--channel CHANNEL",
155
- description: "Install from the given channel. Default is 'stable'.",
156
- default: "stable",
157
- in: %w{stable current unstable}
158
-
159
- # client.rb content via chef-full/bootstrap_context
160
- option :bootstrap_proxy,
161
- long: "--bootstrap-proxy PROXY_URL",
162
- description: "The proxy server for the node being bootstrapped."
163
-
164
- # client.rb content via bootstrap_context
165
- option :bootstrap_proxy_user,
166
- long: "--bootstrap-proxy-user PROXY_USER",
167
- description: "The proxy authentication username for the node being bootstrapped."
168
-
169
- # client.rb content via bootstrap_context
170
- option :bootstrap_proxy_pass,
171
- long: "--bootstrap-proxy-pass PROXY_PASS",
172
- description: "The proxy authentication password for the node being bootstrapped."
173
-
174
- # client.rb content via bootstrap_context
175
- option :bootstrap_no_proxy,
176
- long: "--bootstrap-no-proxy [NO_PROXY_URL|NO_PROXY_IP]",
177
- description: "Do not proxy locations for the node being bootstrapped"
178
-
179
- # client.rb content via bootstrap_context
180
- option :bootstrap_template,
181
- short: "-t TEMPLATE",
182
- long: "--bootstrap-template TEMPLATE",
183
- description: "Bootstrap #{ChefUtils::Dist::Infra::PRODUCT} using a built-in or custom template. Set to the full path of an erb template or use one of the built-in templates."
184
-
185
- # client.rb content via bootstrap_context
186
- option :node_ssl_verify_mode,
187
- long: "--node-ssl-verify-mode [peer|none]",
188
- description: "Whether or not to verify the SSL cert for all HTTPS requests.",
189
- proc: Proc.new { |v|
190
- valid_values = %w{none peer}
191
- unless valid_values.include?(v)
192
- raise "Invalid value '#{v}' for --node-ssl-verify-mode. Valid values are: #{valid_values.join(", ")}"
193
- end
194
-
195
- v
196
- }
197
-
198
- # bootstrap_context - client.rb
199
- option :node_verify_api_cert,
200
- long: "--[no-]node-verify-api-cert",
201
- description: "Verify the SSL cert for HTTPS requests to the #{ChefUtils::Dist::Server::PRODUCT} API.",
202
- boolean: true
203
-
204
- # runtime - sudo settings (train handles sudo)
205
- option :use_sudo,
206
- long: "--sudo",
207
- description: "Execute the bootstrap via sudo.",
208
- boolean: true
209
-
210
- # runtime - sudo settings (train handles sudo)
211
- option :preserve_home,
212
- long: "--sudo-preserve-home",
213
- description: "Preserve non-root user HOME environment variable with sudo.",
214
- boolean: true
215
-
216
- # runtime - sudo settings (train handles sudo)
217
- option :use_sudo_password,
218
- long: "--use-sudo-password",
219
- description: "Execute the bootstrap via sudo with password.",
220
- boolean: false
221
-
222
- # runtime - su user
223
- option :su_user,
224
- long: "--su-user NAME",
225
- description: "The su - USER name to perform bootstrap command using a non-root user."
226
-
227
- # runtime - su user password
228
- option :su_password,
229
- long: "--su-password PASSWORD",
230
- description: "The su USER password for authentication."
231
-
232
- # runtime - client_builder
233
- option :chef_node_name,
234
- short: "-N NAME",
235
- long: "--node-name NAME",
236
- description: "The node name for your new node."
237
-
238
- # runtime - client_builder - set runlist when creating node
239
- option :run_list,
240
- short: "-r RUN_LIST",
241
- long: "--run-list RUN_LIST",
242
- description: "Comma separated list of roles/recipes to apply.",
243
- proc: lambda { |o| o.split(/[\s,]+/) },
244
- default: []
245
-
246
- # runtime - client_builder - set policy name when creating node
247
- option :policy_name,
248
- long: "--policy-name POLICY_NAME",
249
- description: "Policyfile name to use (--policy-group must also be given).",
250
- default: nil
251
-
252
- # runtime - client_builder - set policy group when creating node
253
- option :policy_group,
254
- long: "--policy-group POLICY_GROUP",
255
- description: "Policy group name to use (--policy-name must also be given).",
256
- default: nil
257
-
258
- # runtime - client_builder - node tags
259
- option :tags,
260
- long: "--tags TAGS",
261
- description: "Comma separated list of tags to apply to the node.",
262
- proc: lambda { |o| o.split(/[\s,]+/) },
263
- default: []
264
-
265
- # bootstrap template
266
- option :first_boot_attributes,
267
- short: "-j JSON_ATTRIBS",
268
- long: "--json-attributes",
269
- description: "A JSON string to be added to the first run of #{ChefUtils::Dist::Infra::CLIENT}.",
270
- proc: lambda { |o| Chef::JSONCompat.parse(o) },
271
- default: nil
272
-
273
- # bootstrap template
274
- option :first_boot_attributes_from_file,
275
- long: "--json-attribute-file FILE",
276
- description: "A JSON file to be used to the first run of #{ChefUtils::Dist::Infra::CLIENT}.",
277
- proc: lambda { |o| Chef::JSONCompat.parse(File.read(o)) },
278
- default: nil
279
-
280
- # bootstrap template
281
- # Create ohai hints in /etc/chef/ohai/hints, fname=hintname, content=value
282
- option :hints,
283
- long: "--hint HINT_NAME[=HINT_FILE]",
284
- description: "Specify an Ohai hint to be set on the bootstrap target. Use multiple --hint options to specify multiple hints.",
285
- proc: Proc.new { |hint, accumulator|
286
- accumulator ||= {}
287
- name, path = hint.split("=", 2)
288
- accumulator[name] = path ? Chef::JSONCompat.parse(::File.read(path)) : {}
289
- accumulator
290
- }
291
-
292
- # bootstrap override: url of a an installer shell script to use in place of omnitruck
293
- # Note that the bootstrap template _only_ references this out of Chef::Config, and not from
294
- # the provided options to knife bootstrap, so we set the Chef::Config option here.
295
- option :bootstrap_url,
296
- long: "--bootstrap-url URL",
297
- description: "URL to a custom installation script."
298
-
299
- option :bootstrap_product,
300
- long: "--bootstrap-product PRODUCT",
301
- description: "Product to install.",
302
- default: "chef"
303
-
304
- option :msi_url, # Windows target only
305
- short: "-m URL",
306
- long: "--msi-url URL",
307
- description: "Location of the #{ChefUtils::Dist::Infra::PRODUCT} MSI. The default templates will prefer to download from this location. The MSI will be downloaded from #{ChefUtils::Dist::Org::WEBSITE} if not provided (Windows).",
308
- default: ""
309
-
310
- # bootstrap override: Do this instead of our own setup.sh from omnitruck. Causes bootstrap_url to be ignored.
311
- option :bootstrap_install_command,
312
- long: "--bootstrap-install-command COMMANDS",
313
- description: "Custom command to install #{ChefUtils::Dist::Infra::PRODUCT}."
314
-
315
- # bootstrap template: Run this command first in the bootstrap script
316
- option :bootstrap_preinstall_command,
317
- long: "--bootstrap-preinstall-command COMMANDS",
318
- description: "Custom commands to run before installing #{ChefUtils::Dist::Infra::PRODUCT}."
319
-
320
- # bootstrap template
321
- option :bootstrap_wget_options,
322
- long: "--bootstrap-wget-options OPTIONS",
323
- description: "Add options to wget when installing #{ChefUtils::Dist::Infra::PRODUCT}."
324
-
325
- # bootstrap template
326
- option :bootstrap_curl_options,
327
- long: "--bootstrap-curl-options OPTIONS",
328
- description: "Add options to curl when install #{ChefUtils::Dist::Infra::PRODUCT}."
329
-
330
- # chef_vault_handler
331
- option :bootstrap_vault_file,
332
- long: "--bootstrap-vault-file VAULT_FILE",
333
- description: "A JSON file with a list of vault(s) and item(s) to be updated."
334
-
335
- # chef_vault_handler
336
- option :bootstrap_vault_json,
337
- long: "--bootstrap-vault-json VAULT_JSON",
338
- description: "A JSON string with the vault(s) and item(s) to be updated."
339
-
340
- # chef_vault_handler
341
- option :bootstrap_vault_item,
342
- long: "--bootstrap-vault-item VAULT_ITEM",
343
- description: 'A single vault and item to update as "vault:item".',
344
- proc: Proc.new { |i, accumulator|
345
- (vault, item) = i.split(":")
346
- accumulator ||= {}
347
- accumulator[vault] ||= []
348
- accumulator[vault].push(item)
349
- accumulator
350
- }
351
-
352
- # Deprecated options. These must be declared after
353
- # regular options because they refer to the replacement
354
- # option definitions implicitly.
355
- deprecated_option :auth_timeout,
356
- replacement: :max_wait,
357
- long: "--max-wait SECONDS"
358
-
359
- deprecated_option :forward_agent,
360
- replacement: :ssh_forward_agent,
361
- boolean: true, long: "--forward-agent"
362
-
363
- deprecated_option :host_key_verify,
364
- replacement: :ssh_verify_host_key,
365
- boolean: true, long: "--[no-]host-key-verify",
366
- value_mapper: Proc.new { |verify| verify ? "always" : "never" }
367
-
368
- deprecated_option :prerelease,
369
- replacement: :channel,
370
- long: "--prerelease",
371
- boolean: true, value_mapper: Proc.new { "current" }
372
-
373
- deprecated_option :ssh_user,
374
- replacement: :connection_user,
375
- long: "--ssh-user USERNAME"
376
-
377
- deprecated_option :ssh_password,
378
- replacement: :connection_password,
379
- long: "--ssh-password PASSWORD"
380
-
381
- deprecated_option :ssh_port,
382
- replacement: :connection_port,
383
- long: "--ssh-port PASSWORD"
384
-
385
- deprecated_option :ssl_peer_fingerprint,
386
- replacement: :winrm_ssl_peer_fingerprint,
387
- long: "--ssl-peer-fingerprint FINGERPRINT"
388
-
389
- deprecated_option :winrm_user,
390
- replacement: :connection_user,
391
- long: "--winrm-user USERNAME", short: "-x USERNAME"
392
-
393
- deprecated_option :winrm_password,
394
- replacement: :connection_password,
395
- long: "--winrm-password PASSWORD"
396
-
397
- deprecated_option :winrm_port,
398
- replacement: :connection_port,
399
- long: "--winrm-port PORT"
400
-
401
- deprecated_option :winrm_authentication_protocol,
402
- replacement: :winrm_auth_method,
403
- long: "--winrm-authentication-protocol PROTOCOL"
404
-
405
- deprecated_option :winrm_session_timeout,
406
- replacement: :session_timeout,
407
- long: "--winrm-session-timeout MINUTES"
408
-
409
- deprecated_option :winrm_ssl_verify_mode,
410
- replacement: :winrm_no_verify_cert,
411
- long: "--winrm-ssl-verify-mode MODE"
412
-
413
- deprecated_option :winrm_transport, replacement: :winrm_ssl,
414
- long: "--winrm-transport TRANSPORT",
415
- value_mapper: Proc.new { |value| value == "ssl" }
416
-
417
- attr_reader :connection
418
-
419
- deps do
420
- require "chef-config/path_helper" unless defined?(ChefConfig::PathHelper)
421
- require_relative "bootstrap/chef_vault_handler"
422
- require_relative "bootstrap/client_builder"
423
- require_relative "bootstrap/train_connector"
424
- end
425
-
426
- banner "knife bootstrap [PROTOCOL://][USER@]FQDN (options)"
427
-
428
- def client_builder
429
- @client_builder ||= Chef::Knife::Bootstrap::ClientBuilder.new(
430
- chef_config: Chef::Config,
431
- config: config,
432
- ui: ui
433
- )
434
- end
435
-
436
- def chef_vault_handler
437
- @chef_vault_handler ||= Chef::Knife::Bootstrap::ChefVaultHandler.new(
438
- config: config,
439
- ui: ui
440
- )
441
- end
442
-
443
- # Determine if we need to accept the Chef Infra license locally in order to successfully bootstrap
444
- # the remote node. Remote 'chef-client' run will fail if it is >= 15 and the license is not accepted locally.
445
- def check_license
446
- Chef::Log.debug("Checking if we need to accept Chef license to bootstrap node")
447
- version = config[:bootstrap_version] || Chef::VERSION.split(".").first
448
- acceptor = LicenseAcceptance::Acceptor.new(logger: Chef::Log, provided: Chef::Config[:chef_license])
449
- if acceptor.license_required?("chef", version)
450
- Chef::Log.debug("License acceptance required for chef version: #{version}")
451
- license_id = acceptor.id_from_mixlib("chef")
452
- acceptor.check_and_persist(license_id, version)
453
- Chef::Config[:chef_license] ||= acceptor.acceptance_value
454
- end
455
- end
456
-
457
- # The default bootstrap template to use to bootstrap a server.
458
- # This is a public API hook which knife plugins use or inherit and override.
459
- #
460
- # @return [String] Default bootstrap template
461
- def default_bootstrap_template
462
- if connection.windows?
463
- "windows-chef-client-msi"
464
- else
465
- "chef-full"
466
- end
467
- end
468
-
469
- def host_descriptor
470
- Array(@name_args).first
471
- end
472
-
473
- # The server_name is the DNS or IP we are going to connect to, it is not necessarily
474
- # the node name, the fqdn, or the hostname of the server. This is a public API hook
475
- # which knife plugins use or inherit and override.
476
- #
477
- # @return [String] The DNS or IP that bootstrap will connect to
478
- def server_name
479
- if host_descriptor
480
- @server_name ||= host_descriptor.split("@").reverse[0]
481
- end
482
- end
483
-
484
- # @return [String] The CLI specific bootstrap template or the default
485
- def bootstrap_template
486
- # Allow passing a bootstrap template or use the default
487
- config[:bootstrap_template] || default_bootstrap_template
488
- end
489
-
490
- def find_template
491
- template = bootstrap_template
492
-
493
- # Use the template directly if it's a path to an actual file
494
- if File.exist?(template)
495
- Chef::Log.trace("Using the specified bootstrap template: #{File.dirname(template)}")
496
- return template
497
- end
498
-
499
- # Otherwise search the template directories until we find the right one
500
- bootstrap_files = []
501
- bootstrap_files << File.join(__dir__, "bootstrap/templates", "#{template}.erb")
502
- bootstrap_files << File.join(Knife.chef_config_dir, "bootstrap", "#{template}.erb") if Chef::Knife.chef_config_dir
503
- ChefConfig::PathHelper.home(".chef", "bootstrap", "#{template}.erb") { |p| bootstrap_files << p }
504
- bootstrap_files << Gem.find_files(File.join("chef", "knife", "bootstrap", "#{template}.erb"))
505
- bootstrap_files.flatten!
506
-
507
- template_file = Array(bootstrap_files).find do |bootstrap_template|
508
- Chef::Log.trace("Looking for bootstrap template in #{File.dirname(bootstrap_template)}")
509
- File.exist?(bootstrap_template)
510
- end
511
-
512
- unless template_file
513
- ui.info("Can not find bootstrap definition for #{template}")
514
- raise Errno::ENOENT
515
- end
516
-
517
- Chef::Log.trace("Found bootstrap template: #{template_file}")
518
-
519
- template_file
520
- end
521
-
522
- def secret
523
- @secret ||= encryption_secret_provided_ignore_encrypt_flag? ? read_secret : nil
524
- end
525
-
526
- # Establish bootstrap context for template rendering.
527
- # Requires connection to be a live connection in order to determine
528
- # the correct platform.
529
- def bootstrap_context
530
- @bootstrap_context ||=
531
- if connection.windows?
532
- require_relative "core/windows_bootstrap_context"
533
- Knife::Core::WindowsBootstrapContext.new(config, config[:run_list], Chef::Config, secret)
534
- else
535
- require_relative "core/bootstrap_context"
536
- Knife::Core::BootstrapContext.new(config, config[:run_list], Chef::Config, secret)
537
- end
538
- end
539
-
540
- def first_boot_attributes
541
- @config[:first_boot_attributes] || @config[:first_boot_attributes_from_file] || {}
542
- end
543
-
544
- def render_template
545
- require "erubis" unless defined?(Erubis)
546
- @config[:first_boot_attributes] = first_boot_attributes
547
- template_file = find_template
548
- template = IO.read(template_file).chomp
549
- Erubis::Eruby.new(template).evaluate(bootstrap_context)
550
- end
551
-
552
- def run
553
- check_license if ChefUtils::Dist::Org::ENFORCE_LICENSE
554
-
555
- plugin_setup!
556
- validate_name_args!
557
- validate_protocol!
558
- validate_first_boot_attributes!
559
- validate_winrm_transport_opts!
560
- validate_policy_options!
561
- plugin_validate_options!
562
-
563
- winrm_warn_no_ssl_verification
564
- warn_on_short_session_timeout
565
-
566
- plugin_create_instance!
567
- $stdout.sync = true
568
- connect!
569
- register_client
570
-
571
- content = render_template
572
- bootstrap_path = upload_bootstrap(content)
573
- perform_bootstrap(bootstrap_path)
574
- plugin_finalize
575
- ensure
576
- connection.del_file!(bootstrap_path) if connection && bootstrap_path
577
- end
578
-
579
- def register_client
580
- # chef-vault integration must use the new client-side hawtness, otherwise to use the
581
- # new client-side hawtness, just delete your validation key.
582
- if chef_vault_handler.doing_chef_vault? ||
583
- (Chef::Config[:validation_key] &&
584
- !File.exist?(File.expand_path(Chef::Config[:validation_key])))
585
-
586
- unless config[:chef_node_name]
587
- ui.error("You must pass a node name with -N when bootstrapping with user credentials")
588
- exit 1
589
- end
590
- client_builder.run
591
- chef_vault_handler.run(client_builder.client)
592
-
593
- bootstrap_context.client_pem = client_builder.client_path
594
- else
595
- ui.warn "Performing legacy client registration with the validation key at #{Chef::Config[:validation_key]}..."
596
- ui.warn "Remove the key file or remove the 'validation_key' configuration option from your config.rb (knife.rb) to use more secure user credentials for client registration."
597
- end
598
- end
599
-
600
- def perform_bootstrap(remote_bootstrap_script_path)
601
- ui.info("Bootstrapping #{ui.color(server_name, :bold)}")
602
- cmd = bootstrap_command(remote_bootstrap_script_path)
603
- bootstrap_run_command(cmd)
604
- end
605
-
606
- # Actual bootstrap command to be run on the node.
607
- # Handles recursive calls if su USER failed to authenticate.
608
- def bootstrap_run_command(cmd)
609
- r = connection.run_command(cmd) do |data, channel|
610
- ui.msg("#{ui.color(" [#{connection.hostname}]", :cyan)} #{data}")
611
- channel.send_data("#{config[:su_password] || config[:connection_password]}\n") if data.match?("Password:")
612
- end
613
-
614
- if r.exit_status != 0
615
- ui.error("The following error occurred on #{server_name}:")
616
- ui.error("#{r.stdout} #{r.stderr}".strip)
617
- exit(r.exit_status)
618
- end
619
- rescue Train::UserError => e
620
- limit ||= 0
621
- if e.reason == :bad_su_user_password && limit < 3
622
- limit += 1
623
- ui.warn("Failed to authenticate su - #{config[:su_user]} to #{server_name}")
624
- config[:su_password] = ui.ask("Enter password for su - #{config[:su_user]}@#{server_name}:", echo: false)
625
- retry
626
- else
627
- raise
628
- end
629
- end
630
-
631
- def connect!
632
- ui.info("Connecting to #{ui.color(server_name, :bold)} using #{connection_protocol}")
633
- opts ||= connection_opts.dup
634
- do_connect(opts)
635
- rescue Train::Error => e
636
- # We handle these by message text only because train only loads the
637
- # transports and protocols that it needs - so the exceptions may not be defined,
638
- # and we don't want to require files internal to train.
639
- if e.message =~ /fingerprint (\S+) is unknown for "(.+)"/ # Train::Transports::SSHFailed
640
- fingerprint = $1
641
- hostname, ip = $2.split(",")
642
- # TODO: convert the SHA256 base64 value to hex with colons
643
- # 'ssh' example output:
644
- # RSA key fingerprint is e5:cb:c0:e2:21:3b:12:52:f8:ce:cb:00:24:e2:0c:92.
645
- # ECDSA key fingerprint is 5d:67:61:08:a9:d7:01:fd:5e:ae:7e:09:40:ef:c0:3c.
646
- # will exit 3 on N
647
- ui.confirm <<~EOM
648
- The authenticity of host '#{hostname} (#{ip})' can't be established.
649
- fingerprint is #{fingerprint}.
650
-
651
- Are you sure you want to continue connecting
652
- EOM
653
- # FIXME: this should save the key to known_hosts but doesn't appear to be
654
- config[:ssh_verify_host_key] = :accept_new
655
- conn_opts = connection_opts(reset: true)
656
- opts.merge! conn_opts
657
- retry
658
- elsif (ssh? && e.cause && e.cause.class == Net::SSH::AuthenticationFailed) || (ssh? && e.class == Train::ClientError && e.reason == :no_ssh_password_or_key_available)
659
- if connection.password_auth?
660
- raise
661
- else
662
- ui.warn("Failed to authenticate #{opts[:user]} to #{server_name} - trying password auth")
663
- password = ui.ask("Enter password for #{opts[:user]}@#{server_name}:", echo: false)
664
- end
665
-
666
- opts.merge! force_ssh_password_opts(password)
667
- retry
668
- else
669
- raise
670
- end
671
- rescue RuntimeError => e
672
- if winrm? && e.message == "password is a required option"
673
- if connection.password_auth?
674
- raise
675
- else
676
- ui.warn("Failed to authenticate #{opts[:user]} to #{server_name} - trying password auth")
677
- password = ui.ask("Enter password for #{opts[:user]}@#{server_name}:", echo: false)
678
- end
679
-
680
- opts.merge! force_winrm_password_opts(password)
681
- retry
682
- else
683
- raise
684
- end
685
- end
686
-
687
- def handle_ssh_error(e); end
688
-
689
- # url values override CLI flags, if you provide both
690
- # we'll use the one that you gave in the URL.
691
- def connection_protocol
692
- return @connection_protocol if @connection_protocol
693
-
694
- from_url = host_descriptor =~ %r{^(.*)://} ? $1 : nil
695
- from_knife = config[:connection_protocol]
696
- @connection_protocol = from_url || from_knife || "ssh"
697
- end
698
-
699
- def do_connect(conn_options)
700
- @connection = TrainConnector.new(host_descriptor, connection_protocol, conn_options)
701
- connection.connect!
702
- rescue Train::UserError => e
703
- limit ||= 1
704
- if !conn_options.key?(:pty) && e.reason == :sudo_no_tty
705
- ui.warn("#{e.message} - trying with pty request")
706
- conn_options[:pty] = true # ensure we can talk to systems with requiretty set true in sshd config
707
- retry
708
- elsif e.reason == :sudo_missing_terminal
709
- ui.error "Sudo password is required for this operation. Please enter password using -P or --ssh-password option"
710
- elsif config[:use_sudo_password] && (e.reason == :sudo_password_required || e.reason == :bad_sudo_password) && limit < 3
711
- ui.warn("Failed to authenticate #{conn_options[:user]} to #{server_name} - #{e.message} \n sudo: #{limit} incorrect password attempt")
712
- sudo_password = ui.ask("Enter sudo password for #{conn_options[:user]}@#{server_name}:", echo: false)
713
- limit += 1
714
- conn_options[:sudo_password] = sudo_password
715
-
716
- retry
717
- else
718
- raise
719
- end
720
- end
721
-
722
- # Fail if both first_boot_attributes and first_boot_attributes_from_file
723
- # are set.
724
- def validate_first_boot_attributes!
725
- if @config[:first_boot_attributes] && @config[:first_boot_attributes_from_file]
726
- raise Chef::Exceptions::BootstrapCommandInputError
727
- end
728
-
729
- true
730
- end
731
-
732
- # FIXME: someone needs to clean this up properly: https://github.com/chef/chef/issues/9645
733
- # This code is deliberately left without an abstraction around deprecating the config options to avoid knife plugins from
734
- # using those methods (which will need to be deprecated and break them) via inheritance (ruby does not have a true `private`
735
- # so the lack of any inheritable implementation is because of that).
736
- #
737
- def winrm_auth_method
738
- config.key?(:winrm_auth_method) ? config[:winrm_auth_method] : config.key?(:winrm_authentications_protocol) ? config[:winrm_authentication_protocol] : "negotiate" # rubocop:disable Style/NestedTernaryOperator
739
- end
740
-
741
- def ssh_verify_host_key
742
- config.key?(:ssh_verify_host_key) ? config[:ssh_verify_host_key] : config.key?(:host_key_verify) ? config[:host_key_verify] : "always" # rubocop:disable Style/NestedTernaryOperator
743
- end
744
-
745
- # Fail if using plaintext auth without ssl because
746
- # this can expose keys in plaintext on the wire.
747
- # TODO test for this method
748
- # TODO check that the protocol is valid.
749
- def validate_winrm_transport_opts!
750
- return true unless winrm?
751
-
752
- if Chef::Config[:validation_key] && !File.exist?(File.expand_path(Chef::Config[:validation_key]))
753
- if winrm_auth_method == "plaintext" &&
754
- config[:winrm_ssl] != true
755
- ui.error <<~EOM
756
- Validatorless bootstrap over unsecure winrm channels could expose your
757
- key to network sniffing.
758
- Please use a 'winrm_auth_method' other than 'plaintext',
759
- or enable ssl on #{server_name} then use the ---winrm-ssl flag
760
- to connect.
761
- EOM
762
-
763
- exit 1
764
- end
765
- end
766
- true
767
- end
768
-
769
- # fail if the server_name is nil
770
- def validate_name_args!
771
- if server_name.nil?
772
- ui.error("Must pass an FQDN or ip to bootstrap")
773
- exit 1
774
- end
775
- end
776
-
777
- # Ensure options are valid by checking policyfile values.
778
- #
779
- # The method call will cause the program to exit(1) if:
780
- # * Only one of --policy-name and --policy-group is specified
781
- # * Policyfile options are set and --run-list is set as well
782
- #
783
- # @return [TrueClass] If options are valid.
784
- def validate_policy_options!
785
- if incomplete_policyfile_options?
786
- ui.error("--policy-name and --policy-group must be specified together")
787
- exit 1
788
- elsif policyfile_and_run_list_given?
789
- ui.error("Policyfile options and --run-list are exclusive")
790
- exit 1
791
- end
792
- end
793
-
794
- # Ensure a valid protocol is provided for target host connection
795
- #
796
- # The method call will cause the program to exit(1) if:
797
- # * Conflicting protocols are given via the target URI and the --protocol option
798
- # * The protocol is not a supported protocol
799
- #
800
- # @return [TrueClass] If options are valid.
801
- def validate_protocol!
802
- from_cli = config[:connection_protocol]
803
- if from_cli && connection_protocol != from_cli
804
- # Hanging indent to align with the ERROR: prefix
805
- ui.error <<~EOM
806
- The URL '#{host_descriptor}' indicates protocol is '#{connection_protocol}'
807
- while the --protocol flag specifies '#{from_cli}'. Please include
808
- only one or the other.
809
- EOM
810
- exit 1
811
- end
812
-
813
- unless SUPPORTED_CONNECTION_PROTOCOLS.include?(connection_protocol)
814
- ui.error <<~EOM
815
- Unsupported protocol '#{connection_protocol}'.
816
-
817
- Supported protocols are: #{SUPPORTED_CONNECTION_PROTOCOLS.join(" ")}
818
- EOM
819
- exit 1
820
- end
821
- true
822
- end
823
-
824
- # Validate any additional options
825
- #
826
- # Plugins that subclass bootstrap, e.g. knife-ec2, can use this method to validate any additional options before any other actions are executed
827
- #
828
- # @return [TrueClass] If options are valid or exits
829
- def plugin_validate_options!
830
- true
831
- end
832
-
833
- # Create the server that we will bootstrap, if necessary
834
- #
835
- # Plugins that subclass bootstrap, e.g. knife-ec2, can use this method to call out to an API to build an instance of the server we wish to bootstrap
836
- #
837
- # @return [TrueClass] If instance successfully created, or exits
838
- def plugin_create_instance!
839
- true
840
- end
841
-
842
- # Perform any setup necessary by the plugin
843
- #
844
- # Plugins that subclass bootstrap, e.g. knife-ec2, can use this method to create connection objects
845
- #
846
- # @return [TrueClass] If instance successfully created, or exits
847
- def plugin_setup!; end
848
-
849
- # Perform any teardown or cleanup necessary by the plugin
850
- #
851
- # Plugins that subclass bootstrap, e.g. knife-ec2, can use this method to display a message or perform any cleanup
852
- #
853
- # @return [void]
854
- def plugin_finalize; end
855
-
856
- # If session_timeout is too short, it is likely
857
- # a holdover from "--winrm-session-timeout" which used
858
- # minutes as its unit, instead of seconds.
859
- # Warn the human so that they are not surprised.
860
- #
861
- def warn_on_short_session_timeout
862
- if session_timeout && session_timeout <= 15
863
- ui.warn <<~EOM
864
- You provided '--session-timeout #{session_timeout}' second(s).
865
- Did you mean '--session-timeout #{session_timeout * 60}' seconds?
866
- EOM
867
- end
868
- end
869
-
870
- def winrm_warn_no_ssl_verification
871
- return unless winrm?
872
-
873
- # REVIEWER NOTE
874
- # The original check from knife plugin did not include winrm_ssl_peer_fingerprint
875
- # Reference:
876
- # https://github.com/chef/knife-windows/blob/92d151298142be4a4750c5b54bb264f8d5b81b8a/lib/chef/knife/winrm_knife_base.rb#L271-L273
877
- # TODO Seems like we should also do a similar warning if ssh_verify_host == false
878
- if config[:ca_trust_file].nil? &&
879
- config[:winrm_no_verify_cert] &&
880
- config[:winrm_ssl_peer_fingerprint].nil?
881
- ui.warn <<~WARN
882
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
883
- SSL validation of HTTPS requests for the WinRM transport is disabled.
884
- HTTPS WinRM connections are still encrypted, but knife is not able
885
- to detect forged replies or spoofing attacks.
886
-
887
- To work around this issue you can use the flag `--winrm-no-verify-cert`
888
- or add an entry like this to your knife configuration file:
889
-
890
- # Verify all WinRM HTTPS connections
891
- knife[:winrm_no_verify_cert] = true
892
-
893
- You can also specify a ca_trust_file via --ca-trust-file,
894
- or the expected fingerprint of the target host's certificate
895
- via --winrm-ssl-peer-fingerprint.
896
- WARN
897
- end
898
- end
899
-
900
- # @return a configuration hash suitable for connecting to the remote
901
- # host via train
902
- def connection_opts(reset: false)
903
- return @connection_opts unless @connection_opts.nil? || reset == true
904
-
905
- @connection_opts = {}
906
- @connection_opts.merge! base_opts
907
- @connection_opts.merge! host_verify_opts
908
- @connection_opts.merge! gateway_opts
909
- @connection_opts.merge! sudo_opts
910
- @connection_opts.merge! winrm_opts
911
- @connection_opts.merge! ssh_opts
912
- @connection_opts.merge! ssh_identity_opts
913
- @connection_opts
914
- end
915
-
916
- def winrm?
917
- connection_protocol == "winrm"
918
- end
919
-
920
- def ssh?
921
- connection_protocol == "ssh"
922
- end
923
-
924
- # Common configuration for all protocols
925
- def base_opts
926
- port = config_for_protocol(:port)
927
- user = config_for_protocol(:user)
928
- {}.tap do |opts|
929
- opts[:logger] = Chef::Log
930
- opts[:password] = config[:connection_password] if config.key?(:connection_password)
931
- opts[:user] = user if user
932
- opts[:max_wait_until_ready] = config[:max_wait].to_i unless config[:max_wait].nil?
933
- # TODO - when would we need to provide rdp_port vs port? Or are they not mutually exclusive?
934
- opts[:port] = port if port
935
- end
936
- end
937
-
938
- def host_verify_opts
939
- if winrm?
940
- { self_signed: config[:winrm_no_verify_cert] === true }
941
- elsif ssh?
942
- # Fall back to the old knife config key name for back compat.
943
- { verify_host_key: ssh_verify_host_key }
944
- else
945
- {}
946
- end
947
- end
948
-
949
- def ssh_opts
950
- opts = {}
951
- return opts if winrm?
952
-
953
- opts[:non_interactive] = true # Prevent password prompts from underlying net/ssh
954
- opts[:forward_agent] = (config[:ssh_forward_agent] === true)
955
- opts[:connection_timeout] = session_timeout
956
- opts
957
- end
958
-
959
- def ssh_identity_opts
960
- opts = {}
961
- return opts if winrm?
962
-
963
- identity_file = config[:ssh_identity_file]
964
- if identity_file
965
- opts[:key_files] = [identity_file]
966
- # We only set keys_only based on the explicit ssh_identity_file;
967
- # someone may use a gateway key and still expect password auth
968
- # on the target. Similarly, someone may have a default key specified
969
- # in knife config, but have provided a password on the CLI.
970
-
971
- # REVIEW NOTE: this is a new behavior. Originally, ssh_identity_file
972
- # could only be populated from CLI options, so there was no need to check
973
- # for this. We will also set keys_only to false only if there are keys
974
- # and no password.
975
- # If both are present, train(via net/ssh) will prefer keys, falling back to password.
976
- # Reference: https://github.com/chef/chef/blob/main/lib/chef/knife/ssh.rb#L272
977
- opts[:keys_only] = config.key?(:connection_password) == false
978
- else
979
- opts[:key_files] = []
980
- opts[:keys_only] = false
981
- end
982
-
983
- gateway_identity_file = config[:ssh_gateway] ? config[:ssh_gateway_identity] : nil
984
- unless gateway_identity_file.nil?
985
- opts[:key_files] << gateway_identity_file
986
- end
987
-
988
- opts
989
- end
990
-
991
- def gateway_opts
992
- opts = {}
993
- if config[:ssh_gateway]
994
- split = config[:ssh_gateway].split("@", 2)
995
- if split.length == 1
996
- gw_host = split[0]
997
- else
998
- gw_user = split[0]
999
- gw_host = split[1]
1000
- end
1001
- gw_host, gw_port = gw_host.split(":", 2)
1002
- # TODO - validate convertible port in config validation?
1003
- gw_port = Integer(gw_port) rescue nil
1004
- opts[:bastion_host] = gw_host
1005
- opts[:bastion_user] = gw_user
1006
- opts[:bastion_port] = gw_port
1007
- end
1008
- opts
1009
- end
1010
-
1011
- # use_sudo - tells bootstrap to use the sudo command to run bootstrap
1012
- # use_sudo_password - tells bootstrap to use the sudo command to run bootstrap
1013
- # and to use the password specified with --password
1014
- # TODO: I'd like to make our sudo options sane:
1015
- # --sudo (bool) - use sudo
1016
- # --sudo-password PASSWORD (default: :password) - use this password for sudo
1017
- # --sudo-options "opt,opt,opt" to pass into sudo
1018
- # --sudo-command COMMAND sudo command other than sudo
1019
- # REVIEW NOTE: knife bootstrap did not pull sudo values from Chef::Config,
1020
- # should we change that for consistency?
1021
- def sudo_opts
1022
- return {} if winrm?
1023
-
1024
- opts = { sudo: false }
1025
- if config[:use_sudo]
1026
- opts[:sudo] = true
1027
- if config[:use_sudo_password]
1028
- opts[:sudo_password] = config[:connection_password]
1029
- end
1030
- if config[:preserve_home]
1031
- opts[:sudo_options] = "-H"
1032
- end
1033
- end
1034
- opts
1035
- end
1036
-
1037
- def winrm_opts
1038
- return {} unless winrm?
1039
-
1040
- opts = {
1041
- winrm_transport: winrm_auth_method, # winrm gem and train calls auth method 'transport'
1042
- winrm_basic_auth_only: config[:winrm_basic_auth_only] || false,
1043
- ssl: config[:winrm_ssl] === true,
1044
- ssl_peer_fingerprint: config[:winrm_ssl_peer_fingerprint],
1045
- }
1046
-
1047
- if winrm_auth_method == "kerberos"
1048
- opts[:kerberos_service] = config[:kerberos_service] if config[:kerberos_service]
1049
- opts[:kerberos_realm] = config[:kerberos_realm] if config[:kerberos_service]
1050
- end
1051
-
1052
- if config[:ca_trust_file]
1053
- opts[:ca_trust_path] = config[:ca_trust_file]
1054
- end
1055
-
1056
- opts[:operation_timeout] = session_timeout
1057
-
1058
- opts
1059
- end
1060
-
1061
- # Config overrides to force password auth.
1062
- def force_ssh_password_opts(password)
1063
- {
1064
- password: password,
1065
- non_interactive: false,
1066
- keys_only: false,
1067
- key_files: [],
1068
- auth_methods: %i{password keyboard_interactive},
1069
- }
1070
- end
1071
-
1072
- def force_winrm_password_opts(password)
1073
- {
1074
- password: password,
1075
- }
1076
- end
1077
-
1078
- # This is for deprecating config options. The fallback_key can be used
1079
- # to pull an old knife config option out of the config file when the
1080
- # cli value has been renamed. This is different from the deprecated
1081
- # cli values, since these are for config options that have no corresponding
1082
- # cli value.
1083
- #
1084
- # DO NOT USE - this whole API is considered deprecated
1085
- #
1086
- # @api deprecated
1087
- #
1088
- def config_value(key, fallback_key = nil, default = nil)
1089
- Chef.deprecated(:knife_bootstrap_apis, "Use of config_value is deprecated. Knife plugin authors should access the config hash directly, which does correct merging of cli and config options.")
1090
- if config.key?(key)
1091
- # the first key is the primary key so we check the merged hash first
1092
- config[key]
1093
- elsif config.key?(fallback_key)
1094
- # we get the old config option here (the deprecated cli option shouldn't exist)
1095
- config[fallback_key]
1096
- else
1097
- default
1098
- end
1099
- end
1100
-
1101
- def upload_bootstrap(content)
1102
- script_name = connection.windows? ? "bootstrap.bat" : "bootstrap.sh"
1103
- remote_path = connection.normalize_path(File.join(connection.temp_dir, script_name))
1104
- connection.upload_file_content!(content, remote_path)
1105
- remote_path
1106
- end
1107
-
1108
- # build the command string for bootstrapping
1109
- # @return String
1110
- def bootstrap_command(remote_path)
1111
- if connection.windows?
1112
- "cmd.exe /C #{remote_path}"
1113
- else
1114
- cmd = "sh #{remote_path}"
1115
-
1116
- if config[:su_user]
1117
- # su - USER is subject to required an interactive console
1118
- # Otherwise, it will raise: su: must be run from a terminal
1119
- set_transport_options(pty: true)
1120
- cmd = "su - #{config[:su_user]} -c '#{cmd}'"
1121
- cmd = "sudo " << cmd if config[:use_sudo]
1122
- end
1123
-
1124
- cmd
1125
- end
1126
- end
1127
-
1128
- private
1129
-
1130
- # To avoid cluttering the CLI options, some flags (such as port and user)
1131
- # are shared between protocols. However, there is still a need to allow the operator
1132
- # to specify defaults separately, since they may not be the same values for different
1133
- # protocols.
1134
-
1135
- # These keys are available in Chef::Config, and are prefixed with the protocol name.
1136
- # For example, :user CLI option will map to :winrm_user and :ssh_user Chef::Config keys,
1137
- # based on the connection protocol in use.
1138
-
1139
- # @api private
1140
- def config_for_protocol(option)
1141
- if option == :port
1142
- config[:connection_port] || config[knife_key_for_protocol(option)]
1143
- else
1144
- config[:connection_user] || config[knife_key_for_protocol(option)]
1145
- end
1146
- end
1147
-
1148
- # @api private
1149
- def knife_key_for_protocol(option)
1150
- "#{connection_protocol}_#{option}".to_sym
1151
- end
1152
-
1153
- # True if policy_name and run_list are both given
1154
- def policyfile_and_run_list_given?
1155
- run_list_given? && policyfile_options_given?
1156
- end
1157
-
1158
- def run_list_given?
1159
- !config[:run_list].nil? && !config[:run_list].empty?
1160
- end
1161
-
1162
- def policyfile_options_given?
1163
- !!config[:policy_name]
1164
- end
1165
-
1166
- # True if one of policy_name or policy_group was given, but not both
1167
- def incomplete_policyfile_options?
1168
- (!!config[:policy_name] ^ config[:policy_group])
1169
- end
1170
-
1171
- # session_timeout option has a default that may not arrive, particularly if
1172
- # we're being invoked from a plugin that doesn't merge_config.
1173
- def session_timeout
1174
- timeout = config[:session_timeout]
1175
- return options[:session_timeout][:default] if timeout.nil?
1176
-
1177
- timeout.to_i
1178
- end
1179
-
1180
- # Train::Transports::SSH::Connection#transport_options
1181
- # Append the options to connection transport_options
1182
- #
1183
- # @param opts [Hash] the opts to be added to connection transport_options.
1184
- # @return [Hash] transport_options if the opts contains any option to be set.
1185
- #
1186
- def set_transport_options(opts)
1187
- return unless opts.is_a?(Hash) || !opts.empty?
1188
-
1189
- connection&.connection&.transport_options&.merge! opts
1190
- end
1191
- end
1192
- end
1193
- end
1
+ #
2
+ # Author:: Adam Jacob (<adam@chef.io>)
3
+ # Copyright:: Copyright (c) Chef Software Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require_relative "../knife"
20
+ require_relative "data_bag_secret_options"
21
+ require "chef-utils/dist" unless defined?(ChefUtils::Dist)
22
+ require "license_acceptance/cli_flags/mixlib_cli"
23
+ require "chef/json_compat" unless defined?(Chef::JSONCompat) # can't be lazy loaded since it's used in options
24
+
25
+ module LicenseAcceptance
26
+ autoload :Acceptor, "license_acceptance/acceptor"
27
+ end
28
+
29
+ class Chef
30
+ class Knife
31
+ class Bootstrap < Knife
32
+ include DataBagSecretOptions
33
+ include LicenseAcceptance::CLIFlags::MixlibCLI
34
+
35
+ SUPPORTED_CONNECTION_PROTOCOLS ||= %w{ssh winrm}.freeze
36
+ WINRM_AUTH_PROTOCOL_LIST ||= %w{plaintext kerberos ssl negotiate}.freeze
37
+
38
+ # Common connectivity options
39
+ option :connection_user,
40
+ short: "-U USERNAME",
41
+ long: "--connection-user USERNAME",
42
+ description: "Authenticate to the target host with this user account."
43
+
44
+ option :connection_password,
45
+ short: "-P PASSWORD",
46
+ long: "--connection-password PASSWORD",
47
+ description: "Authenticate to the target host with this password."
48
+
49
+ option :connection_port,
50
+ short: "-p PORT",
51
+ long: "--connection-port PORT",
52
+ description: "The port on the target node to connect to."
53
+
54
+ option :connection_protocol,
55
+ short: "-o PROTOCOL",
56
+ long: "--connection-protocol PROTOCOL",
57
+ description: "The protocol to use to connect to the target node.",
58
+ in: SUPPORTED_CONNECTION_PROTOCOLS
59
+
60
+ option :max_wait,
61
+ short: "-W SECONDS",
62
+ long: "--max-wait SECONDS",
63
+ description: "The maximum time to wait for the initial connection to be established."
64
+
65
+ option :session_timeout,
66
+ long: "--session-timeout SECONDS",
67
+ description: "The number of seconds to wait for each connection operation to be acknowledged while running bootstrap.",
68
+ default: 60
69
+
70
+ # WinRM Authentication
71
+ option :winrm_ssl_peer_fingerprint,
72
+ long: "--winrm-ssl-peer-fingerprint FINGERPRINT",
73
+ description: "SSL certificate fingerprint expected from the target."
74
+
75
+ option :ca_trust_file,
76
+ short: "-f CA_TRUST_PATH",
77
+ long: "--ca-trust-file CA_TRUST_PATH",
78
+ description: "The Certificate Authority (CA) trust file used for SSL transport."
79
+
80
+ option :winrm_no_verify_cert,
81
+ long: "--winrm-no-verify-cert",
82
+ description: "Do not verify the SSL certificate of the target node for WinRM.",
83
+ boolean: true
84
+
85
+ option :winrm_ssl,
86
+ long: "--winrm-ssl",
87
+ description: "Use SSL in the WinRM connection."
88
+
89
+ option :winrm_auth_method,
90
+ short: "-w AUTH-METHOD",
91
+ long: "--winrm-auth-method AUTH-METHOD",
92
+ description: "The WinRM authentication method to use.",
93
+ in: WINRM_AUTH_PROTOCOL_LIST
94
+
95
+ option :winrm_basic_auth_only,
96
+ long: "--winrm-basic-auth-only",
97
+ description: "For WinRM basic authentication when using the 'ssl' auth method.",
98
+ boolean: true
99
+
100
+ # This option was provided in knife bootstrap windows winrm,
101
+ # but it is ignored in knife-windows/WinrmSession, and so remains unimplemented here.
102
+ # option :kerberos_keytab_file,
103
+ # :short => "-T KEYTAB_FILE",
104
+ # :long => "--keytab-file KEYTAB_FILE",
105
+ # :description => "The Kerberos keytab file used for authentication"
106
+
107
+ option :kerberos_realm,
108
+ short: "-R KERBEROS_REALM",
109
+ long: "--kerberos-realm KERBEROS_REALM",
110
+ description: "The Kerberos realm used for authentication."
111
+
112
+ option :kerberos_service,
113
+ short: "-S KERBEROS_SERVICE",
114
+ long: "--kerberos-service KERBEROS_SERVICE",
115
+ description: "The Kerberos service used for authentication."
116
+
117
+ ## SSH Authentication
118
+ option :ssh_gateway,
119
+ short: "-G GATEWAY",
120
+ long: "--ssh-gateway GATEWAY",
121
+ description: "The SSH gateway."
122
+
123
+ option :ssh_gateway_identity,
124
+ long: "--ssh-gateway-identity SSH_GATEWAY_IDENTITY",
125
+ description: "The SSH identity file used for gateway authentication."
126
+
127
+ option :ssh_forward_agent,
128
+ short: "-A",
129
+ long: "--ssh-forward-agent",
130
+ description: "Enable SSH agent forwarding.",
131
+ boolean: true
132
+
133
+ option :ssh_identity_file,
134
+ short: "-i IDENTITY_FILE",
135
+ long: "--ssh-identity-file IDENTITY_FILE",
136
+ description: "The SSH identity file used for authentication."
137
+
138
+ option :ssh_verify_host_key,
139
+ long: "--ssh-verify-host-key VALUE",
140
+ description: "Verify host key. Default is 'always'.",
141
+ in: %w{always accept_new accept_new_or_local_tunnel never},
142
+ default: "always"
143
+
144
+ #
145
+ # bootstrap options
146
+ #
147
+
148
+ # client.rb content via chef-full/bootstrap_context
149
+ option :bootstrap_version,
150
+ long: "--bootstrap-version VERSION",
151
+ description: "The version of #{ChefUtils::Dist::Infra::PRODUCT} to install."
152
+
153
+ option :channel,
154
+ long: "--channel CHANNEL",
155
+ description: "Install from the given channel. Default is 'stable'.",
156
+ default: "stable",
157
+ in: %w{stable current unstable}
158
+
159
+ # client.rb content via chef-full/bootstrap_context
160
+ option :bootstrap_proxy,
161
+ long: "--bootstrap-proxy PROXY_URL",
162
+ description: "The proxy server for the node being bootstrapped."
163
+
164
+ # client.rb content via bootstrap_context
165
+ option :bootstrap_proxy_user,
166
+ long: "--bootstrap-proxy-user PROXY_USER",
167
+ description: "The proxy authentication username for the node being bootstrapped."
168
+
169
+ # client.rb content via bootstrap_context
170
+ option :bootstrap_proxy_pass,
171
+ long: "--bootstrap-proxy-pass PROXY_PASS",
172
+ description: "The proxy authentication password for the node being bootstrapped."
173
+
174
+ # client.rb content via bootstrap_context
175
+ option :bootstrap_no_proxy,
176
+ long: "--bootstrap-no-proxy [NO_PROXY_URL|NO_PROXY_IP]",
177
+ description: "Do not proxy locations for the node being bootstrapped"
178
+
179
+ # client.rb content via bootstrap_context
180
+ option :bootstrap_template,
181
+ short: "-t TEMPLATE",
182
+ long: "--bootstrap-template TEMPLATE",
183
+ description: "Bootstrap #{ChefUtils::Dist::Infra::PRODUCT} using a built-in or custom template. Set to the full path of an erb template or use one of the built-in templates."
184
+
185
+ # client.rb content via bootstrap_context
186
+ option :node_ssl_verify_mode,
187
+ long: "--node-ssl-verify-mode [peer|none]",
188
+ description: "Whether or not to verify the SSL cert for all HTTPS requests.",
189
+ proc: Proc.new { |v|
190
+ valid_values = %w{none peer}
191
+ unless valid_values.include?(v)
192
+ raise "Invalid value '#{v}' for --node-ssl-verify-mode. Valid values are: #{valid_values.join(", ")}"
193
+ end
194
+
195
+ v
196
+ }
197
+
198
+ # bootstrap_context - client.rb
199
+ option :node_verify_api_cert,
200
+ long: "--[no-]node-verify-api-cert",
201
+ description: "Verify the SSL cert for HTTPS requests to the #{ChefUtils::Dist::Server::PRODUCT} API.",
202
+ boolean: true
203
+
204
+ # runtime - sudo settings (train handles sudo)
205
+ option :use_sudo,
206
+ long: "--sudo",
207
+ description: "Execute the bootstrap via sudo.",
208
+ boolean: true
209
+
210
+ # runtime - sudo settings (train handles sudo)
211
+ option :preserve_home,
212
+ long: "--sudo-preserve-home",
213
+ description: "Preserve non-root user HOME environment variable with sudo.",
214
+ boolean: true
215
+
216
+ # runtime - sudo settings (train handles sudo)
217
+ option :use_sudo_password,
218
+ long: "--use-sudo-password",
219
+ description: "Execute the bootstrap via sudo with password.",
220
+ boolean: false
221
+
222
+ # runtime - su user
223
+ option :su_user,
224
+ long: "--su-user NAME",
225
+ description: "The su - USER name to perform bootstrap command using a non-root user."
226
+
227
+ # runtime - su user password
228
+ option :su_password,
229
+ long: "--su-password PASSWORD",
230
+ description: "The su USER password for authentication."
231
+
232
+ # runtime - client_builder
233
+ option :chef_node_name,
234
+ short: "-N NAME",
235
+ long: "--node-name NAME",
236
+ description: "The node name for your new node."
237
+
238
+ # runtime - client_builder - set runlist when creating node
239
+ option :run_list,
240
+ short: "-r RUN_LIST",
241
+ long: "--run-list RUN_LIST",
242
+ description: "Comma separated list of roles/recipes to apply.",
243
+ proc: lambda { |o| o.split(/[\s,]+/) },
244
+ default: []
245
+
246
+ # runtime - client_builder - set policy name when creating node
247
+ option :policy_name,
248
+ long: "--policy-name POLICY_NAME",
249
+ description: "Policyfile name to use (--policy-group must also be given).",
250
+ default: nil
251
+
252
+ # runtime - client_builder - set policy group when creating node
253
+ option :policy_group,
254
+ long: "--policy-group POLICY_GROUP",
255
+ description: "Policy group name to use (--policy-name must also be given).",
256
+ default: nil
257
+
258
+ # runtime - client_builder - node tags
259
+ option :tags,
260
+ long: "--tags TAGS",
261
+ description: "Comma separated list of tags to apply to the node.",
262
+ proc: lambda { |o| o.split(/[\s,]+/) },
263
+ default: []
264
+
265
+ # bootstrap template
266
+ option :first_boot_attributes,
267
+ short: "-j JSON_ATTRIBS",
268
+ long: "--json-attributes",
269
+ description: "A JSON string to be added to the first run of #{ChefUtils::Dist::Infra::CLIENT}.",
270
+ proc: lambda { |o| Chef::JSONCompat.parse(o) },
271
+ default: nil
272
+
273
+ # bootstrap template
274
+ option :first_boot_attributes_from_file,
275
+ long: "--json-attribute-file FILE",
276
+ description: "A JSON file to be used to the first run of #{ChefUtils::Dist::Infra::CLIENT}.",
277
+ proc: lambda { |o| Chef::JSONCompat.parse(File.read(o)) },
278
+ default: nil
279
+
280
+ # bootstrap template
281
+ # Create ohai hints in /etc/chef/ohai/hints, fname=hintname, content=value
282
+ option :hints,
283
+ long: "--hint HINT_NAME[=HINT_FILE]",
284
+ description: "Specify an Ohai hint to be set on the bootstrap target. Use multiple --hint options to specify multiple hints.",
285
+ proc: Proc.new { |hint, accumulator|
286
+ accumulator ||= {}
287
+ name, path = hint.split("=", 2)
288
+ accumulator[name] = path ? Chef::JSONCompat.parse(::File.read(path)) : {}
289
+ accumulator
290
+ }
291
+
292
+ # bootstrap override: url of a an installer shell script to use in place of omnitruck
293
+ # Note that the bootstrap template _only_ references this out of Chef::Config, and not from
294
+ # the provided options to knife bootstrap, so we set the Chef::Config option here.
295
+ option :bootstrap_url,
296
+ long: "--bootstrap-url URL",
297
+ description: "URL to a custom installation script."
298
+
299
+ option :bootstrap_product,
300
+ long: "--bootstrap-product PRODUCT",
301
+ description: "Product to install.",
302
+ default: "chef"
303
+
304
+ option :msi_url, # Windows target only
305
+ short: "-m URL",
306
+ long: "--msi-url URL",
307
+ description: "Location of the #{ChefUtils::Dist::Infra::PRODUCT} MSI. The default templates will prefer to download from this location. The MSI will be downloaded from #{ChefUtils::Dist::Org::WEBSITE} if not provided (Windows).",
308
+ default: ""
309
+
310
+ # bootstrap override: Do this instead of our own setup.sh from omnitruck. Causes bootstrap_url to be ignored.
311
+ option :bootstrap_install_command,
312
+ long: "--bootstrap-install-command COMMANDS",
313
+ description: "Custom command to install #{ChefUtils::Dist::Infra::PRODUCT}."
314
+
315
+ # bootstrap template: Run this command first in the bootstrap script
316
+ option :bootstrap_preinstall_command,
317
+ long: "--bootstrap-preinstall-command COMMANDS",
318
+ description: "Custom commands to run before installing #{ChefUtils::Dist::Infra::PRODUCT}."
319
+
320
+ # bootstrap template
321
+ option :bootstrap_wget_options,
322
+ long: "--bootstrap-wget-options OPTIONS",
323
+ description: "Add options to wget when installing #{ChefUtils::Dist::Infra::PRODUCT}."
324
+
325
+ # bootstrap template
326
+ option :bootstrap_curl_options,
327
+ long: "--bootstrap-curl-options OPTIONS",
328
+ description: "Add options to curl when install #{ChefUtils::Dist::Infra::PRODUCT}."
329
+
330
+ # chef_vault_handler
331
+ option :bootstrap_vault_file,
332
+ long: "--bootstrap-vault-file VAULT_FILE",
333
+ description: "A JSON file with a list of vault(s) and item(s) to be updated."
334
+
335
+ # chef_vault_handler
336
+ option :bootstrap_vault_json,
337
+ long: "--bootstrap-vault-json VAULT_JSON",
338
+ description: "A JSON string with the vault(s) and item(s) to be updated."
339
+
340
+ # chef_vault_handler
341
+ option :bootstrap_vault_item,
342
+ long: "--bootstrap-vault-item VAULT_ITEM",
343
+ description: 'A single vault and item to update as "vault:item".',
344
+ proc: Proc.new { |i, accumulator|
345
+ (vault, item) = i.split(":")
346
+ accumulator ||= {}
347
+ accumulator[vault] ||= []
348
+ accumulator[vault].push(item)
349
+ accumulator
350
+ }
351
+
352
+ # Deprecated options. These must be declared after
353
+ # regular options because they refer to the replacement
354
+ # option definitions implicitly.
355
+ deprecated_option :auth_timeout,
356
+ replacement: :max_wait,
357
+ long: "--max-wait SECONDS"
358
+
359
+ deprecated_option :forward_agent,
360
+ replacement: :ssh_forward_agent,
361
+ boolean: true, long: "--forward-agent"
362
+
363
+ deprecated_option :host_key_verify,
364
+ replacement: :ssh_verify_host_key,
365
+ boolean: true, long: "--[no-]host-key-verify",
366
+ value_mapper: Proc.new { |verify| verify ? "always" : "never" }
367
+
368
+ deprecated_option :prerelease,
369
+ replacement: :channel,
370
+ long: "--prerelease",
371
+ boolean: true, value_mapper: Proc.new { "current" }
372
+
373
+ deprecated_option :ssh_user,
374
+ replacement: :connection_user,
375
+ long: "--ssh-user USERNAME"
376
+
377
+ deprecated_option :ssh_password,
378
+ replacement: :connection_password,
379
+ long: "--ssh-password PASSWORD"
380
+
381
+ deprecated_option :ssh_port,
382
+ replacement: :connection_port,
383
+ long: "--ssh-port PASSWORD"
384
+
385
+ deprecated_option :ssl_peer_fingerprint,
386
+ replacement: :winrm_ssl_peer_fingerprint,
387
+ long: "--ssl-peer-fingerprint FINGERPRINT"
388
+
389
+ deprecated_option :winrm_user,
390
+ replacement: :connection_user,
391
+ long: "--winrm-user USERNAME", short: "-x USERNAME"
392
+
393
+ deprecated_option :winrm_password,
394
+ replacement: :connection_password,
395
+ long: "--winrm-password PASSWORD"
396
+
397
+ deprecated_option :winrm_port,
398
+ replacement: :connection_port,
399
+ long: "--winrm-port PORT"
400
+
401
+ deprecated_option :winrm_authentication_protocol,
402
+ replacement: :winrm_auth_method,
403
+ long: "--winrm-authentication-protocol PROTOCOL"
404
+
405
+ deprecated_option :winrm_session_timeout,
406
+ replacement: :session_timeout,
407
+ long: "--winrm-session-timeout MINUTES"
408
+
409
+ deprecated_option :winrm_ssl_verify_mode,
410
+ replacement: :winrm_no_verify_cert,
411
+ long: "--winrm-ssl-verify-mode MODE"
412
+
413
+ deprecated_option :winrm_transport, replacement: :winrm_ssl,
414
+ long: "--winrm-transport TRANSPORT",
415
+ value_mapper: Proc.new { |value| value == "ssl" }
416
+
417
+ attr_reader :connection
418
+
419
+ deps do
420
+ require "chef-config/path_helper" unless defined?(ChefConfig::PathHelper)
421
+ require_relative "bootstrap/chef_vault_handler"
422
+ require_relative "bootstrap/client_builder"
423
+ require_relative "bootstrap/train_connector"
424
+ end
425
+
426
+ banner "knife bootstrap [PROTOCOL://][USER@]FQDN (options)"
427
+
428
+ def client_builder
429
+ @client_builder ||= Chef::Knife::Bootstrap::ClientBuilder.new(
430
+ chef_config: Chef::Config,
431
+ config: config,
432
+ ui: ui
433
+ )
434
+ end
435
+
436
+ def chef_vault_handler
437
+ @chef_vault_handler ||= Chef::Knife::Bootstrap::ChefVaultHandler.new(
438
+ config: config,
439
+ ui: ui
440
+ )
441
+ end
442
+
443
+ # Determine if we need to accept the Chef Infra license locally in order to successfully bootstrap
444
+ # the remote node. Remote 'chef-client' run will fail if it is >= 15 and the license is not accepted locally.
445
+ def check_license
446
+ Chef::Log.debug("Checking if we need to accept Chef license to bootstrap node")
447
+ version = config[:bootstrap_version] || Chef::VERSION.split(".").first
448
+ acceptor = LicenseAcceptance::Acceptor.new(logger: Chef::Log, provided: Chef::Config[:chef_license])
449
+ if acceptor.license_required?("chef", version)
450
+ Chef::Log.debug("License acceptance required for chef version: #{version}")
451
+ license_id = acceptor.id_from_mixlib("chef")
452
+ acceptor.check_and_persist(license_id, version)
453
+ Chef::Config[:chef_license] ||= acceptor.acceptance_value
454
+ end
455
+ end
456
+
457
+ # The default bootstrap template to use to bootstrap a server.
458
+ # This is a public API hook which knife plugins use or inherit and override.
459
+ #
460
+ # @return [String] Default bootstrap template
461
+ def default_bootstrap_template
462
+ if connection.windows?
463
+ "windows-chef-client-msi"
464
+ else
465
+ "chef-full"
466
+ end
467
+ end
468
+
469
+ def host_descriptor
470
+ Array(@name_args).first
471
+ end
472
+
473
+ # The server_name is the DNS or IP we are going to connect to, it is not necessarily
474
+ # the node name, the fqdn, or the hostname of the server. This is a public API hook
475
+ # which knife plugins use or inherit and override.
476
+ #
477
+ # @return [String] The DNS or IP that bootstrap will connect to
478
+ def server_name
479
+ if host_descriptor
480
+ @server_name ||= host_descriptor.split("@").reverse[0]
481
+ end
482
+ end
483
+
484
+ # @return [String] The CLI specific bootstrap template or the default
485
+ def bootstrap_template
486
+ # Allow passing a bootstrap template or use the default
487
+ config[:bootstrap_template] || default_bootstrap_template
488
+ end
489
+
490
+ def find_template
491
+ template = bootstrap_template
492
+
493
+ # Use the template directly if it's a path to an actual file
494
+ if File.exist?(template)
495
+ Chef::Log.trace("Using the specified bootstrap template: #{File.dirname(template)}")
496
+ return template
497
+ end
498
+
499
+ # Otherwise search the template directories until we find the right one
500
+ bootstrap_files = []
501
+ bootstrap_files << File.join(__dir__, "bootstrap/templates", "#{template}.erb")
502
+ bootstrap_files << File.join(Knife.chef_config_dir, "bootstrap", "#{template}.erb") if Chef::Knife.chef_config_dir
503
+ ChefConfig::PathHelper.home(".chef", "bootstrap", "#{template}.erb") { |p| bootstrap_files << p }
504
+ bootstrap_files << Gem.find_files(File.join("chef", "knife", "bootstrap", "#{template}.erb"))
505
+ bootstrap_files.flatten!
506
+
507
+ template_file = Array(bootstrap_files).find do |bootstrap_template|
508
+ Chef::Log.trace("Looking for bootstrap template in #{File.dirname(bootstrap_template)}")
509
+ File.exist?(bootstrap_template)
510
+ end
511
+
512
+ unless template_file
513
+ ui.info("Can not find bootstrap definition for #{template}")
514
+ raise Errno::ENOENT
515
+ end
516
+
517
+ Chef::Log.trace("Found bootstrap template: #{template_file}")
518
+
519
+ template_file
520
+ end
521
+
522
+ def secret
523
+ @secret ||= encryption_secret_provided_ignore_encrypt_flag? ? read_secret : nil
524
+ end
525
+
526
+ # Establish bootstrap context for template rendering.
527
+ # Requires connection to be a live connection in order to determine
528
+ # the correct platform.
529
+ def bootstrap_context
530
+ @bootstrap_context ||=
531
+ if connection.windows?
532
+ require_relative "core/windows_bootstrap_context"
533
+ Knife::Core::WindowsBootstrapContext.new(config, config[:run_list], Chef::Config, secret)
534
+ else
535
+ require_relative "core/bootstrap_context"
536
+ Knife::Core::BootstrapContext.new(config, config[:run_list], Chef::Config, secret)
537
+ end
538
+ end
539
+
540
+ def first_boot_attributes
541
+ @config[:first_boot_attributes] || @config[:first_boot_attributes_from_file] || {}
542
+ end
543
+
544
+ def render_template
545
+ require "erubis" unless defined?(Erubis)
546
+ @config[:first_boot_attributes] = first_boot_attributes
547
+ template_file = find_template
548
+ template = IO.read(template_file).chomp
549
+ Erubis::Eruby.new(template).evaluate(bootstrap_context)
550
+ end
551
+
552
+ def run
553
+ check_license if ChefUtils::Dist::Org::ENFORCE_LICENSE
554
+
555
+ plugin_setup!
556
+ validate_name_args!
557
+ validate_protocol!
558
+ validate_first_boot_attributes!
559
+ validate_winrm_transport_opts!
560
+ validate_policy_options!
561
+ plugin_validate_options!
562
+
563
+ winrm_warn_no_ssl_verification
564
+ warn_on_short_session_timeout
565
+
566
+ plugin_create_instance!
567
+ $stdout.sync = true
568
+ connect!
569
+ register_client
570
+
571
+ content = render_template
572
+ bootstrap_path = upload_bootstrap(content)
573
+ perform_bootstrap(bootstrap_path)
574
+ plugin_finalize
575
+ ensure
576
+ connection.del_file!(bootstrap_path) if connection && bootstrap_path
577
+ end
578
+
579
+ def register_client
580
+ # chef-vault integration must use the new client-side hawtness, otherwise to use the
581
+ # new client-side hawtness, just delete your validation key.
582
+ if chef_vault_handler.doing_chef_vault? ||
583
+ (Chef::Config[:validation_key] &&
584
+ !File.exist?(File.expand_path(Chef::Config[:validation_key])))
585
+
586
+ unless config[:chef_node_name]
587
+ ui.error("You must pass a node name with -N when bootstrapping with user credentials")
588
+ exit 1
589
+ end
590
+ client_builder.run
591
+ chef_vault_handler.run(client_builder.client)
592
+
593
+ bootstrap_context.client_pem = client_builder.client_path
594
+ else
595
+ ui.warn "Performing legacy client registration with the validation key at #{Chef::Config[:validation_key]}..."
596
+ ui.warn "Remove the key file or remove the 'validation_key' configuration option from your config.rb (knife.rb) to use more secure user credentials for client registration."
597
+ end
598
+ end
599
+
600
+ def perform_bootstrap(remote_bootstrap_script_path)
601
+ ui.info("Bootstrapping #{ui.color(server_name, :bold)}")
602
+ cmd = bootstrap_command(remote_bootstrap_script_path)
603
+ bootstrap_run_command(cmd)
604
+ end
605
+
606
+ # Actual bootstrap command to be run on the node.
607
+ # Handles recursive calls if su USER failed to authenticate.
608
+ def bootstrap_run_command(cmd)
609
+ r = connection.run_command(cmd) do |data, channel|
610
+ ui.msg("#{ui.color(" [#{connection.hostname}]", :cyan)} #{data}")
611
+ channel.send_data("#{config[:su_password] || config[:connection_password]}\n") if data.match?("Password:")
612
+ end
613
+
614
+ if r.exit_status != 0
615
+ ui.error("The following error occurred on #{server_name}:")
616
+ ui.error("#{r.stdout} #{r.stderr}".strip)
617
+ exit(r.exit_status)
618
+ end
619
+ rescue Train::UserError => e
620
+ limit ||= 0
621
+ if e.reason == :bad_su_user_password && limit < 3
622
+ limit += 1
623
+ ui.warn("Failed to authenticate su - #{config[:su_user]} to #{server_name}")
624
+ config[:su_password] = ui.ask("Enter password for su - #{config[:su_user]}@#{server_name}:", echo: false)
625
+ retry
626
+ else
627
+ raise
628
+ end
629
+ end
630
+
631
+ def connect!
632
+ ui.info("Connecting to #{ui.color(server_name, :bold)} using #{connection_protocol}")
633
+ opts ||= connection_opts.dup
634
+ do_connect(opts)
635
+ rescue Train::Error => e
636
+ # We handle these by message text only because train only loads the
637
+ # transports and protocols that it needs - so the exceptions may not be defined,
638
+ # and we don't want to require files internal to train.
639
+ if e.message =~ /fingerprint (\S+) is unknown for "(.+)"/ # Train::Transports::SSHFailed
640
+ fingerprint = $1
641
+ hostname, ip = $2.split(",")
642
+ # TODO: convert the SHA256 base64 value to hex with colons
643
+ # 'ssh' example output:
644
+ # RSA key fingerprint is e5:cb:c0:e2:21:3b:12:52:f8:ce:cb:00:24:e2:0c:92.
645
+ # ECDSA key fingerprint is 5d:67:61:08:a9:d7:01:fd:5e:ae:7e:09:40:ef:c0:3c.
646
+ # will exit 3 on N
647
+ ui.confirm <<~EOM
648
+ The authenticity of host '#{hostname} (#{ip})' can't be established.
649
+ fingerprint is #{fingerprint}.
650
+
651
+ Are you sure you want to continue connecting
652
+ EOM
653
+ # FIXME: this should save the key to known_hosts but doesn't appear to be
654
+ config[:ssh_verify_host_key] = :accept_new
655
+ conn_opts = connection_opts(reset: true, sudo_pass: opts[:password])
656
+ opts.merge! conn_opts
657
+ retry
658
+ elsif (ssh? && e.cause && e.cause.class == Net::SSH::AuthenticationFailed) || (ssh? && e.class == Train::ClientError && e.reason == :no_ssh_password_or_key_available)
659
+ if connection.password_auth?
660
+ raise
661
+ else
662
+ ui.warn("Failed to authenticate #{opts[:user]} to #{server_name} - trying password auth")
663
+ password = ui.ask("Enter password for #{opts[:user]}@#{server_name}:", echo: false)
664
+ end
665
+
666
+ opts.merge! force_ssh_password_opts(password)
667
+ retry
668
+ else
669
+ raise
670
+ end
671
+ rescue RuntimeError => e
672
+ if winrm? && e.message == "password is a required option"
673
+ if connection.password_auth?
674
+ raise
675
+ else
676
+ ui.warn("Failed to authenticate #{opts[:user]} to #{server_name} - trying password auth")
677
+ password = ui.ask("Enter password for #{opts[:user]}@#{server_name}:", echo: false)
678
+ end
679
+
680
+ opts.merge! force_winrm_password_opts(password)
681
+ retry
682
+ else
683
+ raise
684
+ end
685
+ end
686
+
687
+ def handle_ssh_error(e); end
688
+
689
+ # url values override CLI flags, if you provide both
690
+ # we'll use the one that you gave in the URL.
691
+ def connection_protocol
692
+ return @connection_protocol if @connection_protocol
693
+
694
+ from_url = host_descriptor =~ %r{^(.*)://} ? $1 : nil
695
+ from_knife = config[:connection_protocol]
696
+ @connection_protocol = from_url || from_knife || "ssh"
697
+ end
698
+
699
+ def do_connect(conn_options)
700
+ @connection = TrainConnector.new(host_descriptor, connection_protocol, conn_options)
701
+ connection.connect!
702
+ rescue Train::UserError => e
703
+ limit ||= 1
704
+ if !conn_options.key?(:pty) && e.reason == :sudo_no_tty
705
+ ui.warn("#{e.message} - trying with pty request")
706
+ conn_options[:pty] = true # ensure we can talk to systems with requiretty set true in sshd config
707
+ retry
708
+ elsif e.reason == :sudo_missing_terminal
709
+ ui.error "Sudo password is required for this operation. Please enter password using -P or --ssh-password option"
710
+ elsif config[:use_sudo_password] && (e.reason == :sudo_password_required || e.reason == :bad_sudo_password) && limit < 3
711
+ ui.warn("Failed to authenticate #{conn_options[:user]} to #{server_name} - #{e.message} \n sudo: #{limit} incorrect password attempt")
712
+ sudo_password = ui.ask("Enter sudo password for #{conn_options[:user]}@#{server_name}:", echo: false)
713
+ limit += 1
714
+ conn_options[:sudo_password] = sudo_password
715
+
716
+ retry
717
+ else
718
+ raise
719
+ end
720
+ end
721
+
722
+ # Fail if both first_boot_attributes and first_boot_attributes_from_file
723
+ # are set.
724
+ def validate_first_boot_attributes!
725
+ if @config[:first_boot_attributes] && @config[:first_boot_attributes_from_file]
726
+ raise Chef::Exceptions::BootstrapCommandInputError
727
+ end
728
+
729
+ true
730
+ end
731
+
732
+ # FIXME: someone needs to clean this up properly: https://github.com/chef/chef/issues/9645
733
+ # This code is deliberately left without an abstraction around deprecating the config options to avoid knife plugins from
734
+ # using those methods (which will need to be deprecated and break them) via inheritance (ruby does not have a true `private`
735
+ # so the lack of any inheritable implementation is because of that).
736
+ #
737
+ def winrm_auth_method
738
+ config.key?(:winrm_auth_method) ? config[:winrm_auth_method] : config.key?(:winrm_authentications_protocol) ? config[:winrm_authentication_protocol] : "negotiate" # rubocop:disable Style/NestedTernaryOperator
739
+ end
740
+
741
+ def ssh_verify_host_key
742
+ config.key?(:ssh_verify_host_key) ? config[:ssh_verify_host_key] : config.key?(:host_key_verify) ? config[:host_key_verify] : "always" # rubocop:disable Style/NestedTernaryOperator
743
+ end
744
+
745
+ # Fail if using plaintext auth without ssl because
746
+ # this can expose keys in plaintext on the wire.
747
+ # TODO test for this method
748
+ # TODO check that the protocol is valid.
749
+ def validate_winrm_transport_opts!
750
+ return true unless winrm?
751
+
752
+ if Chef::Config[:validation_key] && !File.exist?(File.expand_path(Chef::Config[:validation_key]))
753
+ if winrm_auth_method == "plaintext" &&
754
+ config[:winrm_ssl] != true
755
+ ui.error <<~EOM
756
+ Validatorless bootstrap over unsecure winrm channels could expose your
757
+ key to network sniffing.
758
+ Please use a 'winrm_auth_method' other than 'plaintext',
759
+ or enable ssl on #{server_name} then use the ---winrm-ssl flag
760
+ to connect.
761
+ EOM
762
+
763
+ exit 1
764
+ end
765
+ end
766
+ true
767
+ end
768
+
769
+ # fail if the server_name is nil
770
+ def validate_name_args!
771
+ if server_name.nil?
772
+ ui.error("Must pass an FQDN or ip to bootstrap")
773
+ exit 1
774
+ end
775
+ end
776
+
777
+ # Ensure options are valid by checking policyfile values.
778
+ #
779
+ # The method call will cause the program to exit(1) if:
780
+ # * Only one of --policy-name and --policy-group is specified
781
+ # * Policyfile options are set and --run-list is set as well
782
+ #
783
+ # @return [TrueClass] If options are valid.
784
+ def validate_policy_options!
785
+ if incomplete_policyfile_options?
786
+ ui.error("--policy-name and --policy-group must be specified together")
787
+ exit 1
788
+ elsif policyfile_and_run_list_given?
789
+ ui.error("Policyfile options and --run-list are exclusive")
790
+ exit 1
791
+ end
792
+ end
793
+
794
+ # Ensure a valid protocol is provided for target host connection
795
+ #
796
+ # The method call will cause the program to exit(1) if:
797
+ # * Conflicting protocols are given via the target URI and the --protocol option
798
+ # * The protocol is not a supported protocol
799
+ #
800
+ # @return [TrueClass] If options are valid.
801
+ def validate_protocol!
802
+ from_cli = config[:connection_protocol]
803
+ if from_cli && connection_protocol != from_cli
804
+ # Hanging indent to align with the ERROR: prefix
805
+ ui.error <<~EOM
806
+ The URL '#{host_descriptor}' indicates protocol is '#{connection_protocol}'
807
+ while the --protocol flag specifies '#{from_cli}'. Please include
808
+ only one or the other.
809
+ EOM
810
+ exit 1
811
+ end
812
+
813
+ unless SUPPORTED_CONNECTION_PROTOCOLS.include?(connection_protocol)
814
+ ui.error <<~EOM
815
+ Unsupported protocol '#{connection_protocol}'.
816
+
817
+ Supported protocols are: #{SUPPORTED_CONNECTION_PROTOCOLS.join(" ")}
818
+ EOM
819
+ exit 1
820
+ end
821
+ true
822
+ end
823
+
824
+ # Validate any additional options
825
+ #
826
+ # Plugins that subclass bootstrap, e.g. knife-ec2, can use this method to validate any additional options before any other actions are executed
827
+ #
828
+ # @return [TrueClass] If options are valid or exits
829
+ def plugin_validate_options!
830
+ true
831
+ end
832
+
833
+ # Create the server that we will bootstrap, if necessary
834
+ #
835
+ # Plugins that subclass bootstrap, e.g. knife-ec2, can use this method to call out to an API to build an instance of the server we wish to bootstrap
836
+ #
837
+ # @return [TrueClass] If instance successfully created, or exits
838
+ def plugin_create_instance!
839
+ true
840
+ end
841
+
842
+ # Perform any setup necessary by the plugin
843
+ #
844
+ # Plugins that subclass bootstrap, e.g. knife-ec2, can use this method to create connection objects
845
+ #
846
+ # @return [TrueClass] If instance successfully created, or exits
847
+ def plugin_setup!; end
848
+
849
+ # Perform any teardown or cleanup necessary by the plugin
850
+ #
851
+ # Plugins that subclass bootstrap, e.g. knife-ec2, can use this method to display a message or perform any cleanup
852
+ #
853
+ # @return [void]
854
+ def plugin_finalize; end
855
+
856
+ # If session_timeout is too short, it is likely
857
+ # a holdover from "--winrm-session-timeout" which used
858
+ # minutes as its unit, instead of seconds.
859
+ # Warn the human so that they are not surprised.
860
+ #
861
+ def warn_on_short_session_timeout
862
+ if session_timeout && session_timeout <= 15
863
+ ui.warn <<~EOM
864
+ You provided '--session-timeout #{session_timeout}' second(s).
865
+ Did you mean '--session-timeout #{session_timeout * 60}' seconds?
866
+ EOM
867
+ end
868
+ end
869
+
870
+ def winrm_warn_no_ssl_verification
871
+ return unless winrm?
872
+
873
+ # REVIEWER NOTE
874
+ # The original check from knife plugin did not include winrm_ssl_peer_fingerprint
875
+ # Reference:
876
+ # https://github.com/chef/knife-windows/blob/92d151298142be4a4750c5b54bb264f8d5b81b8a/lib/chef/knife/winrm_knife_base.rb#L271-L273
877
+ # TODO Seems like we should also do a similar warning if ssh_verify_host == false
878
+ if config[:ca_trust_file].nil? &&
879
+ config[:winrm_no_verify_cert] &&
880
+ config[:winrm_ssl_peer_fingerprint].nil?
881
+ ui.warn <<~WARN
882
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
883
+ SSL validation of HTTPS requests for the WinRM transport is disabled.
884
+ HTTPS WinRM connections are still encrypted, but knife is not able
885
+ to detect forged replies or spoofing attacks.
886
+
887
+ To work around this issue you can use the flag `--winrm-no-verify-cert`
888
+ or add an entry like this to your knife configuration file:
889
+
890
+ # Verify all WinRM HTTPS connections
891
+ knife[:winrm_no_verify_cert] = true
892
+
893
+ You can also specify a ca_trust_file via --ca-trust-file,
894
+ or the expected fingerprint of the target host's certificate
895
+ via --winrm-ssl-peer-fingerprint.
896
+ WARN
897
+ end
898
+ end
899
+
900
+ # @return a configuration hash suitable for connecting to the remote
901
+ # host via train
902
+ def connection_opts(reset: false, sudo_pass: nil )
903
+ return @connection_opts unless @connection_opts.nil? || reset == true
904
+
905
+ @connection_opts = {}
906
+ @connection_opts.merge! base_opts
907
+ @connection_opts.merge! host_verify_opts
908
+ @connection_opts.merge! gateway_opts
909
+ @connection_opts.merge! sudo_opts(sudo_pass)
910
+ @connection_opts.merge! winrm_opts
911
+ @connection_opts.merge! ssh_opts
912
+ @connection_opts.merge! ssh_identity_opts
913
+ @connection_opts
914
+ end
915
+
916
+ def winrm?
917
+ connection_protocol == "winrm"
918
+ end
919
+
920
+ def ssh?
921
+ connection_protocol == "ssh"
922
+ end
923
+
924
+ # Common configuration for all protocols
925
+ def base_opts
926
+ port = config_for_protocol(:port)
927
+ user = config_for_protocol(:user)
928
+ {}.tap do |opts|
929
+ opts[:logger] = Chef::Log
930
+ opts[:password] = config[:connection_password] if config.key?(:connection_password)
931
+ opts[:user] = user if user
932
+ opts[:max_wait_until_ready] = config[:max_wait].to_i unless config[:max_wait].nil?
933
+ # TODO - when would we need to provide rdp_port vs port? Or are they not mutually exclusive?
934
+ opts[:port] = port if port
935
+ end
936
+ end
937
+
938
+ def host_verify_opts
939
+ if winrm?
940
+ { self_signed: config[:winrm_no_verify_cert] === true }
941
+ elsif ssh?
942
+ # Fall back to the old knife config key name for back compat.
943
+ { verify_host_key: ssh_verify_host_key }
944
+ else
945
+ {}
946
+ end
947
+ end
948
+
949
+ def ssh_opts
950
+ opts = {}
951
+ return opts if winrm?
952
+
953
+ opts[:non_interactive] = true # Prevent password prompts from underlying net/ssh
954
+ opts[:forward_agent] = (config[:ssh_forward_agent] === true)
955
+ opts[:connection_timeout] = session_timeout
956
+ opts
957
+ end
958
+
959
+ def ssh_identity_opts
960
+ opts = {}
961
+ return opts if winrm?
962
+
963
+ identity_file = config[:ssh_identity_file]
964
+ if identity_file
965
+ opts[:key_files] = [identity_file]
966
+ # We only set keys_only based on the explicit ssh_identity_file;
967
+ # someone may use a gateway key and still expect password auth
968
+ # on the target. Similarly, someone may have a default key specified
969
+ # in knife config, but have provided a password on the CLI.
970
+
971
+ # REVIEW NOTE: this is a new behavior. Originally, ssh_identity_file
972
+ # could only be populated from CLI options, so there was no need to check
973
+ # for this. We will also set keys_only to false only if there are keys
974
+ # and no password.
975
+ # If both are present, train(via net/ssh) will prefer keys, falling back to password.
976
+ # Reference: https://github.com/chef/chef/blob/main/lib/chef/knife/ssh.rb#L272
977
+ opts[:keys_only] = config.key?(:connection_password) == false
978
+ else
979
+ opts[:key_files] = []
980
+ opts[:keys_only] = false
981
+ end
982
+
983
+ gateway_identity_file = config[:ssh_gateway] ? config[:ssh_gateway_identity] : nil
984
+ unless gateway_identity_file.nil?
985
+ opts[:key_files] << gateway_identity_file
986
+ end
987
+
988
+ opts
989
+ end
990
+
991
+ def gateway_opts
992
+ opts = {}
993
+ if config[:ssh_gateway]
994
+ split = config[:ssh_gateway].split("@", 2)
995
+ if split.length == 1
996
+ gw_host = split[0]
997
+ else
998
+ gw_user = split[0]
999
+ gw_host = split[1]
1000
+ end
1001
+ gw_host, gw_port = gw_host.split(":", 2)
1002
+ # TODO - validate convertible port in config validation?
1003
+ gw_port = Integer(gw_port) rescue nil
1004
+ opts[:bastion_host] = gw_host
1005
+ opts[:bastion_user] = gw_user
1006
+ opts[:bastion_port] = gw_port
1007
+ end
1008
+ opts
1009
+ end
1010
+
1011
+ # use_sudo - tells bootstrap to use the sudo command to run bootstrap
1012
+ # use_sudo_password - tells bootstrap to use the sudo command to run bootstrap
1013
+ # and to use the password specified with --password
1014
+ # TODO: I'd like to make our sudo options sane:
1015
+ # --sudo (bool) - use sudo
1016
+ # --sudo-password PASSWORD (default: :password) - use this password for sudo
1017
+ # --sudo-options "opt,opt,opt" to pass into sudo
1018
+ # --sudo-command COMMAND sudo command other than sudo
1019
+ # REVIEW NOTE: knife bootstrap did not pull sudo values from Chef::Config,
1020
+ # should we change that for consistency?
1021
+ def sudo_opts(sudo_pass)
1022
+ return {} if winrm?
1023
+
1024
+ opts = { sudo: false }
1025
+ if config[:use_sudo]
1026
+ opts[:sudo] = true
1027
+ if config[:use_sudo_password]
1028
+ opts[:sudo_password] = !config[:connection_password].nil? ? config[:connection_password] : sudo_pass
1029
+ end
1030
+ if config[:preserve_home]
1031
+ opts[:sudo_options] = "-H"
1032
+ end
1033
+ end
1034
+ opts
1035
+ end
1036
+
1037
+ def winrm_opts
1038
+ return {} unless winrm?
1039
+
1040
+ opts = {
1041
+ winrm_transport: winrm_auth_method, # winrm gem and train calls auth method 'transport'
1042
+ winrm_basic_auth_only: config[:winrm_basic_auth_only] || false,
1043
+ ssl: config[:winrm_ssl] === true,
1044
+ ssl_peer_fingerprint: config[:winrm_ssl_peer_fingerprint],
1045
+ }
1046
+
1047
+ if winrm_auth_method == "kerberos"
1048
+ opts[:kerberos_service] = config[:kerberos_service] if config[:kerberos_service]
1049
+ opts[:kerberos_realm] = config[:kerberos_realm] if config[:kerberos_service]
1050
+ end
1051
+
1052
+ if config[:ca_trust_file]
1053
+ opts[:ca_trust_path] = config[:ca_trust_file]
1054
+ end
1055
+
1056
+ opts[:operation_timeout] = session_timeout
1057
+
1058
+ opts
1059
+ end
1060
+
1061
+ # Config overrides to force password auth.
1062
+ def force_ssh_password_opts(password)
1063
+ {
1064
+ password: password,
1065
+ non_interactive: false,
1066
+ keys_only: false,
1067
+ key_files: [],
1068
+ auth_methods: %i{password keyboard_interactive},
1069
+ }
1070
+ end
1071
+
1072
+ def force_winrm_password_opts(password)
1073
+ {
1074
+ password: password,
1075
+ }
1076
+ end
1077
+
1078
+ # This is for deprecating config options. The fallback_key can be used
1079
+ # to pull an old knife config option out of the config file when the
1080
+ # cli value has been renamed. This is different from the deprecated
1081
+ # cli values, since these are for config options that have no corresponding
1082
+ # cli value.
1083
+ #
1084
+ # DO NOT USE - this whole API is considered deprecated
1085
+ #
1086
+ # @api deprecated
1087
+ #
1088
+ def config_value(key, fallback_key = nil, default = nil)
1089
+ Chef.deprecated(:knife_bootstrap_apis, "Use of config_value is deprecated. Knife plugin authors should access the config hash directly, which does correct merging of cli and config options.")
1090
+ if config.key?(key)
1091
+ # the first key is the primary key so we check the merged hash first
1092
+ config[key]
1093
+ elsif config.key?(fallback_key)
1094
+ # we get the old config option here (the deprecated cli option shouldn't exist)
1095
+ config[fallback_key]
1096
+ else
1097
+ default
1098
+ end
1099
+ end
1100
+
1101
+ def upload_bootstrap(content)
1102
+ script_name = connection.windows? ? "bootstrap.bat" : "bootstrap.sh"
1103
+ remote_path = connection.normalize_path(File.join(connection.temp_dir, script_name))
1104
+ connection.upload_file_content!(content, remote_path)
1105
+ remote_path
1106
+ end
1107
+
1108
+ # build the command string for bootstrapping
1109
+ # @return String
1110
+ def bootstrap_command(remote_path)
1111
+ if connection.windows?
1112
+ "cmd.exe /C #{remote_path}"
1113
+ else
1114
+ cmd = "sh #{remote_path}"
1115
+
1116
+ if config[:su_user]
1117
+ # su - USER is subject to required an interactive console
1118
+ # Otherwise, it will raise: su: must be run from a terminal
1119
+ set_transport_options(pty: true)
1120
+ cmd = "su - #{config[:su_user]} -c '#{cmd}'"
1121
+ cmd = "sudo " << cmd if config[:use_sudo]
1122
+ end
1123
+
1124
+ cmd
1125
+ end
1126
+ end
1127
+
1128
+ private
1129
+
1130
+ # To avoid cluttering the CLI options, some flags (such as port and user)
1131
+ # are shared between protocols. However, there is still a need to allow the operator
1132
+ # to specify defaults separately, since they may not be the same values for different
1133
+ # protocols.
1134
+
1135
+ # These keys are available in Chef::Config, and are prefixed with the protocol name.
1136
+ # For example, :user CLI option will map to :winrm_user and :ssh_user Chef::Config keys,
1137
+ # based on the connection protocol in use.
1138
+
1139
+ # @api private
1140
+ def config_for_protocol(option)
1141
+ if option == :port
1142
+ config[:connection_port] || config[knife_key_for_protocol(option)]
1143
+ else
1144
+ config[:connection_user] || config[knife_key_for_protocol(option)]
1145
+ end
1146
+ end
1147
+
1148
+ # @api private
1149
+ def knife_key_for_protocol(option)
1150
+ "#{connection_protocol}_#{option}".to_sym
1151
+ end
1152
+
1153
+ # True if policy_name and run_list are both given
1154
+ def policyfile_and_run_list_given?
1155
+ run_list_given? && policyfile_options_given?
1156
+ end
1157
+
1158
+ def run_list_given?
1159
+ !config[:run_list].nil? && !config[:run_list].empty?
1160
+ end
1161
+
1162
+ def policyfile_options_given?
1163
+ !!config[:policy_name]
1164
+ end
1165
+
1166
+ # True if one of policy_name or policy_group was given, but not both
1167
+ def incomplete_policyfile_options?
1168
+ (!!config[:policy_name] ^ config[:policy_group])
1169
+ end
1170
+
1171
+ # session_timeout option has a default that may not arrive, particularly if
1172
+ # we're being invoked from a plugin that doesn't merge_config.
1173
+ def session_timeout
1174
+ timeout = config[:session_timeout]
1175
+ return options[:session_timeout][:default] if timeout.nil?
1176
+
1177
+ timeout.to_i
1178
+ end
1179
+
1180
+ # Train::Transports::SSH::Connection#transport_options
1181
+ # Append the options to connection transport_options
1182
+ #
1183
+ # @param opts [Hash] the opts to be added to connection transport_options.
1184
+ # @return [Hash] transport_options if the opts contains any option to be set.
1185
+ #
1186
+ def set_transport_options(opts)
1187
+ return unless opts.is_a?(Hash) || !opts.empty?
1188
+
1189
+ connection&.connection&.transport_options&.merge! opts
1190
+ end
1191
+ end
1192
+ end
1193
+ end