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,365 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'powershell/snippets'
4
+
5
+ module Bolt
6
+ class Shell
7
+ class Powershell < Shell
8
+ DEFAULT_EXTENSIONS = Set.new(%w[.ps1 .rb .pp])
9
+ PS_ARGS = %w[-NoProfile -NonInteractive -NoLogo -ExecutionPolicy Bypass].freeze
10
+
11
+ def initialize(target, conn)
12
+ super
13
+
14
+ extensions = [target.options['extensions'] || []].flatten.map { |ext| ext[0] == '.' ? ext : '.' + ext }
15
+ extensions += target.options['interpreters'].keys if target.options['interpreters']
16
+ @extensions = DEFAULT_EXTENSIONS + extensions
17
+ validate_ps_version
18
+ end
19
+
20
+ def validate_ps_version
21
+ version = execute("$PSVersionTable.PSVersion.Major").stdout.string.chomp
22
+ if !version.empty? && version.to_i < 3
23
+ # This lets us know how many targets have Powershell 2, and lets the
24
+ # user know how many targets they have with PS2
25
+ msg = "Detected PowerShell 2 on one or more targets.\nPowerShell 2 "\
26
+ "is unsupported. See bolt-debug.log or run with '--log-level debug' to see the full "\
27
+ "list of targets with PowerShell 2."
28
+
29
+ Bolt::Logger.deprecate_once("powershell_2", msg)
30
+ @logger.debug("Detected PowerShell 2 on #{target}.")
31
+ end
32
+ end
33
+
34
+ def provided_features
35
+ ['powershell']
36
+ end
37
+
38
+ def default_input_method(executable)
39
+ powershell_file?(executable) ? 'powershell' : 'both'
40
+ end
41
+
42
+ def powershell_file?(path)
43
+ File.extname(path).downcase == '.ps1'
44
+ end
45
+
46
+ def validate_extensions(ext)
47
+ unless @extensions.include?(ext)
48
+ raise Bolt::Node::FileError.new("File extension #{ext} is not enabled, "\
49
+ "to run it please add to 'winrm: extensions'", 'FILETYPE_ERROR')
50
+ end
51
+ end
52
+
53
+ def process_from_extension(path)
54
+ case Pathname(path).extname.downcase
55
+ when '.rb'
56
+ [
57
+ 'ruby.exe',
58
+ %W[-S "#{path}"]
59
+ ]
60
+ when '.ps1'
61
+ [
62
+ 'powershell.exe',
63
+ [*PS_ARGS, '-File', path]
64
+ ]
65
+ when '.pp'
66
+ [
67
+ 'puppet.bat',
68
+ %W[apply "#{path}"]
69
+ ]
70
+ else
71
+ # Run the script via cmd, letting Windows extension handling determine how
72
+ [
73
+ 'cmd.exe',
74
+ %W[/c "#{path}"]
75
+ ]
76
+ end
77
+ end
78
+
79
+ def escape_arguments(arguments)
80
+ arguments.map do |arg|
81
+ if arg =~ / /
82
+ "\"#{arg}\""
83
+ else
84
+ arg
85
+ end
86
+ end
87
+ end
88
+
89
+ def env_declarations(env_vars)
90
+ env_vars.map do |var, val|
91
+ "[Environment]::SetEnvironmentVariable('#{var}', @'\n#{val}\n'@)"
92
+ end
93
+ end
94
+
95
+ def quote_string(string)
96
+ "'" + string.gsub("'", "''") + "'"
97
+ end
98
+
99
+ def write_executable(dir, file, filename = nil)
100
+ filename ||= File.basename(file)
101
+ validate_extensions(File.extname(filename))
102
+ destination = "#{dir}\\#{filename}"
103
+ conn.upload_file(file, destination)
104
+ destination
105
+ end
106
+
107
+ def execute_process(path, arguments, stdin = nil)
108
+ quoted_args = arguments.map { |arg| quote_string(arg) }.join(' ')
109
+
110
+ quoted_path = if path =~ /^'.*'$/ || path =~ /^".*"$/
111
+ path
112
+ else
113
+ quote_string(path)
114
+ end
115
+ exec_cmd =
116
+ if stdin.nil?
117
+ "& #{quoted_path} #{quoted_args}"
118
+ else
119
+ <<~STR
120
+ $command_stdin = @'
121
+ #{stdin}
122
+ '@
123
+
124
+ $command_stdin | & #{quoted_path} #{quoted_args}
125
+ STR
126
+ end
127
+ Snippets.execute_process(exec_cmd)
128
+ end
129
+
130
+ def mkdirs(dirs)
131
+ paths = dirs.uniq.sort.join('","')
132
+ mkdir_command = "mkdir -Force -Path (\"#{paths}\")"
133
+ result = execute(mkdir_command)
134
+ if result.exit_code != 0
135
+ message = "Could not create directories: #{result.stderr.string}"
136
+ raise Bolt::Node::FileError.new(message, 'MKDIR_ERROR')
137
+ end
138
+ end
139
+
140
+ def make_tmpdir
141
+ find_parent = target.options['tmpdir'] ? "\"#{target.options['tmpdir']}\"" : '[System.IO.Path]::GetTempPath()'
142
+ result = execute(Snippets.make_tmpdir(find_parent))
143
+ if result.exit_code != 0
144
+ raise Bolt::Node::FileError.new("Could not make tmpdir: #{result.stderr.string}", 'TMPDIR_ERROR')
145
+ end
146
+ result.stdout.string.chomp
147
+ end
148
+
149
+ def rmdir(dir)
150
+ execute(Snippets.rmdir(dir))
151
+ end
152
+
153
+ def with_tmpdir
154
+ unless @tmpdir
155
+ # Only cleanup the directory afterward if we made it to begin with
156
+ owner = true
157
+ @tmpdir = make_tmpdir
158
+ end
159
+ yield @tmpdir
160
+ ensure
161
+ if owner && @tmpdir
162
+ if target.options['cleanup']
163
+ rmdir(@tmpdir)
164
+ else
165
+ Bolt::Logger.warn("Skipping cleanup of tmpdir '#{@tmpdir}'", "skip_cleanup")
166
+ end
167
+ end
168
+ end
169
+
170
+ def run_ps_task(task_path, arguments, input_method)
171
+ # NOTE: cannot redirect STDIN to a .ps1 script inside of PowerShell
172
+ # must create new powershell.exe process like other interpreters
173
+ # fortunately, using PS with stdin input_method should never happen
174
+ if input_method == 'powershell'
175
+ Snippets.ps_task(task_path, arguments)
176
+ else
177
+ Snippets.try_catch(task_path)
178
+ end
179
+ end
180
+
181
+ def upload(source, destination, _options = {})
182
+ conn.upload_file(source, destination)
183
+ Bolt::Result.for_upload(target, source, destination)
184
+ end
185
+
186
+ def download(source, destination, _options = {})
187
+ download = File.join(destination, Bolt::Util.windows_basename(source))
188
+ conn.download_file(source, destination, download)
189
+ Bolt::Result.for_download(target, source, destination, download)
190
+ end
191
+
192
+ def run_command(command, options = {}, position = [])
193
+ command = [*env_declarations(options[:env_vars]), command].join("\r\n") if options[:env_vars]
194
+
195
+ wrap_command = conn.is_a?(Bolt::Transport::Local::Connection)
196
+ output = execute(command, wrap_command)
197
+ Bolt::Result.for_command(target,
198
+ output.to_h,
199
+ 'command',
200
+ command,
201
+ position)
202
+ end
203
+
204
+ def run_script(script, arguments, options = {}, position = [])
205
+ # unpack any Sensitive data
206
+ arguments = unwrap_sensitive_args(arguments)
207
+ with_tmpdir do |dir|
208
+ script_path = write_executable(dir, script)
209
+ command = if powershell_file?(script_path) && options[:pwsh_params]
210
+ # Scripts run with pwsh_params can be run like tasks
211
+ Snippets.ps_task(script_path, options[:pwsh_params])
212
+ elsif powershell_file?(script_path)
213
+ Snippets.run_script(arguments, script_path)
214
+ else
215
+ interpreter = select_interpreter(script_path, target.options['interpreters'])
216
+ if options[:script_interpreter] && interpreter
217
+ # interpreter can be a String or Array here. Cast it to an array.
218
+ interpreter_array = Array(interpreter)
219
+ # Make path the first part of the array - this should be the binary
220
+ path = interpreter_array.shift
221
+ # Anything else in interpreters should get prepended to
222
+ # the command. If interpreters was a string this will
223
+ # just be [script_path]
224
+ args = escape_arguments(interpreter_array + Array(script_path))
225
+ logger.trace("Running '#{script_path}' using '#{interpreter}' interpreter")
226
+ else
227
+ path, args = *process_from_extension(script_path)
228
+ end
229
+ args += escape_arguments(arguments)
230
+ execute_process(path, args)
231
+ end
232
+ env_assignments = options[:env_vars] ? env_declarations(options[:env_vars]) : []
233
+ shell_init = options[:pwsh_params] ? Snippets.shell_init : ''
234
+
235
+ output = execute([shell_init, *env_assignments, command].join("\r\n"))
236
+
237
+ Bolt::Result.for_command(target,
238
+ output.to_h,
239
+ 'script',
240
+ script,
241
+ position)
242
+ end
243
+ end
244
+
245
+ def run_task(task, arguments, _options = {}, position = [])
246
+ implementation = select_implementation(target, task)
247
+ executable = implementation['path']
248
+ input_method = implementation['input_method']
249
+ extra_files = implementation['files']
250
+ input_method ||= powershell_file?(executable) ? 'powershell' : 'both'
251
+
252
+ # unpack any Sensitive data
253
+ arguments = unwrap_sensitive_args(arguments)
254
+ with_tmpdir do |dir|
255
+ if extra_files.empty?
256
+ task_dir = dir
257
+ else
258
+ # TODO: optimize upload of directories
259
+ arguments['_installdir'] = dir
260
+ task_dir = File.join(dir, task.tasks_dir)
261
+ mkdirs([task_dir] + extra_files.map { |file| File.join(dir, File.dirname(file['name'])) })
262
+ extra_files.each do |file|
263
+ conn.upload_file(file['path'], File.join(dir, file['name']))
264
+ end
265
+ end
266
+
267
+ task_path = write_executable(task_dir, executable)
268
+
269
+ if Bolt::Task::STDIN_METHODS.include?(input_method)
270
+ stdin = JSON.dump(arguments)
271
+ end
272
+
273
+ command = if powershell_file?(task_path) && stdin.nil?
274
+ run_ps_task(task_path, arguments, input_method)
275
+ else
276
+ if (interpreter = select_interpreter(task_path, target.options['interpreters']))
277
+ # interpreter can be a String or Array here. Cast it to an array.
278
+ interpreter_array = Array(interpreter)
279
+ # Make path the first part of the array - this should be the binary
280
+ path = interpreter_array.shift
281
+ # Anything else in interpreters should get prepended to
282
+ # the command. If interpreters was a string this will
283
+ # just be [task_path]
284
+ args = interpreter_array + [task_path]
285
+ else
286
+ path, args = *process_from_extension(task_path)
287
+ end
288
+ execute_process(path, args, stdin)
289
+ end
290
+
291
+ env_assignments = if Bolt::Task::ENVIRONMENT_METHODS.include?(input_method)
292
+ env_declarations(envify_params(arguments))
293
+ else
294
+ []
295
+ end
296
+
297
+ output = execute([
298
+ Snippets.shell_init,
299
+ Snippets.append_ps_module_path(dir),
300
+ *env_assignments,
301
+ command
302
+ ].join("\n"))
303
+
304
+ Bolt::Result.for_task(target, output.stdout.string,
305
+ output.stderr.string,
306
+ output.exit_code,
307
+ task.name,
308
+ position)
309
+ end
310
+ end
311
+
312
+ def execute(command, wrap_command = false)
313
+ if (conn.max_command_length && command.length > conn.max_command_length) ||
314
+ wrap_command
315
+ return with_tmpdir do |dir|
316
+ command += "\r\nif (!$?) { if($LASTEXITCODE) { exit $LASTEXITCODE } else { exit 1 } }"
317
+ script_file = File.join(dir, "#{SecureRandom.uuid}_wrapper.ps1")
318
+ conn.upload_file(StringIO.new(command), script_file)
319
+ args = escape_arguments([script_file])
320
+ script_invocation = ['powershell.exe', *PS_ARGS, '-File', *args].join(' ')
321
+ execute(script_invocation)
322
+ end
323
+ end
324
+ inp, out, err, t = conn.execute(command)
325
+
326
+ result = Bolt::Node::Output.new
327
+ inp.close
328
+ stdout = Thread.new do
329
+ # Set to binmode to preserve \r\n line endings, but save and restore
330
+ # the proper encoding so the string isn't later misinterpreted
331
+ encoding = out.external_encoding
332
+ out.binmode
333
+ to_print = out.read.force_encoding(encoding)
334
+ if !to_print.chomp.empty? && @stream_logger
335
+ formatted = to_print.lines.map do |msg|
336
+ "[#{@target.safe_name}] out: #{msg.chomp}"
337
+ end.join("\n")
338
+ @stream_logger.warn(formatted)
339
+ end
340
+ result.stdout << to_print
341
+ result.merged_output << to_print
342
+ end
343
+ stderr = Thread.new do
344
+ encoding = err.external_encoding
345
+ err.binmode
346
+ to_print = err.read.force_encoding(encoding)
347
+ if !to_print.chomp.empty? && @stream_logger
348
+ formatted = to_print.lines.map do |msg|
349
+ "[#{@target.safe_name}] err: #{msg.chomp}"
350
+ end.join("\n")
351
+ @stream_logger.warn(formatted)
352
+ end
353
+ result.stderr << to_print
354
+ result.merged_output << to_print
355
+ end
356
+
357
+ stdout.join
358
+ stderr.join
359
+ result.exit_code = t.value.respond_to?(:exitstatus) ? t.value.exitstatus : t.value
360
+
361
+ result
362
+ end
363
+ end
364
+ end
365
+ end
data/lib/bolt/shell.rb ADDED
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bolt
4
+ class Shell
5
+ attr_reader :target, :conn, :logger
6
+
7
+ def initialize(target, conn)
8
+ @target = target
9
+ @conn = conn
10
+ @logger = Bolt::Logger.logger(@target.safe_name)
11
+
12
+ if Bolt::Logger.stream
13
+ Bolt::Logger.warn_once("stream_experimental",
14
+ "The 'stream' option is experimental, and might "\
15
+ "include breaking changes between minor versions.")
16
+ @stream_logger = Bolt::Logger.logger(:stream)
17
+ # Don't send stream messages to the parent logger
18
+ @stream_logger.additive = false
19
+
20
+ # Log stream messages without any other data or color
21
+ pattern = Logging.layouts.pattern(pattern: '%m\n')
22
+ @stream_logger.appenders = Logging.appenders.stdout(
23
+ 'console',
24
+ layout: pattern
25
+ )
26
+ end
27
+ end
28
+
29
+ def run_command(*_args)
30
+ raise NotImplementedError, "run_command() must be implemented by the shell class"
31
+ end
32
+
33
+ def upload(*_args)
34
+ raise NotImplementedError, "upload() must be implemented by the shell class"
35
+ end
36
+
37
+ def run_script(*_args)
38
+ raise NotImplementedError, "run_script() must be implemented by the shell class"
39
+ end
40
+
41
+ def run_task(*_args)
42
+ raise NotImplementedError, "run_task() must be implemented by the shell class"
43
+ end
44
+
45
+ def provided_features
46
+ []
47
+ end
48
+
49
+ def default_input_method(_executable)
50
+ 'both'
51
+ end
52
+
53
+ # The above methods are the API that must be implemented by a Shell. Below
54
+ # are helper methods.
55
+
56
+ def select_implementation(target, task)
57
+ impl = task.select_implementation(target, provided_features)
58
+ impl['input_method'] ||= default_input_method(impl['path'])
59
+ impl
60
+ end
61
+
62
+ def select_interpreter(executable, interpreters)
63
+ interpreters[Pathname(executable).extname] if interpreters
64
+ end
65
+
66
+ # Transform a parameter map to an environment variable map, with parameter names prefixed
67
+ # with 'PT_' and values transformed to JSON unless they're strings.
68
+ def envify_params(params)
69
+ params.each_with_object({}) do |(k, v), h|
70
+ v = v.to_json unless v.is_a?(String)
71
+ h["PT_#{k}"] = v
72
+ end
73
+ end
74
+
75
+ # Unwraps any Sensitive data in an arguments Hash, so the plain-text is passed
76
+ # to the Task/Script.
77
+ #
78
+ # This works on deeply nested data structures composed of Hashes, Arrays, and
79
+ # and plain-old data types (int, string, etc).
80
+ def unwrap_sensitive_args(arguments)
81
+ # Skip this if Puppet isn't loaded
82
+ return arguments unless defined?(Puppet::Pops::Types::PSensitiveType::Sensitive)
83
+
84
+ case arguments
85
+ when Array
86
+ # iterate over the array, unwrapping all elements
87
+ arguments.map { |x| unwrap_sensitive_args(x) }
88
+ when Hash
89
+ # iterate over the arguments hash and unwrap all keys and values
90
+ arguments.each_with_object({}) { |(k, v), h|
91
+ h[unwrap_sensitive_args(k)] = unwrap_sensitive_args(v)
92
+ }
93
+ when Puppet::Pops::Types::PSensitiveType::Sensitive
94
+ # this value is Sensitive, unwrap it
95
+ unwrap_sensitive_args(arguments.unwrap)
96
+ else
97
+ # unknown data type, just return it
98
+ arguments
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ require_relative 'shell/bash'
105
+ require_relative 'shell/powershell'
@@ -0,0 +1,174 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../bolt/error'
4
+ require_relative '../bolt/util'
5
+
6
+ module Bolt
7
+ class Target
8
+ attr_accessor :inventory
9
+
10
+ # Target.new from a data hash
11
+ def self.from_hash(hash, inventory)
12
+ target = inventory.create_target_from_hash(hash)
13
+ new(target.name, inventory)
14
+ end
15
+
16
+ # Target.new from a plan initialized with a hash
17
+ def self.from_asserted_hash(hash)
18
+ inventory = Puppet.lookup(:bolt_inventory)
19
+ from_hash(hash, inventory)
20
+ end
21
+
22
+ # TODO: Disallow any positional argument other than URI.
23
+ # Target.new from a plan with just a uri. Puppet requires the arguments to
24
+ # this method to match (by name) the attributes defined on the datatype.
25
+ # rubocop:disable Lint/UnusedMethodArgument
26
+ def self.from_asserted_args(uri = nil,
27
+ name = nil,
28
+ safe_name = nil,
29
+ target_alias = nil,
30
+ config = nil,
31
+ facts = nil,
32
+ vars = nil,
33
+ features = nil,
34
+ plugin_hooks = nil,
35
+ resources = nil)
36
+ from_asserted_hash('uri' => uri)
37
+ end
38
+ # rubocop:enable Lint/UnusedMethodArgument
39
+
40
+ def initialize(name, inventory = nil)
41
+ @name = name
42
+ @inventory = inventory
43
+ end
44
+
45
+ # features returns an array to be compatible with plans
46
+ def features
47
+ @inventory.features(self).to_a
48
+ end
49
+
50
+ # Use feature_set internally to access set
51
+ def feature_set
52
+ @inventory.features(self)
53
+ end
54
+
55
+ def vars
56
+ @inventory.vars(self)
57
+ end
58
+
59
+ def facts
60
+ @inventory.facts(self)
61
+ end
62
+
63
+ def to_s
64
+ safe_name
65
+ end
66
+
67
+ def config
68
+ inventory_target.config
69
+ end
70
+
71
+ def safe_name
72
+ inventory_target.safe_name
73
+ end
74
+
75
+ def target_alias
76
+ inventory_target.target_alias
77
+ end
78
+
79
+ def resources
80
+ inventory_target.resources
81
+ end
82
+
83
+ # rubocop:disable Naming/AccessorMethodName
84
+ def set_resource(resource)
85
+ inventory_target.set_resource(resource)
86
+ end
87
+ # rubocop:enable Naming/AccessorMethodName
88
+
89
+ def to_h
90
+ options.to_h.merge(
91
+ 'name' => name,
92
+ 'uri' => uri,
93
+ 'protocol' => protocol,
94
+ 'user' => user,
95
+ 'password' => password,
96
+ 'host' => host,
97
+ 'port' => port
98
+ )
99
+ end
100
+
101
+ def detail
102
+ {
103
+ 'name' => name,
104
+ 'uri' => uri,
105
+ 'alias' => target_alias,
106
+ 'config' => {
107
+ 'transport' => transport,
108
+ transport => options.to_h
109
+ },
110
+ 'vars' => vars,
111
+ 'features' => features,
112
+ 'facts' => facts,
113
+ 'plugin_hooks' => plugin_hooks,
114
+ 'groups' => @inventory.group_names_for(name)
115
+ }
116
+ end
117
+
118
+ def inventory_target
119
+ @inventory.targets[@name]
120
+ end
121
+
122
+ def host
123
+ inventory_target.host
124
+ end
125
+
126
+ attr_reader :name
127
+
128
+ def uri
129
+ inventory_target.uri
130
+ end
131
+
132
+ def remote?
133
+ protocol == 'remote'
134
+ end
135
+
136
+ def port
137
+ inventory_target.port
138
+ end
139
+
140
+ def transport
141
+ inventory_target.transport
142
+ end
143
+
144
+ def transport_config
145
+ inventory_target.transport_config.to_h
146
+ end
147
+ alias options transport_config
148
+
149
+ def protocol
150
+ inventory_target.protocol || inventory_target.transport
151
+ end
152
+
153
+ def user
154
+ inventory_target.user
155
+ end
156
+
157
+ def password
158
+ inventory_target.password
159
+ end
160
+
161
+ def plugin_hooks
162
+ inventory_target.plugin_hooks
163
+ end
164
+
165
+ def eql?(other)
166
+ self.class.equal?(other.class) && @name == other.name
167
+ end
168
+ alias == eql?
169
+
170
+ def hash
171
+ @name.hash
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bolt
4
+ class Task
5
+ class PuppetServer < Bolt::Task
6
+ def remote_instance
7
+ self.class.new(@name, @metadata, @files, @file_cache, true)
8
+ end
9
+
10
+ def initialize(name, metadata, files, file_cache, remote = false)
11
+ super(name, metadata, files, remote)
12
+ @file_cache = file_cache
13
+ update_file_data
14
+ end
15
+
16
+ # puppetserver file entries have 'filename' rather then 'name'
17
+ def update_file_data
18
+ @files.each { |f| f['name'] = f['filename'] }
19
+ end
20
+
21
+ def file_path(file_name)
22
+ file = file_map[file_name]
23
+ file['path'] ||= @file_cache.update_file(file)
24
+ end
25
+ end
26
+ end
27
+ end