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,223 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../../bolt/util'
|
4
|
+
|
5
|
+
module Bolt
|
6
|
+
class PAL
|
7
|
+
class YamlPlan
|
8
|
+
class Step
|
9
|
+
attr_reader :body
|
10
|
+
|
11
|
+
STEP_KEYS = %w[
|
12
|
+
command
|
13
|
+
download
|
14
|
+
eval
|
15
|
+
message
|
16
|
+
verbose
|
17
|
+
plan
|
18
|
+
resources
|
19
|
+
script
|
20
|
+
task
|
21
|
+
upload
|
22
|
+
].freeze
|
23
|
+
|
24
|
+
class StepError < Bolt::Error
|
25
|
+
def initialize(message, name, step_number)
|
26
|
+
identifier = name ? name.inspect : "number #{step_number}"
|
27
|
+
error = "Parse error in step #{identifier}: \n #{message}"
|
28
|
+
|
29
|
+
super(error, 'bolt/invalid-plan')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Keys that are allowed for the step
|
34
|
+
#
|
35
|
+
def self.allowed_keys
|
36
|
+
required_keys + option_keys + Set['name', 'description', 'targets']
|
37
|
+
end
|
38
|
+
|
39
|
+
# Keys that translate to metaparameters for the plan step's function call
|
40
|
+
#
|
41
|
+
def self.option_keys
|
42
|
+
Set.new
|
43
|
+
end
|
44
|
+
|
45
|
+
# Keys that are required for the step
|
46
|
+
#
|
47
|
+
def self.required_keys
|
48
|
+
Set.new
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.create(step_body, step_number)
|
52
|
+
type_keys = (STEP_KEYS & step_body.keys)
|
53
|
+
case type_keys.length
|
54
|
+
when 0
|
55
|
+
raise StepError.new("No valid action detected", step_body['name'], step_number)
|
56
|
+
when 1
|
57
|
+
type = type_keys.first
|
58
|
+
else
|
59
|
+
raise StepError.new("Multiple action keys detected: #{type_keys.inspect}", step_body['name'], step_number)
|
60
|
+
end
|
61
|
+
|
62
|
+
step_class = const_get("Bolt::PAL::YamlPlan::Step::#{type.capitalize}")
|
63
|
+
step_class.validate(step_body, step_number)
|
64
|
+
step_class.new(step_body)
|
65
|
+
end
|
66
|
+
|
67
|
+
def initialize(body)
|
68
|
+
@body = body
|
69
|
+
end
|
70
|
+
|
71
|
+
# Transpiles the step into the plan language
|
72
|
+
#
|
73
|
+
def transpile
|
74
|
+
code = String.new(" ")
|
75
|
+
code << "$#{body['name']} = " if body['name']
|
76
|
+
code << function_call(function, format_args(body))
|
77
|
+
code << "\n"
|
78
|
+
end
|
79
|
+
|
80
|
+
# Evaluates the step
|
81
|
+
#
|
82
|
+
def evaluate(scope, evaluator)
|
83
|
+
evaluated = evaluator.evaluate_code_blocks(scope, body)
|
84
|
+
scope.call_function(function, format_args(evaluated))
|
85
|
+
end
|
86
|
+
|
87
|
+
# Formats a list of args from the provided body
|
88
|
+
#
|
89
|
+
private def format_args(_body)
|
90
|
+
raise NotImplementedError, "Step class #{self.class} does not implement #format_args"
|
91
|
+
end
|
92
|
+
|
93
|
+
# Returns the step's corresponding Puppet language function call
|
94
|
+
#
|
95
|
+
private def function_call(function, args)
|
96
|
+
code_args = args.map { |arg| Bolt::Util.to_code(arg) }
|
97
|
+
"#{function}(#{code_args.join(', ')})"
|
98
|
+
end
|
99
|
+
|
100
|
+
# The function that corresponds to the step
|
101
|
+
#
|
102
|
+
private def function
|
103
|
+
raise NotImplementedError, "Step class #{self.class} does not implement #function"
|
104
|
+
end
|
105
|
+
|
106
|
+
# Returns a hash of options formatted for function calls
|
107
|
+
#
|
108
|
+
private def format_options(body)
|
109
|
+
body.slice(*self.class.option_keys).transform_keys { |key| "_#{key}" }
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.validate(body, step_number)
|
113
|
+
validate_step_keys(body, step_number)
|
114
|
+
|
115
|
+
begin
|
116
|
+
body.each { |k, v| validate_puppet_code(k, v) }
|
117
|
+
rescue Bolt::Error => e
|
118
|
+
raise StepError.new(e.msg, body['name'], step_number)
|
119
|
+
end
|
120
|
+
|
121
|
+
if body.key?('parameters')
|
122
|
+
unless body['parameters'].is_a?(Hash)
|
123
|
+
raise StepError.new("Parameters key must be a hash", body['name'], step_number)
|
124
|
+
end
|
125
|
+
|
126
|
+
metaparams = body['parameters'].keys
|
127
|
+
.select { |key| key.start_with?('_') }
|
128
|
+
.map { |key| key.sub(/^_/, '') }
|
129
|
+
|
130
|
+
if (dups = body.keys & metaparams).any?
|
131
|
+
raise StepError.new(
|
132
|
+
"Cannot specify metaparameters when using top-level keys with same name: #{dups.join(', ')}",
|
133
|
+
body['name'],
|
134
|
+
step_number
|
135
|
+
)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
unless body.fetch('parameters', {}).is_a?(Hash)
|
140
|
+
msg = "Parameters key must be a hash"
|
141
|
+
raise StepError.new(msg, body['name'], step_number)
|
142
|
+
end
|
143
|
+
|
144
|
+
if body.key?('name')
|
145
|
+
name = body['name']
|
146
|
+
unless name.is_a?(String) && name.match?(Bolt::PAL::YamlPlan::VAR_NAME_PATTERN)
|
147
|
+
error_message = "Invalid step name: #{name.inspect}"
|
148
|
+
raise StepError.new(error_message, body['name'], step_number)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.validate_step_keys(body, step_number)
|
154
|
+
step_type = name.split('::').last.downcase
|
155
|
+
|
156
|
+
# For validated step action, ensure only valid keys
|
157
|
+
illegal_keys = body.keys.to_set - allowed_keys
|
158
|
+
if illegal_keys.any?
|
159
|
+
error_message = "The #{step_type.inspect} step does not support: #{illegal_keys.to_a.inspect} key(s)"
|
160
|
+
raise StepError.new(error_message, body['name'], step_number)
|
161
|
+
end
|
162
|
+
|
163
|
+
# Ensure all required keys are present
|
164
|
+
missing_keys = required_keys - body.keys
|
165
|
+
|
166
|
+
if missing_keys.any?
|
167
|
+
error_message = "The #{step_type.inspect} step requires: #{missing_keys.to_a.inspect} key(s)"
|
168
|
+
raise StepError.new(error_message, body['name'], step_number)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Recursively ensure all puppet code can be parsed
|
173
|
+
def self.validate_puppet_code(step_key, value)
|
174
|
+
case value
|
175
|
+
when Array
|
176
|
+
value.map { |element| validate_puppet_code(step_key, element) }
|
177
|
+
when Hash
|
178
|
+
value.each_with_object({}) do |(k, v), o|
|
179
|
+
key = k.is_a?(Bolt::PAL::YamlPlan::EvaluableString) ? k.value : k
|
180
|
+
o[key] = validate_puppet_code(key, v)
|
181
|
+
end
|
182
|
+
# CodeLiterals can be parsed directly
|
183
|
+
when Bolt::PAL::YamlPlan::CodeLiteral
|
184
|
+
parse_code_string(value.value)
|
185
|
+
# BareString is parsed directly if it starts with '$'
|
186
|
+
when Bolt::PAL::YamlPlan::BareString
|
187
|
+
if value.value.start_with?('$')
|
188
|
+
parse_code_string(value.value)
|
189
|
+
else
|
190
|
+
parse_code_string(value.value, true)
|
191
|
+
end
|
192
|
+
when Bolt::PAL::YamlPlan::EvaluableString
|
193
|
+
# Must quote parsed strings to evaluate them
|
194
|
+
parse_code_string(value.value, true)
|
195
|
+
end
|
196
|
+
rescue Puppet::Error => e
|
197
|
+
raise Bolt::Error.new("Error parsing #{step_key.inspect}: #{e.basic_message}", "bolt/invalid-plan")
|
198
|
+
end
|
199
|
+
|
200
|
+
# Parses the an evaluable string, optionally quote it before parsing
|
201
|
+
def self.parse_code_string(code, quote = false)
|
202
|
+
if quote
|
203
|
+
quoted = Puppet::Pops::Parser::EvaluatingParser.quote(code)
|
204
|
+
Puppet::Pops::Parser::EvaluatingParser.new.parse_string(quoted)
|
205
|
+
else
|
206
|
+
Puppet::Pops::Parser::EvaluatingParser.new.parse_string(code)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
require_relative 'step/command'
|
215
|
+
require_relative 'step/eval'
|
216
|
+
require_relative 'step/plan'
|
217
|
+
require_relative 'step/resources'
|
218
|
+
require_relative 'step/script'
|
219
|
+
require_relative 'step/task'
|
220
|
+
require_relative 'step/upload'
|
221
|
+
require_relative 'step/download'
|
222
|
+
require_relative 'step/message'
|
223
|
+
require_relative 'step/verbose'
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../../bolt/error'
|
4
|
+
require_relative '../../../bolt/pal/yaml_plan/loader'
|
5
|
+
require_relative '../../../bolt/util'
|
6
|
+
|
7
|
+
module Bolt
|
8
|
+
class PAL
|
9
|
+
class YamlPlan
|
10
|
+
class Transpiler
|
11
|
+
class ConvertError < Bolt::Error
|
12
|
+
def initialize(msg, plan_path)
|
13
|
+
super(msg, 'bolt/convert-error', { "plan_path" => plan_path })
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def transpile(plan_path)
|
18
|
+
@plan_path = plan_path
|
19
|
+
@modulename = Bolt::Util.module_name(@plan_path)
|
20
|
+
@filename = @plan_path.split(File::SEPARATOR)[-1]
|
21
|
+
validate_path
|
22
|
+
|
23
|
+
plan_object = parse_plan
|
24
|
+
param_descriptions = plan_object.parameters.map do |param|
|
25
|
+
str = String.new("# @param #{param.name}")
|
26
|
+
str << " #{param.description}" if param.description
|
27
|
+
str
|
28
|
+
end.join("\n")
|
29
|
+
|
30
|
+
plan_string = String.new('')
|
31
|
+
plan_string << "# #{plan_object.description}\n" if plan_object.description
|
32
|
+
plan_string << "# WARNING: This is an autogenerated plan. It might not behave as expected.\n"
|
33
|
+
plan_string << "# @api #{plan_object.private ? 'private' : 'public'}\n" unless plan_object.private.nil?
|
34
|
+
plan_string << "#{param_descriptions}\n" unless param_descriptions.empty?
|
35
|
+
|
36
|
+
plan_string << "plan #{plan_object.name}("
|
37
|
+
# Parameters are Bolt::PAL::YamlPlan::Parameter
|
38
|
+
plan_object.parameters&.each_with_index do |param, i|
|
39
|
+
plan_string << param.transpile
|
40
|
+
|
41
|
+
# If it's the last parameter add a newline and no comma
|
42
|
+
last = i + 1 == plan_object.parameters.length ? "\n" : ","
|
43
|
+
# This encodes strangely if we << directly to plan_string
|
44
|
+
plan_string << last
|
45
|
+
end
|
46
|
+
plan_string << ") {\n"
|
47
|
+
|
48
|
+
plan_object.steps&.each do |step|
|
49
|
+
plan_string << step.transpile
|
50
|
+
end
|
51
|
+
|
52
|
+
plan_string << "\n return #{Bolt::Util.to_code(plan_object.return)}\n" if plan_object.return
|
53
|
+
plan_string << "}"
|
54
|
+
# We always print the plan, even if there's an error
|
55
|
+
puts plan_string
|
56
|
+
validate_plan(plan_string)
|
57
|
+
plan_string
|
58
|
+
end
|
59
|
+
|
60
|
+
def parse_plan
|
61
|
+
begin
|
62
|
+
file_contents = File.read(@plan_path)
|
63
|
+
rescue Errno::ENOENT
|
64
|
+
msg = "Could not read yaml plan file: #{@plan_path}"
|
65
|
+
raise Bolt::FileError.new(msg, @plan_path)
|
66
|
+
end
|
67
|
+
|
68
|
+
begin
|
69
|
+
Bolt::PAL::YamlPlan::Loader.from_string(@modulename, file_contents, @plan_path)
|
70
|
+
rescue Puppet::PreformattedError, StandardError => e
|
71
|
+
raise PALError.from_preformatted_error(e)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def validate_path
|
76
|
+
unless File.extname(@filename) == ".yaml"
|
77
|
+
raise ConvertError.new("You can only convert plans written in yaml", @plan_path)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def validate_plan(plan)
|
82
|
+
Puppet::Pops::Parser::EvaluatingParser.new.parse_string(plan)
|
83
|
+
rescue Puppet::Error => e
|
84
|
+
$stderr.puts "The converted puppet plan contains invalid puppet code: #{e.message}"
|
85
|
+
exit 1
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'yaml_plan/parameter'
|
4
|
+
require_relative 'yaml_plan/step'
|
5
|
+
|
6
|
+
module Bolt
|
7
|
+
class PAL
|
8
|
+
class YamlPlan
|
9
|
+
PLAN_KEYS = Set['parameters', 'private', 'steps', 'return', 'version', 'description']
|
10
|
+
VAR_NAME_PATTERN = /\A[a-z_][a-z0-9_]*\z/.freeze
|
11
|
+
|
12
|
+
attr_reader :name, :parameters, :private, :steps, :return, :description
|
13
|
+
|
14
|
+
def initialize(name, plan)
|
15
|
+
# Top-level plan keys aren't allowed to be Puppet code, so force them
|
16
|
+
# all to strings.
|
17
|
+
plan = Bolt::Util.walk_keys(plan) { |key| stringify(key) }
|
18
|
+
@name = name.freeze
|
19
|
+
@description = stringify(plan['description']) if plan['description']
|
20
|
+
|
21
|
+
params_hash = stringify(plan.fetch('parameters', {}))
|
22
|
+
# Ensure params is a hash
|
23
|
+
unless params_hash.is_a?(Hash)
|
24
|
+
raise Bolt::Error.new("Plan parameters must be a Hash", "bolt/invalid-plan")
|
25
|
+
end
|
26
|
+
|
27
|
+
# Munge parameters into an array of Parameter objects, which is what
|
28
|
+
# the Puppet API expects
|
29
|
+
@parameters = params_hash.map do |param, definition|
|
30
|
+
Parameter.new(param, definition)
|
31
|
+
end.freeze
|
32
|
+
|
33
|
+
@private = plan['private']
|
34
|
+
unless @private.nil? || @private.is_a?(TrueClass) || @private.is_a?(FalseClass)
|
35
|
+
msg = "Plan #{@name} key 'private' must be a boolean, received: #{@private.inspect}"
|
36
|
+
raise Bolt::Error.new(msg, "bolt/invalid-plan")
|
37
|
+
end
|
38
|
+
|
39
|
+
# Validate top level plan keys
|
40
|
+
top_level_keys = plan.keys.to_set
|
41
|
+
unless PLAN_KEYS.superset?(top_level_keys)
|
42
|
+
invalid_keys = top_level_keys - PLAN_KEYS
|
43
|
+
raise Bolt::Error.new("Plan contains illegal key(s) #{invalid_keys.to_a.inspect}",
|
44
|
+
"bolt/invalid-plan")
|
45
|
+
end
|
46
|
+
|
47
|
+
unless plan['steps'].is_a?(Array)
|
48
|
+
raise Bolt::Error.new("Plan must specify an array of steps", "bolt/invalid-plan")
|
49
|
+
end
|
50
|
+
|
51
|
+
used_names = Set.new(@parameters.map(&:name))
|
52
|
+
|
53
|
+
@steps = plan['steps'].each_with_index.map do |step, index|
|
54
|
+
unless step.is_a?(Hash)
|
55
|
+
raise Bolt::Error.new(
|
56
|
+
"Parse error in step number #{index + 1}: Plan step must be an object with valid step keys.",
|
57
|
+
'bolt/invalid-plan'
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Step keys also aren't allowed to be code and neither is the value of "name"
|
62
|
+
stringified_step = Bolt::Util.walk_keys(step) { |key| stringify(key) }
|
63
|
+
stringified_step['name'] = stringify(stringified_step['name']) if stringified_step.key?('name')
|
64
|
+
|
65
|
+
step = Step.create(stringified_step, index + 1)
|
66
|
+
duplicate_check(used_names, stringified_step['name'], index + 1)
|
67
|
+
used_names << stringified_step['name'] if stringified_step['name']
|
68
|
+
step
|
69
|
+
end.freeze
|
70
|
+
@return = plan['return']
|
71
|
+
end
|
72
|
+
|
73
|
+
def duplicate_check(used_names, name, step_number)
|
74
|
+
if used_names.include?(name)
|
75
|
+
error_message = "Duplicate step name or parameter detected: #{name.inspect}"
|
76
|
+
raise Step::StepError.new(error_message, name, step_number)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def body
|
81
|
+
self
|
82
|
+
end
|
83
|
+
|
84
|
+
def return_type
|
85
|
+
Puppet::Pops::Types::TypeParser.singleton.parse('Boltlib::PlanResult')
|
86
|
+
end
|
87
|
+
|
88
|
+
# Turn all "potential" strings in the object into actual strings.
|
89
|
+
# Because we interpret bare strings as potential Puppet code, even in
|
90
|
+
# places where Puppet code isn't allowed (like some hash keys), we need
|
91
|
+
# to be able to force them back into regular strings, as if we had
|
92
|
+
# parsed them normally.
|
93
|
+
def stringify(value)
|
94
|
+
case value
|
95
|
+
when Array
|
96
|
+
value.map { |element| stringify(element) }
|
97
|
+
when Hash
|
98
|
+
value.each_with_object({}) do |(k, v), o|
|
99
|
+
o[stringify(k)] = stringify(v)
|
100
|
+
end
|
101
|
+
when EvaluableString
|
102
|
+
value.value
|
103
|
+
else
|
104
|
+
value
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# This class wraps a value parsed from YAML which may be Puppet code.
|
109
|
+
# That includes double-quoted strings and string literals, each of which
|
110
|
+
# subclasses this parent class in order to implement its own evaluation
|
111
|
+
# logic.
|
112
|
+
class EvaluableString
|
113
|
+
attr_reader :file, :line, :value
|
114
|
+
|
115
|
+
def initialize(value, file = nil, line = nil)
|
116
|
+
@value = value
|
117
|
+
@file = file
|
118
|
+
@line = line
|
119
|
+
end
|
120
|
+
|
121
|
+
def ==(other)
|
122
|
+
self.class == other.class && @value == other.value
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# This class represents a double-quoted YAML string, which is interpreted
|
127
|
+
# as though it were a double-quoted Puppet string (with associated
|
128
|
+
# variable interpolations)
|
129
|
+
class DoubleQuotedString < EvaluableString
|
130
|
+
def evaluate(scope, evaluator)
|
131
|
+
# "inspect" allows us to get back a double-quoted string literal with
|
132
|
+
# special characters escaped. This is based on the assumption that
|
133
|
+
# YAML, Ruby and Puppet all support similar escape sequences.
|
134
|
+
parse_result = evaluator.parse_string(@value.inspect)
|
135
|
+
|
136
|
+
scope.with_local_scope({}) do
|
137
|
+
evaluator.evaluate(scope, parse_result)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# This represents a literal snippet of Puppet code
|
143
|
+
class CodeLiteral < EvaluableString
|
144
|
+
def evaluate(scope, evaluator)
|
145
|
+
parse_result = evaluator.parse_string(@value)
|
146
|
+
|
147
|
+
scope.with_local_scope({}) do
|
148
|
+
evaluator.evaluate(scope, parse_result)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# This class stores a bare YAML string, which is fuzzily interpreted as
|
154
|
+
# either Puppet code or a literal string, depending on whether it starts
|
155
|
+
# with a variable reference.
|
156
|
+
class BareString < EvaluableString
|
157
|
+
def evaluate(scope, evaluator)
|
158
|
+
if @value.start_with?('$')
|
159
|
+
# Try to parse the string as Puppet code. If it's invalid code,
|
160
|
+
# return the original string.
|
161
|
+
parse_result = evaluator.parse_string(@value)
|
162
|
+
scope.with_local_scope({}) do
|
163
|
+
evaluator.evaluate(scope, parse_result)
|
164
|
+
end
|
165
|
+
else
|
166
|
+
@value
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|