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,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Is this Bolt::Pobs::Module?
4
+ module Bolt
5
+ class Module
6
+ CONTENT_NAME_REGEX = /\A[a-z][a-z0-9_]*(::[a-z][a-z0-9_]*)*\z/.freeze
7
+ MODULE_NAME_REGEX = /\A[a-z][a-z0-9_]*\z/.freeze
8
+
9
+ def self.discover(modulepath, project)
10
+ mods = {}
11
+
12
+ if project.load_as_module?
13
+ mods[project.name] = Bolt::Module.new(project.name, project.path.to_s)
14
+ end
15
+
16
+ modulepath.each do |path|
17
+ next unless File.exist?(path) && File.directory?(path)
18
+ Dir.children(path)
19
+ .map { |dir| File.join(path, dir) }
20
+ .select { |dir| File.directory?(dir) }
21
+ .each do |dir|
22
+ module_name = File.basename(dir)
23
+ if module_name =~ MODULE_NAME_REGEX
24
+ # Puppet will load some objects from shadowed modules but this won't
25
+ mods[module_name] ||= Bolt::Module.new(module_name, dir)
26
+ end
27
+ end
28
+ end
29
+
30
+ mods
31
+ end
32
+
33
+ attr_reader :name, :path
34
+
35
+ def initialize(name, path)
36
+ @name = name
37
+ @path = path
38
+ end
39
+
40
+ def plugin_data_file
41
+ File.join(path, 'bolt_plugin.json')
42
+ end
43
+
44
+ def plugin?
45
+ if File.exist?(File.join(path, 'bolt-plugin.json'))
46
+ msg = "Found bolt-plugin.json in module #{name} at #{path}. Bolt looks for " \
47
+ "bolt_plugin.json to determine if the module contains plugins. " \
48
+ "Rename the file for Bolt to recognize it."
49
+ Bolt::Logger.warn_once('plugin_file_name', msg)
50
+ end
51
+ File.exist?(plugin_data_file)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../bolt/r10k_log_proxy'
4
+ require_relative '../../bolt/error'
5
+
6
+ # This class is used to install modules from a Puppetfile to a module directory.
7
+ #
8
+ module Bolt
9
+ class ModuleInstaller
10
+ class Installer
11
+ def initialize(config = {})
12
+ @config = config
13
+ end
14
+
15
+ def install(path, moduledir)
16
+ require 'r10k/cli'
17
+
18
+ unless File.exist?(path)
19
+ raise Bolt::FileError.new(
20
+ "Could not find a Puppetfile at #{path}",
21
+ path
22
+ )
23
+ end
24
+
25
+ r10k_opts = {
26
+ root: File.dirname(path),
27
+ puppetfile: path.to_s,
28
+ moduledir: moduledir.to_s
29
+ }
30
+
31
+ settings = R10K::Settings.global_settings.evaluate(@config)
32
+ R10K::Initializers::GlobalInitializer.new(settings).call
33
+ install_action = R10K::Action::Puppetfile::Install.new(r10k_opts, nil, {})
34
+
35
+ # Override the r10k logger with a proxy to our own logger
36
+ R10K::Logging.instance_variable_set(:@outputter, Bolt::R10KLogProxy.new)
37
+
38
+ install_action.call
39
+ rescue R10K::Error => e
40
+ raise PuppetfileError, e
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'semantic_puppet'
4
+ require_relative '../../../bolt/module_installer/puppetfile/module'
5
+
6
+ # This class represents a resolved Forge module.
7
+ #
8
+ module Bolt
9
+ class ModuleInstaller
10
+ class Puppetfile
11
+ class ForgeModule < Module
12
+ attr_reader :version
13
+
14
+ def initialize(name, version)
15
+ super(name)
16
+ @version = parse_version(version)
17
+ @type = :forge
18
+ end
19
+
20
+ # Parses the version into a Semantic Puppet version.
21
+ #
22
+ private def parse_version(version)
23
+ return unless version.is_a?(String)
24
+
25
+ unless SemanticPuppet::Version.valid?(version)
26
+ raise Bolt::ValidationError,
27
+ "Invalid version for Forge module #{@full_name}: #{version.inspect}"
28
+ end
29
+
30
+ SemanticPuppet::Version.parse(version)
31
+ end
32
+
33
+ # Returns a Puppetfile module specification.
34
+ #
35
+ def to_spec
36
+ if @version
37
+ "mod '#{@full_name}', '#{@version}'"
38
+ else
39
+ "mod '#{@full_name}'"
40
+ end
41
+ end
42
+
43
+ # Returns a hash that can be used to create a module specification.
44
+ #
45
+ def to_hash
46
+ {
47
+ 'name' => @full_name,
48
+ 'version_requirement' => @version ? @version.to_s : nil
49
+ }.compact
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../bolt/module_installer/puppetfile/module'
4
+
5
+ # This class represents a resolved Git module.
6
+ #
7
+ module Bolt
8
+ class ModuleInstaller
9
+ class Puppetfile
10
+ class GitModule < Module
11
+ attr_reader :git, :ref
12
+
13
+ def initialize(name, git, ref)
14
+ super(name)
15
+ @git = git
16
+ @ref = ref
17
+ @type = :git
18
+ end
19
+
20
+ # Returns a Puppetfile module specification.
21
+ #
22
+ def to_spec
23
+ "mod '#{@name}',\n git: '#{@git}',\n ref: '#{@ref}'"
24
+ end
25
+
26
+ # Returns a hash that can be used to create a module specification.
27
+ #
28
+ def to_hash
29
+ {
30
+ 'git' => @git,
31
+ 'ref' => @ref
32
+ }
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../bolt/error'
4
+
5
+ module Bolt
6
+ class ModuleInstaller
7
+ class Puppetfile
8
+ class Module
9
+ attr_reader :full_name, :name, :type
10
+
11
+ def initialize(name)
12
+ @full_name, @name = parse_name(name)
13
+ end
14
+
15
+ # Formats the full name and extracts the module name.
16
+ #
17
+ protected def parse_name(name)
18
+ full_name = name.tr('-', '/')
19
+ first, second = full_name.split('/', 2)
20
+
21
+ [full_name, second || first]
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../bolt/error'
4
+ require_relative 'puppetfile/forge_module'
5
+ require_relative 'puppetfile/git_module'
6
+
7
+ # This class manages the logical contents of a Puppetfile. It includes methods
8
+ # for parsing and generating a Puppetfile.
9
+ #
10
+ module Bolt
11
+ class ModuleInstaller
12
+ class Puppetfile
13
+ attr_reader :modules
14
+
15
+ def initialize(modules = [])
16
+ @modules = modules
17
+ end
18
+
19
+ # Loads a Puppetfile and parses its modules.
20
+ #
21
+ def self.parse(path, skip_unsupported_modules: false)
22
+ require 'puppetfile-resolver'
23
+
24
+ return new unless path.exist?
25
+
26
+ begin
27
+ parsed = ::PuppetfileResolver::Puppetfile::Parser::R10KEval.parse(File.read(path))
28
+ rescue StandardError => e
29
+ raise Bolt::Error.new(
30
+ "Unable to parse Puppetfile #{path}: #{e.message}",
31
+ 'bolt/puppetfile-parsing'
32
+ )
33
+ end
34
+
35
+ unless parsed.valid?
36
+ raise Bolt::ValidationError, <<~MSG
37
+ Unable to parse Puppetfile #{path}:
38
+ #{parsed.validation_errors.join("\n\n")}.
39
+ This Puppetfile might not be managed by Bolt.
40
+ MSG
41
+ end
42
+
43
+ modules = parsed.modules.each_with_object([]) do |mod, acc|
44
+ case mod.module_type
45
+ when :forge
46
+ acc << ForgeModule.new(
47
+ mod.title,
48
+ mod.version.is_a?(String) ? mod.version[1..-1] : nil
49
+ )
50
+ when :git
51
+ acc << GitModule.new(
52
+ mod.name,
53
+ mod.remote,
54
+ mod.ref || mod.commit || mod.tag
55
+ )
56
+ else
57
+ unless skip_unsupported_modules
58
+ raise Bolt::ValidationError,
59
+ "Cannot parse Puppetfile at #{path}, module '#{mod.title}' is not a "\
60
+ "Puppet Forge or Git module."
61
+ end
62
+ end
63
+ end
64
+
65
+ new(modules)
66
+ end
67
+
68
+ # Writes a Puppetfile that includes specifications for each of the
69
+ # modules.
70
+ #
71
+ def write(path, moduledir = nil)
72
+ File.open(path, 'w') do |file|
73
+ if moduledir
74
+ file.puts "# This Puppetfile is managed by Bolt. Do not edit."
75
+ file.puts "# For more information, see https://pup.pt/bolt-modules"
76
+ file.puts
77
+ file.puts "# The following directive installs modules to the managed moduledir."
78
+ file.puts "moduledir '#{moduledir.basename}'"
79
+ file.puts
80
+ end
81
+
82
+ @modules.each { |mod| file.puts mod.to_spec }
83
+ end
84
+ rescue SystemCallError => e
85
+ raise Bolt::FileError.new(
86
+ "#{e.message}: unable to write Puppetfile.",
87
+ path
88
+ )
89
+ end
90
+
91
+ # Asserts that the Puppetfile satisfies the given specifications.
92
+ #
93
+ def assert_satisfies(specs)
94
+ unsatisfied_specs = specs.specs.reject do |spec|
95
+ @modules.any? do |mod|
96
+ spec.satisfied_by?(mod)
97
+ end
98
+ end
99
+
100
+ versionless_mods = @modules.select { |mod| mod.is_a?(ForgeModule) && mod.version.nil? }
101
+ command = Bolt::Util.windows? ? 'Install-BoltModule -Force' : 'bolt module install --force'
102
+
103
+ if unsatisfied_specs.any?
104
+ message = <<~MESSAGE.chomp
105
+ Puppetfile does not include modules that satisfy the following specifications:
106
+
107
+ #{unsatisfied_specs.map(&:to_hash).to_yaml.lines.drop(1).join.chomp}
108
+
109
+ This Puppetfile might not be managed by Bolt. To forcibly overwrite the
110
+ Puppetfile, run '#{command}'.
111
+ MESSAGE
112
+
113
+ raise Bolt::Error.new(message, 'bolt/missing-module-specs')
114
+ end
115
+
116
+ if versionless_mods.any?
117
+ message = <<~MESSAGE.chomp
118
+ Puppetfile includes Forge modules without a version requirement:
119
+
120
+ #{versionless_mods.map(&:to_spec).join.chomp}
121
+
122
+ This Puppetfile might not be managed by Bolt. To forcibly overwrite the
123
+ Puppetfile, run '#{command}'.
124
+ MESSAGE
125
+
126
+ raise Bolt::Error.new(message, 'bolt/missing-module-version-specs')
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../bolt/error'
4
+ require_relative '../../bolt/module_installer/puppetfile'
5
+ require_relative '../../bolt/module_installer/specs'
6
+
7
+ module Bolt
8
+ class ModuleInstaller
9
+ class Resolver
10
+ # Resolves module specs and returns a Puppetfile object.
11
+ #
12
+ def resolve(specs, config = {})
13
+ require 'puppetfile-resolver'
14
+
15
+ # Build the document model from the specs.
16
+ document = PuppetfileResolver::Puppetfile::Document.new('')
17
+ unresolved = []
18
+
19
+ specs.specs.each do |spec|
20
+ if spec.resolve
21
+ document.add_module(spec.to_resolver_module)
22
+ else
23
+ unresolved << spec
24
+ end
25
+ end
26
+
27
+ # Make sure the document model is valid.
28
+ unless document.valid?
29
+ message = <<~MESSAGE.chomp
30
+ Unable to resolve module specifications:
31
+
32
+ #{document.validation_errors.map(&:message).join("\n")}
33
+ MESSAGE
34
+
35
+ raise Bolt::Error.new(message, 'bolt/module-resolver-error')
36
+ end
37
+
38
+ # Create the resolver using the Puppetfile model. nil disables Puppet
39
+ # version restrictions.
40
+ resolver = PuppetfileResolver::Resolver.new(document, nil)
41
+
42
+ # Configure and resolve the dependency graph, catching any errors
43
+ # raised by puppetfile-resolver and re-raising them as Bolt errors.
44
+ begin
45
+ result = resolver.resolve(
46
+ cache: nil,
47
+ ui: nil,
48
+ allow_missing_modules: false,
49
+ spec_searcher_configuration: spec_searcher_config(config)
50
+ )
51
+ rescue StandardError => e
52
+ raise Bolt::Error.new("Unable to resolve modules: #{e.message}", 'bolt/module-resolver-error')
53
+ end
54
+
55
+ # Create the Puppetfile object.
56
+ generate_puppetfile(specs, result.specifications.values, unresolved)
57
+ end
58
+
59
+ # Creates a puppetfile-resolver config object.
60
+ #
61
+ private def spec_searcher_config(config)
62
+ PuppetfileResolver::SpecSearchers::Configuration.new.tap do |obj|
63
+ obj.forge.proxy = config.dig('forge', 'proxy') || config.dig('proxy')
64
+ obj.git.proxy = config.dig('proxy')
65
+ obj.forge.forge_api = config.dig('forge', 'baseurl')
66
+ end
67
+ end
68
+
69
+ # Creates a Puppetfile object with Module objects created from resolved and
70
+ # unresolved specs.
71
+ #
72
+ private def generate_puppetfile(specs, resolved, unresolved)
73
+ modules = []
74
+
75
+ # Convert the resolved specs into Bolt module objects.
76
+ resolved.each do |mod|
77
+ # Skip over anything that isn't a module spec, such as a Puppet spec.
78
+ next unless mod.is_a? PuppetfileResolver::Models::ModuleSpecification
79
+
80
+ case mod.origin
81
+ when :forge
82
+ modules << Bolt::ModuleInstaller::Puppetfile::ForgeModule.new(
83
+ "#{mod.owner}/#{mod.name}",
84
+ mod.version.to_s
85
+ )
86
+ when :git
87
+ spec = specs.specs.find { |s| s.name == mod.name }
88
+ modules << Bolt::ModuleInstaller::Puppetfile::GitModule.new(
89
+ spec.name,
90
+ spec.git,
91
+ spec.sha
92
+ )
93
+ end
94
+ end
95
+
96
+ # Error if there are any name conflicts between unresolved specs and
97
+ # resolved modules. r10k will error if a Puppetfile includes duplicate
98
+ # names, but we error early here to provide a more helpful message.
99
+ if (name_conflicts = modules.map(&:name) & unresolved.map(&:name)).any?
100
+ raise Bolt::Error.new(
101
+ "Detected unresolved module specifications with the same name as a resolved module "\
102
+ "dependency: #{name_conflicts.join(', ')}. Either remove the unresolved module specification "\
103
+ "or set the module with the conflicting dependency to not resolve.",
104
+ "bolt/module-name-conflict-error"
105
+ )
106
+ end
107
+
108
+ # Convert the unresolved specs into Bolt module objects.
109
+ unresolved.each do |spec|
110
+ case spec.type
111
+ when :forge
112
+ modules << Bolt::ModuleInstaller::Puppetfile::ForgeModule.new(
113
+ spec.full_name,
114
+ spec.version_requirement
115
+ )
116
+ when :git
117
+ modules << Bolt::ModuleInstaller::Puppetfile::GitModule.new(
118
+ spec.name,
119
+ spec.git,
120
+ spec.ref
121
+ )
122
+ end
123
+ end
124
+
125
+ Bolt::ModuleInstaller::Puppetfile.new(modules)
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'semantic_puppet'
4
+ require 'set'
5
+
6
+ require_relative '../../../bolt/error'
7
+
8
+ # This class represents a Forge module specification.
9
+ #
10
+ module Bolt
11
+ class ModuleInstaller
12
+ class Specs
13
+ class ForgeSpec
14
+ NAME_REGEX = %r{\A[a-zA-Z0-9]+[-/](?<name>[a-z][a-z0-9_]*)\z}.freeze
15
+ REQUIRED_KEYS = Set.new(%w[name]).freeze
16
+ KNOWN_KEYS = Set.new(%w[name resolve version_requirement]).freeze
17
+
18
+ attr_reader :full_name, :name, :resolve, :semantic_version, :type, :version_requirement
19
+
20
+ def initialize(init_hash)
21
+ @resolve = init_hash.key?('resolve') ? init_hash['resolve'] : true
22
+ @full_name, @name = parse_name(init_hash['name'])
23
+ @version_requirement, @semantic_version = parse_version_requirement(init_hash['version_requirement'])
24
+ @type = :forge
25
+
26
+ unless @resolve == true || @resolve == false
27
+ raise Bolt::ValidationError,
28
+ "Option 'resolve' for module spec #{@full_name} must be a Boolean"
29
+ end
30
+ end
31
+
32
+ def self.implements?(hash)
33
+ KNOWN_KEYS.superset?(hash.keys.to_set) && REQUIRED_KEYS.subset?(hash.keys.to_set)
34
+ end
35
+
36
+ # Formats the full name and extracts the module name.
37
+ #
38
+ private def parse_name(name)
39
+ unless (match = name.match(NAME_REGEX))
40
+ raise Bolt::ValidationError,
41
+ "Invalid name for Forge module specification: #{name}. Name must match "\
42
+ "'owner/name'. Owner segment can only include letters or digits. Name "\
43
+ "segment must start with a lowercase letter and can only include lowercase "\
44
+ "letters, digits, and underscores."
45
+ end
46
+
47
+ [name.tr('-', '/'), match[:name]]
48
+ end
49
+
50
+ # Parses the version into a Semantic Puppet version range.
51
+ #
52
+ private def parse_version_requirement(version_requirement)
53
+ [version_requirement, SemanticPuppet::VersionRange.parse(version_requirement || '>= 0')]
54
+ rescue StandardError
55
+ raise Bolt::ValidationError,
56
+ "Invalid version requirement for Forge module specification #{@full_name}: "\
57
+ "#{version_requirement.inspect}"
58
+ end
59
+
60
+ # Returns true if the specification is satisfied by the module.
61
+ #
62
+ def satisfied_by?(mod)
63
+ @type == mod.type &&
64
+ @full_name.downcase == mod.full_name.downcase &&
65
+ !mod.version.nil? &&
66
+ @semantic_version.cover?(mod.version)
67
+ end
68
+
69
+ # Returns a hash matching the module spec in bolt-project.yaml
70
+ #
71
+ def to_hash
72
+ {
73
+ 'name' => @full_name,
74
+ 'version_requirement' => @version_requirement
75
+ }.compact
76
+ end
77
+
78
+ # Creates a PuppetfileResolver::Puppetfile::ForgeModule object, which is
79
+ # used to generate a graph of resolved modules.
80
+ #
81
+ def to_resolver_module
82
+ require 'puppetfile-resolver'
83
+
84
+ PuppetfileResolver::Puppetfile::ForgeModule.new(@full_name).tap do |mod|
85
+ mod.version = @version_requirement
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end