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.
- checksums.yaml +7 -0
- data/Puppetfile +52 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/applyresult.rb +60 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/containerresult.rb +51 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/future.rb +25 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/resourceinstance.rb +71 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/result.rb +55 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/resultset.rb +65 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/target.rb +93 -0
- data/bolt-modules/boltlib/lib/puppet/functions/add_facts.rb +33 -0
- data/bolt-modules/boltlib/lib/puppet/functions/add_to_group.rb +38 -0
- data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +208 -0
- data/bolt-modules/boltlib/lib/puppet/functions/background.rb +62 -0
- data/bolt-modules/boltlib/lib/puppet/functions/catch_errors.rb +57 -0
- data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +130 -0
- data/bolt-modules/boltlib/lib/puppet/functions/facts.rb +31 -0
- data/bolt-modules/boltlib/lib/puppet/functions/fail_plan.rb +52 -0
- data/bolt-modules/boltlib/lib/puppet/functions/get_resources.rb +87 -0
- data/bolt-modules/boltlib/lib/puppet/functions/get_target.rb +34 -0
- data/bolt-modules/boltlib/lib/puppet/functions/get_targets.rb +35 -0
- data/bolt-modules/boltlib/lib/puppet/functions/parallelize.rb +74 -0
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_command.rb +97 -0
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_fact.rb +47 -0
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_query.rb +52 -0
- data/bolt-modules/boltlib/lib/puppet/functions/remove_from_group.rb +40 -0
- data/bolt-modules/boltlib/lib/puppet/functions/resolve_references.rb +42 -0
- data/bolt-modules/boltlib/lib/puppet/functions/resource.rb +53 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +106 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_container.rb +162 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +291 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +145 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +164 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +211 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_config.rb +48 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_feature.rb +43 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_resources.rb +145 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_var.rb +38 -0
- data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +101 -0
- data/bolt-modules/boltlib/lib/puppet/functions/vars.rb +29 -0
- data/bolt-modules/boltlib/lib/puppet/functions/wait.rb +131 -0
- data/bolt-modules/boltlib/lib/puppet/functions/wait_until_available.rb +59 -0
- data/bolt-modules/boltlib/lib/puppet/functions/without_default_logging.rb +39 -0
- data/bolt-modules/boltlib/lib/puppet/functions/write_file.rb +50 -0
- data/bolt-modules/boltlib/types/planresult.pp +18 -0
- data/bolt-modules/boltlib/types/targetspec.pp +7 -0
- data/bolt-modules/ctrl/lib/puppet/functions/ctrl/do_until.rb +42 -0
- data/bolt-modules/ctrl/lib/puppet/functions/ctrl/sleep.rb +20 -0
- data/bolt-modules/dir/lib/puppet/functions/dir/children.rb +35 -0
- data/bolt-modules/file/lib/puppet/functions/file/delete.rb +21 -0
- data/bolt-modules/file/lib/puppet/functions/file/exists.rb +28 -0
- data/bolt-modules/file/lib/puppet/functions/file/join.rb +20 -0
- data/bolt-modules/file/lib/puppet/functions/file/read.rb +33 -0
- data/bolt-modules/file/lib/puppet/functions/file/readable.rb +28 -0
- data/bolt-modules/file/lib/puppet/functions/file/write.rb +24 -0
- data/bolt-modules/log/lib/puppet/functions/log/debug.rb +39 -0
- data/bolt-modules/log/lib/puppet/functions/log/error.rb +40 -0
- data/bolt-modules/log/lib/puppet/functions/log/fatal.rb +40 -0
- data/bolt-modules/log/lib/puppet/functions/log/info.rb +39 -0
- data/bolt-modules/log/lib/puppet/functions/log/trace.rb +39 -0
- data/bolt-modules/log/lib/puppet/functions/log/warn.rb +41 -0
- data/bolt-modules/out/lib/puppet/functions/out/message.rb +36 -0
- data/bolt-modules/out/lib/puppet/functions/out/verbose.rb +35 -0
- data/bolt-modules/prompt/lib/puppet/functions/prompt/menu.rb +103 -0
- data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +65 -0
- data/bolt-modules/system/lib/puppet/functions/system/env.rb +20 -0
- data/exe/bolt +17 -0
- data/guides/debugging.yaml +27 -0
- data/guides/inventory.yaml +23 -0
- data/guides/links.yaml +12 -0
- data/guides/logging.yaml +17 -0
- data/guides/module.yaml +18 -0
- data/guides/modulepath.yaml +24 -0
- data/guides/project.yaml +21 -0
- data/guides/targets.yaml +28 -0
- data/guides/transports.yaml +22 -0
- data/lib/bolt/analytics.rb +233 -0
- data/lib/bolt/application.rb +806 -0
- data/lib/bolt/applicator.rb +368 -0
- data/lib/bolt/apply_inventory.rb +93 -0
- data/lib/bolt/apply_result.rb +154 -0
- data/lib/bolt/apply_target.rb +90 -0
- data/lib/bolt/bolt_option_parser.rb +1226 -0
- data/lib/bolt/catalog/logging.rb +15 -0
- data/lib/bolt/catalog.rb +144 -0
- data/lib/bolt/cli.rb +949 -0
- data/lib/bolt/config/modulepath.rb +30 -0
- data/lib/bolt/config/options.rb +673 -0
- data/lib/bolt/config/transport/base.rb +133 -0
- data/lib/bolt/config/transport/docker.rb +34 -0
- data/lib/bolt/config/transport/jail.rb +33 -0
- data/lib/bolt/config/transport/local.rb +39 -0
- data/lib/bolt/config/transport/lxd.rb +34 -0
- data/lib/bolt/config/transport/options.rb +431 -0
- data/lib/bolt/config/transport/orch.rb +41 -0
- data/lib/bolt/config/transport/podman.rb +33 -0
- data/lib/bolt/config/transport/remote.rb +24 -0
- data/lib/bolt/config/transport/ssh.rb +138 -0
- data/lib/bolt/config/transport/winrm.rb +63 -0
- data/lib/bolt/config.rb +515 -0
- data/lib/bolt/container_result.rb +105 -0
- data/lib/bolt/error.rb +194 -0
- data/lib/bolt/executor.rb +539 -0
- data/lib/bolt/fiber_executor.rb +190 -0
- data/lib/bolt/inventory/group.rb +446 -0
- data/lib/bolt/inventory/inventory.rb +391 -0
- data/lib/bolt/inventory/options.rb +139 -0
- data/lib/bolt/inventory/target.rb +293 -0
- data/lib/bolt/inventory.rb +120 -0
- data/lib/bolt/logger.rb +252 -0
- data/lib/bolt/module.rb +54 -0
- data/lib/bolt/module_installer/installer.rb +44 -0
- data/lib/bolt/module_installer/puppetfile/forge_module.rb +54 -0
- data/lib/bolt/module_installer/puppetfile/git_module.rb +37 -0
- data/lib/bolt/module_installer/puppetfile/module.rb +26 -0
- data/lib/bolt/module_installer/puppetfile.rb +131 -0
- data/lib/bolt/module_installer/resolver.rb +129 -0
- data/lib/bolt/module_installer/specs/forge_spec.rb +91 -0
- data/lib/bolt/module_installer/specs/git_spec.rb +150 -0
- data/lib/bolt/module_installer/specs/id/base.rb +116 -0
- data/lib/bolt/module_installer/specs/id/gitclone.rb +120 -0
- data/lib/bolt/module_installer/specs/id/github.rb +90 -0
- data/lib/bolt/module_installer/specs/id/gitlab.rb +92 -0
- data/lib/bolt/module_installer/specs.rb +95 -0
- data/lib/bolt/module_installer.rb +208 -0
- data/lib/bolt/node/errors.rb +55 -0
- data/lib/bolt/node/output.rb +29 -0
- data/lib/bolt/outputter/human.rb +958 -0
- data/lib/bolt/outputter/json.rb +205 -0
- data/lib/bolt/outputter/logger.rb +76 -0
- data/lib/bolt/outputter/rainbow.rb +118 -0
- data/lib/bolt/outputter.rb +57 -0
- data/lib/bolt/pal/issues.rb +19 -0
- data/lib/bolt/pal/logging.rb +17 -0
- data/lib/bolt/pal/yaml_plan/evaluator.rb +83 -0
- data/lib/bolt/pal/yaml_plan/loader.rb +94 -0
- data/lib/bolt/pal/yaml_plan/parameter.rb +63 -0
- data/lib/bolt/pal/yaml_plan/step/command.rb +45 -0
- data/lib/bolt/pal/yaml_plan/step/download.rb +37 -0
- data/lib/bolt/pal/yaml_plan/step/eval.rb +42 -0
- data/lib/bolt/pal/yaml_plan/step/message.rb +31 -0
- data/lib/bolt/pal/yaml_plan/step/plan.rb +42 -0
- data/lib/bolt/pal/yaml_plan/step/resources.rb +170 -0
- data/lib/bolt/pal/yaml_plan/step/script.rb +62 -0
- data/lib/bolt/pal/yaml_plan/step/task.rb +42 -0
- data/lib/bolt/pal/yaml_plan/step/upload.rb +37 -0
- data/lib/bolt/pal/yaml_plan/step/verbose.rb +31 -0
- data/lib/bolt/pal/yaml_plan/step.rb +223 -0
- data/lib/bolt/pal/yaml_plan/transpiler.rb +90 -0
- data/lib/bolt/pal/yaml_plan.rb +172 -0
- data/lib/bolt/pal.rb +847 -0
- data/lib/bolt/plan_creator.rb +219 -0
- data/lib/bolt/plan_future.rb +86 -0
- data/lib/bolt/plan_result.rb +44 -0
- data/lib/bolt/plugin/cache.rb +76 -0
- data/lib/bolt/plugin/env_var.rb +54 -0
- data/lib/bolt/plugin/module.rb +276 -0
- data/lib/bolt/plugin/prompt.rb +36 -0
- data/lib/bolt/plugin/puppet_connect_data.rb +84 -0
- data/lib/bolt/plugin/puppetdb.rb +124 -0
- data/lib/bolt/plugin/task.rb +72 -0
- data/lib/bolt/plugin.rb +380 -0
- data/lib/bolt/project.rb +219 -0
- data/lib/bolt/project_manager/config_migrator.rb +113 -0
- data/lib/bolt/project_manager/inventory_migrator.rb +67 -0
- data/lib/bolt/project_manager/migrator.rb +39 -0
- data/lib/bolt/project_manager/module_migrator.rb +203 -0
- data/lib/bolt/project_manager.rb +221 -0
- data/lib/bolt/puppetdb/client.rb +153 -0
- data/lib/bolt/puppetdb/config.rb +176 -0
- data/lib/bolt/puppetdb/instance.rb +146 -0
- data/lib/bolt/puppetdb.rb +15 -0
- data/lib/bolt/r10k_log_proxy.rb +30 -0
- data/lib/bolt/rerun.rb +55 -0
- data/lib/bolt/resource_instance.rb +133 -0
- data/lib/bolt/result.rb +247 -0
- data/lib/bolt/result_set.rb +128 -0
- data/lib/bolt/shell/bash/tmpdir.rb +62 -0
- data/lib/bolt/shell/bash.rb +516 -0
- data/lib/bolt/shell/powershell/snippets.rb +181 -0
- data/lib/bolt/shell/powershell.rb +365 -0
- data/lib/bolt/shell.rb +105 -0
- data/lib/bolt/target.rb +174 -0
- data/lib/bolt/task/puppet_server.rb +27 -0
- data/lib/bolt/task/run.rb +55 -0
- data/lib/bolt/task.rb +163 -0
- data/lib/bolt/transport/base.rb +252 -0
- data/lib/bolt/transport/docker/connection.rb +150 -0
- data/lib/bolt/transport/docker.rb +23 -0
- data/lib/bolt/transport/jail/connection.rb +81 -0
- data/lib/bolt/transport/jail.rb +21 -0
- data/lib/bolt/transport/local/connection.rb +106 -0
- data/lib/bolt/transport/local.rb +20 -0
- data/lib/bolt/transport/lxd/connection.rb +115 -0
- data/lib/bolt/transport/lxd.rb +26 -0
- data/lib/bolt/transport/orch/connection.rb +111 -0
- data/lib/bolt/transport/orch.rb +271 -0
- data/lib/bolt/transport/podman/connection.rb +102 -0
- data/lib/bolt/transport/podman.rb +19 -0
- data/lib/bolt/transport/remote.rb +41 -0
- data/lib/bolt/transport/simple.rb +54 -0
- data/lib/bolt/transport/ssh/connection.rb +321 -0
- data/lib/bolt/transport/ssh/exec_connection.rb +140 -0
- data/lib/bolt/transport/ssh.rb +48 -0
- data/lib/bolt/transport/winrm/connection.rb +378 -0
- data/lib/bolt/transport/winrm.rb +33 -0
- data/lib/bolt/util/format.rb +68 -0
- data/lib/bolt/util/puppet_log_level.rb +21 -0
- data/lib/bolt/util.rb +465 -0
- data/lib/bolt/validator.rb +227 -0
- data/lib/bolt/version.rb +5 -0
- data/lib/bolt.rb +8 -0
- data/lib/bolt_server/acl.rb +39 -0
- data/lib/bolt_server/base_config.rb +112 -0
- data/lib/bolt_server/config.rb +64 -0
- data/lib/bolt_server/file_cache.rb +200 -0
- data/lib/bolt_server/request_error.rb +11 -0
- data/lib/bolt_server/schemas/action-check_node_connections.json +14 -0
- data/lib/bolt_server/schemas/action-run_command.json +12 -0
- data/lib/bolt_server/schemas/action-run_script.json +47 -0
- data/lib/bolt_server/schemas/action-run_task.json +20 -0
- data/lib/bolt_server/schemas/action-upload_file.json +47 -0
- data/lib/bolt_server/schemas/partials/target-any.json +10 -0
- data/lib/bolt_server/schemas/partials/target-ssh.json +88 -0
- data/lib/bolt_server/schemas/partials/target-winrm.json +67 -0
- data/lib/bolt_server/schemas/partials/task.json +94 -0
- data/lib/bolt_server/schemas/transport-ssh.json +25 -0
- data/lib/bolt_server/schemas/transport-winrm.json +19 -0
- data/lib/bolt_server/transport_app.rb +554 -0
- data/lib/bolt_spec/bolt_context.rb +226 -0
- data/lib/bolt_spec/plans/action_stubs/command_stub.rb +51 -0
- data/lib/bolt_spec/plans/action_stubs/download_stub.rb +66 -0
- data/lib/bolt_spec/plans/action_stubs/plan_stub.rb +55 -0
- data/lib/bolt_spec/plans/action_stubs/script_stub.rb +59 -0
- data/lib/bolt_spec/plans/action_stubs/task_stub.rb +57 -0
- data/lib/bolt_spec/plans/action_stubs/upload_stub.rb +65 -0
- data/lib/bolt_spec/plans/action_stubs.rb +196 -0
- data/lib/bolt_spec/plans/mock_executor.rb +361 -0
- data/lib/bolt_spec/plans/publish_stub.rb +49 -0
- data/lib/bolt_spec/plans.rb +190 -0
- data/lib/bolt_spec/run.rb +246 -0
- data/lib/logging_extensions/logging.rb +13 -0
- data/libexec/apply_catalog.rb +130 -0
- data/libexec/bolt_catalog +68 -0
- data/libexec/custom_facts.rb +63 -0
- data/libexec/query_resources.rb +75 -0
- data/modules/aggregate/lib/puppet/functions/aggregate/count.rb +21 -0
- data/modules/aggregate/lib/puppet/functions/aggregate/nodes.rb +22 -0
- data/modules/aggregate/lib/puppet/functions/aggregate/targets.rb +21 -0
- data/modules/aggregate/plans/count.pp +56 -0
- data/modules/aggregate/plans/targets.pp +56 -0
- data/modules/canary/lib/puppet/functions/canary/merge.rb +13 -0
- data/modules/canary/lib/puppet/functions/canary/random_split.rb +22 -0
- data/modules/canary/lib/puppet/functions/canary/skip.rb +25 -0
- data/modules/canary/plans/init.pp +100 -0
- data/modules/puppet_connect/plans/test_input_data.pp +94 -0
- data/modules/puppetdb_fact/plans/init.pp +20 -0
- data/resources/bolt_bash_completion.sh +214 -0
- 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'
|
data/lib/bolt/target.rb
ADDED
@@ -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
|