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
@@ -0,0 +1,196 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/plan_result'
|
4
|
+
require 'bolt/result'
|
5
|
+
require 'bolt/util'
|
6
|
+
|
7
|
+
module BoltSpec
|
8
|
+
module Plans
|
9
|
+
# Nothing in the ActionDouble is 'public'
|
10
|
+
class ActionDouble
|
11
|
+
def initialize(action_stub)
|
12
|
+
@stubs = []
|
13
|
+
@action_stub = action_stub
|
14
|
+
end
|
15
|
+
|
16
|
+
def process(*args)
|
17
|
+
matches = @stubs.select { |s| s.matches(*args) }
|
18
|
+
unless matches.empty?
|
19
|
+
matches[0].call(*args)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def assert_called(object)
|
24
|
+
@stubs.each { |s| s.assert_called(object) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def add_stub(inventory = nil)
|
28
|
+
stub = Plans.const_get(@action_stub).new(false, inventory)
|
29
|
+
@stubs.unshift stub
|
30
|
+
stub
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class ActionStub
|
35
|
+
attr_reader :invocation
|
36
|
+
|
37
|
+
def initialize(expect = false, inventory = nil)
|
38
|
+
@calls = 0
|
39
|
+
@expect = expect
|
40
|
+
@expected_calls = nil
|
41
|
+
# invocation spec
|
42
|
+
@invocation = {}
|
43
|
+
# return value
|
44
|
+
@data = { default: {} }
|
45
|
+
@inventory = inventory
|
46
|
+
end
|
47
|
+
|
48
|
+
def assert_called(object)
|
49
|
+
satisfied = if @expect
|
50
|
+
(@expected_calls.nil? && @calls > 0) || @calls == @expected_calls
|
51
|
+
else
|
52
|
+
@expected_calls.nil? || @calls <= @expected_calls
|
53
|
+
end
|
54
|
+
unless satisfied
|
55
|
+
unless (times = @expected_calls)
|
56
|
+
times = @expect ? "at least one" : "any number of"
|
57
|
+
end
|
58
|
+
message = "Expected #{object} to be called #{times} times"
|
59
|
+
message += " with targets #{@invocation[:targets]}" if @invocation[:targets]
|
60
|
+
if parameters
|
61
|
+
# Print the parameters hash by converting it to JSON and then re-parsing.
|
62
|
+
# This prevents issues in Bolt data types, such as Targets, from generating
|
63
|
+
# gigantic, unreadable, data when converted to string by interpolation.
|
64
|
+
# Targets exhibit this behavior because they have a reference to @inventory.
|
65
|
+
# When the target is converted into a string, it converts the full Inventory
|
66
|
+
# into a string recursively.
|
67
|
+
parameters_str = JSON.parse(parameters.to_json)
|
68
|
+
message += " with parameters #{parameters_str}"
|
69
|
+
end
|
70
|
+
raise message
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# This changes the stub from an allow to an expect which will validate
|
75
|
+
# that it has been called.
|
76
|
+
def expect_call
|
77
|
+
@expected_calls = 1
|
78
|
+
@expect = true
|
79
|
+
self
|
80
|
+
end
|
81
|
+
|
82
|
+
# Used to create a valid Bolt::Result object from result data.
|
83
|
+
def default_for(target)
|
84
|
+
case @data[:default]
|
85
|
+
when Bolt::PlanFailure
|
86
|
+
# Bolt::PlanFailure needs to be declared before Bolt::Error because
|
87
|
+
# Bolt::PlanFailure is an instance of Bolt::Error, so it can match both
|
88
|
+
# in this case we need to treat Bolt::PlanFailure's in a different way
|
89
|
+
#
|
90
|
+
# raise Bolt::PlanFailure errors so that the PAL can catch them and wrap
|
91
|
+
# them into Bolt::PlanResult's for us.
|
92
|
+
raise @data[:default]
|
93
|
+
when Bolt::Error
|
94
|
+
Bolt::Result.from_exception(target, @data[:default])
|
95
|
+
when Hash
|
96
|
+
result_for(target, **Bolt::Util.walk_keys(@data[:default], &:to_sym))
|
97
|
+
else
|
98
|
+
raise 'Default result must be a Hash'
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def check_resultset(result_set, object)
|
103
|
+
unless result_set.is_a?(Bolt::ResultSet)
|
104
|
+
raise "Return block for #{object} did not return a Bolt::ResultSet"
|
105
|
+
end
|
106
|
+
result_set
|
107
|
+
end
|
108
|
+
|
109
|
+
def check_plan_result(plan_result, plan_clj)
|
110
|
+
unless plan_result.is_a?(Bolt::PlanResult)
|
111
|
+
raise "Return block for #{plan_clj.closure_name} did not return a Bolt::PlanResult"
|
112
|
+
end
|
113
|
+
plan_result
|
114
|
+
end
|
115
|
+
|
116
|
+
# Below here are the intended 'public' methods of the stub
|
117
|
+
|
118
|
+
# Restricts the stub to only match invocations with
|
119
|
+
# the correct targets
|
120
|
+
def with_targets(targets)
|
121
|
+
targets = Array(targets)
|
122
|
+
@invocation[:targets] = targets.map do |target|
|
123
|
+
if target.is_a? String
|
124
|
+
target
|
125
|
+
else
|
126
|
+
target.name
|
127
|
+
end
|
128
|
+
end
|
129
|
+
self
|
130
|
+
end
|
131
|
+
|
132
|
+
# limit the maximum number of times an allow stub may be called or
|
133
|
+
# specify how many times an expect stub must be called.
|
134
|
+
def be_called_times(times)
|
135
|
+
@expected_calls = times
|
136
|
+
self
|
137
|
+
end
|
138
|
+
|
139
|
+
# error if the stub is called at all.
|
140
|
+
def not_be_called
|
141
|
+
@expected_calls = 0
|
142
|
+
self
|
143
|
+
end
|
144
|
+
|
145
|
+
def return(&block)
|
146
|
+
raise "Cannot set return values and return block." if @data_set
|
147
|
+
@return_block = block
|
148
|
+
self
|
149
|
+
end
|
150
|
+
|
151
|
+
# Set different result values for each target. May use string or symbol keys, but allowed key names
|
152
|
+
# are restricted based on action.
|
153
|
+
def return_for_targets(data)
|
154
|
+
data.each_with_object(@data) do |(target, result), hsh|
|
155
|
+
raise "Mocked results must be hashes: #{target}: #{result}" unless result.is_a? Hash
|
156
|
+
# set the inventory from the BoltSpec::Plans, otherwise if we try to convert
|
157
|
+
# this target to a string, it will fail to string conversion because the
|
158
|
+
# inventory is nil
|
159
|
+
hsh[target] = result_for(Bolt::Target.new(target, @inventory), **Bolt::Util.walk_keys(result, &:to_sym))
|
160
|
+
end
|
161
|
+
raise "Cannot set return values and return block." if @return_block
|
162
|
+
@data_set = true
|
163
|
+
self
|
164
|
+
end
|
165
|
+
|
166
|
+
# Set a default return value for all targets, specific targets may be overridden with return_for_targets.
|
167
|
+
# Follows the same rules for data as return_for_targets.
|
168
|
+
def always_return(data)
|
169
|
+
@data[:default] = data
|
170
|
+
@data_set = true
|
171
|
+
self
|
172
|
+
end
|
173
|
+
|
174
|
+
# Set a default error result for all targets.
|
175
|
+
def error_with(data, clazz = Bolt::Error)
|
176
|
+
data = Bolt::Util.walk_keys(data, &:to_s)
|
177
|
+
if data['msg'] && data['kind'] && (data.keys - %w[msg kind details issue_code]).empty?
|
178
|
+
@data[:default] = clazz.new(data['msg'], data['kind'], data['details'], data['issue_code'])
|
179
|
+
else
|
180
|
+
$stderr.puts "In the future 'error_with()' might require msg and kind, and " \
|
181
|
+
"optionally accept only details and issue_code."
|
182
|
+
@data[:default] = data
|
183
|
+
end
|
184
|
+
@data_set = true
|
185
|
+
self
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
require_relative 'action_stubs/command_stub'
|
192
|
+
require_relative 'action_stubs/plan_stub'
|
193
|
+
require_relative 'action_stubs/script_stub'
|
194
|
+
require_relative 'action_stubs/task_stub'
|
195
|
+
require_relative 'action_stubs/upload_stub'
|
196
|
+
require_relative 'action_stubs/download_stub'
|
@@ -0,0 +1,361 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt_spec/plans/action_stubs'
|
4
|
+
require 'bolt_spec/plans/publish_stub'
|
5
|
+
require 'bolt/error'
|
6
|
+
require 'bolt/executor'
|
7
|
+
require 'bolt/result_set'
|
8
|
+
require 'bolt/result'
|
9
|
+
require 'pathname'
|
10
|
+
require 'set'
|
11
|
+
|
12
|
+
module BoltSpec
|
13
|
+
module Plans
|
14
|
+
MOCKED_ACTIONS = %i[command download plan script task upload].freeze
|
15
|
+
|
16
|
+
class UnexpectedInvocation < ArgumentError; end
|
17
|
+
|
18
|
+
# Nothing on the executor is 'public'
|
19
|
+
class MockExecutor
|
20
|
+
attr_reader :noop, :error_message, :transports, :future
|
21
|
+
attr_accessor :run_as, :transport_features, :execute_any_plan
|
22
|
+
|
23
|
+
def initialize(modulepath)
|
24
|
+
@noop = false
|
25
|
+
@run_as = nil
|
26
|
+
@future = {}
|
27
|
+
@error_message = nil
|
28
|
+
@allow_apply = false
|
29
|
+
@modulepath = [modulepath].flatten.map { |path| File.absolute_path(path) }
|
30
|
+
MOCKED_ACTIONS.each { |action| instance_variable_set(:"@#{action}_doubles", {}) }
|
31
|
+
@stub_out_message = nil
|
32
|
+
@stub_out_verbose = nil
|
33
|
+
@transport_features = ['puppet-agent']
|
34
|
+
@executor_real = Bolt::Executor.new
|
35
|
+
# by default, we want to execute any plan that we come across without error
|
36
|
+
# or mocking. users can toggle this behavior so that plans will either need to
|
37
|
+
# be mocked out, or an error will be thrown.
|
38
|
+
@execute_any_plan = true
|
39
|
+
# plans that are allowed to be executed by the @executor_real
|
40
|
+
@allowed_exec_plans = {}
|
41
|
+
@id = 0
|
42
|
+
@plan_futures = []
|
43
|
+
end
|
44
|
+
|
45
|
+
def module_file_id(file)
|
46
|
+
modpath = @modulepath.select { |path| file =~ /^#{path}/ }
|
47
|
+
return nil unless modpath.size == 1
|
48
|
+
|
49
|
+
path = Pathname.new(file)
|
50
|
+
relative = path.relative_path_from(Pathname.new(modpath.first))
|
51
|
+
segments = relative.to_path.split('/')
|
52
|
+
([segments[0]] + segments[2..-1]).join('/')
|
53
|
+
end
|
54
|
+
|
55
|
+
def run_command(targets, command, options = {}, _position = [])
|
56
|
+
result = nil
|
57
|
+
if (doub = @command_doubles[command] || @command_doubles[:default])
|
58
|
+
result = doub.process(targets, command, options)
|
59
|
+
end
|
60
|
+
unless result
|
61
|
+
targets = targets.map(&:name)
|
62
|
+
@error_message = "Unexpected call to 'run_command(#{command}, #{targets}, #{options})'"
|
63
|
+
raise UnexpectedInvocation, @error_message
|
64
|
+
end
|
65
|
+
result
|
66
|
+
end
|
67
|
+
|
68
|
+
def run_script(targets, script_path, arguments, options = {}, _position = [])
|
69
|
+
script = module_file_id(script_path) || script_path
|
70
|
+
result = nil
|
71
|
+
if (doub = @script_doubles[script] || @script_doubles[:default])
|
72
|
+
result = doub.process(targets, script, arguments, options)
|
73
|
+
end
|
74
|
+
unless result
|
75
|
+
targets = targets.map(&:name)
|
76
|
+
params = options.merge('arguments' => arguments)
|
77
|
+
@error_message = "Unexpected call to 'run_script(#{script}, #{targets}, #{params})'"
|
78
|
+
raise UnexpectedInvocation, @error_message
|
79
|
+
end
|
80
|
+
result
|
81
|
+
end
|
82
|
+
|
83
|
+
def run_task(targets, task, arguments, options = {}, _position = [])
|
84
|
+
result = nil
|
85
|
+
if (doub = @task_doubles[task.name] || @task_doubles[:default])
|
86
|
+
result = doub.process(targets, task.name, arguments, options)
|
87
|
+
end
|
88
|
+
unless result
|
89
|
+
targets = targets.map(&:name)
|
90
|
+
params = arguments.merge(options)
|
91
|
+
@error_message = "Unexpected call to 'run_task(#{task.name}, #{targets}, #{params})'"
|
92
|
+
raise UnexpectedInvocation, @error_message
|
93
|
+
end
|
94
|
+
result
|
95
|
+
end
|
96
|
+
|
97
|
+
def run_task_with(target_mapping, task, options = {}, _position = [])
|
98
|
+
resultsets = target_mapping.map do |target, arguments|
|
99
|
+
run_task([target], task, arguments, options)
|
100
|
+
end.compact
|
101
|
+
|
102
|
+
Bolt::ResultSet.new(resultsets.map(&:results).flatten)
|
103
|
+
end
|
104
|
+
|
105
|
+
def download_file(targets, source, destination, options = {}, _position = [])
|
106
|
+
result = nil
|
107
|
+
if (doub = @download_doubles[source] || @download_doubles[:default])
|
108
|
+
result = doub.process(targets, source, destination, options)
|
109
|
+
end
|
110
|
+
unless result
|
111
|
+
targets = targets.map(&:name)
|
112
|
+
@error_message = "Unexpected call to 'download_file(#{source}, #{destination}, #{targets}, #{options})'"
|
113
|
+
raise UnexpectedInvocation, @error_message
|
114
|
+
end
|
115
|
+
result
|
116
|
+
end
|
117
|
+
|
118
|
+
def upload_file(targets, source_path, destination, options = {}, _position = [])
|
119
|
+
source = module_file_id(source_path) || source_path
|
120
|
+
result = nil
|
121
|
+
if (doub = @upload_doubles[source] || @upload_doubles[:default])
|
122
|
+
result = doub.process(targets, source, destination, options)
|
123
|
+
end
|
124
|
+
unless result
|
125
|
+
targets = targets.map(&:name)
|
126
|
+
@error_message = "Unexpected call to 'upload_file(#{source}, #{destination}, #{targets}, #{options})'"
|
127
|
+
raise UnexpectedInvocation, @error_message
|
128
|
+
end
|
129
|
+
result
|
130
|
+
end
|
131
|
+
|
132
|
+
def with_plan_allowed_exec(plan_name, params)
|
133
|
+
@allowed_exec_plans[plan_name] = params
|
134
|
+
result = yield
|
135
|
+
@allowed_exec_plans.delete(plan_name)
|
136
|
+
result
|
137
|
+
end
|
138
|
+
|
139
|
+
def run_plan(scope, plan_clj, params)
|
140
|
+
result = nil
|
141
|
+
plan_name = plan_clj.closure_name
|
142
|
+
|
143
|
+
# get the mock object either by plan name, or the default in case allow_any_plan
|
144
|
+
# was called, if both are nil / don't exist, then dub will be nil and we'll fall
|
145
|
+
# through to another conditional statement
|
146
|
+
doub = @plan_doubles[plan_name] || @plan_doubles[:default]
|
147
|
+
|
148
|
+
# rubocop:disable Lint/DuplicateBranch
|
149
|
+
# High level:
|
150
|
+
# - If we've explicitly allowed execution of the plan (normally the main plan
|
151
|
+
# passed into BoltSpec::Plan::run_plan()), then execute it
|
152
|
+
# - If we've explicitly "allowed/expected" the plan (mocked),
|
153
|
+
# then run it through the mock object
|
154
|
+
# - If we're allowing "any" plan to be executed,
|
155
|
+
# then execute it
|
156
|
+
# - Otherwise we have an error
|
157
|
+
if @allowed_exec_plans.key?(plan_name) && @allowed_exec_plans[plan_name] == params
|
158
|
+
# This plan's name + parameters were explicitly allowed to be executed.
|
159
|
+
# run it with the real executor.
|
160
|
+
# We require this functionality so that the BoltSpec::Plans.run_plan()
|
161
|
+
# function can kick off the initial plan. In reality, no other plans should
|
162
|
+
# be in this hash.
|
163
|
+
result = @executor_real.run_plan(scope, plan_clj, params)
|
164
|
+
elsif doub
|
165
|
+
result = doub.process(scope, plan_clj, params)
|
166
|
+
# the throw here is how Puppet exits out of a closure and returns a result
|
167
|
+
# it throws this special symbol with a result object that is captured by
|
168
|
+
# the run_plan Puppet function
|
169
|
+
throw :return, result
|
170
|
+
elsif @execute_any_plan
|
171
|
+
# if the plan wasn't allowed or mocked out, and we're allowing any plan to be
|
172
|
+
# executed, then execute the plan
|
173
|
+
result = @executor_real.run_plan(scope, plan_clj, params)
|
174
|
+
else
|
175
|
+
# convert to JSON and back so that we get the ruby representation with all keys and
|
176
|
+
# values converted to a string .to_s instead of their ruby object notation
|
177
|
+
params_str = JSON.parse(params.to_json)
|
178
|
+
@error_message = "Unexpected call to 'run_plan(#{plan_name}, #{params_str})'"
|
179
|
+
raise UnexpectedInvocation, @error_message
|
180
|
+
end
|
181
|
+
# rubocop:enable Lint/DuplicateBranch
|
182
|
+
result
|
183
|
+
end
|
184
|
+
|
185
|
+
def assert_call_expectations
|
186
|
+
MOCKED_ACTIONS.each do |action|
|
187
|
+
instance_variable_get(:"@#{action}_doubles").map do |object, doub|
|
188
|
+
doub.assert_called(object)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
@stub_out_message.assert_called('out::message') if @stub_out_message
|
192
|
+
@stub_out_verbose.assert_called('out::verbose') if @stub_out_verbose
|
193
|
+
end
|
194
|
+
|
195
|
+
MOCKED_ACTIONS.each do |action|
|
196
|
+
define_method(:"stub_#{action}") do |object|
|
197
|
+
instance_variable_get(:"@#{action}_doubles")[object] ||= ActionDouble.new(:"#{action.capitalize}Stub")
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def stub_out_message
|
202
|
+
@stub_out_message ||= ActionDouble.new(:PublishStub)
|
203
|
+
end
|
204
|
+
|
205
|
+
def stub_out_verbose
|
206
|
+
@stub_out_verbose ||= ActionDouble.new(:PublishStub)
|
207
|
+
end
|
208
|
+
|
209
|
+
def stub_apply
|
210
|
+
@allow_apply = true
|
211
|
+
end
|
212
|
+
|
213
|
+
def wait_until_available(targets, **_options)
|
214
|
+
Bolt::ResultSet.new(targets.map { |target| Bolt::Result.new(target) })
|
215
|
+
end
|
216
|
+
|
217
|
+
def log_action(*_args)
|
218
|
+
yield
|
219
|
+
end
|
220
|
+
|
221
|
+
def log_plan(_plan_name)
|
222
|
+
yield
|
223
|
+
end
|
224
|
+
|
225
|
+
def without_default_logging
|
226
|
+
yield
|
227
|
+
end
|
228
|
+
|
229
|
+
def publish_event(event)
|
230
|
+
case event[:type]
|
231
|
+
when :message
|
232
|
+
unless @stub_out_message
|
233
|
+
@error_message = "Unexpected call to 'out::message(#{event[:message]})'"
|
234
|
+
raise UnexpectedInvocation, @error_message
|
235
|
+
end
|
236
|
+
@stub_out_message.process(event[:message])
|
237
|
+
|
238
|
+
when :verbose
|
239
|
+
unless @stub_out_verbose
|
240
|
+
@error_message = "Unexpected call to 'out::verbose(#{event[:message]})'"
|
241
|
+
raise UnexpectedInvocation, @error_message
|
242
|
+
end
|
243
|
+
@stub_out_verbose.process(event[:message])
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
# Mocked for Apply so it does not compile and execute.
|
248
|
+
def with_node_logging(_description, targets)
|
249
|
+
raise "Unexpected call to apply(#{targets})" unless @allow_apply
|
250
|
+
end
|
251
|
+
|
252
|
+
def queue_execute(targets)
|
253
|
+
raise "Unexpected call to apply(#{targets})" unless @allow_apply
|
254
|
+
targets
|
255
|
+
end
|
256
|
+
|
257
|
+
def await_results(promises)
|
258
|
+
raise "Unexpected call to apply(#{targets})" unless @allow_apply
|
259
|
+
Bolt::ResultSet.new(promises.map { |target| Bolt::ApplyResult.new(target) })
|
260
|
+
end
|
261
|
+
# End Apply mocking
|
262
|
+
|
263
|
+
# Mocked for apply_prep
|
264
|
+
def transport(_protocol)
|
265
|
+
Class.new do
|
266
|
+
attr_reader :provided_features
|
267
|
+
|
268
|
+
def initialize(features)
|
269
|
+
@provided_features = features
|
270
|
+
end
|
271
|
+
end.new(transport_features)
|
272
|
+
end
|
273
|
+
# End apply_prep mocking
|
274
|
+
|
275
|
+
# Parallel function mocking
|
276
|
+
def run_in_thread
|
277
|
+
yield
|
278
|
+
end
|
279
|
+
|
280
|
+
def in_parallel?
|
281
|
+
false
|
282
|
+
end
|
283
|
+
|
284
|
+
def create_future(plan_id:, scope: nil, name: nil)
|
285
|
+
newscope = nil
|
286
|
+
if scope
|
287
|
+
# Create the new scope
|
288
|
+
newscope = Puppet::Parser::Scope.new(scope.compiler)
|
289
|
+
local = Puppet::Parser::Scope::LocalScope.new
|
290
|
+
|
291
|
+
# Compress the current scopes into a single vars hash to add to the new scope
|
292
|
+
scope.to_hash(true, true).each_pair { |k, v| local[k] = v }
|
293
|
+
newscope.push_ephemerals([local])
|
294
|
+
end
|
295
|
+
|
296
|
+
# Execute "futures" serially when running in BoltSpec
|
297
|
+
result = yield newscope
|
298
|
+
@id += 1
|
299
|
+
future = Bolt::PlanFuture.new(nil, @id, name: name, plan_id: plan_id)
|
300
|
+
future.value = result
|
301
|
+
@plan_futures << future
|
302
|
+
future
|
303
|
+
end
|
304
|
+
|
305
|
+
def get_futures_for_plan(plan_id:)
|
306
|
+
@plan_futures.select { |future| future.plan_id == plan_id }
|
307
|
+
end
|
308
|
+
|
309
|
+
def wait(futures, **_kwargs)
|
310
|
+
futures.map(&:value)
|
311
|
+
end
|
312
|
+
|
313
|
+
# Since Futures are executed immediately once created, this will always
|
314
|
+
# be true by the time it's called.
|
315
|
+
def plan_complete?
|
316
|
+
true
|
317
|
+
end
|
318
|
+
|
319
|
+
def get_current_future(fiber)
|
320
|
+
@plan_futures.select { |f| f.fiber == fiber }&.first
|
321
|
+
end
|
322
|
+
|
323
|
+
def get_current_plan_id(fiber)
|
324
|
+
get_current_future(fiber)&.current_plan
|
325
|
+
end
|
326
|
+
|
327
|
+
# Public methods on Bolt::Executor that need to be mocked so there aren't
|
328
|
+
# "undefined method" errors.
|
329
|
+
|
330
|
+
def batch_execute(_targets); end
|
331
|
+
|
332
|
+
def finish_plan(_plan_result); end
|
333
|
+
|
334
|
+
def handle_event(_event); end
|
335
|
+
|
336
|
+
def prompt(_prompt, _options); end
|
337
|
+
|
338
|
+
def report_function_call(_function); end
|
339
|
+
|
340
|
+
def report_bundled_content(_mode, _name); end
|
341
|
+
|
342
|
+
def report_file_source(_plan_function, _source); end
|
343
|
+
|
344
|
+
def report_apply(_statements, _resources); end
|
345
|
+
|
346
|
+
def report_yaml_plan(_plan); end
|
347
|
+
|
348
|
+
def report_noop_mode(_mode); end
|
349
|
+
|
350
|
+
def shutdown; end
|
351
|
+
|
352
|
+
def start_plan(_plan_context); end
|
353
|
+
|
354
|
+
def subscribe(_subscriber, _types = nil); end
|
355
|
+
|
356
|
+
def unsubscribe(_subscriber, _types = nil); end
|
357
|
+
|
358
|
+
def round_robin; end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/result'
|
4
|
+
require 'bolt/util'
|
5
|
+
|
6
|
+
module BoltSpec
|
7
|
+
module Plans
|
8
|
+
class PublishStub < ActionStub
|
9
|
+
def return
|
10
|
+
raise "return is not implemented for out module functions"
|
11
|
+
end
|
12
|
+
|
13
|
+
def return_for_targets(_data)
|
14
|
+
raise "return_for_targets is not implemented for out module functions"
|
15
|
+
end
|
16
|
+
|
17
|
+
def always_return(_data)
|
18
|
+
raise "always_return is not implemented for out module functions"
|
19
|
+
end
|
20
|
+
|
21
|
+
def error_with(_data)
|
22
|
+
raise "error_with is not implemented for out module functions"
|
23
|
+
end
|
24
|
+
|
25
|
+
def matches(message)
|
26
|
+
if @invocation[:options] && message != @invocation[:options]
|
27
|
+
return false
|
28
|
+
end
|
29
|
+
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
def call(_event)
|
34
|
+
@calls += 1
|
35
|
+
end
|
36
|
+
|
37
|
+
def parameters
|
38
|
+
@invocation[:options]
|
39
|
+
end
|
40
|
+
|
41
|
+
# Public methods
|
42
|
+
|
43
|
+
def with_params(params)
|
44
|
+
@invocation[:options] = params
|
45
|
+
self
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|