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,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../bolt/error'
4
+ require_relative '../../../bolt/config/transport/base'
5
+
6
+ module Bolt
7
+ class Config
8
+ module Transport
9
+ class WinRM < Base
10
+ OPTIONS = %w[
11
+ basic-auth-only
12
+ cacert
13
+ cleanup
14
+ connect-timeout
15
+ extensions
16
+ file-protocol
17
+ host
18
+ interpreters
19
+ password
20
+ port
21
+ realm
22
+ smb-port
23
+ ssl
24
+ ssl-verify
25
+ tmpdir
26
+ user
27
+ ].freeze
28
+
29
+ DEFAULTS = {
30
+ "basic-auth-only" => false,
31
+ "cleanup" => true,
32
+ "connect-timeout" => 10,
33
+ "ssl" => true,
34
+ "ssl-verify" => true,
35
+ "file-protocol" => "winrm"
36
+ }.freeze
37
+
38
+ private def validate
39
+ super
40
+
41
+ if @config['ssl']
42
+ if @config['file-protocol'] == 'smb'
43
+ raise Bolt::ValidationError, "SMB file transfers are not allowed with SSL enabled"
44
+ end
45
+
46
+ if @config['cacert']
47
+ @config['cacert'] = File.expand_path(@config['cacert'], @project)
48
+ Bolt::Util.validate_file('cacert', @config['cacert'])
49
+ end
50
+ end
51
+
52
+ if !@config['ssl'] && @config['basic-auth-only']
53
+ raise Bolt::ValidationError, "Basic auth is only allowed when using SSL"
54
+ end
55
+
56
+ if @config['interpreters']
57
+ @config['interpreters'] = normalize_interpreters(@config['interpreters'])
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,515 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'etc'
4
+ require 'logging'
5
+ require 'pathname'
6
+ require_relative '../bolt/project'
7
+ require_relative '../bolt/logger'
8
+ require_relative '../bolt/util'
9
+ require_relative 'config/options'
10
+ require_relative '../bolt/validator'
11
+
12
+ module Bolt
13
+ class UnknownTransportError < Bolt::Error
14
+ def initialize(transport, uri = nil)
15
+ msg = uri.nil? ? "Unknown transport #{transport}" : "Unknown transport #{transport} found for #{uri}"
16
+ super(msg, 'bolt/unknown-transport')
17
+ end
18
+ end
19
+
20
+ class Config
21
+ include Bolt::Config::Options
22
+
23
+ attr_reader :config_files, :data, :transports, :project, :modified_concurrency
24
+
25
+ DEFAULTS_NAME = 'bolt-defaults.yaml'
26
+
27
+ # The default concurrency value that is used when the ulimit is not low (i.e. < 700)
28
+ DEFAULT_DEFAULT_CONCURRENCY = 100
29
+
30
+ def self.default
31
+ new(Bolt::Project.default_project, {})
32
+ end
33
+
34
+ def self.from_project(project, overrides = {})
35
+ data = load_defaults.push(
36
+ filepath: project.project_file,
37
+ data: project.data
38
+ )
39
+
40
+ new(project, data, overrides)
41
+ end
42
+
43
+ # Builds a hash of definitions for transport configuration.
44
+ #
45
+ def self.transport_definitions
46
+ INVENTORY_OPTIONS.each_with_object({}) do |(option, definition), acc|
47
+ acc[option] = TRANSPORT_CONFIG.key?(option) ? definition.merge(TRANSPORT_CONFIG[option].schema) : definition
48
+ end
49
+ end
50
+
51
+ # Builds the schema for bolt-defaults.yaml used by the validator.
52
+ #
53
+ def self.defaults_schema
54
+ schema = {
55
+ type: Hash,
56
+ properties: DEFAULTS_OPTIONS.map { |opt| [opt, _ref: opt] }.to_h,
57
+ definitions: OPTIONS.merge(transport_definitions)
58
+ }
59
+
60
+ schema[:definitions]['inventory-config'][:properties] = transport_definitions
61
+
62
+ schema
63
+ end
64
+
65
+ def self.system_path
66
+ if Bolt::Util.windows?
67
+ Pathname.new(File.join(ENV['ALLUSERSPROFILE'], 'PuppetLabs', 'bolt', 'etc'))
68
+ else
69
+ Pathname.new(File.join('/etc', 'puppetlabs', 'bolt'))
70
+ end
71
+ end
72
+
73
+ def self.user_path
74
+ Pathname.new(File.expand_path(File.join('~', '.puppetlabs', 'etc', 'bolt')))
75
+ rescue StandardError
76
+ nil
77
+ end
78
+
79
+ # Loads a 'bolt-defaults.yaml' file, which contains default configuration that applies to all
80
+ # projects. This file does not allow project-specific configuration such as 'hiera-config'
81
+ # and nests all default inventory configuration under an 'inventory-config' key.
82
+ def self.load_bolt_defaults_yaml(dir)
83
+ filepath = dir + DEFAULTS_NAME
84
+ data = Bolt::Util.read_yaml_hash(filepath, 'config')
85
+
86
+ Bolt::Logger.debug("Loaded configuration from #{filepath}")
87
+
88
+ # Validate the config against the schema. This will raise a single error
89
+ # with all validation errors.
90
+ Bolt::Validator.new.tap do |validator|
91
+ validator.validate(data, defaults_schema, filepath)
92
+ validator.warnings.each { |warning| Bolt::Logger.warn(warning[:id], warning[:msg]) }
93
+ validator.deprecations.each { |dep| Bolt::Logger.deprecate(dep[:id], dep[:msg]) }
94
+ end
95
+
96
+ # Remove project-specific config such as hiera-config, etc.
97
+ project_config = data.slice(*(PROJECT_OPTIONS - DEFAULTS_OPTIONS))
98
+
99
+ if project_config.any?
100
+ data.reject! { |key, _| project_config.include?(key) }
101
+
102
+ Bolt::Logger.warn(
103
+ "unsupported_project_config",
104
+ "Unsupported project configuration detected in '#{filepath}': #{project_config.keys}. "\
105
+ "Project configuration should be set in 'bolt-project.yaml'."
106
+ )
107
+ end
108
+
109
+ # Remove top-level transport config such as transport, ssh, etc.
110
+ transport_config = data.slice(*INVENTORY_OPTIONS.keys)
111
+
112
+ if transport_config.any?
113
+ data.reject! { |key, _| transport_config.include?(key) }
114
+
115
+ Bolt::Logger.warn(
116
+ "unsupported_inventory_config",
117
+ "Unsupported inventory configuration detected in '#{filepath}': #{transport_config.keys}. "\
118
+ "Transport configuration should be set under the 'inventory-config' option or "\
119
+ "in 'inventory.yaml'."
120
+ )
121
+ end
122
+
123
+ # Move data under inventory-config to top-level so it can be easily merged with
124
+ # config from other sources. Error early if inventory-config is not a hash or
125
+ # has a plugin reference.
126
+ if data.key?('inventory-config')
127
+ unless data['inventory-config'].is_a?(Hash)
128
+ raise Bolt::ValidationError,
129
+ "Option 'inventory-config' must be of type Hash, received #{data['inventory-config']} "\
130
+ "#{data['inventory-config']} (file: #{filepath})"
131
+ end
132
+
133
+ if data['inventory-config'].key?('_plugin')
134
+ raise Bolt::ValidationError,
135
+ "Found unsupported key '_plugin' for option 'inventory-config'; supported keys are "\
136
+ "'#{INVENTORY_OPTIONS.keys.join("', '")}' (file: #{filepath})"
137
+ end
138
+
139
+ data = data.merge(data.delete('inventory-config'))
140
+ end
141
+
142
+ { filepath: filepath, data: data }
143
+ end
144
+
145
+ def self.load_defaults
146
+ confs = []
147
+
148
+ # Load system-level config.
149
+ if File.exist?(system_path + DEFAULTS_NAME)
150
+ confs << load_bolt_defaults_yaml(system_path)
151
+ end
152
+
153
+ # Load user-level config if there is a homedir.
154
+ if user_path && File.exist?(user_path + DEFAULTS_NAME)
155
+ confs << load_bolt_defaults_yaml(user_path)
156
+ end
157
+
158
+ confs
159
+ end
160
+
161
+ def initialize(project, config_data, overrides = {})
162
+ unless config_data.is_a?(Array)
163
+ config_data = [{ filepath: project.project_file, data: config_data }]
164
+ end
165
+
166
+ @logger = Bolt::Logger.logger(self)
167
+ @project = project
168
+ @transports = {}
169
+ @config_files = []
170
+
171
+ default_data = {
172
+ 'analytics' => false,
173
+ 'apply-settings' => {},
174
+ 'color' => true,
175
+ 'compile-concurrency' => Etc.nprocessors,
176
+ 'concurrency' => default_concurrency,
177
+ 'disable-warnings' => [],
178
+ 'format' => 'human',
179
+ 'log' => { 'console' => {} },
180
+ 'module-install' => {},
181
+ 'plugin-hooks' => {},
182
+ 'plugins' => {},
183
+ 'puppetdb' => {},
184
+ 'puppetdb-instances' => {},
185
+ 'save-rerun' => true,
186
+ 'spinner' => true,
187
+ 'transport' => 'ssh'
188
+ }
189
+
190
+ if project.path.directory?
191
+ default_data['log']['bolt-debug.log'] = {
192
+ 'level' => 'debug',
193
+ 'append' => false
194
+ }
195
+ end
196
+
197
+ loaded_data = config_data.each_with_object([]) do |data, acc|
198
+ if data[:data].any?
199
+ @config_files.push(data[:filepath])
200
+ acc.push(data[:data])
201
+ end
202
+ end
203
+
204
+ override_data = normalize_overrides(overrides)
205
+
206
+ # If we need to lower concurrency and concurrency is not configured
207
+ ld_concurrency = loaded_data.map(&:keys).flatten.include?('concurrency')
208
+ @modified_concurrency = default_concurrency != DEFAULT_DEFAULT_CONCURRENCY &&
209
+ !ld_concurrency &&
210
+ !override_data.key?('concurrency')
211
+
212
+ @data = merge_config_layers(default_data, *loaded_data, override_data)
213
+
214
+ TRANSPORT_CONFIG.each do |transport, config|
215
+ @transports[transport] = config.new(@data.delete(transport), @project.path)
216
+ end
217
+
218
+ finalize_data
219
+ validate
220
+ end
221
+
222
+ # Transforms CLI options into a config hash that can be merged with
223
+ # default and loaded config.
224
+ def normalize_overrides(options)
225
+ opts = options.transform_keys(&:to_s)
226
+
227
+ # Pull out config options. We need to add 'transport' and 'inventoryfile' as they're
228
+ # not part of the OPTIONS hash but are valid options that can be set with CLI options
229
+ overrides = opts.slice(*OPTIONS.keys, 'inventoryfile', 'transport', 'default_puppetdb')
230
+
231
+ # Pull out transport config options
232
+ TRANSPORT_CONFIG.each do |transport, config|
233
+ overrides[transport] = opts.slice(*config.options)
234
+ end
235
+
236
+ overrides['trace'] = opts['trace'] if opts.key?('trace')
237
+
238
+ # Validate the overrides that can have arbitrary values
239
+ schema = {
240
+ type: Hash,
241
+ properties: CLI_OPTIONS.map { |opt| [opt, _ref: opt] }.to_h,
242
+ definitions: OPTIONS.merge(INVENTORY_OPTIONS)
243
+ }
244
+
245
+ Bolt::Validator.new.validate(overrides.slice(*CLI_OPTIONS), schema, 'command line')
246
+
247
+ overrides
248
+ end
249
+
250
+ # Merge configuration from all sources into a single hash. Precedence from lowest to highest:
251
+ # defaults, system-wide, user-level, project-level, CLI overrides
252
+ def merge_config_layers(*config_data)
253
+ config_data.inject({}) do |acc, config|
254
+ acc.merge(config) do |key, val1, val2|
255
+ case key
256
+ # Shallow merge config for each plugin
257
+ when 'plugins'
258
+ val1.merge(val2) { |_, v1, v2| v1.merge(v2) }
259
+ # Transports are deep merged
260
+ when *TRANSPORT_CONFIG.keys
261
+ Bolt::Util.deep_merge(val1, val2)
262
+ # Hash values are shallow merged
263
+ when 'apply-settings', 'log', 'plugin-hooks', 'puppetdb', 'puppetdb-instances'
264
+ val1.merge(val2)
265
+ # Disabled warnings are concatenated
266
+ when 'disable-warnings'
267
+ val1.concat(val2)
268
+ when 'analytics'
269
+ val1 && val2
270
+ # All other values are overwritten
271
+ else
272
+ val2
273
+ end
274
+ end
275
+ end
276
+ end
277
+
278
+ def deep_clone
279
+ Bolt::Util.deep_clone(self)
280
+ end
281
+
282
+ private def finalize_data
283
+ if @data['log'].is_a?(Hash)
284
+ @data['log'] = update_logs(@data['log'])
285
+ end
286
+
287
+ # Expand paths relative to the project. Any settings that came from the
288
+ # CLI will already be absolute, so the expand will be skipped.
289
+ if @data.key?('modulepath')
290
+ moduledirs = if data['modulepath'].is_a?(String)
291
+ data['modulepath'].split(File::PATH_SEPARATOR)
292
+ else
293
+ data['modulepath']
294
+ end
295
+ @data['modulepath'] = moduledirs.map do |moduledir|
296
+ File.expand_path(moduledir, @project.path)
297
+ end
298
+ end
299
+
300
+ %w[hiera-config inventoryfile trusted-external-command].each do |opt|
301
+ @data[opt] = File.expand_path(@data[opt], @project.path) if @data.key?(opt)
302
+ end
303
+
304
+ # Filter hashes to only include valid options
305
+ %w[apply-settings module-install].each do |opt|
306
+ @data[opt] = @data[opt].slice(*OPTIONS.dig(opt, :properties).keys)
307
+ end
308
+ end
309
+
310
+ private def normalize_log(target)
311
+ return target if target == 'console'
312
+ target = target[5..-1] if target.start_with?('file:')
313
+ 'file:' + File.expand_path(target, @project.path)
314
+ end
315
+
316
+ private def update_logs(logs)
317
+ begin
318
+ if logs['bolt-debug.log'] && logs['bolt-debug.log'] != 'disable'
319
+ FileUtils.touch(File.expand_path('bolt-debug.log', @project.path))
320
+ end
321
+ rescue StandardError
322
+ logs.delete('bolt-debug.log')
323
+ end
324
+
325
+ logs.each_with_object({}) do |(key, val), acc|
326
+ # Remove any disabled logs
327
+ next if val == 'disable'
328
+
329
+ name = normalize_log(key)
330
+ acc[name] = val.slice('append', 'level').transform_keys(&:to_sym)
331
+ end
332
+ end
333
+
334
+ def validate
335
+ if @data['modulepath']&.include?(@project.managed_moduledir.to_s)
336
+ raise Bolt::ValidationError,
337
+ "Found invalid path in modulepath: #{@project.managed_moduledir}. This path "\
338
+ "is automatically appended to the modulepath and cannot be configured."
339
+ end
340
+
341
+ compile_limit = 2 * Etc.nprocessors
342
+ unless compile_concurrency < compile_limit
343
+ raise Bolt::ValidationError, "Compilation is CPU-intensive, set concurrency less than #{compile_limit}"
344
+ end
345
+
346
+ %w[hiera-config trusted-external-command inventoryfile].each do |opt|
347
+ Bolt::Util.validate_file(opt, @data[opt]) if @data[opt]
348
+ end
349
+
350
+ if File.exist?(default_inventoryfile)
351
+ Bolt::Util.validate_file('inventory file', default_inventoryfile)
352
+ end
353
+ end
354
+
355
+ def default_inventoryfile
356
+ @project.inventory_file
357
+ end
358
+
359
+ def rerunfile
360
+ @project.rerunfile
361
+ end
362
+
363
+ def hiera_config
364
+ @data['hiera-config'] || @project.hiera_config
365
+ end
366
+
367
+ def puppetfile
368
+ @project.puppetfile
369
+ end
370
+
371
+ def modulepath
372
+ (@data['modulepath'] || @project.modulepath) + [@project.managed_moduledir.to_s]
373
+ end
374
+
375
+ def modulepath=(value)
376
+ @data['modulepath'] = Array(value)
377
+ end
378
+
379
+ def plugin_cache
380
+ @project.plugin_cache || @data['plugin-cache'] || {}
381
+ end
382
+
383
+ def concurrency
384
+ @data['concurrency']
385
+ end
386
+
387
+ def format
388
+ @data['format']
389
+ end
390
+
391
+ def format=(value)
392
+ @data['format'] = value
393
+ end
394
+
395
+ def future
396
+ @data['future']
397
+ end
398
+
399
+ def trace
400
+ @data['trace']
401
+ end
402
+
403
+ def log
404
+ @data['log']
405
+ end
406
+
407
+ def puppetdb
408
+ @data['puppetdb']
409
+ end
410
+
411
+ def puppetdb_instances
412
+ @data['puppetdb-instances']
413
+ end
414
+
415
+ def default_puppetdb
416
+ @data['default_puppetdb']
417
+ end
418
+
419
+ def color
420
+ @data['color']
421
+ end
422
+
423
+ def save_rerun
424
+ @data['save-rerun']
425
+ end
426
+
427
+ def spinner
428
+ @data['spinner']
429
+ end
430
+
431
+ def stream
432
+ @data['stream']
433
+ end
434
+
435
+ def inventoryfile
436
+ @data['inventoryfile']
437
+ end
438
+
439
+ def compile_concurrency
440
+ @data['compile-concurrency']
441
+ end
442
+
443
+ def plugins
444
+ @data['plugins']
445
+ end
446
+
447
+ def plugin_hooks
448
+ @data['plugin-hooks']
449
+ end
450
+
451
+ def policies
452
+ @data['policies']
453
+ end
454
+
455
+ def trusted_external
456
+ @data['trusted-external-command']
457
+ end
458
+
459
+ def apply_settings
460
+ @data['apply-settings']
461
+ end
462
+
463
+ def transport
464
+ @data['transport']
465
+ end
466
+
467
+ def module_install
468
+ @project.module_install || @data['module-install']
469
+ end
470
+
471
+ def disable_warnings
472
+ Set.new(@project.disable_warnings + @data['disable-warnings'])
473
+ end
474
+
475
+ def analytics
476
+ @data['analytics']
477
+ end
478
+
479
+ # Check if there is a case-insensitive match to the path
480
+ def check_path_case(type, paths)
481
+ return if paths.nil?
482
+ matches = matching_paths(paths)
483
+
484
+ if matches.any?
485
+ msg = "WARNING: Bolt is case sensitive when specifying a #{type}. Did you mean:\n"
486
+ matches.each { |path| msg += " #{path}\n" }
487
+ Bolt::Logger.warn("path_case", msg)
488
+ end
489
+ end
490
+
491
+ def matching_paths(paths)
492
+ Array(paths).map { |p| Dir.glob([p, casefold(p)]) }.flatten.uniq.reject { |p| Array(paths).include?(p) }
493
+ end
494
+
495
+ private def casefold(path)
496
+ path.chars.map do |l|
497
+ l =~ /[A-Za-z]/ ? "[#{l.upcase}#{l.downcase}]" : l
498
+ end.join
499
+ end
500
+
501
+ # Etc::SC_OPEN_MAX is meaningless on windows, not defined in PE Jruby and not available
502
+ # on some platforms. This method holds the logic to decide whether or not to even consider it.
503
+ def sc_open_max_available?
504
+ !Bolt::Util.windows? && defined?(Etc::SC_OPEN_MAX) && Etc.sysconf(Etc::SC_OPEN_MAX)
505
+ end
506
+
507
+ def default_concurrency
508
+ @default_concurrency ||= if !sc_open_max_available? || Etc.sysconf(Etc::SC_OPEN_MAX) >= 300
509
+ DEFAULT_DEFAULT_CONCURRENCY
510
+ else
511
+ Etc.sysconf(Etc::SC_OPEN_MAX) / 7
512
+ end
513
+ end
514
+ end
515
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require_relative '../bolt/error'
5
+ require_relative '../bolt/result'
6
+
7
+ module Bolt
8
+ class ContainerResult
9
+ attr_reader :value, :object
10
+
11
+ def self.from_exception(exception, exit_code, image, position: [])
12
+ details = Bolt::Result.create_details(position)
13
+ error = {
14
+ 'kind' => 'puppetlabs.tasks/container-error',
15
+ 'issue_code' => 'CONTAINER_ERROR',
16
+ 'msg' => "Error running container '#{image}': #{exception}",
17
+ 'details' => details
18
+ }
19
+ error['details']['exit_code'] = exit_code
20
+ ContainerResult.new({ '_error' => error }, object: image)
21
+ end
22
+
23
+ def _pcore_init_hash
24
+ { 'value' => @value,
25
+ 'object' => @image }
26
+ end
27
+
28
+ # First argument can't be named given the way that Puppet deserializes variables
29
+ def initialize(value = nil, object: nil)
30
+ @value = value || {}
31
+ @object = object
32
+ end
33
+
34
+ def eql?(other)
35
+ self.class == other.class &&
36
+ value == other.value
37
+ end
38
+ alias == eql?
39
+
40
+ def [](key)
41
+ value[key]
42
+ end
43
+
44
+ def to_json(opts = nil)
45
+ to_data.to_json(opts)
46
+ end
47
+ alias to_s to_json
48
+
49
+ # This is the value with all non-UTF-8 characters removed, suitable for
50
+ # printing or converting to JSON. It *should* only be possible to have
51
+ # non-UTF-8 characters in stdout/stderr keys as they are not allowed from
52
+ # tasks but we scrub the whole thing just in case.
53
+ def safe_value
54
+ Bolt::Util.walk_vals(value) do |val|
55
+ if val.is_a?(String)
56
+ # Replace invalid bytes with hex codes, ie. \xDE\xAD\xBE\xEF
57
+ val.scrub { |c| c.bytes.map { |b| "\\x" + b.to_s(16).upcase }.join }
58
+ else
59
+ val
60
+ end
61
+ end
62
+ end
63
+
64
+ def stdout
65
+ value['stdout']
66
+ end
67
+
68
+ def stderr
69
+ value['stderr']
70
+ end
71
+
72
+ def to_data
73
+ {
74
+ "object" => object,
75
+ "status" => status,
76
+ "value" => safe_value
77
+ }
78
+ end
79
+
80
+ def status
81
+ ok? ? 'success' : 'failure'
82
+ end
83
+
84
+ def ok?
85
+ error_hash.nil?
86
+ end
87
+ alias ok ok?
88
+ alias success? ok?
89
+
90
+ # This allows access to errors outside puppet compilation
91
+ # it should be prefered over error in bolt code
92
+ def error_hash
93
+ value['_error']
94
+ end
95
+
96
+ # Warning: This will fail outside of a compilation.
97
+ # Use error_hash inside bolt.
98
+ # Is it crazy for this to behave differently outside a compiler?
99
+ def error
100
+ if error_hash
101
+ Puppet::DataTypes::Error.from_asserted_hash(error_hash)
102
+ end
103
+ end
104
+ end
105
+ end