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,176 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require_relative '../../bolt/util'
5
+
6
+ module Bolt
7
+ module PuppetDB
8
+ class Config
9
+ if ENV['HOME'].nil?
10
+ DEFAULT_TOKEN = Bolt::Util.windows? ? 'nul' : '/dev/null'
11
+ DEFAULT_CONFIG = { user: '/etc/puppetlabs/puppet/puppetdb.conf',
12
+ global: '/etc/puppetlabs/puppet/puppetdb.conf' }.freeze
13
+ else
14
+ DEFAULT_TOKEN = File.expand_path('~/.puppetlabs/token')
15
+ DEFAULT_CONFIG = { user: File.expand_path('~/.puppetlabs/client-tools/puppetdb.conf'),
16
+ global: '/etc/puppetlabs/client-tools/puppetdb.conf' }.freeze
17
+
18
+ end
19
+
20
+ def initialize(config:, project: nil, load_defaults: false)
21
+ @settings = if load_defaults
22
+ self.class.default_config.merge(config)
23
+ else
24
+ config
25
+ end
26
+
27
+ expand_paths(project)
28
+ end
29
+
30
+ # Returns the path to the puppetdb.conf file on Windows.
31
+ #
32
+ # @return [String]
33
+ #
34
+ def self.default_windows_config
35
+ File.expand_path(File.join(ENV['ALLUSERSPROFILE'], 'PuppetLabs/client-tools/puppetdb.conf'))
36
+ end
37
+
38
+ # Loads default configuration from the puppetdb.conf file on system. If
39
+ # the file is not present, defaults to an empty hash.
40
+ #
41
+ # @return [Hash]
42
+ #
43
+ def self.default_config
44
+ config = {}
45
+ global_path = Bolt::Util.windows? ? default_windows_config : DEFAULT_CONFIG[:global]
46
+
47
+ if File.exist?(DEFAULT_CONFIG[:user])
48
+ filepath = DEFAULT_CONFIG[:user]
49
+ elsif File.exist?(global_path)
50
+ filepath = global_path
51
+ end
52
+
53
+ begin
54
+ config = JSON.parse(File.read(filepath)) if filepath
55
+ rescue StandardError => e
56
+ Bolt::Logger.logger(self).error("Could not load puppetdb.conf from #{filepath}: #{e.message}")
57
+ end
58
+
59
+ config.fetch('puppetdb', {})
60
+ end
61
+
62
+ def token
63
+ return @token if @token_computed
64
+ # Allow nil in config to skip loading a token
65
+ if @settings.include?('token')
66
+ if @settings['token']
67
+ @token = File.read(@settings['token'])
68
+ end
69
+ elsif File.exist?(DEFAULT_TOKEN)
70
+ @token = File.read(DEFAULT_TOKEN)
71
+ end
72
+ # Only use cert based auth in the case token and cert are both configured
73
+ if @token && cert
74
+ Bolt::Logger.logger(self).debug("Both cert and token based auth configured, using cert only")
75
+ @token = nil
76
+ end
77
+ @token_computed = true
78
+ @token = @token.strip if @token
79
+ end
80
+
81
+ def expand_paths(project_path)
82
+ %w[cacert cert key token].each do |file|
83
+ next unless @settings[file]
84
+ @settings[file] = File.expand_path(@settings[file], project_path)
85
+ end
86
+ end
87
+
88
+ def validate_file_exists(file)
89
+ if @settings[file] && !File.exist?(@settings[file])
90
+ raise Bolt::PuppetDBError, "#{file} file #{@settings[file]} does not exist"
91
+ end
92
+ true
93
+ end
94
+
95
+ def server_urls
96
+ case @settings['server_urls']
97
+ when String
98
+ [@settings['server_urls']]
99
+ when Array
100
+ @settings['server_urls']
101
+ when nil
102
+ raise Bolt::PuppetDBError, "server_urls must be specified"
103
+ else
104
+ raise Bolt::PuppetDBError, "server_urls must be a string or array"
105
+ end
106
+ end
107
+
108
+ def uri
109
+ return @uri if @uri
110
+ require 'addressable/uri'
111
+
112
+ uri = case @settings['server_urls']
113
+ when String
114
+ @settings['server_urls']
115
+ when Array
116
+ @settings['server_urls'].first
117
+ when nil
118
+ raise Bolt::PuppetDBError, "server_urls must be specified"
119
+ else
120
+ raise Bolt::PuppetDBError, "server_urls must be a string or array"
121
+ end
122
+
123
+ @uri = Addressable::URI.parse(uri)
124
+ @uri.port ||= 8081
125
+ @uri
126
+ end
127
+
128
+ def cacert
129
+ if @settings['cacert'] && validate_file_exists('cacert')
130
+ @settings['cacert']
131
+ else
132
+ raise Bolt::PuppetDBError, "cacert must be specified"
133
+ end
134
+ end
135
+
136
+ def cert
137
+ validate_cert_and_key
138
+ validate_file_exists('cert')
139
+ @settings['cert']
140
+ end
141
+
142
+ def key
143
+ validate_cert_and_key
144
+ validate_file_exists('key')
145
+ @settings['key']
146
+ end
147
+
148
+ def validate_cert_and_key
149
+ if (@settings['cert'] && !@settings['key']) ||
150
+ (!@settings['cert'] && @settings['key'])
151
+ raise Bolt::PuppetDBError, "cert and key must be specified together"
152
+ end
153
+ end
154
+
155
+ def connect_timeout
156
+ validate_timeout('connect_timeout')
157
+ @settings['connect_timeout']
158
+ end
159
+
160
+ def read_timeout
161
+ validate_timeout('read_timeout')
162
+ @settings['read_timeout']
163
+ end
164
+
165
+ def validate_timeout(timeout)
166
+ unless @settings[timeout].nil? || (@settings[timeout].is_a?(Integer) && @settings[timeout] > 0)
167
+ raise Bolt::PuppetDBError, "#{timeout} must be a positive integer, received #{@settings[timeout]}"
168
+ end
169
+ end
170
+
171
+ def to_hash
172
+ @settings.dup
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'logging'
5
+ require_relative '../../bolt/puppetdb/config'
6
+
7
+ module Bolt
8
+ module PuppetDB
9
+ class Instance
10
+ attr_reader :config
11
+
12
+ def initialize(config:, project: nil, load_defaults: false)
13
+ @config = Bolt::PuppetDB::Config.new(config: config, project: project, load_defaults: load_defaults)
14
+ @bad_urls = []
15
+ @current_url = nil
16
+ @logger = Bolt::Logger.logger(self)
17
+ end
18
+
19
+ def make_query(query, path = nil)
20
+ body = JSON.generate(query: query)
21
+ url = "#{uri}/pdb/query/v4"
22
+ url += "/#{path}" if path
23
+
24
+ begin
25
+ @logger.debug("Sending PuppetDB query to #{url}")
26
+ response = http_client.post(url, body: body, header: headers)
27
+ rescue StandardError => e
28
+ raise Bolt::PuppetDBFailoverError, "Failed to query PuppetDB: #{e}"
29
+ end
30
+
31
+ @logger.debug("Got response code #{response.code} from PuppetDB")
32
+ if response.code != 200
33
+ msg = "Failed to query PuppetDB: #{response.body}"
34
+ if response.code == 400
35
+ raise Bolt::PuppetDBError, msg
36
+ else
37
+ raise Bolt::PuppetDBFailoverError, msg
38
+ end
39
+ end
40
+
41
+ begin
42
+ JSON.parse(response.body)
43
+ rescue JSON::ParserError
44
+ raise Bolt::PuppetDBError, "Unable to parse response as JSON: #{response.body}"
45
+ end
46
+ rescue Bolt::PuppetDBFailoverError => e
47
+ @logger.error("Request to puppetdb at #{@current_url} failed with #{e}.")
48
+ reject_url
49
+ make_query(query, path)
50
+ end
51
+
52
+ # Sends a command to PuppetDB using version 1 of the commands API.
53
+ # https://puppet.com/docs/puppetdb/latest/api/command/v1/commands.html
54
+ #
55
+ # @param command [String] The command to invoke.
56
+ # @param version [Integer] The version of the command to invoke.
57
+ # @param payload [Hash] The payload to send with the command.
58
+ # @return A UUID identifying the submitted command.
59
+ #
60
+ def send_command(command, version, payload)
61
+ command = command.dup.force_encoding('utf-8')
62
+ body = JSON.generate(payload)
63
+
64
+ # PDB requires the following query parameters to the POST request.
65
+ # Error early if there's no certname, as PDB does not return a
66
+ # message indicating it's required.
67
+ unless payload['certname']
68
+ raise Bolt::Error.new(
69
+ "Payload must include 'certname', unable to invoke command.",
70
+ 'bolt/pdb-command'
71
+ )
72
+ end
73
+
74
+ url = uri.tap do |u|
75
+ u.path = 'pdb/cmd/v1'
76
+ u.query_values = { 'command' => command,
77
+ 'version' => version,
78
+ 'certname' => payload['certname'] }
79
+ end
80
+
81
+ # Send the command to PDB
82
+ begin
83
+ @logger.debug("Sending PuppetDB command '#{command}' to #{url}")
84
+ response = http_client.post(url.to_s, body: body, header: headers)
85
+ rescue StandardError => e
86
+ raise Bolt::PuppetDBFailoverError, "Failed to invoke PuppetDB command: #{e}"
87
+ end
88
+
89
+ @logger.debug("Got response code #{response.code} from PuppetDB")
90
+ if response.code != 200
91
+ raise Bolt::PuppetDBError, "Failed to invoke PuppetDB command: #{response.body}"
92
+ end
93
+
94
+ # Return the UUID string from the response body
95
+ begin
96
+ JSON.parse(response.body).fetch('uuid', nil)
97
+ rescue JSON::ParserError
98
+ raise Bolt::PuppetDBError, "Unable to parse response as JSON: #{response.body}"
99
+ end
100
+ rescue Bolt::PuppetDBFailoverError => e
101
+ @logger.error("Request to puppetdb at #{@current_url} failed with #{e}.")
102
+ reject_url
103
+ send_command(command, version, payload)
104
+ end
105
+
106
+ def http_client
107
+ return @http if @http
108
+ # lazy-load expensive gem code
109
+ require 'httpclient'
110
+ @logger.trace("Creating HTTP Client")
111
+ @http = HTTPClient.new
112
+ @http.ssl_config.set_client_cert_file(@config.cert, @config.key) if @config.cert
113
+ @http.ssl_config.add_trust_ca(@config.cacert)
114
+ @http.connect_timeout = @config.connect_timeout if @config.connect_timeout
115
+ @http.receive_timeout = @config.read_timeout if @config.read_timeout
116
+
117
+ @http
118
+ end
119
+
120
+ def reject_url
121
+ @bad_urls << @current_url if @current_url
122
+ @current_url = nil
123
+ end
124
+
125
+ def uri
126
+ require 'addressable/uri'
127
+
128
+ @current_url ||= (@config.server_urls - @bad_urls).first
129
+ unless @current_url
130
+ msg = "Failed to connect to all PuppetDB server_urls: #{@config.server_urls.to_a.join(', ')}."
131
+ raise Bolt::PuppetDBError, msg
132
+ end
133
+
134
+ uri = Addressable::URI.parse(@current_url)
135
+ uri.port ||= 8081
136
+ uri
137
+ end
138
+
139
+ def headers
140
+ headers = { 'Content-Type' => 'application/json' }
141
+ headers['X-Authentication'] = @config.token if @config.token
142
+ headers
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../bolt/error'
4
+ require_relative 'puppetdb/client'
5
+ require_relative 'puppetdb/config'
6
+
7
+ module Bolt
8
+ class PuppetDBError < Bolt::Error
9
+ def initialize(msg)
10
+ super(msg, "bolt/puppetdb-error")
11
+ end
12
+ end
13
+
14
+ class PuppetDBFailoverError < PuppetDBError; end
15
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'log4r/outputter/outputter'
4
+
5
+ module Bolt
6
+ class R10KLogProxy < Log4r::Outputter
7
+ def initialize
8
+ super('bolt')
9
+
10
+ @logger = Bolt::Logger.logger(self)
11
+ end
12
+
13
+ def canonical_log(event)
14
+ level = to_bolt_level(event.level)
15
+ @logger.send(level, event.data)
16
+ end
17
+
18
+ # Convert an r10k log level to a bolt log level. These correspond 1-to-1
19
+ # except that r10k has debug, debug1, and debug2. The log event has the log
20
+ # level as an integer that we need to look up.
21
+ def to_bolt_level(level_num)
22
+ level_str = Log4r::LNAMES[level_num]&.downcase || 'debug'
23
+ if level_str =~ /debug/
24
+ :debug
25
+ else
26
+ level_str.to_sym
27
+ end
28
+ end
29
+ end
30
+ end
data/lib/bolt/rerun.rb ADDED
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'logging'
5
+
6
+ module Bolt
7
+ class Rerun
8
+ def initialize(path, save_failures)
9
+ @path = path
10
+ @save_failures = save_failures
11
+ @logger = Bolt::Logger.logger(self)
12
+ end
13
+
14
+ def data
15
+ @data ||= Bolt::Util.read_json_file(@path, 'rerun')
16
+ unless @data.is_a?(Array) && @data.all? { |r| r['target'] && r['status'] }
17
+ raise Bolt::FileError.new("Missing data in rerun file: #{@path}", @path)
18
+ end
19
+ @data
20
+ end
21
+
22
+ def get_targets(filter)
23
+ filtered = case filter
24
+ when 'all'
25
+ data
26
+ when 'failure'
27
+ data.select { |result| result['status'] == 'failure' }
28
+ when 'success'
29
+ data.select { |result| result['status'] == 'success' }
30
+ else
31
+ raise Bolt::CLIError, "Unexpected option #{filter} for '--retry'"
32
+ end
33
+ filtered.map { |result| result['target'] }
34
+ end
35
+
36
+ def update(result_set)
37
+ unless @save_failures == false
38
+ if result_set.is_a?(Bolt::PlanResult)
39
+ result_set = result_set.value
40
+ result_set = result_set.result_set if result_set.is_a?(Bolt::RunFailure)
41
+ end
42
+
43
+ if result_set.is_a?(Bolt::ResultSet)
44
+ data = result_set.map { |res| { target: res.target.name, status: res.status } }
45
+ FileUtils.mkdir_p(File.dirname(@path))
46
+ File.write(@path, data.to_json)
47
+ elsif File.exist?(@path)
48
+ FileUtils.rm(@path)
49
+ end
50
+ end
51
+ rescue StandardError => e
52
+ Bolt::Logger.warn_once("unwriteable_file", "Failed to save result to #{@path}: #{e.message}")
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Bolt
6
+ class ResourceInstance
7
+ attr_reader :target, :type, :title, :state, :desired_state
8
+ attr_accessor :events
9
+
10
+ # Needed by Puppet to recognize Bolt::ResourceInstance as a Puppet object when deserializing
11
+ def self._pcore_type
12
+ ResourceInstance
13
+ end
14
+
15
+ # Needed by Puppet to serialize with _pcore_init_hash instead of the object's attributes
16
+ def self._pcore_init_from_hash(_init_hash)
17
+ raise "ResourceInstance shouldn't be instantiated from a pcore_init class method. "\
18
+ "How did this get called?"
19
+ end
20
+
21
+ def _pcore_init_from_hash(init_hash)
22
+ initialize(init_hash)
23
+ end
24
+
25
+ # Parameters will already be validated when calling ResourceInstance.new or
26
+ # set_resources() from a plan. We don't perform any validation in the class
27
+ # itself since Puppet will pass an empty hash to the initializer as part of
28
+ # the deserialization process before passing the _pcore_init_hash.
29
+ def initialize(resource_hash)
30
+ @target = resource_hash['target']
31
+ @type = resource_hash['type'].to_s.capitalize
32
+ @title = resource_hash['title']
33
+ @state = resource_hash['state'] || {}
34
+ @desired_state = resource_hash['desired_state'] || {}
35
+ @events = resource_hash['events'] || []
36
+ end
37
+
38
+ # Creates a ResourceInstance from a data hash in a plan when calling
39
+ # ResourceInstance.new($resource_hash) or $target.set_resources($resource_hash)
40
+ def self.from_asserted_hash(resource_hash)
41
+ new(resource_hash)
42
+ end
43
+
44
+ # Creates a ResourceInstance from positional arguments in a plan when
45
+ # calling ResourceInstance.new(target, type, title, ...)
46
+ def self.from_asserted_args(target,
47
+ type,
48
+ title,
49
+ state = nil,
50
+ desired_state = nil,
51
+ events = nil)
52
+ new(
53
+ 'target' => target,
54
+ 'type' => type,
55
+ 'title' => title,
56
+ 'state' => state,
57
+ 'desired_state' => desired_state,
58
+ 'events' => events
59
+ )
60
+ end
61
+
62
+ def eql?(other)
63
+ self.class.equal?(other.class) &&
64
+ target == other.target &&
65
+ type == other.type &&
66
+ title == other.title
67
+ end
68
+ alias == eql?
69
+
70
+ def to_hash
71
+ {
72
+ 'target' => target,
73
+ 'type' => type,
74
+ 'title' => title,
75
+ 'state' => state,
76
+ 'desired_state' => desired_state,
77
+ 'events' => events
78
+ }
79
+ end
80
+ alias _pcore_init_hash to_hash
81
+
82
+ def to_json(opts = nil)
83
+ to_hash.to_json(opts)
84
+ end
85
+
86
+ def self.format_reference(type, title)
87
+ "#{type.capitalize}[#{title}]"
88
+ end
89
+
90
+ def reference
91
+ self.class.format_reference(@type, @title)
92
+ end
93
+ alias to_s reference
94
+
95
+ def [](attribute)
96
+ @state[attribute]
97
+ end
98
+
99
+ def add_event(event)
100
+ @events << event
101
+ end
102
+
103
+ # rubocop:disable Naming/AccessorMethodName
104
+ def set_state(state)
105
+ assert_hash('state', state)
106
+ @state.merge!(state)
107
+ end
108
+ # rubocop:enable Naming/AccessorMethodName
109
+
110
+ def overwrite_state(state)
111
+ assert_hash('state', state)
112
+ @state = state
113
+ end
114
+
115
+ # rubocop:disable Naming/AccessorMethodName
116
+ def set_desired_state(desired_state)
117
+ assert_hash('desired_state', desired_state)
118
+ @desired_state.merge!(desired_state)
119
+ end
120
+ # rubocop:enable Naming/AccessorMethodName
121
+
122
+ def overwrite_desired_state(desired_state)
123
+ assert_hash('desired_state', desired_state)
124
+ @desired_state = desired_state
125
+ end
126
+
127
+ def assert_hash(loc, value)
128
+ unless value.is_a?(Hash)
129
+ raise Bolt::ValidationError, "#{loc} must be of type Hash; got #{value.class}"
130
+ end
131
+ end
132
+ end
133
+ end