bolt 1.31.1 → 1.32.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bolt might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/bolt-modules/boltlib/lib/puppet/datatypes/target.rb +49 -16
- data/bolt-modules/boltlib/lib/puppet/functions/get_resources.rb +9 -2
- data/bolt-modules/boltlib/lib/puppet/functions/get_target.rb +36 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +42 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_config.rb +46 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_feature.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_var.rb +6 -2
- data/lib/bolt/analytics.rb +1 -1
- data/lib/bolt/apply_result.rb +23 -0
- data/lib/bolt/bolt_option_parser.rb +16 -0
- data/lib/bolt/boltdir.rb +3 -1
- data/lib/bolt/cli.rb +20 -6
- data/lib/bolt/config.rb +1 -3
- data/lib/bolt/inventory/group2.rb +40 -34
- data/lib/bolt/inventory/inventory2.rb +203 -134
- data/lib/bolt/inventory.rb +2 -3
- data/lib/bolt/pal/issues.rb +6 -0
- data/lib/bolt/pal.rb +29 -1
- data/lib/bolt/plugin.rb +2 -2
- data/lib/bolt/secret/base.rb +3 -1
- data/lib/bolt/target.rb +147 -0
- data/lib/bolt/task.rb +1 -1
- data/lib/bolt/transport/powershell.rb +12 -5
- data/lib/bolt/transport/winrm.rb +1 -2
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/schemas/action-run_script.json +4 -1
- data/lib/bolt_spec/plans/mock_executor.rb +17 -0
- data/lib/bolt_spec/plans/publish_stub.rb +49 -0
- data/lib/bolt_spec/plans.rb +18 -1
- data/lib/bolt_spec/run.rb +5 -2
- data/lib/plan_executor/app.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40fdd97c4150459a56f711d9ece1e1c0b57ff19c4e64ddb88459ea947bf4bbab
|
4
|
+
data.tar.gz: d2079c900a66e50d0bd9cccd17da37ab965686c6b6c9402b262f148ba2549c44
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bd59adfaa8bb6178de571eef84a08f124666ca94abb9a2a0550127ff105dc7c55e47ba789f9d09c1eae33a517dd4baaf95fce21543dc77bdedfb35c9472ffe88
|
7
|
+
data.tar.gz: 7f80fcb3d13a832338ab4da986d2ac25bdfbf67785f3fd863692cbd4eb018a30e7a687020c8d7c551ec1f58da9abefadbcd3976820c3d83a3c1cdfd9efb79f47
|
@@ -1,22 +1,55 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
Puppet::DataTypes.create_type('Target') do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
port => Callable[[], Optional[Integer]],
|
14
|
-
protocol => Callable[[], Optional[String[1]]],
|
15
|
-
user => Callable[[], Optional[String[1]]],
|
16
|
-
}
|
17
|
-
PUPPET
|
18
|
-
|
4
|
+
begin
|
5
|
+
inventory = Puppet.lookup(:bolt_inventory)
|
6
|
+
inventory_version = inventory.version
|
7
|
+
if inventory_version != 1
|
8
|
+
target_implementation_class = inventory.target_implementation_class
|
9
|
+
end
|
10
|
+
rescue Puppet::Context::UndefinedBindingError
|
11
|
+
inventory_version = 1
|
12
|
+
end
|
19
13
|
load_file('bolt/target')
|
20
14
|
|
21
|
-
|
15
|
+
if inventory_version == 1
|
16
|
+
interface <<-PUPPET
|
17
|
+
attributes => {
|
18
|
+
uri => String[1],
|
19
|
+
options => { type => Hash[String[1], Data], value => {} }
|
20
|
+
},
|
21
|
+
functions => {
|
22
|
+
name => Callable[[], String[1]],
|
23
|
+
host => Callable[[], Optional[String]],
|
24
|
+
password => Callable[[], Optional[String[1]]],
|
25
|
+
port => Callable[[], Optional[Integer]],
|
26
|
+
protocol => Callable[[], Optional[String[1]]],
|
27
|
+
user => Callable[[], Optional[String[1]]],
|
28
|
+
}
|
29
|
+
PUPPET
|
30
|
+
implementation_class Bolt::Target
|
31
|
+
else
|
32
|
+
interface <<-PUPPET
|
33
|
+
attributes => {
|
34
|
+
uri => { type => Optional[String[1]], kind => given_or_derived },
|
35
|
+
name => { type => Optional[String[1]] , kind => given_or_derived },
|
36
|
+
target_alias => { type => Optional[Variant[String[1], Array[String[1]]]], kind => given_or_derived },
|
37
|
+
config => { type => Optional[Hash[String[1], Data]], kind => given_or_derived },
|
38
|
+
vars => { type => Optional[Hash[String[1], Data]], kind => given_or_derived },
|
39
|
+
facts => { type => Optional[Hash[String[1], Data]], kind => given_or_derived },
|
40
|
+
features => { type => Optional[Array[String[1]]], kind => given_or_derived },
|
41
|
+
plugin_hooks => { type => Optional[Hash[String[1], Data]], kind => given_or_derived }
|
42
|
+
},
|
43
|
+
functions => {
|
44
|
+
safe_name => Callable[[], String[1]],
|
45
|
+
host => Callable[[], Optional[String]],
|
46
|
+
password => Callable[[], Optional[String[1]]],
|
47
|
+
port => Callable[[], Optional[Integer]],
|
48
|
+
protocol => Callable[[], Optional[String[1]]],
|
49
|
+
user => Callable[[], Optional[String[1]]],
|
50
|
+
}
|
51
|
+
PUPPET
|
52
|
+
|
53
|
+
implementation_class target_implementation_class
|
54
|
+
end
|
22
55
|
end
|
@@ -6,7 +6,10 @@ require 'bolt/task'
|
|
6
6
|
# The results are returned as a list of hashes representing each resource.
|
7
7
|
#
|
8
8
|
# Requires the Puppet Agent be installed on the target, which can be accomplished with apply_prep
|
9
|
-
# or by directly running the puppet_agent::install task.
|
9
|
+
# or by directly running the puppet_agent::install task. In order to be able to reference types without
|
10
|
+
# string quoting (for example `get_resources($target, Package)` instead of `get_resources($target, 'Package')`)
|
11
|
+
# run the command `bolt puppetfile generate-types` to generate type references in `$Boldir/.resource_types`.
|
12
|
+
#
|
10
13
|
#
|
11
14
|
# **NOTE:** Not available in apply block
|
12
15
|
Puppet::Functions.create_function(:get_resources) do
|
@@ -16,7 +19,7 @@ Puppet::Functions.create_function(:get_resources) do
|
|
16
19
|
# get_resources('target1,target2', [Package, File[/etc/puppetlabs]])
|
17
20
|
dispatch :get_resources do
|
18
21
|
param 'Boltlib::TargetSpec', :targets
|
19
|
-
param 'Variant[String, Resource, Array[Variant[String, Resource]]]', :resources
|
22
|
+
param 'Variant[String, Type[Resource], Array[Variant[String, Type[Resource]]]]', :resources
|
20
23
|
end
|
21
24
|
|
22
25
|
def script_compiler
|
@@ -44,6 +47,10 @@ Puppet::Functions.create_function(:get_resources) do
|
|
44
47
|
inventory = Puppet.lookup(:bolt_inventory)
|
45
48
|
|
46
49
|
resources = [resources].flatten
|
50
|
+
|
51
|
+
# Stringify resource types to pass to task
|
52
|
+
resources.map! { |r| r.is_a?(String) ? r : r.to_s }
|
53
|
+
|
47
54
|
resources.each do |resource|
|
48
55
|
if resource !~ /^\w+$/ && resource !~ /^\w+\[.+\]$/
|
49
56
|
raise Bolt::Error.new("#{resource} is not a valid resource type or type instance name", 'bolt/get-resources')
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/error'
|
4
|
+
|
5
|
+
# Get a single target from inventory if it exists, otherwise create a new Target.
|
6
|
+
#
|
7
|
+
# **NOTE:** Calling `get_target` inside an `apply` block with a
|
8
|
+
# version 2 inventory creates a new Target object.
|
9
|
+
# `get_target('all')` returns an empty array.
|
10
|
+
# **NOTE:** Only compatible with inventory v2
|
11
|
+
Puppet::Functions.create_function(:get_target) do
|
12
|
+
# @param name A Target name.
|
13
|
+
# @return A single target, either new or from inventory.
|
14
|
+
# @example Create a new Target from a URI
|
15
|
+
# get_target('winrm://host2:54321')
|
16
|
+
# @example Get an existing Target from inventory
|
17
|
+
# get_target('existing-target')
|
18
|
+
dispatch :get_target do
|
19
|
+
param 'Boltlib::TargetSpec', :name
|
20
|
+
return_type 'Target'
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_target(name)
|
24
|
+
inventory = Puppet.lookup(:bolt_inventory)
|
25
|
+
# Bolt executor not expected when invoked from apply block
|
26
|
+
executor = Puppet.lookup(:bolt_executor) { nil }
|
27
|
+
executor&.report_function_call(self.class.name)
|
28
|
+
|
29
|
+
unless inventory.version > 1
|
30
|
+
raise Puppet::ParseErrorWithIssue
|
31
|
+
.from_issue_and_stack(Bolt::PAL::Issues::UNSUPPORTED_INVENTORY_VERSION, action: 'get_target')
|
32
|
+
end
|
33
|
+
|
34
|
+
inventory.get_target(name)
|
35
|
+
end
|
36
|
+
end
|
@@ -83,6 +83,22 @@ Puppet::Functions.create_function(:run_plan, Puppet::Functions::InternalFunction
|
|
83
83
|
executor.report_yaml_plan(closure.model.body)
|
84
84
|
end
|
85
85
|
|
86
|
+
# If a TargetSpec parameter is passed, ensure it is in inventory
|
87
|
+
inventory = Puppet.lookup(:bolt_inventory)
|
88
|
+
|
89
|
+
if inventory.version > 1
|
90
|
+
param_types = closure.parameters.each_with_object({}) do |param, param_acc|
|
91
|
+
param_acc[param.name] = extract_parameter_types(param.type_expr).flatten
|
92
|
+
end
|
93
|
+
params.each do |param, value|
|
94
|
+
# Note the safe lookup operator is needed to handle case where a parameter is passed to a
|
95
|
+
# plan that the plan is not expecting
|
96
|
+
if param_types[param]&.include?('TargetSpec') || param_types[param]&.include?('Boltlib::TargetSpec')
|
97
|
+
inventory.get_targets(value)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
86
102
|
# wrap plan execution in logging messages
|
87
103
|
executor.log_plan(plan_name) do
|
88
104
|
result = nil
|
@@ -116,4 +132,30 @@ Puppet::Functions.create_function(:run_plan, Puppet::Functions::InternalFunction
|
|
116
132
|
result
|
117
133
|
end
|
118
134
|
end
|
135
|
+
|
136
|
+
# Recursively examine the type_expr to build a list of types
|
137
|
+
def extract_parameter_types(type_expr)
|
138
|
+
# No type
|
139
|
+
if type_expr.nil?
|
140
|
+
[]
|
141
|
+
# Multiple types to extract (ex. Variant[TargetSpec, String])
|
142
|
+
elsif defined?(type_expr.keys)
|
143
|
+
type_expr.keys.flat_map { |param| extract_parameter_types(param) }
|
144
|
+
# Store cased value
|
145
|
+
elsif defined?(type_expr.cased_value)
|
146
|
+
[type_expr.cased_value]
|
147
|
+
# Type alias, able to resolve alias
|
148
|
+
elsif defined?(type_expr.resolved_type.name)
|
149
|
+
[type_expr.resolved_type.name]
|
150
|
+
# Nested type alias, recurse
|
151
|
+
elsif defined?(type_expr.type)
|
152
|
+
extract_parameter_types(type_expr.type)
|
153
|
+
# Array conatins alias types
|
154
|
+
elsif defined?(type_expr.types)
|
155
|
+
type_expr.types.flat_map { |param| extract_parameter_types(param) }
|
156
|
+
# Each element can be handled by a resolver above
|
157
|
+
elsif defined?(type_expr.element_type)
|
158
|
+
extract_parameter_types(type_expr.element_type)
|
159
|
+
end
|
160
|
+
end
|
119
161
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/error'
|
4
|
+
|
5
|
+
# Set configuration options on a target
|
6
|
+
#
|
7
|
+
# **NOTE:** Not available in apply block
|
8
|
+
# **NOTE:** Only compatible with inventory v2
|
9
|
+
Puppet::Functions.create_function(:set_config) do
|
10
|
+
# @param target The Target object to configure. See {get_targets}.
|
11
|
+
# @param key_or_key_path The configuration setting to update.
|
12
|
+
# @param value The configuration value
|
13
|
+
# @return The Target with the updated config
|
14
|
+
# @example Set the transport for a target
|
15
|
+
# set_config($target, 'transport', 'ssh')
|
16
|
+
# @example Set the ssh password
|
17
|
+
# set_config($target, ['ssh', 'password'], 'secret')
|
18
|
+
# @example Overwrite ssh config
|
19
|
+
# set_config($target, 'ssh', { user => 'me', password => 'secret' })
|
20
|
+
dispatch :set_config do
|
21
|
+
param 'Target', :target
|
22
|
+
param 'Variant[String, Array[String]]', :key_or_key_path
|
23
|
+
param 'Any', :value
|
24
|
+
return_type 'Target'
|
25
|
+
end
|
26
|
+
|
27
|
+
def set_config(target, key_or_key_path, value = true)
|
28
|
+
unless Puppet[:tasks]
|
29
|
+
raise Puppet::ParseErrorWithIssue
|
30
|
+
.from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, action: 'set_config')
|
31
|
+
end
|
32
|
+
|
33
|
+
inventory = Puppet.lookup(:bolt_inventory)
|
34
|
+
executor = Puppet.lookup(:bolt_executor)
|
35
|
+
executor.report_function_call(self.class.name)
|
36
|
+
|
37
|
+
unless inventory.version > 1
|
38
|
+
raise Puppet::ParseErrorWithIssue
|
39
|
+
.from_issue_and_stack(Bolt::PAL::Issues::UNSUPPORTED_INVENTORY_VERSION, action: 'set_config')
|
40
|
+
end
|
41
|
+
|
42
|
+
inventory.set_config(target, key_or_key_path, value)
|
43
|
+
|
44
|
+
target
|
45
|
+
end
|
46
|
+
end
|
@@ -9,13 +9,14 @@ Puppet::Functions.create_function(:set_var) do
|
|
9
9
|
# @param target The Target object to set the variable for. See {get_targets}.
|
10
10
|
# @param key The key for the variable.
|
11
11
|
# @param value The value of the variable.
|
12
|
-
# @return
|
12
|
+
# @return The target with the updated feature
|
13
13
|
# @example Set a variable on a target
|
14
14
|
# $target.set_var('ephemeral', true)
|
15
15
|
dispatch :set_var do
|
16
16
|
param 'Target', :target
|
17
17
|
param 'String', :key
|
18
18
|
param 'Data', :value
|
19
|
+
return_type 'Target'
|
19
20
|
end
|
20
21
|
|
21
22
|
def set_var(target, key, value)
|
@@ -28,6 +29,9 @@ Puppet::Functions.create_function(:set_var) do
|
|
28
29
|
executor = Puppet.lookup(:bolt_executor)
|
29
30
|
executor.report_function_call(self.class.name)
|
30
31
|
|
31
|
-
|
32
|
+
var_hash = { key => value }
|
33
|
+
inventory.set_var(target, var_hash)
|
34
|
+
|
35
|
+
target
|
32
36
|
end
|
33
37
|
end
|
data/lib/bolt/analytics.rb
CHANGED
data/lib/bolt/apply_result.rb
CHANGED
@@ -46,6 +46,25 @@ module Bolt
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
+
def self.invalid_report_error(result)
|
50
|
+
# These are the keys ApplyResult methods rely on.
|
51
|
+
expected_report_keys = %w[metrics resource_statuses status]
|
52
|
+
missing_keys = expected_report_keys.reject { |k| result.value.include?(k) }
|
53
|
+
|
54
|
+
unless missing_keys.empty?
|
55
|
+
if result['_output']
|
56
|
+
# rubocop:disable Metrics/LineLength
|
57
|
+
msg = "Report result contains an '_output' key. Catalog application may have printed extraneous output to stdout: #{result['_output']}"
|
58
|
+
# rubocop:enable Metrics/LineLength
|
59
|
+
else
|
60
|
+
msg = "Report did not contain all expected keys missing: #{missing_keys.join(' ,')}"
|
61
|
+
end
|
62
|
+
|
63
|
+
{ 'msg' => msg,
|
64
|
+
'kind' => 'bolt/invalid-report' }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
49
68
|
def self.from_task_result(result)
|
50
69
|
if (puppet_missing = puppet_missing_error(result))
|
51
70
|
new(result.target,
|
@@ -53,6 +72,10 @@ module Bolt
|
|
53
72
|
report: result.value.reject { |k| k == '_error' })
|
54
73
|
elsif !result.ok?
|
55
74
|
new(result.target, error: result.error_hash)
|
75
|
+
elsif (invalid_report = invalid_report_error(result))
|
76
|
+
new(result.target,
|
77
|
+
error: invalid_report,
|
78
|
+
report: result.value.reject { |k| %w[_error _output].include?(k) })
|
56
79
|
elsif (resource_error = resource_error(result))
|
57
80
|
new(result.target,
|
58
81
|
error: resource_error,
|
@@ -54,6 +54,9 @@ module Bolt
|
|
54
54
|
when 'show-modules'
|
55
55
|
{ flags: OPTIONS[:global] + OPTIONS[:global_config_setters],
|
56
56
|
banner: PUPPETFILE_SHOWMODULES_HELP }
|
57
|
+
when 'generate-types'
|
58
|
+
{ flags: OPTIONS[:global] + OPTIONS[:global_config_setters],
|
59
|
+
banner: PUPPETFILE_GENERATETYPES_HELP }
|
57
60
|
else
|
58
61
|
{ flags: OPTIONS[:global] + OPTIONS[:global_config_setters],
|
59
62
|
banner: PUPPETFILE_HELP }
|
@@ -229,6 +232,7 @@ module Bolt
|
|
229
232
|
Available actions are:
|
230
233
|
install Install modules from a Puppetfile into a Boltdir
|
231
234
|
show-modules List modules available to Bolt
|
235
|
+
generate-types Generate type references to register in Plans
|
232
236
|
|
233
237
|
Install modules into the local Boltdir
|
234
238
|
bolt puppetfile install
|
@@ -248,6 +252,18 @@ module Bolt
|
|
248
252
|
PUPPETFILE_SHOWMODULES_HELP = <<~HELP
|
249
253
|
Usage: bolt puppetfile show-modules
|
250
254
|
|
255
|
+
List modules available to Bolt
|
256
|
+
bolt puppetfile show-modules
|
257
|
+
|
258
|
+
Available options are:
|
259
|
+
HELP
|
260
|
+
|
261
|
+
PUPPETFILE_GENERATETYPES_HELP = <<~HELP
|
262
|
+
Usage: bolt puppetfile generate-types
|
263
|
+
|
264
|
+
Generate type references to register in Plans
|
265
|
+
bolt puppetfile generate-types
|
266
|
+
|
251
267
|
Available options are:
|
252
268
|
HELP
|
253
269
|
|
data/lib/bolt/boltdir.rb
CHANGED
@@ -6,7 +6,8 @@ module Bolt
|
|
6
6
|
class Boltdir
|
7
7
|
BOLTDIR_NAME = 'Boltdir'
|
8
8
|
|
9
|
-
attr_reader :path, :config_file, :inventory_file, :modulepath, :hiera_config,
|
9
|
+
attr_reader :path, :config_file, :inventory_file, :modulepath, :hiera_config,
|
10
|
+
:puppetfile, :rerunfile, :type, :resource_types
|
10
11
|
|
11
12
|
def self.default_boltdir
|
12
13
|
Boltdir.new(File.join('~', '.puppetlabs', 'bolt'), 'user')
|
@@ -37,6 +38,7 @@ module Bolt
|
|
37
38
|
@hiera_config = @path + 'hiera.yaml'
|
38
39
|
@puppetfile = @path + 'Puppetfile'
|
39
40
|
@rerunfile = @path + '.rerun.json'
|
41
|
+
@resource_types = @path + '.resource_types'
|
40
42
|
@type = type
|
41
43
|
end
|
42
44
|
|
data/lib/bolt/cli.rb
CHANGED
@@ -33,7 +33,7 @@ module Bolt
|
|
33
33
|
'task' => %w[show run],
|
34
34
|
'plan' => %w[show run convert],
|
35
35
|
'file' => %w[upload],
|
36
|
-
'puppetfile' => %w[install show-modules],
|
36
|
+
'puppetfile' => %w[install show-modules generate-types],
|
37
37
|
'secret' => %w[encrypt decrypt createkeys],
|
38
38
|
'inventory' => %w[show],
|
39
39
|
'apply' => %w[] }.freeze
|
@@ -76,7 +76,6 @@ module Bolt
|
|
76
76
|
|
77
77
|
def parse
|
78
78
|
parser = BoltOptionParser.new(options)
|
79
|
-
|
80
79
|
# This part aims to handle both `bolt <mode> --help` and `bolt help <mode>`.
|
81
80
|
remaining = handle_parser_errors { parser.permute(@argv) } unless @argv.empty?
|
82
81
|
if @argv.empty? || help?(remaining)
|
@@ -320,7 +319,11 @@ module Bolt
|
|
320
319
|
when 'plan'
|
321
320
|
code = run_plan(options[:object], options[:task_options], options[:target_args], options)
|
322
321
|
when 'puppetfile'
|
323
|
-
|
322
|
+
if options[:action] == 'generate-types'
|
323
|
+
code = generate_types
|
324
|
+
elsif options[:action] == 'install'
|
325
|
+
code = install_puppetfile(@config.puppetfile_config, @config.puppetfile, @config.modulepath)
|
326
|
+
end
|
324
327
|
when 'secret'
|
325
328
|
code = Bolt::Secret.execute(plugins, outputter, options)
|
326
329
|
when 'apply'
|
@@ -474,6 +477,12 @@ module Bolt
|
|
474
477
|
outputter.print_module_list(pal.list_modules)
|
475
478
|
end
|
476
479
|
|
480
|
+
def generate_types
|
481
|
+
# generate_types will surface a nice error with helpful message if it fails
|
482
|
+
pal.generate_types
|
483
|
+
0
|
484
|
+
end
|
485
|
+
|
477
486
|
def install_puppetfile(config, puppetfile, modulepath)
|
478
487
|
require 'r10k/cli'
|
479
488
|
require 'bolt/r10k_log_proxy'
|
@@ -495,6 +504,8 @@ module Bolt
|
|
495
504
|
|
496
505
|
ok = install_action.call
|
497
506
|
outputter.print_puppetfile_result(ok, puppetfile, moduledir)
|
507
|
+
# Automatically generate types after installing modules
|
508
|
+
pal.generate_types
|
498
509
|
|
499
510
|
ok ? 0 : 1
|
500
511
|
else
|
@@ -505,7 +516,10 @@ module Bolt
|
|
505
516
|
end
|
506
517
|
|
507
518
|
def pal
|
508
|
-
@pal ||= Bolt::PAL.new(config.modulepath,
|
519
|
+
@pal ||= Bolt::PAL.new(config.modulepath,
|
520
|
+
config.hiera_config,
|
521
|
+
config.boltdir.resource_types,
|
522
|
+
config.compile_concurrency)
|
509
523
|
end
|
510
524
|
|
511
525
|
def convert_plan(plan)
|
@@ -544,9 +558,9 @@ module Bolt
|
|
544
558
|
# We only need to enumerate bundled content when running a task or plan
|
545
559
|
content = { 'Plan' => [],
|
546
560
|
'Task' => [],
|
547
|
-
'Plugin' =>
|
561
|
+
'Plugin' => Bolt::Plugin::BUILTIN_PLUGINS }
|
548
562
|
if %w[plan task].include?(options[:subcommand]) && options[:action] == 'run'
|
549
|
-
default_content = Bolt::PAL.new([], nil)
|
563
|
+
default_content = Bolt::PAL.new([], nil, nil)
|
550
564
|
content['Plan'] = default_content.list_plans.each_with_object([]) do |iter, col|
|
551
565
|
col << iter&.first
|
552
566
|
end
|
data/lib/bolt/config.rb
CHANGED
@@ -276,9 +276,7 @@ module Bolt
|
|
276
276
|
raise Bolt::ValidationError, "Unsupported format: '#{@format}'"
|
277
277
|
end
|
278
278
|
|
279
|
-
|
280
|
-
raise Bolt::FileError, "Could not read hiera-config file #{@hiera_config}", @hiera_config
|
281
|
-
end
|
279
|
+
Bolt::Util.validate_file('hiera-config', @hiera_config) if @hiera_config
|
282
280
|
|
283
281
|
unless @transport.nil? || Bolt::TRANSPORTS.include?(@transport.to_sym)
|
284
282
|
raise UnknownTransportError, @transport
|
@@ -1,19 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'bolt/inventory/group'
|
4
|
+
require 'bolt/inventory/inventory2'
|
4
5
|
|
5
6
|
module Bolt
|
6
7
|
class Inventory
|
7
8
|
class Group2
|
8
9
|
attr_accessor :name, :targets, :aliases, :name_or_alias, :groups,
|
9
|
-
:config, :
|
10
|
+
:config, :facts, :vars, :features, :plugin_hooks
|
10
11
|
|
11
12
|
# THESE are duplicates with the old groups for now.
|
12
13
|
# Regex used to validate group names and target aliases.
|
13
14
|
NAME_REGEX = /\A[a-z0-9_][a-z0-9_-]*\Z/.freeze
|
14
15
|
|
15
16
|
DATA_KEYS = %w[name config facts vars features plugin_hooks].freeze
|
16
|
-
|
17
|
+
TARGET_KEYS = DATA_KEYS + %w[alias uri]
|
17
18
|
GROUP_KEYS = DATA_KEYS + %w[groups targets]
|
18
19
|
CONFIG_KEYS = Bolt::TRANSPORTS.keys.map(&:to_s) + ['transport']
|
19
20
|
|
@@ -59,8 +60,10 @@ module Bolt
|
|
59
60
|
groups = fetch_value(data, 'groups', Array)
|
60
61
|
|
61
62
|
@targets = {}
|
63
|
+
# @target_objects = {}
|
62
64
|
@aliases = {}
|
63
65
|
@name_or_alias = []
|
66
|
+
|
64
67
|
targets.each do |target|
|
65
68
|
# If target is a string, it can refer to either a target name or
|
66
69
|
# alias. Which can't be determined until all groups have been
|
@@ -152,6 +155,7 @@ module Bolt
|
|
152
155
|
unless target.is_a?(Hash)
|
153
156
|
raise ValidationError.new("Node entry must be a Hash, not #{target.class}", @name)
|
154
157
|
end
|
158
|
+
|
155
159
|
# This check prevents plugins from returning plugins
|
156
160
|
raise ValidationError.new("Cannot set target with plugin", @name) if target.key?('_plugin')
|
157
161
|
target.each do |k, v|
|
@@ -159,56 +163,57 @@ module Bolt
|
|
159
163
|
validate_config_plugin(v, k, @name)
|
160
164
|
end
|
161
165
|
|
162
|
-
target['name']
|
166
|
+
t_name = target['name'] || target['uri']
|
163
167
|
|
164
|
-
if
|
168
|
+
if t_name.nil? || t_name.empty?
|
165
169
|
raise ValidationError.new("No name or uri for target: #{target}", @name)
|
166
170
|
end
|
167
171
|
|
168
|
-
|
172
|
+
unless t_name.ascii_only?
|
173
|
+
raise ValidationError.new("Target name must be ASCII characters: #{target}", @name)
|
174
|
+
end
|
175
|
+
|
176
|
+
if @targets.include?(t_name)
|
169
177
|
@logger.warn("Ignoring duplicate target in #{@name}: #{target}")
|
170
178
|
return
|
171
179
|
end
|
172
180
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
unless (unexpected_keys = target.keys - NODE_KEYS).empty?
|
177
|
-
msg = "Found unexpected key(s) #{unexpected_keys.join(', ')} in target #{target['name']}"
|
181
|
+
unless (unexpected_keys = target.keys - TARGET_KEYS).empty?
|
182
|
+
msg = "Found unexpected key(s) #{unexpected_keys.join(', ')} in target #{t_name}"
|
178
183
|
@logger.warn(msg)
|
179
184
|
end
|
180
185
|
|
181
186
|
unless target['config'].nil? || target['config'].is_a?(Hash)
|
182
|
-
raise ValidationError.new("Invalid configuration for target: #{
|
187
|
+
raise ValidationError.new("Invalid configuration for target: #{t_name}", @name)
|
183
188
|
end
|
184
189
|
|
185
190
|
config_keys = target['config']&.keys || []
|
186
191
|
unless (unexpected_keys = config_keys - CONFIG_KEYS).empty?
|
187
|
-
msg = "Found unexpected key(s) #{unexpected_keys.join(', ')} in config for target #{
|
192
|
+
msg = "Found unexpected key(s) #{unexpected_keys.join(', ')} in config for target #{t_name}"
|
188
193
|
@logger.warn(msg)
|
189
194
|
end
|
190
195
|
|
191
196
|
target['config'] = config_only_plugin(target['config'])
|
192
197
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
msg = "Alias entry on #{target['name']} must be a String or Array, not #{aliases.class}"
|
201
|
-
raise ValidationError.new(msg, @name)
|
202
|
-
end
|
198
|
+
if target.include?('alias')
|
199
|
+
aliases = target['alias']
|
200
|
+
aliases = [aliases] if aliases.is_a?(String)
|
201
|
+
unless aliases.is_a?(Array)
|
202
|
+
msg = "Alias entry on #{t_name} must be a String or Array, not #{aliases.class}"
|
203
|
+
raise ValidationError.new(msg, @name)
|
204
|
+
end
|
203
205
|
|
204
|
-
|
205
|
-
|
206
|
+
aliases.each do |alia|
|
207
|
+
raise ValidationError.new("Invalid alias #{alia}", @name) unless alia =~ NAME_REGEX
|
206
208
|
|
207
|
-
|
208
|
-
|
209
|
+
if (found = @aliases[alia])
|
210
|
+
raise ValidationError.new(alias_conflict(alia, found, t_name), @name)
|
211
|
+
end
|
212
|
+
@aliases[alia] = t_name
|
209
213
|
end
|
210
|
-
@aliases[alia] = target['name']
|
211
214
|
end
|
215
|
+
|
216
|
+
@targets[t_name] = target
|
212
217
|
end
|
213
218
|
|
214
219
|
def lookup_targets(lookup)
|
@@ -307,21 +312,22 @@ module Bolt
|
|
307
312
|
|
308
313
|
# Collect target names and aliases into a list used to validate that subgroups don't conflict.
|
309
314
|
# Used names validate that previously used group names don't conflict with new target names/aliases.
|
310
|
-
@targets.
|
315
|
+
@targets.each do |t_name, t_data|
|
311
316
|
# Require targets to be parseable as a Target.
|
312
317
|
begin
|
313
|
-
|
318
|
+
# Catch malformed URI here
|
319
|
+
Bolt::Inventory::Inventory2.parse_uri(t_data['uri'])
|
314
320
|
rescue Bolt::ParseError => e
|
315
321
|
@logger.debug(e)
|
316
|
-
raise ValidationError.new("Invalid target
|
322
|
+
raise ValidationError.new("Invalid target uri #{t_data['uri']}", @name)
|
317
323
|
end
|
318
324
|
|
319
|
-
raise ValidationError.new(group_target_conflict(
|
320
|
-
if aliased.include?(
|
321
|
-
raise ValidationError.new(alias_target_conflict(
|
325
|
+
raise ValidationError.new(group_target_conflict(t_name), @name) if used_names.include?(t_name)
|
326
|
+
if aliased.include?(t_name)
|
327
|
+
raise ValidationError.new(alias_target_conflict(t_name), @name)
|
322
328
|
end
|
323
329
|
|
324
|
-
target_names <<
|
330
|
+
target_names << t_name
|
325
331
|
end
|
326
332
|
|
327
333
|
@aliases.each do |n, target|
|