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,293 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../bolt/resource_instance'
4
+
5
+ module Bolt
6
+ class Inventory
7
+ # This class represents the active state of a target within the inventory.
8
+ class Target
9
+ attr_reader :name, :uri, :safe_name, :target_alias, :resources
10
+
11
+ # Illegal characters that are not permitted in target names.
12
+ # These characters are delimiters for target and group names and allowing
13
+ # them would cause unexpected behavior.
14
+ ILLEGAL_CHARS = /[\s,]/.freeze
15
+
16
+ def initialize(target_data, inventory)
17
+ unless target_data['name'] || target_data['uri']
18
+ raise Bolt::Inventory::ValidationError.new("Target must have either a name or uri", nil)
19
+ end
20
+
21
+ @logger = Bolt::Logger.logger(inventory)
22
+
23
+ # If the target isn't mentioned by any groups, it won't have a uri or
24
+ # name and we will use the target_name as both
25
+ @uri = target_data['uri']
26
+ @uri_obj = self.class.parse_uri(@uri)
27
+
28
+ # If the target has a name, use that as the safe name. Otherwise, turn
29
+ # the uri into a safe name by omitting the password.
30
+ if target_data['name']
31
+ @name = target_data['name']
32
+ @safe_name = target_data['name']
33
+ else
34
+ @name = @uri
35
+ @safe_name = @uri_obj.omit(:password).to_str.sub(%r{^//}, '')
36
+ end
37
+ # handle special localhost target
38
+ if @name == 'localhost'
39
+ default = { 'config' => { 'transport' => 'local' } }
40
+ target_data = Bolt::Util.deep_merge(default, target_data)
41
+ end
42
+
43
+ @config = target_data['config'] || {}
44
+ @vars = target_data['vars'] || {}
45
+ @facts = target_data['facts'] || {}
46
+ @features = target_data['features'] || Set.new
47
+ @plugin_hooks = target_data['plugin_hooks'] || {}
48
+ # When alias is specified in a plan, the key will be `target_alias`, when
49
+ # alias is specified in inventory the key will be `alias`.
50
+ @target_alias = target_data['target_alias'] || target_data['alias'] || []
51
+ @resources = {}
52
+
53
+ @inventory = inventory
54
+
55
+ validate
56
+ # after setting config, apply local defaults when using bundled ruby
57
+ set_local_defaults if transport_config['bundled-ruby']
58
+ end
59
+
60
+ def set_local_defaults
61
+ return if @set_local_default
62
+ defaults = {
63
+ 'local' => { 'interpreters' => { '.rb' => RbConfig.ruby } }
64
+ }
65
+ old_config = @config
66
+ @config = Bolt::Util.deep_merge(defaults, @config)
67
+ invalidate_config_cache! if old_config != @config
68
+ set_feature('puppet-agent')
69
+ @set_local_default = true
70
+ end
71
+
72
+ # rubocop:disable Naming/AccessorMethodName
73
+ def set_resource(resource)
74
+ if (existing_resource = resources[resource.reference])
75
+ existing_resource.overwrite_state(resource.state)
76
+ existing_resource.overwrite_desired_state(resource.desired_state)
77
+ existing_resource.events = existing_resource.events + resource.events
78
+ existing_resource
79
+ else
80
+ @resources[resource.reference] = resource
81
+ end
82
+ end
83
+ # rubocop:enable Naming/AccessorMethodName
84
+
85
+ def vars
86
+ group_cache['vars'].merge(@vars)
87
+ end
88
+
89
+ # This method isn't actually an accessor and we want the name to
90
+ # correspond to the Puppet function
91
+ # rubocop:disable Naming/AccessorMethodName
92
+ def set_var(var_hash)
93
+ @vars.merge!(var_hash)
94
+ end
95
+ # rubocop:enable Naming/AccessorMethodName
96
+
97
+ def facts
98
+ Bolt::Util.deep_merge(group_cache['facts'], @facts)
99
+ end
100
+
101
+ def add_facts(new_facts = {})
102
+ validate_fact_names(new_facts)
103
+ Bolt::Util.deep_merge!(@facts, new_facts)
104
+ end
105
+
106
+ def features
107
+ group_cache['features'] + @features
108
+ end
109
+
110
+ def set_feature(feature, value = true)
111
+ if value
112
+ @features << feature
113
+ else
114
+ @features.delete(feature)
115
+ end
116
+ end
117
+
118
+ def resource(type, title)
119
+ resources[Bolt::ResourceInstance.format_reference(type, title)]
120
+ end
121
+
122
+ def plugin_hooks
123
+ # Merge plugin_hooks from the config file with any defined by the group
124
+ # or assigned dynamically to the target
125
+ @inventory.plugins.plugin_hooks.merge(group_cache['plugin_hooks']).merge(@plugin_hooks)
126
+ end
127
+
128
+ def set_config(key_or_key_path, value)
129
+ if key_or_key_path.empty?
130
+ @config = value
131
+ else
132
+ *path, key = Array(key_or_key_path)
133
+ location = path.inject(@config) do |working_object, p|
134
+ working_object[p] ||= {}
135
+ end
136
+ location[key] = value
137
+ end
138
+
139
+ invalidate_config_cache!
140
+ end
141
+
142
+ def invalidate_group_cache!
143
+ @group_cache = nil
144
+ # The config cache depends on the group cache, so invalidate it as well
145
+ invalidate_config_cache!
146
+ end
147
+
148
+ def invalidate_config_cache!
149
+ @transport = nil
150
+ @transport_config = nil
151
+ end
152
+
153
+ # Validate the target. This implicitly also primes the group and config
154
+ # caches and resolves any config references in the target's groups.
155
+ def validate
156
+ unless name.ascii_only?
157
+ raise Bolt::Inventory::ValidationError.new("Target name must be ASCII characters: #{@name}", nil)
158
+ end
159
+
160
+ if (illegal_char = @name.match(ILLEGAL_CHARS))
161
+ raise ValidationError.new("Illegal character '#{illegal_char}' in target name '#{@name}'", nil)
162
+ end
163
+
164
+ unless transport.nil? || Bolt::TRANSPORTS.include?(transport.to_sym)
165
+ raise Bolt::UnknownTransportError.new(transport, uri)
166
+ end
167
+
168
+ validate_fact_names(facts)
169
+
170
+ transport_config
171
+ end
172
+
173
+ # Validate fact names and issue a deprecation warning if any fact names have a dot.
174
+ #
175
+ private def validate_fact_names(facts)
176
+ if (dotted = facts.keys.select { |name| name.include?('.') }).any?
177
+ Bolt::Logger.deprecate(
178
+ 'dotted_fact_name',
179
+ "Target '#{safe_name}' includes dotted fact names: '#{dotted.join("', '")}'. Dotted fact "\
180
+ "names are deprecated and Bolt does not automatically convert facts with dotted names to "\
181
+ "structured facts. For more information, see https://pup.pt/bolt-dotted-facts"
182
+ )
183
+ end
184
+ end
185
+
186
+ def host
187
+ @uri_obj.hostname || transport_config['host']
188
+ end
189
+
190
+ def port
191
+ @uri_obj.port || transport_config['port']
192
+ end
193
+
194
+ # For remote targets, protocol is the value of the URI scheme. For
195
+ # non-remote targets, there is no protocol.
196
+ def protocol
197
+ if remote?
198
+ @uri_obj.scheme
199
+ end
200
+ end
201
+
202
+ # For remote targets, the transport is always 'remote'. Otherwise, it
203
+ # will be either the URI scheme or set explicitly.
204
+ def transport
205
+ if @transport.nil?
206
+ config_transport = @config['transport'] ||
207
+ group_cache.dig('config', 'transport') ||
208
+ @inventory.transport
209
+
210
+ @transport = if @uri_obj.scheme == 'remote' || config_transport == 'remote'
211
+ 'remote'
212
+ else
213
+ @uri_obj.scheme || config_transport
214
+ end
215
+ end
216
+
217
+ @transport
218
+ end
219
+
220
+ def remote?
221
+ transport == 'remote'
222
+ end
223
+
224
+ def user
225
+ Addressable::URI.unencode_component(@uri_obj.user) || transport_config['user']
226
+ end
227
+
228
+ def password
229
+ Addressable::URI.unencode_component(@uri_obj.password) || transport_config['password']
230
+ end
231
+
232
+ # We only want to look up transport config keys for the configured
233
+ # transport
234
+ def transport_config
235
+ if @transport_config.nil?
236
+ config = @inventory.config[transport]
237
+ .merge(group_cache.dig('config', transport), @config[transport])
238
+ @transport_config = config
239
+ end
240
+
241
+ @transport_config
242
+ end
243
+
244
+ def config
245
+ Bolt::Util.deep_merge(group_cache['config'], @config)
246
+ end
247
+
248
+ def group_cache
249
+ if @group_cache.nil?
250
+ group_data = @inventory.group_data_for(@name)
251
+
252
+ unless group_data && group_data['config']
253
+ @logger.debug("Did not find config for #{self} in inventory")
254
+ end
255
+
256
+ group_data ||= {
257
+ 'config' => {},
258
+ 'vars' => {},
259
+ 'facts' => {},
260
+ 'features' => Set.new,
261
+ 'plugin_hooks' => {},
262
+ 'target_alias' => []
263
+ }
264
+
265
+ @group_cache = group_data
266
+ end
267
+
268
+ @group_cache
269
+ end
270
+
271
+ def to_s
272
+ @safe_name
273
+ end
274
+
275
+ def self.parse_uri(string)
276
+ require 'addressable/uri'
277
+ if string.nil?
278
+ Addressable::URI.new
279
+ # Forbid empty uri
280
+ elsif string.empty?
281
+ raise Bolt::ParseError, "Could not parse target URI: URI is empty string"
282
+ elsif string =~ %r{^[^:]+://}
283
+ Addressable::URI.parse(string)
284
+ else
285
+ # Initialize with an empty scheme to ensure we parse the hostname correctly
286
+ Addressable::URI.parse("//#{string}")
287
+ end
288
+ rescue Addressable::URI::InvalidURIError => e
289
+ raise Bolt::ParseError, "Could not parse target URI: #{e.message}"
290
+ end
291
+ end
292
+ end
293
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+ require_relative '../bolt/config'
5
+ require_relative 'inventory/group'
6
+ require_relative 'inventory/inventory'
7
+ require_relative 'inventory/options'
8
+ require_relative '../bolt/target'
9
+ require_relative '../bolt/util'
10
+ require_relative '../bolt/plugin'
11
+ require_relative '../bolt/validator'
12
+ require 'yaml'
13
+
14
+ module Bolt
15
+ class Inventory
16
+ include Bolt::Inventory::Options
17
+
18
+ ENVIRONMENT_VAR = 'BOLT_INVENTORY'
19
+
20
+ class ValidationError < Bolt::Error
21
+ attr_accessor :path
22
+
23
+ def initialize(message, offending_group)
24
+ super(message, 'bolt.inventory/validation-error')
25
+ @_message = message
26
+ @path = [offending_group].compact
27
+ end
28
+
29
+ def details
30
+ { 'path' => path }
31
+ end
32
+
33
+ def add_parent(parent_group)
34
+ @path << parent_group
35
+ end
36
+
37
+ def message
38
+ if path.empty?
39
+ @_message
40
+ else
41
+ "#{@_message} for group at #{path}"
42
+ end
43
+ end
44
+ end
45
+
46
+ class WildcardError < Bolt::Error
47
+ def initialize(target)
48
+ super("Found 0 nodes matching wildcard pattern #{target}", 'bolt.inventory/wildcard-error')
49
+ end
50
+ end
51
+
52
+ # Builds the schema used by the validator.
53
+ #
54
+ def self.schema
55
+ schema = {
56
+ type: Hash,
57
+ properties: OPTIONS.map { |opt| [opt, _ref: opt] }.to_h,
58
+ definitions: DEFINITIONS,
59
+ _plugin: true
60
+ }
61
+
62
+ schema[:definitions]['config'][:properties] = Bolt::Config.transport_definitions
63
+ schema
64
+ end
65
+
66
+ def self.from_config(config, plugins)
67
+ logger = Bolt::Logger.logger(self)
68
+
69
+ if ENV.include?(ENVIRONMENT_VAR)
70
+ begin
71
+ source = ENVIRONMENT_VAR
72
+ data = YAML.safe_load(ENV[ENVIRONMENT_VAR])
73
+ raise Bolt::ParseError, "Could not parse inventory from $#{ENVIRONMENT_VAR}" unless data.is_a?(Hash)
74
+ logger.debug("Loaded inventory from environment variable #{ENVIRONMENT_VAR}")
75
+ rescue Psych::Exception
76
+ raise Bolt::ParseError, "Could not parse inventory from $#{ENVIRONMENT_VAR}"
77
+ end
78
+ elsif config.inventoryfile
79
+ source = config.inventoryfile
80
+ data = Bolt::Util.read_yaml_hash(config.inventoryfile, 'inventory')
81
+ logger.debug("Loaded inventory from #{config.inventoryfile}")
82
+ else
83
+ source = config.default_inventoryfile
84
+ data = Bolt::Util.read_optional_yaml_hash(config.default_inventoryfile, 'inventory')
85
+
86
+ if config.default_inventoryfile.exist?
87
+ logger.debug("Loaded inventory from #{config.default_inventoryfile}")
88
+ else
89
+ source = nil
90
+ logger.debug("Tried to load inventory from #{config.default_inventoryfile}, but the file does not exist")
91
+ end
92
+ end
93
+
94
+ Bolt::Validator.new.tap do |validator|
95
+ validator.validate(data, schema, source)
96
+ validator.warnings.each { |warning| Bolt::Logger.warn(warning[:id], warning[:msg]) }
97
+ end
98
+
99
+ create_version(data, config.transport, config.transports, plugins, source)
100
+ end
101
+
102
+ def self.create_version(data, transport, transports, plugins, source = nil)
103
+ version = (data || {}).delete('version') { 2 }
104
+
105
+ case version
106
+ when 2
107
+ Bolt::Inventory::Inventory.new(data, transport, transports, plugins, source)
108
+ else
109
+ raise ValidationError.new("Unsupported version #{version} specified in inventory", nil)
110
+ end
111
+ end
112
+
113
+ def self.empty
114
+ config = Bolt::Config.default
115
+ plugins = Bolt::Plugin.new(config, nil)
116
+
117
+ create_version({}, config.transport, config.transports, plugins, nil)
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,252 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logging'
4
+
5
+ module Bolt
6
+ module Logger
7
+ LEVELS = %w[trace debug info warn error fatal].freeze
8
+
9
+ # This module is treated as a global singleton so that multiple classes
10
+ # in Bolt can log warnings with IDs. Access to the following variables
11
+ # are controlled by a mutex.
12
+ @mutex = Mutex.new
13
+ @warnings = Set.new
14
+ @disable_warnings = Set.new
15
+ @message_queue = []
16
+
17
+ # This method provides a single point-of-entry to setup logging for both
18
+ # the CLI and for tests. This is necessary because we define custom log
19
+ # levels which create corresponding methods on the logger instances;
20
+ # without first initializing the Logging system, calls to those methods
21
+ # will fail.
22
+ def self.initialize_logging
23
+ # Initialization isn't idempotent and will result in warnings about const
24
+ # redefs, so skip it if the log levels we expect are present. If it's
25
+ # already been initialized with an insufficient set of levels, go ahead
26
+ # and call init anyway or we'll have failures when calling log methods
27
+ # for missing levels.
28
+ unless levels & LEVELS == LEVELS
29
+ Logging.init(*LEVELS)
30
+ end
31
+
32
+ # As above, only create the color scheme if we haven't already created it.
33
+ unless Logging.color_scheme('bolt')
34
+ Logging.color_scheme(
35
+ 'bolt',
36
+ lines: {
37
+ warn: :yellow,
38
+ error: :red,
39
+ fatal: %i[white on_red]
40
+ }
41
+ )
42
+ end
43
+ end
44
+
45
+ def self.configure(destinations, color, disable_warnings = nil)
46
+ root_logger = Bolt::Logger.logger(:root)
47
+
48
+ root_logger.add_appenders Logging.appenders.stderr(
49
+ 'console',
50
+ layout: console_layout(color),
51
+ level: default_console_level
52
+ )
53
+
54
+ # We set the root logger's level so that it logs everything but we do
55
+ # limit what's actually logged in every appender individually.
56
+ root_logger.level = :all
57
+
58
+ destinations.each_pair do |name, params|
59
+ appender = Logging.appenders[name]
60
+ if appender.nil?
61
+ unless name.start_with?('file:')
62
+ raise Bolt::Error.new("Unexpected log: #{name}", 'bolt/internal-error')
63
+ end
64
+
65
+ begin
66
+ appender = Logging.appenders.file(
67
+ name,
68
+ filename: name[5..-1], # strip the "file:" prefix
69
+ truncate: (params[:append] == false),
70
+ layout: default_layout,
71
+ level: default_file_level
72
+ )
73
+ rescue ArgumentError => e
74
+ raise Bolt::Error.new("Failed to open log #{name}: #{e.message}", 'bolt/log-error')
75
+ end
76
+
77
+ root_logger.add_appenders appender
78
+ end
79
+
80
+ appender.level = params[:level] if params[:level]
81
+ end
82
+
83
+ # Set the list of disabled warnings and mark the logger as configured.
84
+ # Log all messages in the message queue and flush the queue.
85
+ if disable_warnings
86
+ @mutex.synchronize { @disable_warnings = disable_warnings }
87
+ end
88
+ end
89
+
90
+ def self.configured?
91
+ Logging.logger[:root].appenders.any?
92
+ end
93
+
94
+ def self.stream
95
+ @stream
96
+ end
97
+
98
+ def self.stream=(stream)
99
+ @stream = stream
100
+ end
101
+
102
+ # A helper to ensure the Logging library is always initialized with our
103
+ # custom log levels before retrieving a Logger instance.
104
+ def self.logger(name)
105
+ initialize_logging
106
+ Logging.logger[name]
107
+ end
108
+
109
+ def self.analytics=(analytics)
110
+ @analytics = analytics
111
+ end
112
+
113
+ def self.console_layout(color)
114
+ color_scheme = :bolt if color
115
+ Logging.layouts.pattern(
116
+ pattern: '%m\e[0m\n',
117
+ color_scheme: color_scheme
118
+ )
119
+ end
120
+
121
+ def self.default_layout
122
+ Logging.layouts.pattern(
123
+ pattern: '%d %-6l [%T] [%c] %m\n',
124
+ date_pattern: '%Y-%m-%dT%H:%M:%S.%6N'
125
+ )
126
+ end
127
+
128
+ def self.default_console_level
129
+ :warn
130
+ end
131
+
132
+ def self.default_file_level
133
+ :warn
134
+ end
135
+
136
+ # Explicitly check the log level names instead of the log level number, as levels
137
+ # that are stringified integers (e.g. "level" => "42") will return a truthy value
138
+ def self.valid_level?(level)
139
+ Logging::LEVELS.include?(Logging.levelify(level))
140
+ end
141
+
142
+ def self.levels
143
+ Logging::LNAMES.map(&:downcase)
144
+ end
145
+
146
+ def self.reset_logging
147
+ Logging.reset
148
+ end
149
+
150
+ # The following methods are used in place of the Logging.logger
151
+ # methods of the same name when logging warning messages or logging
152
+ # any messages prior to the logger being configured. If the logger
153
+ # is not configured when any of these methods are called, the message
154
+ # will be added to a queue, otherwise they are logged immediately.
155
+ # The message queue is flushed by calling #flush_queue, which is
156
+ # called from Bolt::CLI after configuring the logger.
157
+ #
158
+ def self.warn(id, msg)
159
+ log(type: :warn, msg: "#{msg} [ID: #{id}]", id: id)
160
+ end
161
+
162
+ def self.warn_once(id, msg)
163
+ log(type: :warn_once, msg: "#{msg} [ID: #{id}]", id: id)
164
+ end
165
+
166
+ def self.deprecate(id, msg)
167
+ log(type: :deprecate, msg: "#{msg} [ID: #{id}]", id: id)
168
+ end
169
+
170
+ def self.deprecate_once(id, msg)
171
+ log(type: :deprecate_once, msg: "#{msg} [ID: #{id}]", id: id)
172
+ end
173
+
174
+ def self.debug(msg)
175
+ log(type: :debug, msg: msg)
176
+ end
177
+
178
+ def self.info(msg)
179
+ log(type: :info, msg: msg)
180
+ end
181
+
182
+ # Logs a message. If the logger has not been configured, this will queue
183
+ # the message to be logged later. Once the logger is configured, the
184
+ # queue will be flushed of all messages and new messages will be logged
185
+ # immediately.
186
+ #
187
+ # Logging with this method is controlled by a mutex, as the Bolt::Logger
188
+ # module is treated as a global singleton to allow multiple classes
189
+ # access to its methods.
190
+ #
191
+ private_class_method def self.log(type:, msg:, id: nil)
192
+ @mutex.synchronize do
193
+ if configured?
194
+ log_message(type: type, msg: msg, id: id)
195
+ else
196
+ @message_queue << { type: type, msg: msg, id: id }
197
+ end
198
+ end
199
+ end
200
+
201
+ # Logs all messages in the message queue and then flushes the queue.
202
+ #
203
+ def self.flush_queue
204
+ @mutex.synchronize do
205
+ @message_queue.each do |message|
206
+ log_message(**message)
207
+ end
208
+
209
+ @message_queue.clear
210
+ end
211
+ end
212
+
213
+ # Handles the actual logging of a message.
214
+ #
215
+ private_class_method def self.log_message(type:, msg:, id: nil)
216
+ case type
217
+ when :warn
218
+ do_warn(msg, id)
219
+ when :warn_once
220
+ do_warn_once(msg, id)
221
+ when :deprecate
222
+ do_deprecate(msg, id)
223
+ when :deprecate_once
224
+ do_deprecate_once(msg, id)
225
+ else
226
+ logger(self).send(type, msg)
227
+ end
228
+ end
229
+
230
+ # The following methods do the actual warning.
231
+ #
232
+ private_class_method def self.do_warn(msg, id)
233
+ return if @disable_warnings.include?(id)
234
+ logger(self).warn(msg)
235
+ end
236
+
237
+ private_class_method def self.do_warn_once(msg, id)
238
+ return unless @warnings.add?(id)
239
+ do_warn(msg, id)
240
+ end
241
+
242
+ private_class_method def self.do_deprecate(msg, id)
243
+ @analytics&.event('Warn', 'deprecation', label: id)
244
+ do_warn(msg, id)
245
+ end
246
+
247
+ private_class_method def self.do_deprecate_once(msg, id)
248
+ @analytics&.event('Warn', 'deprecation', label: id)
249
+ do_warn_once(msg, id)
250
+ end
251
+ end
252
+ end