openbolt 5.0.0.rc1

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 (258) hide show
  1. checksums.yaml +7 -0
  2. data/Puppetfile +52 -0
  3. data/bolt-modules/boltlib/lib/puppet/datatypes/applyresult.rb +60 -0
  4. data/bolt-modules/boltlib/lib/puppet/datatypes/containerresult.rb +51 -0
  5. data/bolt-modules/boltlib/lib/puppet/datatypes/future.rb +25 -0
  6. data/bolt-modules/boltlib/lib/puppet/datatypes/resourceinstance.rb +71 -0
  7. data/bolt-modules/boltlib/lib/puppet/datatypes/result.rb +55 -0
  8. data/bolt-modules/boltlib/lib/puppet/datatypes/resultset.rb +65 -0
  9. data/bolt-modules/boltlib/lib/puppet/datatypes/target.rb +93 -0
  10. data/bolt-modules/boltlib/lib/puppet/functions/add_facts.rb +33 -0
  11. data/bolt-modules/boltlib/lib/puppet/functions/add_to_group.rb +38 -0
  12. data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +208 -0
  13. data/bolt-modules/boltlib/lib/puppet/functions/background.rb +62 -0
  14. data/bolt-modules/boltlib/lib/puppet/functions/catch_errors.rb +57 -0
  15. data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +130 -0
  16. data/bolt-modules/boltlib/lib/puppet/functions/facts.rb +31 -0
  17. data/bolt-modules/boltlib/lib/puppet/functions/fail_plan.rb +52 -0
  18. data/bolt-modules/boltlib/lib/puppet/functions/get_resources.rb +87 -0
  19. data/bolt-modules/boltlib/lib/puppet/functions/get_target.rb +34 -0
  20. data/bolt-modules/boltlib/lib/puppet/functions/get_targets.rb +35 -0
  21. data/bolt-modules/boltlib/lib/puppet/functions/parallelize.rb +74 -0
  22. data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_command.rb +97 -0
  23. data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_fact.rb +47 -0
  24. data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_query.rb +52 -0
  25. data/bolt-modules/boltlib/lib/puppet/functions/remove_from_group.rb +40 -0
  26. data/bolt-modules/boltlib/lib/puppet/functions/resolve_references.rb +42 -0
  27. data/bolt-modules/boltlib/lib/puppet/functions/resource.rb +53 -0
  28. data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +106 -0
  29. data/bolt-modules/boltlib/lib/puppet/functions/run_container.rb +162 -0
  30. data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +291 -0
  31. data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +145 -0
  32. data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +164 -0
  33. data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +211 -0
  34. data/bolt-modules/boltlib/lib/puppet/functions/set_config.rb +48 -0
  35. data/bolt-modules/boltlib/lib/puppet/functions/set_feature.rb +43 -0
  36. data/bolt-modules/boltlib/lib/puppet/functions/set_resources.rb +145 -0
  37. data/bolt-modules/boltlib/lib/puppet/functions/set_var.rb +38 -0
  38. data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +101 -0
  39. data/bolt-modules/boltlib/lib/puppet/functions/vars.rb +29 -0
  40. data/bolt-modules/boltlib/lib/puppet/functions/wait.rb +131 -0
  41. data/bolt-modules/boltlib/lib/puppet/functions/wait_until_available.rb +59 -0
  42. data/bolt-modules/boltlib/lib/puppet/functions/without_default_logging.rb +39 -0
  43. data/bolt-modules/boltlib/lib/puppet/functions/write_file.rb +50 -0
  44. data/bolt-modules/boltlib/types/planresult.pp +18 -0
  45. data/bolt-modules/boltlib/types/targetspec.pp +7 -0
  46. data/bolt-modules/ctrl/lib/puppet/functions/ctrl/do_until.rb +42 -0
  47. data/bolt-modules/ctrl/lib/puppet/functions/ctrl/sleep.rb +20 -0
  48. data/bolt-modules/dir/lib/puppet/functions/dir/children.rb +35 -0
  49. data/bolt-modules/file/lib/puppet/functions/file/delete.rb +21 -0
  50. data/bolt-modules/file/lib/puppet/functions/file/exists.rb +28 -0
  51. data/bolt-modules/file/lib/puppet/functions/file/join.rb +20 -0
  52. data/bolt-modules/file/lib/puppet/functions/file/read.rb +33 -0
  53. data/bolt-modules/file/lib/puppet/functions/file/readable.rb +28 -0
  54. data/bolt-modules/file/lib/puppet/functions/file/write.rb +24 -0
  55. data/bolt-modules/log/lib/puppet/functions/log/debug.rb +39 -0
  56. data/bolt-modules/log/lib/puppet/functions/log/error.rb +40 -0
  57. data/bolt-modules/log/lib/puppet/functions/log/fatal.rb +40 -0
  58. data/bolt-modules/log/lib/puppet/functions/log/info.rb +39 -0
  59. data/bolt-modules/log/lib/puppet/functions/log/trace.rb +39 -0
  60. data/bolt-modules/log/lib/puppet/functions/log/warn.rb +41 -0
  61. data/bolt-modules/out/lib/puppet/functions/out/message.rb +36 -0
  62. data/bolt-modules/out/lib/puppet/functions/out/verbose.rb +35 -0
  63. data/bolt-modules/prompt/lib/puppet/functions/prompt/menu.rb +103 -0
  64. data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +65 -0
  65. data/bolt-modules/system/lib/puppet/functions/system/env.rb +20 -0
  66. data/exe/bolt +17 -0
  67. data/guides/debugging.yaml +27 -0
  68. data/guides/inventory.yaml +23 -0
  69. data/guides/links.yaml +12 -0
  70. data/guides/logging.yaml +17 -0
  71. data/guides/module.yaml +18 -0
  72. data/guides/modulepath.yaml +24 -0
  73. data/guides/project.yaml +21 -0
  74. data/guides/targets.yaml +28 -0
  75. data/guides/transports.yaml +22 -0
  76. data/lib/bolt/analytics.rb +233 -0
  77. data/lib/bolt/application.rb +806 -0
  78. data/lib/bolt/applicator.rb +368 -0
  79. data/lib/bolt/apply_inventory.rb +93 -0
  80. data/lib/bolt/apply_result.rb +154 -0
  81. data/lib/bolt/apply_target.rb +90 -0
  82. data/lib/bolt/bolt_option_parser.rb +1226 -0
  83. data/lib/bolt/catalog/logging.rb +15 -0
  84. data/lib/bolt/catalog.rb +144 -0
  85. data/lib/bolt/cli.rb +949 -0
  86. data/lib/bolt/config/modulepath.rb +30 -0
  87. data/lib/bolt/config/options.rb +673 -0
  88. data/lib/bolt/config/transport/base.rb +133 -0
  89. data/lib/bolt/config/transport/docker.rb +34 -0
  90. data/lib/bolt/config/transport/jail.rb +33 -0
  91. data/lib/bolt/config/transport/local.rb +39 -0
  92. data/lib/bolt/config/transport/lxd.rb +34 -0
  93. data/lib/bolt/config/transport/options.rb +431 -0
  94. data/lib/bolt/config/transport/orch.rb +41 -0
  95. data/lib/bolt/config/transport/podman.rb +33 -0
  96. data/lib/bolt/config/transport/remote.rb +24 -0
  97. data/lib/bolt/config/transport/ssh.rb +138 -0
  98. data/lib/bolt/config/transport/winrm.rb +63 -0
  99. data/lib/bolt/config.rb +515 -0
  100. data/lib/bolt/container_result.rb +105 -0
  101. data/lib/bolt/error.rb +194 -0
  102. data/lib/bolt/executor.rb +539 -0
  103. data/lib/bolt/fiber_executor.rb +190 -0
  104. data/lib/bolt/inventory/group.rb +446 -0
  105. data/lib/bolt/inventory/inventory.rb +391 -0
  106. data/lib/bolt/inventory/options.rb +139 -0
  107. data/lib/bolt/inventory/target.rb +293 -0
  108. data/lib/bolt/inventory.rb +120 -0
  109. data/lib/bolt/logger.rb +252 -0
  110. data/lib/bolt/module.rb +54 -0
  111. data/lib/bolt/module_installer/installer.rb +44 -0
  112. data/lib/bolt/module_installer/puppetfile/forge_module.rb +54 -0
  113. data/lib/bolt/module_installer/puppetfile/git_module.rb +37 -0
  114. data/lib/bolt/module_installer/puppetfile/module.rb +26 -0
  115. data/lib/bolt/module_installer/puppetfile.rb +131 -0
  116. data/lib/bolt/module_installer/resolver.rb +129 -0
  117. data/lib/bolt/module_installer/specs/forge_spec.rb +91 -0
  118. data/lib/bolt/module_installer/specs/git_spec.rb +150 -0
  119. data/lib/bolt/module_installer/specs/id/base.rb +116 -0
  120. data/lib/bolt/module_installer/specs/id/gitclone.rb +120 -0
  121. data/lib/bolt/module_installer/specs/id/github.rb +90 -0
  122. data/lib/bolt/module_installer/specs/id/gitlab.rb +92 -0
  123. data/lib/bolt/module_installer/specs.rb +95 -0
  124. data/lib/bolt/module_installer.rb +208 -0
  125. data/lib/bolt/node/errors.rb +55 -0
  126. data/lib/bolt/node/output.rb +29 -0
  127. data/lib/bolt/outputter/human.rb +958 -0
  128. data/lib/bolt/outputter/json.rb +205 -0
  129. data/lib/bolt/outputter/logger.rb +76 -0
  130. data/lib/bolt/outputter/rainbow.rb +118 -0
  131. data/lib/bolt/outputter.rb +57 -0
  132. data/lib/bolt/pal/issues.rb +19 -0
  133. data/lib/bolt/pal/logging.rb +17 -0
  134. data/lib/bolt/pal/yaml_plan/evaluator.rb +83 -0
  135. data/lib/bolt/pal/yaml_plan/loader.rb +94 -0
  136. data/lib/bolt/pal/yaml_plan/parameter.rb +63 -0
  137. data/lib/bolt/pal/yaml_plan/step/command.rb +45 -0
  138. data/lib/bolt/pal/yaml_plan/step/download.rb +37 -0
  139. data/lib/bolt/pal/yaml_plan/step/eval.rb +42 -0
  140. data/lib/bolt/pal/yaml_plan/step/message.rb +31 -0
  141. data/lib/bolt/pal/yaml_plan/step/plan.rb +42 -0
  142. data/lib/bolt/pal/yaml_plan/step/resources.rb +170 -0
  143. data/lib/bolt/pal/yaml_plan/step/script.rb +62 -0
  144. data/lib/bolt/pal/yaml_plan/step/task.rb +42 -0
  145. data/lib/bolt/pal/yaml_plan/step/upload.rb +37 -0
  146. data/lib/bolt/pal/yaml_plan/step/verbose.rb +31 -0
  147. data/lib/bolt/pal/yaml_plan/step.rb +223 -0
  148. data/lib/bolt/pal/yaml_plan/transpiler.rb +90 -0
  149. data/lib/bolt/pal/yaml_plan.rb +172 -0
  150. data/lib/bolt/pal.rb +847 -0
  151. data/lib/bolt/plan_creator.rb +219 -0
  152. data/lib/bolt/plan_future.rb +86 -0
  153. data/lib/bolt/plan_result.rb +44 -0
  154. data/lib/bolt/plugin/cache.rb +76 -0
  155. data/lib/bolt/plugin/env_var.rb +54 -0
  156. data/lib/bolt/plugin/module.rb +276 -0
  157. data/lib/bolt/plugin/prompt.rb +36 -0
  158. data/lib/bolt/plugin/puppet_connect_data.rb +84 -0
  159. data/lib/bolt/plugin/puppetdb.rb +124 -0
  160. data/lib/bolt/plugin/task.rb +72 -0
  161. data/lib/bolt/plugin.rb +380 -0
  162. data/lib/bolt/project.rb +219 -0
  163. data/lib/bolt/project_manager/config_migrator.rb +113 -0
  164. data/lib/bolt/project_manager/inventory_migrator.rb +67 -0
  165. data/lib/bolt/project_manager/migrator.rb +39 -0
  166. data/lib/bolt/project_manager/module_migrator.rb +203 -0
  167. data/lib/bolt/project_manager.rb +221 -0
  168. data/lib/bolt/puppetdb/client.rb +153 -0
  169. data/lib/bolt/puppetdb/config.rb +176 -0
  170. data/lib/bolt/puppetdb/instance.rb +146 -0
  171. data/lib/bolt/puppetdb.rb +15 -0
  172. data/lib/bolt/r10k_log_proxy.rb +30 -0
  173. data/lib/bolt/rerun.rb +55 -0
  174. data/lib/bolt/resource_instance.rb +133 -0
  175. data/lib/bolt/result.rb +247 -0
  176. data/lib/bolt/result_set.rb +128 -0
  177. data/lib/bolt/shell/bash/tmpdir.rb +62 -0
  178. data/lib/bolt/shell/bash.rb +516 -0
  179. data/lib/bolt/shell/powershell/snippets.rb +181 -0
  180. data/lib/bolt/shell/powershell.rb +365 -0
  181. data/lib/bolt/shell.rb +105 -0
  182. data/lib/bolt/target.rb +174 -0
  183. data/lib/bolt/task/puppet_server.rb +27 -0
  184. data/lib/bolt/task/run.rb +55 -0
  185. data/lib/bolt/task.rb +163 -0
  186. data/lib/bolt/transport/base.rb +252 -0
  187. data/lib/bolt/transport/docker/connection.rb +150 -0
  188. data/lib/bolt/transport/docker.rb +23 -0
  189. data/lib/bolt/transport/jail/connection.rb +81 -0
  190. data/lib/bolt/transport/jail.rb +21 -0
  191. data/lib/bolt/transport/local/connection.rb +106 -0
  192. data/lib/bolt/transport/local.rb +20 -0
  193. data/lib/bolt/transport/lxd/connection.rb +115 -0
  194. data/lib/bolt/transport/lxd.rb +26 -0
  195. data/lib/bolt/transport/orch/connection.rb +111 -0
  196. data/lib/bolt/transport/orch.rb +271 -0
  197. data/lib/bolt/transport/podman/connection.rb +102 -0
  198. data/lib/bolt/transport/podman.rb +19 -0
  199. data/lib/bolt/transport/remote.rb +41 -0
  200. data/lib/bolt/transport/simple.rb +54 -0
  201. data/lib/bolt/transport/ssh/connection.rb +321 -0
  202. data/lib/bolt/transport/ssh/exec_connection.rb +140 -0
  203. data/lib/bolt/transport/ssh.rb +48 -0
  204. data/lib/bolt/transport/winrm/connection.rb +378 -0
  205. data/lib/bolt/transport/winrm.rb +33 -0
  206. data/lib/bolt/util/format.rb +68 -0
  207. data/lib/bolt/util/puppet_log_level.rb +21 -0
  208. data/lib/bolt/util.rb +465 -0
  209. data/lib/bolt/validator.rb +227 -0
  210. data/lib/bolt/version.rb +5 -0
  211. data/lib/bolt.rb +8 -0
  212. data/lib/bolt_server/acl.rb +39 -0
  213. data/lib/bolt_server/base_config.rb +112 -0
  214. data/lib/bolt_server/config.rb +64 -0
  215. data/lib/bolt_server/file_cache.rb +200 -0
  216. data/lib/bolt_server/request_error.rb +11 -0
  217. data/lib/bolt_server/schemas/action-check_node_connections.json +14 -0
  218. data/lib/bolt_server/schemas/action-run_command.json +12 -0
  219. data/lib/bolt_server/schemas/action-run_script.json +47 -0
  220. data/lib/bolt_server/schemas/action-run_task.json +20 -0
  221. data/lib/bolt_server/schemas/action-upload_file.json +47 -0
  222. data/lib/bolt_server/schemas/partials/target-any.json +10 -0
  223. data/lib/bolt_server/schemas/partials/target-ssh.json +88 -0
  224. data/lib/bolt_server/schemas/partials/target-winrm.json +67 -0
  225. data/lib/bolt_server/schemas/partials/task.json +94 -0
  226. data/lib/bolt_server/schemas/transport-ssh.json +25 -0
  227. data/lib/bolt_server/schemas/transport-winrm.json +19 -0
  228. data/lib/bolt_server/transport_app.rb +554 -0
  229. data/lib/bolt_spec/bolt_context.rb +226 -0
  230. data/lib/bolt_spec/plans/action_stubs/command_stub.rb +51 -0
  231. data/lib/bolt_spec/plans/action_stubs/download_stub.rb +66 -0
  232. data/lib/bolt_spec/plans/action_stubs/plan_stub.rb +55 -0
  233. data/lib/bolt_spec/plans/action_stubs/script_stub.rb +59 -0
  234. data/lib/bolt_spec/plans/action_stubs/task_stub.rb +57 -0
  235. data/lib/bolt_spec/plans/action_stubs/upload_stub.rb +65 -0
  236. data/lib/bolt_spec/plans/action_stubs.rb +196 -0
  237. data/lib/bolt_spec/plans/mock_executor.rb +361 -0
  238. data/lib/bolt_spec/plans/publish_stub.rb +49 -0
  239. data/lib/bolt_spec/plans.rb +190 -0
  240. data/lib/bolt_spec/run.rb +246 -0
  241. data/lib/logging_extensions/logging.rb +13 -0
  242. data/libexec/apply_catalog.rb +130 -0
  243. data/libexec/bolt_catalog +68 -0
  244. data/libexec/custom_facts.rb +63 -0
  245. data/libexec/query_resources.rb +75 -0
  246. data/modules/aggregate/lib/puppet/functions/aggregate/count.rb +21 -0
  247. data/modules/aggregate/lib/puppet/functions/aggregate/nodes.rb +22 -0
  248. data/modules/aggregate/lib/puppet/functions/aggregate/targets.rb +21 -0
  249. data/modules/aggregate/plans/count.pp +56 -0
  250. data/modules/aggregate/plans/targets.pp +56 -0
  251. data/modules/canary/lib/puppet/functions/canary/merge.rb +13 -0
  252. data/modules/canary/lib/puppet/functions/canary/random_split.rb +22 -0
  253. data/modules/canary/lib/puppet/functions/canary/skip.rb +25 -0
  254. data/modules/canary/plans/init.pp +100 -0
  255. data/modules/puppet_connect/plans/test_input_data.pp +94 -0
  256. data/modules/puppetdb_fact/plans/init.pp +20 -0
  257. data/resources/bolt_bash_completion.sh +214 -0
  258. metadata +735 -0
