knife 18.6.2 → 18.7.9

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