openbolt 5.0.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Puppetfile +52 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/applyresult.rb +60 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/containerresult.rb +51 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/future.rb +25 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/resourceinstance.rb +71 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/result.rb +55 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/resultset.rb +65 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/target.rb +93 -0
- data/bolt-modules/boltlib/lib/puppet/functions/add_facts.rb +33 -0
- data/bolt-modules/boltlib/lib/puppet/functions/add_to_group.rb +38 -0
- data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +208 -0
- data/bolt-modules/boltlib/lib/puppet/functions/background.rb +62 -0
- data/bolt-modules/boltlib/lib/puppet/functions/catch_errors.rb +57 -0
- data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +130 -0
- data/bolt-modules/boltlib/lib/puppet/functions/facts.rb +31 -0
- data/bolt-modules/boltlib/lib/puppet/functions/fail_plan.rb +52 -0
- data/bolt-modules/boltlib/lib/puppet/functions/get_resources.rb +87 -0
- data/bolt-modules/boltlib/lib/puppet/functions/get_target.rb +34 -0
- data/bolt-modules/boltlib/lib/puppet/functions/get_targets.rb +35 -0
- data/bolt-modules/boltlib/lib/puppet/functions/parallelize.rb +74 -0
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_command.rb +97 -0
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_fact.rb +47 -0
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_query.rb +52 -0
- data/bolt-modules/boltlib/lib/puppet/functions/remove_from_group.rb +40 -0
- data/bolt-modules/boltlib/lib/puppet/functions/resolve_references.rb +42 -0
- data/bolt-modules/boltlib/lib/puppet/functions/resource.rb +53 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +106 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_container.rb +162 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +291 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +145 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +164 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +211 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_config.rb +48 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_feature.rb +43 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_resources.rb +145 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_var.rb +38 -0
- data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +101 -0
- data/bolt-modules/boltlib/lib/puppet/functions/vars.rb +29 -0
- data/bolt-modules/boltlib/lib/puppet/functions/wait.rb +131 -0
- data/bolt-modules/boltlib/lib/puppet/functions/wait_until_available.rb +59 -0
- data/bolt-modules/boltlib/lib/puppet/functions/without_default_logging.rb +39 -0
- data/bolt-modules/boltlib/lib/puppet/functions/write_file.rb +50 -0
- data/bolt-modules/boltlib/types/planresult.pp +18 -0
- data/bolt-modules/boltlib/types/targetspec.pp +7 -0
- data/bolt-modules/ctrl/lib/puppet/functions/ctrl/do_until.rb +42 -0
- data/bolt-modules/ctrl/lib/puppet/functions/ctrl/sleep.rb +20 -0
- data/bolt-modules/dir/lib/puppet/functions/dir/children.rb +35 -0
- data/bolt-modules/file/lib/puppet/functions/file/delete.rb +21 -0
- data/bolt-modules/file/lib/puppet/functions/file/exists.rb +28 -0
- data/bolt-modules/file/lib/puppet/functions/file/join.rb +20 -0
- data/bolt-modules/file/lib/puppet/functions/file/read.rb +33 -0
- data/bolt-modules/file/lib/puppet/functions/file/readable.rb +28 -0
- data/bolt-modules/file/lib/puppet/functions/file/write.rb +24 -0
- data/bolt-modules/log/lib/puppet/functions/log/debug.rb +39 -0
- data/bolt-modules/log/lib/puppet/functions/log/error.rb +40 -0
- data/bolt-modules/log/lib/puppet/functions/log/fatal.rb +40 -0
- data/bolt-modules/log/lib/puppet/functions/log/info.rb +39 -0
- data/bolt-modules/log/lib/puppet/functions/log/trace.rb +39 -0
- data/bolt-modules/log/lib/puppet/functions/log/warn.rb +41 -0
- data/bolt-modules/out/lib/puppet/functions/out/message.rb +36 -0
- data/bolt-modules/out/lib/puppet/functions/out/verbose.rb +35 -0
- data/bolt-modules/prompt/lib/puppet/functions/prompt/menu.rb +103 -0
- data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +65 -0
- data/bolt-modules/system/lib/puppet/functions/system/env.rb +20 -0
- data/exe/bolt +17 -0
- data/guides/debugging.yaml +27 -0
- data/guides/inventory.yaml +23 -0
- data/guides/links.yaml +12 -0
- data/guides/logging.yaml +17 -0
- data/guides/module.yaml +18 -0
- data/guides/modulepath.yaml +24 -0
- data/guides/project.yaml +21 -0
- data/guides/targets.yaml +28 -0
- data/guides/transports.yaml +22 -0
- data/lib/bolt/analytics.rb +233 -0
- data/lib/bolt/application.rb +806 -0
- data/lib/bolt/applicator.rb +368 -0
- data/lib/bolt/apply_inventory.rb +93 -0
- data/lib/bolt/apply_result.rb +154 -0
- data/lib/bolt/apply_target.rb +90 -0
- data/lib/bolt/bolt_option_parser.rb +1226 -0
- data/lib/bolt/catalog/logging.rb +15 -0
- data/lib/bolt/catalog.rb +144 -0
- data/lib/bolt/cli.rb +949 -0
- data/lib/bolt/config/modulepath.rb +30 -0
- data/lib/bolt/config/options.rb +673 -0
- data/lib/bolt/config/transport/base.rb +133 -0
- data/lib/bolt/config/transport/docker.rb +34 -0
- data/lib/bolt/config/transport/jail.rb +33 -0
- data/lib/bolt/config/transport/local.rb +39 -0
- data/lib/bolt/config/transport/lxd.rb +34 -0
- data/lib/bolt/config/transport/options.rb +431 -0
- data/lib/bolt/config/transport/orch.rb +41 -0
- data/lib/bolt/config/transport/podman.rb +33 -0
- data/lib/bolt/config/transport/remote.rb +24 -0
- data/lib/bolt/config/transport/ssh.rb +138 -0
- data/lib/bolt/config/transport/winrm.rb +63 -0
- data/lib/bolt/config.rb +515 -0
- data/lib/bolt/container_result.rb +105 -0
- data/lib/bolt/error.rb +194 -0
- data/lib/bolt/executor.rb +539 -0
- data/lib/bolt/fiber_executor.rb +190 -0
- data/lib/bolt/inventory/group.rb +446 -0
- data/lib/bolt/inventory/inventory.rb +391 -0
- data/lib/bolt/inventory/options.rb +139 -0
- data/lib/bolt/inventory/target.rb +293 -0
- data/lib/bolt/inventory.rb +120 -0
- data/lib/bolt/logger.rb +252 -0
- data/lib/bolt/module.rb +54 -0
- data/lib/bolt/module_installer/installer.rb +44 -0
- data/lib/bolt/module_installer/puppetfile/forge_module.rb +54 -0
- data/lib/bolt/module_installer/puppetfile/git_module.rb +37 -0
- data/lib/bolt/module_installer/puppetfile/module.rb +26 -0
- data/lib/bolt/module_installer/puppetfile.rb +131 -0
- data/lib/bolt/module_installer/resolver.rb +129 -0
- data/lib/bolt/module_installer/specs/forge_spec.rb +91 -0
- data/lib/bolt/module_installer/specs/git_spec.rb +150 -0
- data/lib/bolt/module_installer/specs/id/base.rb +116 -0
- data/lib/bolt/module_installer/specs/id/gitclone.rb +120 -0
- data/lib/bolt/module_installer/specs/id/github.rb +90 -0
- data/lib/bolt/module_installer/specs/id/gitlab.rb +92 -0
- data/lib/bolt/module_installer/specs.rb +95 -0
- data/lib/bolt/module_installer.rb +208 -0
- data/lib/bolt/node/errors.rb +55 -0
- data/lib/bolt/node/output.rb +29 -0
- data/lib/bolt/outputter/human.rb +958 -0
- data/lib/bolt/outputter/json.rb +205 -0
- data/lib/bolt/outputter/logger.rb +76 -0
- data/lib/bolt/outputter/rainbow.rb +118 -0
- data/lib/bolt/outputter.rb +57 -0
- data/lib/bolt/pal/issues.rb +19 -0
- data/lib/bolt/pal/logging.rb +17 -0
- data/lib/bolt/pal/yaml_plan/evaluator.rb +83 -0
- data/lib/bolt/pal/yaml_plan/loader.rb +94 -0
- data/lib/bolt/pal/yaml_plan/parameter.rb +63 -0
- data/lib/bolt/pal/yaml_plan/step/command.rb +45 -0
- data/lib/bolt/pal/yaml_plan/step/download.rb +37 -0
- data/lib/bolt/pal/yaml_plan/step/eval.rb +42 -0
- data/lib/bolt/pal/yaml_plan/step/message.rb +31 -0
- data/lib/bolt/pal/yaml_plan/step/plan.rb +42 -0
- data/lib/bolt/pal/yaml_plan/step/resources.rb +170 -0
- data/lib/bolt/pal/yaml_plan/step/script.rb +62 -0
- data/lib/bolt/pal/yaml_plan/step/task.rb +42 -0
- data/lib/bolt/pal/yaml_plan/step/upload.rb +37 -0
- data/lib/bolt/pal/yaml_plan/step/verbose.rb +31 -0
- data/lib/bolt/pal/yaml_plan/step.rb +223 -0
- data/lib/bolt/pal/yaml_plan/transpiler.rb +90 -0
- data/lib/bolt/pal/yaml_plan.rb +172 -0
- data/lib/bolt/pal.rb +847 -0
- data/lib/bolt/plan_creator.rb +219 -0
- data/lib/bolt/plan_future.rb +86 -0
- data/lib/bolt/plan_result.rb +44 -0
- data/lib/bolt/plugin/cache.rb +76 -0
- data/lib/bolt/plugin/env_var.rb +54 -0
- data/lib/bolt/plugin/module.rb +276 -0
- data/lib/bolt/plugin/prompt.rb +36 -0
- data/lib/bolt/plugin/puppet_connect_data.rb +84 -0
- data/lib/bolt/plugin/puppetdb.rb +124 -0
- data/lib/bolt/plugin/task.rb +72 -0
- data/lib/bolt/plugin.rb +380 -0
- data/lib/bolt/project.rb +219 -0
- data/lib/bolt/project_manager/config_migrator.rb +113 -0
- data/lib/bolt/project_manager/inventory_migrator.rb +67 -0
- data/lib/bolt/project_manager/migrator.rb +39 -0
- data/lib/bolt/project_manager/module_migrator.rb +203 -0
- data/lib/bolt/project_manager.rb +221 -0
- data/lib/bolt/puppetdb/client.rb +153 -0
- data/lib/bolt/puppetdb/config.rb +176 -0
- data/lib/bolt/puppetdb/instance.rb +146 -0
- data/lib/bolt/puppetdb.rb +15 -0
- data/lib/bolt/r10k_log_proxy.rb +30 -0
- data/lib/bolt/rerun.rb +55 -0
- data/lib/bolt/resource_instance.rb +133 -0
- data/lib/bolt/result.rb +247 -0
- data/lib/bolt/result_set.rb +128 -0
- data/lib/bolt/shell/bash/tmpdir.rb +62 -0
- data/lib/bolt/shell/bash.rb +516 -0
- data/lib/bolt/shell/powershell/snippets.rb +181 -0
- data/lib/bolt/shell/powershell.rb +365 -0
- data/lib/bolt/shell.rb +105 -0
- data/lib/bolt/target.rb +174 -0
- data/lib/bolt/task/puppet_server.rb +27 -0
- data/lib/bolt/task/run.rb +55 -0
- data/lib/bolt/task.rb +163 -0
- data/lib/bolt/transport/base.rb +252 -0
- data/lib/bolt/transport/docker/connection.rb +150 -0
- data/lib/bolt/transport/docker.rb +23 -0
- data/lib/bolt/transport/jail/connection.rb +81 -0
- data/lib/bolt/transport/jail.rb +21 -0
- data/lib/bolt/transport/local/connection.rb +106 -0
- data/lib/bolt/transport/local.rb +20 -0
- data/lib/bolt/transport/lxd/connection.rb +115 -0
- data/lib/bolt/transport/lxd.rb +26 -0
- data/lib/bolt/transport/orch/connection.rb +111 -0
- data/lib/bolt/transport/orch.rb +271 -0
- data/lib/bolt/transport/podman/connection.rb +102 -0
- data/lib/bolt/transport/podman.rb +19 -0
- data/lib/bolt/transport/remote.rb +41 -0
- data/lib/bolt/transport/simple.rb +54 -0
- data/lib/bolt/transport/ssh/connection.rb +321 -0
- data/lib/bolt/transport/ssh/exec_connection.rb +140 -0
- data/lib/bolt/transport/ssh.rb +48 -0
- data/lib/bolt/transport/winrm/connection.rb +378 -0
- data/lib/bolt/transport/winrm.rb +33 -0
- data/lib/bolt/util/format.rb +68 -0
- data/lib/bolt/util/puppet_log_level.rb +21 -0
- data/lib/bolt/util.rb +465 -0
- data/lib/bolt/validator.rb +227 -0
- data/lib/bolt/version.rb +5 -0
- data/lib/bolt.rb +8 -0
- data/lib/bolt_server/acl.rb +39 -0
- data/lib/bolt_server/base_config.rb +112 -0
- data/lib/bolt_server/config.rb +64 -0
- data/lib/bolt_server/file_cache.rb +200 -0
- data/lib/bolt_server/request_error.rb +11 -0
- data/lib/bolt_server/schemas/action-check_node_connections.json +14 -0
- data/lib/bolt_server/schemas/action-run_command.json +12 -0
- data/lib/bolt_server/schemas/action-run_script.json +47 -0
- data/lib/bolt_server/schemas/action-run_task.json +20 -0
- data/lib/bolt_server/schemas/action-upload_file.json +47 -0
- data/lib/bolt_server/schemas/partials/target-any.json +10 -0
- data/lib/bolt_server/schemas/partials/target-ssh.json +88 -0
- data/lib/bolt_server/schemas/partials/target-winrm.json +67 -0
- data/lib/bolt_server/schemas/partials/task.json +94 -0
- data/lib/bolt_server/schemas/transport-ssh.json +25 -0
- data/lib/bolt_server/schemas/transport-winrm.json +19 -0
- data/lib/bolt_server/transport_app.rb +554 -0
- data/lib/bolt_spec/bolt_context.rb +226 -0
- data/lib/bolt_spec/plans/action_stubs/command_stub.rb +51 -0
- data/lib/bolt_spec/plans/action_stubs/download_stub.rb +66 -0
- data/lib/bolt_spec/plans/action_stubs/plan_stub.rb +55 -0
- data/lib/bolt_spec/plans/action_stubs/script_stub.rb +59 -0
- data/lib/bolt_spec/plans/action_stubs/task_stub.rb +57 -0
- data/lib/bolt_spec/plans/action_stubs/upload_stub.rb +65 -0
- data/lib/bolt_spec/plans/action_stubs.rb +196 -0
- data/lib/bolt_spec/plans/mock_executor.rb +361 -0
- data/lib/bolt_spec/plans/publish_stub.rb +49 -0
- data/lib/bolt_spec/plans.rb +190 -0
- data/lib/bolt_spec/run.rb +246 -0
- data/lib/logging_extensions/logging.rb +13 -0
- data/libexec/apply_catalog.rb +130 -0
- data/libexec/bolt_catalog +68 -0
- data/libexec/custom_facts.rb +63 -0
- data/libexec/query_resources.rb +75 -0
- data/modules/aggregate/lib/puppet/functions/aggregate/count.rb +21 -0
- data/modules/aggregate/lib/puppet/functions/aggregate/nodes.rb +22 -0
- data/modules/aggregate/lib/puppet/functions/aggregate/targets.rb +21 -0
- data/modules/aggregate/plans/count.pp +56 -0
- data/modules/aggregate/plans/targets.pp +56 -0
- data/modules/canary/lib/puppet/functions/canary/merge.rb +13 -0
- data/modules/canary/lib/puppet/functions/canary/random_split.rb +22 -0
- data/modules/canary/lib/puppet/functions/canary/skip.rb +25 -0
- data/modules/canary/plans/init.pp +100 -0
- data/modules/puppet_connect/plans/test_input_data.pp +94 -0
- data/modules/puppetdb_fact/plans/init.pp +20 -0
- data/resources/bolt_bash_completion.sh +214 -0
- metadata +735 -0
data/lib/bolt/pal.rb
ADDED
@@ -0,0 +1,847 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../bolt/applicator'
|
4
|
+
require_relative '../bolt/executor'
|
5
|
+
require_relative '../bolt/error'
|
6
|
+
require_relative '../bolt/plan_result'
|
7
|
+
require_relative '../bolt/util'
|
8
|
+
require_relative '../bolt/config/modulepath'
|
9
|
+
require 'etc'
|
10
|
+
|
11
|
+
module Bolt
|
12
|
+
class PAL
|
13
|
+
# PALError is used to convert errors from executing puppet code into
|
14
|
+
# Bolt::Errors
|
15
|
+
class PALError < Bolt::Error
|
16
|
+
def self.from_preformatted_error(err)
|
17
|
+
error = if err.cause.is_a? Bolt::Error
|
18
|
+
err.cause
|
19
|
+
else
|
20
|
+
from_error(err)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Provide the location of an error if it came from a plan
|
24
|
+
details = {}
|
25
|
+
details[:file] = err.file if defined?(err.file)
|
26
|
+
details[:line] = err.line if defined?(err.line)
|
27
|
+
details[:column] = err.pos if defined?(err.pos)
|
28
|
+
|
29
|
+
error.add_filelineno(details.compact)
|
30
|
+
error
|
31
|
+
end
|
32
|
+
|
33
|
+
# Generate a Bolt::Pal::PALError for non-bolt errors
|
34
|
+
def self.from_error(err)
|
35
|
+
# Use the original error message if available
|
36
|
+
message = err.cause ? err.cause.message : err.message
|
37
|
+
e = new(message)
|
38
|
+
e.set_backtrace(err.backtrace)
|
39
|
+
e
|
40
|
+
end
|
41
|
+
|
42
|
+
def initialize(msg, details = {})
|
43
|
+
super(msg, 'bolt/pal-error', details)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def initialize(modulepath, hiera_config, resource_types, max_compiles = Etc.nprocessors,
|
48
|
+
trusted_external = nil, apply_settings = {}, project = nil)
|
49
|
+
unless modulepath.is_a?(Bolt::Config::Modulepath)
|
50
|
+
msg = "Type error in PAL: modulepath must be a Bolt::Config::Modulepath"
|
51
|
+
raise Bolt::Error.new(msg, "bolt/execution-error")
|
52
|
+
end
|
53
|
+
# Nothing works without initialized this global state. Reinitializing
|
54
|
+
# is safe and in practice only happens in tests
|
55
|
+
self.class.load_puppet
|
56
|
+
@modulepath = modulepath
|
57
|
+
@hiera_config = hiera_config
|
58
|
+
@trusted_external = trusted_external
|
59
|
+
@apply_settings = apply_settings
|
60
|
+
@max_compiles = max_compiles
|
61
|
+
@resource_types = resource_types
|
62
|
+
@project = project
|
63
|
+
|
64
|
+
@logger = Bolt::Logger.logger(self)
|
65
|
+
unless user_modulepath.empty?
|
66
|
+
@logger.debug("Loading modules from #{full_modulepath.join(File::PATH_SEPARATOR)}")
|
67
|
+
end
|
68
|
+
|
69
|
+
@loaded = false
|
70
|
+
end
|
71
|
+
|
72
|
+
def full_modulepath
|
73
|
+
@modulepath.full_modulepath
|
74
|
+
end
|
75
|
+
|
76
|
+
def user_modulepath
|
77
|
+
@modulepath.user_modulepath
|
78
|
+
end
|
79
|
+
|
80
|
+
# Puppet logging is global so this is class method to avoid confusion
|
81
|
+
def self.configure_logging
|
82
|
+
Puppet::Util::Log.destinations.clear
|
83
|
+
Puppet::Util::Log.newdestination(Bolt::Logger.logger('Puppet'))
|
84
|
+
# Defer all log level decisions to the Logging library by telling Puppet
|
85
|
+
# to log everything
|
86
|
+
Puppet.settings[:log_level] = 'debug'
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.load_puppet
|
90
|
+
if Bolt::Util.windows?
|
91
|
+
# Windows 'fix' for openssl behaving strangely. Prevents very slow operation
|
92
|
+
# of random_bytes later when establishing winrm connections from a Windows host.
|
93
|
+
# See https://github.com/rails/rails/issues/25805 for background.
|
94
|
+
require 'openssl'
|
95
|
+
OpenSSL::Random.random_bytes(1)
|
96
|
+
end
|
97
|
+
|
98
|
+
begin
|
99
|
+
require 'puppet_pal'
|
100
|
+
rescue LoadError
|
101
|
+
raise Bolt::Error.new("Puppet must be installed to execute tasks", "bolt/puppet-missing")
|
102
|
+
end
|
103
|
+
|
104
|
+
require_relative 'pal/logging'
|
105
|
+
require_relative 'pal/issues'
|
106
|
+
require_relative 'pal/yaml_plan/loader'
|
107
|
+
require_relative 'pal/yaml_plan/transpiler'
|
108
|
+
|
109
|
+
# Now that puppet is loaded we can include puppet mixins in data types
|
110
|
+
Bolt::ResultSet.include_iterable
|
111
|
+
end
|
112
|
+
|
113
|
+
def setup
|
114
|
+
unless @loaded
|
115
|
+
# This is slow so don't do it until we have to
|
116
|
+
Bolt::PAL.load_puppet
|
117
|
+
|
118
|
+
# Make sure we don't create the puppet directories
|
119
|
+
with_puppet_settings { |_| nil }
|
120
|
+
@loaded = true
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Create a top-level alias for TargetSpec and PlanResult so that users don't have to
|
125
|
+
# namespace it with Boltlib, which is just an implementation detail. This
|
126
|
+
# allows them to feel like a built-in type in bolt, rather than
|
127
|
+
# something has been, no pun intended, "bolted on".
|
128
|
+
def alias_types(compiler)
|
129
|
+
compiler.evaluate_string('type TargetSpec = Boltlib::TargetSpec')
|
130
|
+
compiler.evaluate_string('type PlanResult = Boltlib::PlanResult')
|
131
|
+
end
|
132
|
+
|
133
|
+
# Register all resource types defined in $Project/.resource_types as well as
|
134
|
+
# the built in types registered with the runtime_3_init method.
|
135
|
+
def register_resource_types(loaders)
|
136
|
+
static_loader = loaders.static_loader
|
137
|
+
static_loader.runtime_3_init
|
138
|
+
if File.directory?(@resource_types)
|
139
|
+
Dir.children(@resource_types).each do |resource_pp|
|
140
|
+
type_name_from_file = File.basename(resource_pp, '.pp').capitalize
|
141
|
+
typed_name = Puppet::Pops::Loader::TypedName.new(:type, type_name_from_file)
|
142
|
+
resource_type = Puppet::Pops::Types::TypeFactory.resource(type_name_from_file)
|
143
|
+
loaders.static_loader.set_entry(typed_name, resource_type)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def detect_project_conflict(project, environment)
|
149
|
+
return unless project && project.load_as_module?
|
150
|
+
# The environment modulepath has stripped out non-existent directories,
|
151
|
+
# so we don't need to check for them
|
152
|
+
modules = environment.modulepath.flat_map do |path|
|
153
|
+
Dir.children(path).select { |name| Puppet::Module.is_module_directory?(name, path) }
|
154
|
+
end
|
155
|
+
if modules.include?(project.name)
|
156
|
+
Bolt::Logger.warn_once(
|
157
|
+
"project_shadows_module",
|
158
|
+
"The project '#{project.name}' shadows an existing module of the same name"
|
159
|
+
)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# Runs a block in a PAL script compiler configured for Bolt. Catches
|
164
|
+
# exceptions thrown by the block and re-raises them ensuring they are
|
165
|
+
# Bolt::Errors since the script compiler block will squash all exceptions.
|
166
|
+
def in_bolt_compiler(compiler_params: {})
|
167
|
+
# TODO: If we always call this inside a bolt_executor we can remove this here
|
168
|
+
setup
|
169
|
+
compiler_params = compiler_params.merge(set_local_facts: false)
|
170
|
+
r = Puppet::Pal.in_tmp_environment('bolt', modulepath: full_modulepath, facts: {}) do |pal|
|
171
|
+
# Only load the project if it a) exists, b) has a name it can be loaded with
|
172
|
+
Puppet.override(bolt_project: @project,
|
173
|
+
yaml_plan_instantiator: Bolt::PAL::YamlPlan::Loader) do
|
174
|
+
# Because this has the side effect of loading and caching the list
|
175
|
+
# of modules, it must happen *after* we have overridden
|
176
|
+
# bolt_project or the project will be ignored
|
177
|
+
detect_project_conflict(@project, Puppet.lookup(:environments).get('bolt'))
|
178
|
+
pal.with_script_compiler(**compiler_params) do |compiler|
|
179
|
+
alias_types(compiler)
|
180
|
+
register_resource_types(Puppet.lookup(:loaders)) if @resource_types
|
181
|
+
begin
|
182
|
+
yield compiler
|
183
|
+
rescue Bolt::Error => e
|
184
|
+
e
|
185
|
+
rescue Puppet::DataBinding::LookupError => e
|
186
|
+
if e.issue_code == :HIERA_UNDEFINED_VARIABLE
|
187
|
+
message = "Interpolations are not supported in lookups outside of an apply block: #{e.message}"
|
188
|
+
PALError.new(message)
|
189
|
+
else
|
190
|
+
PALError.from_preformatted_error(e)
|
191
|
+
end
|
192
|
+
rescue Puppet::PreformattedError => e
|
193
|
+
if e.issue_code == :UNKNOWN_VARIABLE &&
|
194
|
+
%w[facts trusted server_facts settings].include?(e.arguments[:name])
|
195
|
+
message = "Evaluation Error: Variable '#{e.arguments[:name]}' is not available in the current scope "\
|
196
|
+
"unless explicitly defined."
|
197
|
+
details = { file: e.file, line: e.line, column: e.pos }
|
198
|
+
PALError.new(message, details)
|
199
|
+
else
|
200
|
+
PALError.from_preformatted_error(e)
|
201
|
+
end
|
202
|
+
rescue StandardError => e
|
203
|
+
PALError.from_preformatted_error(e)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# Plans may return PuppetError but nothing should be throwing them
|
210
|
+
if r.is_a?(StandardError) && !r.is_a?(Bolt::PuppetError)
|
211
|
+
raise r
|
212
|
+
end
|
213
|
+
r
|
214
|
+
end
|
215
|
+
|
216
|
+
def with_bolt_executor(executor, inventory, pdb_client = nil, applicator = nil, &block)
|
217
|
+
setup
|
218
|
+
opts = {
|
219
|
+
bolt_project: @project,
|
220
|
+
bolt_executor: executor,
|
221
|
+
bolt_inventory: inventory,
|
222
|
+
bolt_pdb_client: pdb_client,
|
223
|
+
apply_executor: applicator || Applicator.new(
|
224
|
+
inventory,
|
225
|
+
executor,
|
226
|
+
full_modulepath,
|
227
|
+
# Skip syncing built-in plugins, since we vendor some Puppet 6
|
228
|
+
# versions of "core" types, which are already present on the agent,
|
229
|
+
# but may cause issues on Puppet 5 agents.
|
230
|
+
user_modulepath,
|
231
|
+
@project,
|
232
|
+
pdb_client,
|
233
|
+
@hiera_config,
|
234
|
+
@max_compiles,
|
235
|
+
@apply_settings
|
236
|
+
)
|
237
|
+
}
|
238
|
+
Puppet.override(opts, &block)
|
239
|
+
end
|
240
|
+
|
241
|
+
def in_catalog_compiler
|
242
|
+
with_puppet_settings do
|
243
|
+
Puppet.override(bolt_project: @project) do
|
244
|
+
Puppet::Pal.in_tmp_environment('bolt', modulepath: full_modulepath) do |pal|
|
245
|
+
pal.with_catalog_compiler do |compiler|
|
246
|
+
yield compiler
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
rescue Puppet::Error => e
|
251
|
+
raise PALError.from_error(e)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def in_plan_compiler(executor, inventory, pdb_client, applicator = nil)
|
256
|
+
with_bolt_executor(executor, inventory, pdb_client, applicator) do
|
257
|
+
# TODO: remove this call and see if anything breaks when
|
258
|
+
# settings dirs don't actually exist. Plans shouldn't
|
259
|
+
# actually be using them.
|
260
|
+
with_puppet_settings do
|
261
|
+
in_bolt_compiler do |compiler|
|
262
|
+
yield compiler
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
def in_task_compiler(executor, inventory)
|
269
|
+
with_bolt_executor(executor, inventory) do
|
270
|
+
in_bolt_compiler do |compiler|
|
271
|
+
yield compiler
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
# TODO: PUP-8553 should replace this
|
277
|
+
def with_puppet_settings
|
278
|
+
dir = Dir.mktmpdir('bolt')
|
279
|
+
|
280
|
+
cli = []
|
281
|
+
Puppet::Settings::REQUIRED_APP_SETTINGS.each do |setting|
|
282
|
+
cli << "--#{setting}" << dir
|
283
|
+
end
|
284
|
+
Puppet.settings.send(:clear_everything_for_tests)
|
285
|
+
Puppet.initialize_settings(cli)
|
286
|
+
Puppet::GettextConfig.create_default_text_domain
|
287
|
+
Puppet[:trusted_external_command] = @trusted_external
|
288
|
+
Puppet.settings[:hiera_config] = @hiera_config
|
289
|
+
self.class.configure_logging
|
290
|
+
yield
|
291
|
+
ensure
|
292
|
+
# Delete the tmpdir if it still exists. This check is needed to
|
293
|
+
# prevent Bolt from erroring if the tmpdir is somehow deleted
|
294
|
+
# before reaching this point.
|
295
|
+
FileUtils.remove_entry_secure(dir) if File.exist?(dir)
|
296
|
+
end
|
297
|
+
|
298
|
+
# Parses a snippet of Puppet manifest code and returns the AST represented
|
299
|
+
# in JSON.
|
300
|
+
def parse_manifest(code, filename)
|
301
|
+
setup
|
302
|
+
Puppet::Pops::Parser::EvaluatingParser.new.parse_string(code, filename)
|
303
|
+
rescue Puppet::Error => e
|
304
|
+
raise Bolt::PAL::PALError, "Failed to parse manifest: #{e}"
|
305
|
+
end
|
306
|
+
|
307
|
+
# Filters content by a list of names and glob patterns specified in project
|
308
|
+
# configuration.
|
309
|
+
def filter_content(content, patterns)
|
310
|
+
return content unless content && patterns
|
311
|
+
|
312
|
+
content.select do |name,|
|
313
|
+
patterns.any? { |pattern| File.fnmatch?(pattern, name, File::FNM_EXTGLOB) }
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
def list_tasks_with_cache(filter_content: false)
|
318
|
+
# Don't filter content yet, so that if users update their task filters
|
319
|
+
# we don't need to refresh the cache
|
320
|
+
task_names = list_tasks(filter_content: false).map(&:first)
|
321
|
+
task_cache = if @project
|
322
|
+
Bolt::Util.read_optional_json_file(@project.task_cache_file, 'Task cache file')
|
323
|
+
else
|
324
|
+
{}
|
325
|
+
end
|
326
|
+
updated = false
|
327
|
+
|
328
|
+
task_list = task_names.each_with_object([]) do |task_name, list|
|
329
|
+
data = task_cache[task_name] || get_task_info(task_name, with_mtime: true)
|
330
|
+
|
331
|
+
# Make sure all the keys are strings - if we get data from
|
332
|
+
# get_task_info they will be symbols
|
333
|
+
data = Bolt::Util.walk_keys(data, &:to_s)
|
334
|
+
|
335
|
+
# If any files in the task were updated, refresh the cache
|
336
|
+
if data['files']&.any?
|
337
|
+
# For all the files that are part of the task
|
338
|
+
data['files'].each do |f|
|
339
|
+
# If any file has been updated since we last cached, update the
|
340
|
+
# cache
|
341
|
+
next unless file_modified?(f['path'], f['mtime'])
|
342
|
+
data = get_task_info(task_name, with_mtime: true)
|
343
|
+
data = Bolt::Util.walk_keys(data, &:to_s)
|
344
|
+
# Tell Bolt to write to the cache file once we're done
|
345
|
+
updated = true
|
346
|
+
# Update the cache data
|
347
|
+
task_cache[task_name] = data
|
348
|
+
end
|
349
|
+
end
|
350
|
+
metadata = data['metadata'] || {}
|
351
|
+
# Don't add tasks to the list to return if they are private
|
352
|
+
list << [task_name, metadata['description']] unless metadata['private']
|
353
|
+
end
|
354
|
+
|
355
|
+
# Write the cache if any entries were updated
|
356
|
+
File.write(@project.task_cache_file, task_cache.to_json) if updated && @project
|
357
|
+
filter_content ? filter_content(task_list, @project&.tasks) : task_list
|
358
|
+
end
|
359
|
+
|
360
|
+
def list_tasks(filter_content: false)
|
361
|
+
in_bolt_compiler do |compiler|
|
362
|
+
tasks = compiler.list_tasks.map(&:name).sort.each_with_object([]) do |task_name, data|
|
363
|
+
task_sig = compiler.task_signature(task_name)
|
364
|
+
unless task_sig.task_hash['metadata']['private']
|
365
|
+
data << [task_name, task_sig.task_hash['metadata']['description']]
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
filter_content ? filter_content(tasks, @project&.tasks) : tasks
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
def parse_params(type, object_name, params)
|
374
|
+
in_bolt_compiler do |compiler|
|
375
|
+
case type
|
376
|
+
when 'task'
|
377
|
+
param_spec = compiler.task_signature(object_name)&.task_hash&.dig('parameters')
|
378
|
+
when 'plan'
|
379
|
+
plan = compiler.plan_signature(object_name)
|
380
|
+
param_spec = plan.params_type.elements&.each_with_object({}) { |t, h| h[t.name] = t.value_type } if plan
|
381
|
+
end
|
382
|
+
param_spec ||= {}
|
383
|
+
|
384
|
+
params.each_with_object({}) do |(name, str), acc|
|
385
|
+
type = param_spec[name]
|
386
|
+
begin
|
387
|
+
parsed = JSON.parse(str, quirks_mode: true)
|
388
|
+
# The type may not exist if the module is remote on orch or if a task
|
389
|
+
# defines no parameters. Since we treat no parameters as Any we
|
390
|
+
# should parse everything in this case
|
391
|
+
acc[name] = if type && !type.instance?(parsed)
|
392
|
+
str
|
393
|
+
else
|
394
|
+
parsed
|
395
|
+
end
|
396
|
+
rescue JSON::ParserError
|
397
|
+
# This value may not be assignable in which case run_* will error
|
398
|
+
acc[name] = str
|
399
|
+
end
|
400
|
+
acc
|
401
|
+
end
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
def task_signature(task_name)
|
406
|
+
in_bolt_compiler do |compiler|
|
407
|
+
compiler.task_signature(task_name)
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
def get_task(task_name, with_mtime: false)
|
412
|
+
task = task_signature(task_name)
|
413
|
+
|
414
|
+
if task.nil?
|
415
|
+
raise Bolt::Error.unknown_task(task_name)
|
416
|
+
end
|
417
|
+
|
418
|
+
task = Bolt::Task.from_task_signature(task)
|
419
|
+
task.add_mtimes if with_mtime
|
420
|
+
task
|
421
|
+
end
|
422
|
+
|
423
|
+
def get_task_info(task_name, with_mtime: false)
|
424
|
+
get_task(task_name, with_mtime: with_mtime).to_h
|
425
|
+
end
|
426
|
+
|
427
|
+
def list_plans_with_cache(filter_content: false)
|
428
|
+
# Don't filter content yet, so that if users update their plan filters
|
429
|
+
# we don't need to refresh the cache
|
430
|
+
plan_names = list_plans(filter_content: false).map(&:first)
|
431
|
+
plan_cache = if @project
|
432
|
+
Bolt::Util.read_optional_json_file(@project.plan_cache_file, 'Plan cache file')
|
433
|
+
else
|
434
|
+
{}
|
435
|
+
end
|
436
|
+
updated = false
|
437
|
+
|
438
|
+
plan_list = plan_names.each_with_object([]) do |plan_name, list|
|
439
|
+
data = plan_cache[plan_name] || get_plan_info(plan_name, with_mtime: true)
|
440
|
+
# If the plan is a 'local' plan (in the project itself, or the modules/
|
441
|
+
# directory) then verify it hasn't been updated since we cached it. If
|
442
|
+
# it has been updated, refresh the cache and use the new data.
|
443
|
+
if file_modified?(data.dig('file', 'path'), data.dig('file', 'mtime'))
|
444
|
+
data = get_plan_info(plan_name, with_mtime: true)
|
445
|
+
updated = true
|
446
|
+
plan_cache[plan_name] = data
|
447
|
+
end
|
448
|
+
|
449
|
+
list << [plan_name, data['description']] unless data['private']
|
450
|
+
end
|
451
|
+
|
452
|
+
File.write(@project.plan_cache_file, plan_cache.to_json) if updated && @project
|
453
|
+
|
454
|
+
filter_content ? filter_content(plan_list, @project&.plans) : plan_list
|
455
|
+
end
|
456
|
+
|
457
|
+
# Returns true if a file has been modified or no longer exists, false
|
458
|
+
# otherwise.
|
459
|
+
#
|
460
|
+
# @param path [String] The path to the file.
|
461
|
+
# @param mtime [String] The last time the file was modified.
|
462
|
+
#
|
463
|
+
private def file_modified?(path, mtime)
|
464
|
+
path && !(File.exist?(path) && File.mtime(path).to_s == mtime.to_s)
|
465
|
+
end
|
466
|
+
|
467
|
+
def list_plans(filter_content: false)
|
468
|
+
in_bolt_compiler do |compiler|
|
469
|
+
errors = []
|
470
|
+
plans = compiler.list_plans(nil, errors).map { |plan| [plan.name] }.sort
|
471
|
+
errors.each do |error|
|
472
|
+
Bolt::Logger.warn("plan_load_error", error.details['original_error'])
|
473
|
+
end
|
474
|
+
|
475
|
+
filter_content ? filter_content(plans, @project&.plans) : plans
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
def get_plan_info(plan_name, with_mtime: false)
|
480
|
+
plan_sig = in_bolt_compiler do |compiler|
|
481
|
+
compiler.plan_signature(plan_name)
|
482
|
+
end
|
483
|
+
|
484
|
+
if plan_sig.nil?
|
485
|
+
raise Bolt::Error.unknown_plan(plan_name)
|
486
|
+
end
|
487
|
+
|
488
|
+
# path may be a Pathname object, so make sure to stringify it
|
489
|
+
mod = plan_sig.instance_variable_get(:@plan_func).loader.parent.path.to_s
|
490
|
+
|
491
|
+
# If it's a Puppet language plan, use strings to extract data. The only
|
492
|
+
# way to tell is to check which filename exists in the module.
|
493
|
+
plan_subpath = File.join(plan_name.split('::').drop(1))
|
494
|
+
plan_subpath = 'init' if plan_subpath.empty?
|
495
|
+
|
496
|
+
pp_path = File.join(mod, 'plans', "#{plan_subpath}.pp")
|
497
|
+
if File.exist?(pp_path)
|
498
|
+
require 'puppet-strings'
|
499
|
+
require 'puppet-strings/yard'
|
500
|
+
PuppetStrings::Yard.setup!
|
501
|
+
YARD::Logger.instance.level = if YARD::Logger.const_defined?(:Severity)
|
502
|
+
YARD::Logger::Severity::ERROR
|
503
|
+
else
|
504
|
+
# Backward compatility for YARD < 0.9.37
|
505
|
+
YARD::Logger.instance.level = :error
|
506
|
+
end
|
507
|
+
YARD.parse(pp_path)
|
508
|
+
|
509
|
+
plan = YARD::Registry.at("puppet_plans::#{plan_name}")
|
510
|
+
|
511
|
+
description = if plan.tag(:summary)
|
512
|
+
plan.tag(:summary).text
|
513
|
+
elsif !plan.docstring.empty?
|
514
|
+
plan.docstring
|
515
|
+
end
|
516
|
+
|
517
|
+
defaults = plan.parameters.to_h.compact
|
518
|
+
signature_params = Set.new(plan.parameters.map(&:first))
|
519
|
+
parameters = plan.tags(:param).each_with_object({}) do |param, params|
|
520
|
+
name = param.name
|
521
|
+
if signature_params.include?(name)
|
522
|
+
params[name] = { 'type' => param.types.first }
|
523
|
+
params[name]['sensitive'] = param.types.first =~ /\ASensitive(\[.*\])?\z/ ? true : false
|
524
|
+
params[name]['default_value'] = defaults[name] if defaults.key?(name)
|
525
|
+
params[name]['description'] = param.text if param.text && !param.text.empty?
|
526
|
+
else
|
527
|
+
Bolt::Logger.warn(
|
528
|
+
"missing_plan_parameter",
|
529
|
+
"The documented parameter '#{name}' does not exist in signature for plan '#{plan.name}'"
|
530
|
+
)
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
534
|
+
pp_info = {
|
535
|
+
'name' => plan_name,
|
536
|
+
'description' => description,
|
537
|
+
'parameters' => parameters,
|
538
|
+
'module' => mod,
|
539
|
+
'private' => private_plan?(plan),
|
540
|
+
'summary' => plan.tag(:summary)&.text,
|
541
|
+
'docstring' => (plan.docstring unless plan.docstring.empty?)
|
542
|
+
}
|
543
|
+
|
544
|
+
pp_info.merge!(get_plan_mtime(plan.file)) if with_mtime
|
545
|
+
pp_info
|
546
|
+
|
547
|
+
# If it's a YAML plan, fall back to limited data
|
548
|
+
else
|
549
|
+
yaml_path = File.join(mod, 'plans', "#{plan_subpath}.yaml")
|
550
|
+
plan_content = File.read(yaml_path)
|
551
|
+
plan = Bolt::PAL::YamlPlan::Loader.from_string(plan_name, plan_content, yaml_path)
|
552
|
+
|
553
|
+
parameters = plan.parameters.each_with_object({}) do |param, params|
|
554
|
+
name = param.name
|
555
|
+
type_str = case param.type_expr
|
556
|
+
when Puppet::Pops::Types::PTypeReferenceType
|
557
|
+
param.type_expr.type_string
|
558
|
+
when nil
|
559
|
+
'Any'
|
560
|
+
else
|
561
|
+
param.type_expr
|
562
|
+
end
|
563
|
+
params[name] = { 'type' => type_str }
|
564
|
+
params[name]['sensitive'] = param.type_expr.instance_of?(Puppet::Pops::Types::PSensitiveType)
|
565
|
+
params[name]['default_value'] = param.value unless param.value.nil?
|
566
|
+
params[name]['description'] = param.description if param.description
|
567
|
+
end
|
568
|
+
|
569
|
+
yaml_info = {
|
570
|
+
'name' => plan_name,
|
571
|
+
'description' => plan.description,
|
572
|
+
'parameters' => parameters,
|
573
|
+
'module' => mod,
|
574
|
+
'private' => !!plan.private,
|
575
|
+
'docstring' => plan.description,
|
576
|
+
'summary' => nil
|
577
|
+
}
|
578
|
+
|
579
|
+
yaml_info.merge!(get_plan_mtime(yaml_path)) if with_mtime
|
580
|
+
yaml_info
|
581
|
+
end
|
582
|
+
end
|
583
|
+
|
584
|
+
# Returns true if the plan is private, false otherwise.
|
585
|
+
#
|
586
|
+
# @param plan [PuppetStrings::Yard::CodeObjects::Plan] The puppet-strings plan documentation.
|
587
|
+
# @return [Boolean]
|
588
|
+
#
|
589
|
+
private def private_plan?(plan)
|
590
|
+
if plan.tag(:private)
|
591
|
+
value = plan.tag(:private).text
|
592
|
+
api_value = value.downcase == 'true' ? 'private' : 'public'
|
593
|
+
|
594
|
+
Bolt::Logger.deprecate(
|
595
|
+
'plan_private_tag',
|
596
|
+
"Tag '@private #{value}' in plan '#{plan.name}' is deprecated, use '@api #{api_value}' instead"
|
597
|
+
)
|
598
|
+
|
599
|
+
unless %w[true false].include?(plan.tag(:private).text.downcase)
|
600
|
+
msg = "Value for '@private' tag in plan '#{plan.name}' must be a boolean, received: #{value}"
|
601
|
+
raise Bolt::Error.new(msg, 'bolt/invalid-plan')
|
602
|
+
end
|
603
|
+
end
|
604
|
+
|
605
|
+
plan.tag(:api).text == 'private' || plan.tag(:private)&.text&.downcase == 'true'
|
606
|
+
end
|
607
|
+
|
608
|
+
def get_plan_mtime(path)
|
609
|
+
# If the plan is from the project modules/ directory, or is in the
|
610
|
+
# project itself, include the last mtime of the file so we can compare
|
611
|
+
# if the plan has been updated since it was cached.
|
612
|
+
if @project &&
|
613
|
+
File.exist?(path) &&
|
614
|
+
(path.include?(File.join(@project.path, 'modules')) ||
|
615
|
+
path.include?(@project.plans_path.to_s))
|
616
|
+
|
617
|
+
{ 'file' => { 'mtime' => File.mtime(path),
|
618
|
+
'path' => path } }
|
619
|
+
else
|
620
|
+
{}
|
621
|
+
end
|
622
|
+
end
|
623
|
+
|
624
|
+
def convert_plan(plan)
|
625
|
+
path = File.expand_path(plan)
|
626
|
+
|
627
|
+
# If the path doesn't exist, check if it's a plan name
|
628
|
+
unless File.exist?(path)
|
629
|
+
in_bolt_compiler do |compiler|
|
630
|
+
sig = compiler.plan_signature(plan)
|
631
|
+
|
632
|
+
# If the plan was loaded, look for it on the module loader
|
633
|
+
# There has to be an easier way to do this...
|
634
|
+
if sig
|
635
|
+
type = compiler.list_plans.find { |p| p.name == plan }
|
636
|
+
path = sig.instance_variable_get(:@plan_func)
|
637
|
+
.loader
|
638
|
+
.find(type)
|
639
|
+
.origin
|
640
|
+
.first
|
641
|
+
end
|
642
|
+
end
|
643
|
+
end
|
644
|
+
|
645
|
+
Puppet[:tasks] = true
|
646
|
+
transpiler = YamlPlan::Transpiler.new
|
647
|
+
transpiler.transpile(path)
|
648
|
+
end
|
649
|
+
|
650
|
+
# Returns a mapping of all modules available to the Bolt compiler
|
651
|
+
#
|
652
|
+
# @return [Hash{String => Array<Hash{Symbol => String,nil}>}]
|
653
|
+
# A hash that associates each directory on the modulepath with an array
|
654
|
+
# containing a hash of information for each module in that directory.
|
655
|
+
# The information hash provides the name, version, and a string
|
656
|
+
# indicating whether the module belongs to an internal module group.
|
657
|
+
def list_modules
|
658
|
+
internal_module_groups = { Bolt::Config::Modulepath::BOLTLIB_PATH => 'Plan Language Modules',
|
659
|
+
Bolt::Config::Modulepath::MODULES_PATH => 'Packaged Modules',
|
660
|
+
@project.managed_moduledir.to_s => 'Project Dependencies' }
|
661
|
+
|
662
|
+
in_bolt_compiler do
|
663
|
+
# NOTE: Can replace map+to_h with transform_values when Ruby 2.4
|
664
|
+
# is the minimum supported version.
|
665
|
+
Puppet.lookup(:current_environment).modules_by_path.map do |path, modules|
|
666
|
+
module_group = internal_module_groups[path]
|
667
|
+
|
668
|
+
values = modules.map do |mod|
|
669
|
+
mod_info = { name: (mod.forge_name || mod.name),
|
670
|
+
version: mod.version }
|
671
|
+
mod_info[:internal_module_group] = module_group unless module_group.nil?
|
672
|
+
|
673
|
+
mod_info
|
674
|
+
end
|
675
|
+
|
676
|
+
[path, values]
|
677
|
+
end.to_h
|
678
|
+
end
|
679
|
+
end
|
680
|
+
|
681
|
+
# Return information about a module.
|
682
|
+
#
|
683
|
+
# @param name [String] The name of the module.
|
684
|
+
# @return [Hash]
|
685
|
+
#
|
686
|
+
def show_module(name)
|
687
|
+
name = name.tr('-', '/')
|
688
|
+
|
689
|
+
data = in_bolt_compiler do |_compiler|
|
690
|
+
mod = Puppet.lookup(:current_environment).module(name.split(%r{[/-]}, 2).last)
|
691
|
+
|
692
|
+
unless mod && (mod.forge_name == name || mod.name == name)
|
693
|
+
raise Bolt::Error.new("Could not find module '#{name}' on the modulepath.", 'bolt/unknown-module')
|
694
|
+
end
|
695
|
+
|
696
|
+
{
|
697
|
+
name: mod.forge_name || mod.name,
|
698
|
+
metadata: mod.metadata,
|
699
|
+
path: mod.path,
|
700
|
+
plans: mod.plans.map(&:name).sort,
|
701
|
+
tasks: mod.tasks.map(&:name).sort
|
702
|
+
}
|
703
|
+
end
|
704
|
+
|
705
|
+
data[:plans] = list_plans_with_cache.to_h.slice(*data[:plans]).to_a
|
706
|
+
data[:tasks] = list_tasks_with_cache.to_h.slice(*data[:tasks]).to_a
|
707
|
+
|
708
|
+
data
|
709
|
+
end
|
710
|
+
|
711
|
+
def generate_types(cache: false)
|
712
|
+
require 'puppet/face/generate'
|
713
|
+
in_bolt_compiler do
|
714
|
+
generator = Puppet::Generate::Type
|
715
|
+
inputs = generator.find_inputs(:pcore)
|
716
|
+
FileUtils.mkdir_p(@resource_types)
|
717
|
+
cache_plan_info if @project && cache
|
718
|
+
cache_task_info if @project && cache
|
719
|
+
generator.generate(inputs, @resource_types, true)
|
720
|
+
end
|
721
|
+
end
|
722
|
+
|
723
|
+
def cache_plan_info
|
724
|
+
# plan_name is an array here
|
725
|
+
plans_info = list_plans(filter_content: false).map do |plan_name,|
|
726
|
+
data = get_plan_info(plan_name, with_mtime: true)
|
727
|
+
{ plan_name => data }
|
728
|
+
end.reduce({}, :merge)
|
729
|
+
|
730
|
+
FileUtils.touch(@project.plan_cache_file)
|
731
|
+
File.write(@project.plan_cache_file, plans_info.to_json)
|
732
|
+
end
|
733
|
+
|
734
|
+
def cache_task_info
|
735
|
+
# task_name is an array here
|
736
|
+
tasks_info = list_tasks(filter_content: false).map do |task_name,|
|
737
|
+
data = get_task_info(task_name, with_mtime: true)
|
738
|
+
{ task_name => data }
|
739
|
+
end.reduce({}, :merge)
|
740
|
+
|
741
|
+
FileUtils.touch(@project.task_cache_file)
|
742
|
+
File.write(@project.task_cache_file, tasks_info.to_json)
|
743
|
+
end
|
744
|
+
|
745
|
+
def run_task(task_name, targets, params, executor, inventory, description = nil)
|
746
|
+
in_task_compiler(executor, inventory) do |compiler|
|
747
|
+
params = params.merge('_bolt_api_call' => true, '_catch_errors' => true)
|
748
|
+
compiler.call_function('run_task', task_name, targets, description, params)
|
749
|
+
end
|
750
|
+
end
|
751
|
+
|
752
|
+
def run_plan(plan_name, params, executor, inventory = nil, pdb_client = nil, applicator = nil)
|
753
|
+
# Start the round robin inside the plan compiler, so that
|
754
|
+
# backgrounded tasks can finish once the main plan exits
|
755
|
+
in_plan_compiler(executor, inventory, pdb_client, applicator) do |compiler|
|
756
|
+
# Create a Fiber for the main plan. This will be run along with any
|
757
|
+
# other Fibers created during the plan run in the round_robin, with the
|
758
|
+
# main plan always taking precedence in being resumed.
|
759
|
+
#
|
760
|
+
# Every future except for the main plan needs to have a plan id in
|
761
|
+
# order to be tracked for the `wait()` function with no arguments.
|
762
|
+
future = executor.create_future(name: plan_name, plan_id: 0) do |_scope|
|
763
|
+
r = compiler.call_function('run_plan', plan_name, params.merge('_bolt_api_call' => true))
|
764
|
+
Bolt::PlanResult.from_pcore(r, 'success')
|
765
|
+
rescue Bolt::Error => e
|
766
|
+
Bolt::PlanResult.new(e, 'failure')
|
767
|
+
end
|
768
|
+
|
769
|
+
# Round robin until all Fibers, including the main plan, have finished.
|
770
|
+
# This will stay alive until backgrounded tasks have finished.
|
771
|
+
executor.round_robin until executor.plan_complete?
|
772
|
+
|
773
|
+
# Return the result from the main plan
|
774
|
+
future.value
|
775
|
+
end
|
776
|
+
rescue Bolt::Error => e
|
777
|
+
Bolt::PlanResult.new(e, 'failure')
|
778
|
+
end
|
779
|
+
|
780
|
+
def plan_hierarchy_lookup(key, plan_vars: {})
|
781
|
+
# Do a lookup with a script compiler, which uses the 'plan_hierarchy' key in
|
782
|
+
# Hiera config.
|
783
|
+
with_puppet_settings do
|
784
|
+
# We want all of the setup and teardown that `in_bolt_compiler` does,
|
785
|
+
# but also want to pass keys to the script compiler.
|
786
|
+
in_bolt_compiler(compiler_params: { variables: plan_vars }) do |compiler|
|
787
|
+
compiler.call_function('lookup', key)
|
788
|
+
end
|
789
|
+
rescue Puppet::Error => e
|
790
|
+
raise PALError.from_error(e)
|
791
|
+
end
|
792
|
+
end
|
793
|
+
|
794
|
+
def lookup(key, targets, inventory, executor, plan_vars: {})
|
795
|
+
# Install the puppet-agent package and collect facts. Facts are
|
796
|
+
# automatically added to the targets.
|
797
|
+
in_plan_compiler(executor, inventory, nil) do |compiler|
|
798
|
+
compiler.call_function('apply_prep', targets)
|
799
|
+
end
|
800
|
+
|
801
|
+
overrides = {
|
802
|
+
bolt_inventory: inventory,
|
803
|
+
bolt_project: @project
|
804
|
+
}
|
805
|
+
|
806
|
+
# Do a lookup with a catalog compiler, which uses the 'hierarchy' key in
|
807
|
+
# Hiera config.
|
808
|
+
results = targets.map do |target|
|
809
|
+
node = Puppet::Node.from_data_hash(
|
810
|
+
'name' => target.name,
|
811
|
+
'parameters' => { 'clientcert' => target.name }
|
812
|
+
)
|
813
|
+
|
814
|
+
trusted = Puppet::Context::TrustedInformation.local(node).to_h
|
815
|
+
|
816
|
+
# Separate environment configuration from interpolation data the same
|
817
|
+
# way we do when compiling Puppet catalogs.
|
818
|
+
env_conf = {
|
819
|
+
modulepath: @modulepath.full_modulepath,
|
820
|
+
facts: target.facts
|
821
|
+
}
|
822
|
+
|
823
|
+
interpolations = {
|
824
|
+
variables: plan_vars,
|
825
|
+
target_variables: target.vars
|
826
|
+
}
|
827
|
+
|
828
|
+
with_puppet_settings do
|
829
|
+
Puppet::Pal.in_tmp_environment(target.name, **env_conf) do |pal|
|
830
|
+
Puppet.override(overrides) do
|
831
|
+
Puppet.lookup(:pal_current_node).trusted_data = trusted
|
832
|
+
pal.with_catalog_compiler(**interpolations) do |compiler|
|
833
|
+
Bolt::Result.for_lookup(target, key, compiler.call_function('lookup', key))
|
834
|
+
rescue StandardError => e
|
835
|
+
Bolt::Result.from_exception(target, e)
|
836
|
+
end
|
837
|
+
rescue Puppet::Error => e
|
838
|
+
raise PALError.from_error(e)
|
839
|
+
end
|
840
|
+
end
|
841
|
+
end
|
842
|
+
end
|
843
|
+
|
844
|
+
Bolt::ResultSet.new(results)
|
845
|
+
end
|
846
|
+
end
|
847
|
+
end
|