@@ -0,0 +1,378 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../bolt/node/errors'
4
+ require_relative '../../../bolt/node/output'
5
+
6
+ module Bolt
7
+ module Transport
8
+ class WinRM < Simple
9
+ class Connection
10
+ attr_reader :logger, :target
11
+
12
+ def initialize(target, transport_logger)
13
+ raise Bolt::ValidationError, "Target #{target.safe_name} does not have a host" unless target.host
14
+ @target = target
15
+
16
+ default_port = target.options['ssl'] ? HTTPS_PORT : HTTP_PORT
17
+ @port = @target.port || default_port
18
+ @user = @target.user
19
+ # Build set of extensions from extensions config as well as interpreters
20
+
21
+ @logger = Bolt::Logger.logger(@target.safe_name)
22
+ logger.trace("Initializing winrm connection to #{@target.safe_name}")
23
+ @transport_logger = transport_logger
24
+ end
25
+
26
+ HTTP_PORT = 5985
27
+ HTTPS_PORT = 5986
28
+
29
+ def connect
30
+ if target.options['ssl']
31
+ scheme = 'https'
32
+ transport = :ssl
33
+ else
34
+ scheme = 'http'
35
+ transport = :negotiate
36
+ end
37
+
38
+ transport = :kerberos if target.options['realm']
39
+ endpoint = "#{scheme}://#{target.host}:#{@port}/wsman"
40
+ cacert = target.options['cacert'] && target.options['ssl'] ? File.expand_path(target.options['cacert']) : nil
41
+ options = { endpoint: endpoint,
42
+ # https://github.com/WinRb/WinRM/issues/270
43
+ user: target.options['realm'] ? 'dummy' : @user,
44
+ password: target.options['realm'] ? 'dummy' : target.password,
45
+ retry_limit: 1,
46
+ transport: transport,
47
+ basic_auth_only: target.options['basic-auth-only'],
48
+ ca_trust_path: cacert,
49
+ realm: target.options['realm'],
50
+ no_ssl_peer_verification: !target.options['ssl-verify'] }
51
+
52
+ Timeout.timeout(target.options['connect-timeout']) do
53
+ @connection = ::WinRM::Connection.new(options)
54
+ @connection.logger = @transport_logger
55
+
56
+ @connection.shell(:powershell) do |session|
57
+ session.run('$PSVersionTable.PSVersion')
58
+ end
59
+
60
+ @logger.trace { "Opened connection" }
61
+ end
62
+ rescue Timeout::Error
63
+ # If we're using the default port with SSL, a timeout probably means the
64
+ # host doesn't support SSL.
65
+ if target.options['ssl'] && @port == HTTPS_PORT
66
+ the_problem = "\nVerify that required WinRM ports are open, " \
67
+ "or use --no-ssl if this host isn't configured to use SSL for WinRM."
68
+ end
69
+ raise Bolt::Node::ConnectError.new(
70
+ "Timeout after #{target.options['connect-timeout']} seconds connecting to #{endpoint}#{the_problem}",
71
+ 'CONNECT_ERROR'
72
+ )
73
+ rescue ::WinRM::WinRMAuthorizationError
74
+ raise Bolt::Node::ConnectError.new(
75
+ "Authentication failed for #{endpoint}",
76
+ 'AUTH_ERROR'
77
+ )
78
+ rescue OpenSSL::SSL::SSLError => e
79
+ # If we're using SSL with the default non-SSL port, mention that as a likely problem
80
+ if target.options['ssl'] && @port == HTTP_PORT
81
+ theres_your_problem = "\nAre you using SSL to connect to a non-SSL port?"
82
+ end
83
+ if target.options['ssl-verify'] && e.message.include?('certificate verify failed')
84
+ theres_your_problem = "\nIs the remote host using a self-signed SSL "\
85
+ "certificate? Use --no-ssl-verify to disable "\
86
+ "remote host SSL verification."
87
+ end
88
+ raise Bolt::Node::ConnectError.new(
89
+ "Failed to connect to #{endpoint}: #{e.message}#{theres_your_problem}",
90
+ "CONNECT_ERROR"
91
+ )
92
+ rescue StandardError => e
93
+ raise Bolt::Node::ConnectError.new(
94
+ "Failed to connect to #{endpoint}: #{e.message}",
95
+ 'CONNECT_ERROR'
96
+ )
97
+ end
98
+
99
+ def disconnect
100
+ @client&.disconnect!
101
+ @logger.trace { "Closed connection" }
102
+ end
103
+
104
+ def execute(command)
105
+ @logger.trace { "Executing command: #{command}" }
106
+
107
+ inp = StringIO.new
108
+ # This transport doesn't accept stdin, so close the stream to ensure
109
+ # it will fail if the shell attempts to provide stdin
110
+ inp.close
111
+
112
+ out_rd, out_wr = IO.pipe('UTF-8')
113
+ err_rd, err_wr = IO.pipe('UTF-8')
114
+ th = Thread.new do
115
+ # By default, any exception raised in a thread will be reported to
116
+ # stderr as a stacktrace. Since we know these errors are going to
117
+ # propagate to the main thread via the shell, there's no chance
118
+ # they will be unhandled, so the default stack trace is unneeded.
119
+ Thread.current.report_on_exception = false
120
+
121
+ # Open a new shell instance for each command executed. PowerShell is
122
+ # unable to unload any DLLs loaded when running a PowerShell script
123
+ # or task from the same shell instance they were loaded in, which
124
+ # prevents Bolt from cleaning up the temp directory successfully.
125
+ # Using a new PowerShell instance avoids this limitation.
126
+ @connection.shell(:powershell) do |session|
127
+ result = session.run(command)
128
+ out_wr << result.stdout
129
+ err_wr << result.stderr
130
+ result.exitcode
131
+ end
132
+ ensure
133
+ # Close the streams to avoid the caller deadlocking
134
+ out_wr.close
135
+ err_wr.close
136
+ end
137
+
138
+ [inp, out_rd, err_rd, th]
139
+ rescue Errno::EMFILE => e
140
+ msg = "#{e.message}. This might be resolved by increasing your user limit "\
141
+ "with 'ulimit -n 1024'. See https://puppet.com/docs/bolt/latest/bolt_known_issues.html for details."
142
+ raise Bolt::Error.new(msg, 'bolt/too-many-files')
143
+ rescue StandardError
144
+ @logger.trace { "Command aborted" }
145
+ raise
146
+ end
147
+
148
+ def upload_file(source, destination)
149
+ @logger.trace { "Uploading #{source} to #{destination}" }
150
+ if target.options['file-protocol'] == 'smb'
151
+ upload_file_smb(source, destination)
152
+ else
153
+ upload_file_winrm(source, destination)
154
+ end
155
+ end
156
+
157
+ def upload_file_winrm(source, destination)
158
+ fs = ::WinRM::FS::FileManager.new(@connection)
159
+ fs.upload(source, destination)
160
+ rescue StandardError => e
161
+ raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
162
+ end
163
+
164
+ def upload_file_smb(source, destination)
165
+ # lazy-load expensive gem code
166
+ require 'ruby_smb'
167
+
168
+ win_dest = destination.tr('/', '\\')
169
+ if (md = win_dest.match(/^([a-z]):\\(.*)/i))
170
+ # if drive, use admin share for that drive, so path is '\\host\C$'
171
+ path = "\\\\#{@target.host}\\#{md[1]}$"
172
+ dest = md[2]
173
+ elsif (md = win_dest.match(/^(\\\\[^\\]+\\[^\\]+)\\(.*)/))
174
+ # if unc, path is '\\host\share'
175
+ path = md[1]
176
+ dest = md[2]
177
+ else
178
+ raise ArgumentError, "Unknown destination '#{destination}'"
179
+ end
180
+
181
+ client = smb_client_login
182
+ tree = client.tree_connect(path)
183
+ begin
184
+ upload_file_smb_recursive(tree, source, dest)
185
+ ensure
186
+ tree.disconnect!
187
+ end
188
+ rescue ::RubySMB::Error::UnexpectedStatusCode => e
189
+ raise Bolt::Node::FileError.new("SMB Error: #{e.message}", 'WRITE_ERROR')
190
+ rescue StandardError => e
191
+ raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
192
+ end
193
+
194
+ def download_file(source, destination, download)
195
+ @logger.trace { "Downloading #{source} to #{destination}" }
196
+ if target.options['file-protocol'] == 'smb'
197
+ download_file_smb(source, destination)
198
+ else
199
+ download_file_winrm(source, destination, download)
200
+ end
201
+ end
202
+
203
+ def download_file_winrm(source, destination, download)
204
+ # The winrm gem doesn't create the destination directory if it's missing,
205
+ # so create it here
206
+ FileUtils.mkdir_p(destination)
207
+ fs = ::WinRM::FS::FileManager.new(@connection)
208
+ # params: source, destination, chunksize, first
209
+ # first needs to be set to false, otherwise if the source is a directory it
210
+ # will be nested inside a directory with the same name
211
+ fs.download(source, download, 1024 * 1024, false)
212
+ rescue StandardError => e
213
+ raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
214
+ end
215
+
216
+ def download_file_smb(source, destination)
217
+ # lazy-load expensive gem code
218
+ require 'ruby_smb'
219
+
220
+ win_source = source.tr('/', '\\')
221
+ if (md = win_source.match(/^([a-z]):\\(.*)/i))
222
+ # if drive, use admin share for that drive, so path is '\\host\C$'
223
+ path = "\\\\#{@target.host}\\#{md[1]}$"
224
+ src = md[2]
225
+ elsif (md = win_source.match(/^(\\\\[^\\]+\\[^\\]+)\\(.*)/))
226
+ # if unc, path is '\\host\share'
227
+ path = md[1]
228
+ src = md[2]
229
+ else
230
+ raise ArgumentError, "Unknown source '#{source}'"
231
+ end
232
+
233
+ client = smb_client_login
234
+ tree = client.tree_connect(path)
235
+
236
+ begin
237
+ # Make sure the root download directory for the target exists
238
+ FileUtils.mkdir_p(destination)
239
+ download_file_smb_recursive(tree, src, destination)
240
+ ensure
241
+ tree.disconnect!
242
+ end
243
+ rescue ::RubySMB::Error::UnexpectedStatusCode => e
244
+ raise Bolt::Node::FileError.new("SMB Error: #{e.message}", 'DOWNLOAD_ERROR')
245
+ rescue StandardError => e
246
+ raise Bolt::Node::FileError.new(e.message, 'DOWNLOAD_ERROR')
247
+ end
248
+
249
+ def shell
250
+ @shell ||= Bolt::Shell::Powershell.new(target, self)
251
+ end
252
+
253
+ def max_command_length
254
+ nil
255
+ end
256
+
257
+ private
258
+
259
+ def smb_client_login
260
+ return @client if @client
261
+
262
+ dispatcher = RubySMB::Dispatcher::Socket.new(smb_socket_connect)
263
+ @client = RubySMB::Client.new(dispatcher, smb1: false, smb2: true, username: @user, password: target.password)
264
+ status = @client.login
265
+ case status
266
+ when WindowsError::NTStatus::STATUS_SUCCESS
267
+ @logger.trace { "Connected to #{@client.dns_host_name}" }
268
+ when WindowsError::NTStatus::STATUS_LOGON_FAILURE
269
+ raise Bolt::Node::ConnectError.new(
270
+ "SMB authentication failed for #{target.safe_name}",
271
+ 'AUTH_ERROR'
272
+ )
273
+ else
274
+ raise Bolt::Node::ConnectError.new(
275
+ "Failed to connect to #{target.safe_name} using SMB: #{status.description}",
276
+ 'CONNECT_ERROR'
277
+ )
278
+ end
279
+
280
+ @client
281
+ end
282
+
283
+ SMB_PORT = 445
284
+
285
+ def smb_socket_connect
286
+ # It's lame that TCPSocket doesn't take a connect timeout
287
+ # Using Timeout.timeout is bad, but is done elsewhere...
288
+ Timeout.timeout(target.options['connect-timeout']) do
289
+ TCPSocket.new(target.host, target.options['smb-port'] || SMB_PORT)
290
+ end
291
+ rescue Errno::ECONNREFUSED => e
292
+ # handle this to prevent obscuring error message as SMB problem
293
+ raise Bolt::Node::ConnectError.new(
294
+ "Failed to connect to #{target.safe_name} using SMB: #{e.message}",
295
+ 'CONNECT_ERROR'
296
+ )
297
+ rescue Timeout::Error
298
+ raise Bolt::Node::ConnectError.new(
299
+ "Timeout after #{target.options['connect-timeout']} seconds connecting to #{target.safe_name}",
300
+ 'CONNECT_ERROR'
301
+ )
302
+ end
303
+
304
+ def upload_file_smb_recursive(tree, source, dest)
305
+ if Dir.exist?(source)
306
+ tree.open_directory(directory: dest, write: true, disposition: ::RubySMB::Dispositions::FILE_OPEN_IF)
307
+
308
+ Dir.children(source).each do |child|
309
+ child_dest = dest + '\\' + child
310
+ upload_file_smb_recursive(tree, File.join(source, child), child_dest)
311
+ end
312
+ return
313
+ end
314
+
315
+ file = tree.open_file(filename: dest, write: true, disposition: ::RubySMB::Dispositions::FILE_OVERWRITE_IF)
316
+ begin
317
+ # `file` doesn't derive from IO, so can't use IO.copy_stream
318
+ File.open(source, 'rb') do |f|
319
+ pos = 0
320
+ while (buf = f.read(8 * 1024 * 1024))
321
+ file.write(data: buf, offset: pos)
322
+ pos += buf.length
323
+ end
324
+ end
325
+ ensure
326
+ file.close
327
+ end
328
+ end
329
+
330
+ def download_file_smb_recursive(tree, source, destination)
331
+ dest = File.expand_path(Bolt::Util.windows_basename(source), destination)
332
+
333
+ # Check if the source is a directory by attempting to list its children.
334
+ # If the source is a directory, create the directory on the host and then
335
+ # recurse through the children.
336
+ if (children = list_directory_children_smb(tree, source))
337
+ FileUtils.mkdir_p(dest)
338
+
339
+ children.each do |child|
340
+ # File names are encoded UTF_16LE.
341
+ filename = child.file_name.encode(Encoding::UTF_8)
342
+
343
+ next if %w[. ..].include?(filename)
344
+
345
+ src = source + '\\' + filename
346
+ download_file_smb_recursive(tree, src, dest)
347
+ end
348
+ # If the source wasn't a directory and just returns 'STATUS_NOT_A_DIRECTORY, then
349
+ # it is a file. Write it to the host.
350
+ else
351
+ begin
352
+ file = tree.open_file(filename: source)
353
+ data = file.read
354
+
355
+ # Files may be encoded UTF_16LE
356
+ data = data.encode(Encoding::UTF_8) if data.encoding == Encoding::UTF_16LE
357
+
358
+ File.write(dest, data)
359
+ ensure
360
+ file.close
361
+ end
362
+ end
363
+ end
364
+
365
+ # Lists the children of a directory using rb_smb
366
+ # Returns an array of RubySMB::Fscc::FileInformation::FileIdFullDirectoryInformation objects
367
+ # if the source is a directory, or raises RubySMB::Error::UnexpectedStatusCode otherwise.
368
+ def list_directory_children_smb(tree, source)
369
+ tree.list(directory: source)
370
+ rescue RubySMB::Error::UnexpectedStatusCode => e
371
+ unless e.message == 'STATUS_NOT_A_DIRECTORY'
372
+ raise e
373
+ end
374
+ end
375
+ end
376
+ end
377
+ end
378
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../bolt/node/errors'
4
+ require_relative '../../bolt/transport/base'
5
+
6
+ module Bolt
7
+ module Transport
8
+ class WinRM < Simple
9
+ def initialize
10
+ super
11
+ require 'winrm'
12
+ require 'winrm-fs'
13
+
14
+ @transport_logger = Bolt::Logger.logger(::WinRM)
15
+ @transport_logger.level = :warn
16
+ end
17
+
18
+ def with_connection(target)
19
+ conn = Connection.new(target, @transport_logger)
20
+ conn.connect
21
+ yield conn
22
+ ensure
23
+ begin
24
+ conn&.disconnect
25
+ rescue StandardError => e
26
+ logger.info("Failed to close connection to #{target.safe_name} : #{e.message}")
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ require_relative 'winrm/connection'
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bolt
4
+ module Util
5
+ module Format
6
+ class << self
7
+ # Stringifies an object, formatted as valid JSON.
8
+ #
9
+ # @param message [Object] The object to stringify.
10
+ # @return [String] The JSON string.
11
+ #
12
+ def stringify(message)
13
+ formatted = format_message(message)
14
+ if formatted.is_a?(Hash) || formatted.is_a?(Array)
15
+ ::JSON.pretty_generate(formatted)
16
+ else
17
+ formatted
18
+ end
19
+ end
20
+
21
+ # Recursively formats an object into a format that can be represented by
22
+ # JSON.
23
+ #
24
+ # @param message [Object] The object to stringify.
25
+ # @return [Array, Hash, String]
26
+ #
27
+ private def format_message(message)
28
+ case message
29
+ when Array
30
+ message.map { |item| format_message(item) }
31
+ when Bolt::ApplyResult
32
+ format_apply_result(message)
33
+ when Bolt::Result, Bolt::ResultSet
34
+ # This is equivalent to to_s, but formattable
35
+ message.to_data
36
+ when Bolt::RunFailure
37
+ formatted_resultset = message.result_set.to_data
38
+ message.to_h.merge('result_set' => formatted_resultset)
39
+ when Hash
40
+ message.each_with_object({}) do |(k, v), h|
41
+ h[format_message(k)] = format_message(v)
42
+ end
43
+ when Integer, Float, NilClass
44
+ message
45
+ else
46
+ message.to_s
47
+ end
48
+ end
49
+
50
+ # Formats a Bolt::ApplyResult object.
51
+ #
52
+ # @param result [Bolt::ApplyResult] The apply result.
53
+ # @return [Hash]
54
+ #
55
+ private def format_apply_result(result)
56
+ logs = result.resource_logs&.map do |log|
57
+ # Omit low-level info/debug messages
58
+ next if %w[info debug].include?(log['level'])
59
+ indent(2, format_log(log))
60
+ end
61
+ hash = result.to_data
62
+ hash['logs'] = logs unless logs.empty?
63
+ hash
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bolt
4
+ module Util
5
+ module PuppetLogLevel
6
+ MAPPING = {
7
+ # Demote Puppet's logs by one level, since Puppet is an implementation detail of Bolt
8
+ debug: :trace,
9
+ info: :debug,
10
+ notice: :info,
11
+ warning: :warn,
12
+ err: :error,
13
+ # The following are used by Puppet functions of the same name, and are all treated as
14
+ # error types in the Windows EventLog and log colors.
15
+ alert: :error,
16
+ emerg: :fatal,
17
+ crit: :fatal
18
+ }.freeze
19
+ end
20
+ end
21
+ end