bolt 2.37.0 → 2.38.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/Puppetfile +1 -1
- data/lib/bolt/cli.rb +60 -23
- data/lib/bolt/config.rb +60 -18
- data/lib/bolt/config/options.rb +71 -77
- data/lib/bolt/config/transport/base.rb +2 -2
- data/lib/bolt/config/transport/local.rb +1 -0
- data/lib/bolt/config/transport/options.rb +9 -67
- data/lib/bolt/inventory.rb +25 -0
- data/lib/bolt/inventory/options.rb +130 -0
- data/lib/bolt/inventory/target.rb +10 -11
- data/lib/bolt/module_installer.rb +21 -13
- data/lib/bolt/module_installer/resolver.rb +1 -1
- data/lib/bolt/outputter.rb +19 -5
- data/lib/bolt/outputter/human.rb +20 -1
- data/lib/bolt/outputter/json.rb +1 -1
- data/lib/bolt/outputter/logger.rb +1 -1
- data/lib/bolt/outputter/rainbow.rb +12 -1
- data/lib/bolt/plugin/puppetdb.rb +1 -1
- data/lib/bolt/project.rb +20 -7
- data/lib/bolt/project_manager.rb +2 -0
- data/lib/bolt/project_manager/config_migrator.rb +9 -1
- data/lib/bolt/project_manager/module_migrator.rb +2 -0
- data/lib/bolt/puppetdb/client.rb +8 -0
- data/lib/bolt/shell/powershell.rb +5 -3
- data/lib/bolt/target.rb +4 -0
- data/lib/bolt/transport/local.rb +13 -0
- data/lib/bolt/validator.rb +226 -0
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/transport_app.rb +26 -26
- metadata +10 -3
- data/lib/bolt/config/validator.rb +0 -231
data/lib/bolt/version.rb
CHANGED
@@ -51,8 +51,8 @@ module BoltServer
|
|
51
51
|
# See the `orchestrator.bolt.codedir` tk config setting.
|
52
52
|
DEFAULT_BOLT_CODEDIR = '/opt/puppetlabs/server/data/orchestration-services/code'
|
53
53
|
|
54
|
-
|
55
|
-
400, Bolt::ValidationError.new('`
|
54
|
+
MISSING_VERSIONED_PROJECT_RESPONSE = [
|
55
|
+
400, Bolt::ValidationError.new('`versioned_project` is a required argument').to_json
|
56
56
|
].freeze
|
57
57
|
|
58
58
|
def initialize(config)
|
@@ -268,16 +268,16 @@ module BoltServer
|
|
268
268
|
end
|
269
269
|
end
|
270
270
|
|
271
|
-
def config_from_project(
|
272
|
-
project_dir = File.join(@config['projects-dir'],
|
273
|
-
raise Bolt::ValidationError, "`
|
271
|
+
def config_from_project(versioned_project)
|
272
|
+
project_dir = File.join(@config['projects-dir'], versioned_project)
|
273
|
+
raise Bolt::ValidationError, "`versioned_project`: #{project_dir} does not exist" unless Dir.exist?(project_dir)
|
274
274
|
project = Bolt::Project.create_project(project_dir)
|
275
275
|
Bolt::Config.from_project(project, { log: { 'bolt-debug.log' => 'disable' } })
|
276
276
|
end
|
277
277
|
|
278
|
-
def in_bolt_project(
|
278
|
+
def in_bolt_project(versioned_project)
|
279
279
|
@pal_mutex.synchronize do
|
280
|
-
bolt_config = config_from_project(
|
280
|
+
bolt_config = config_from_project(versioned_project)
|
281
281
|
modulepath_object = Bolt::Config::Modulepath.new(
|
282
282
|
bolt_config.modulepath,
|
283
283
|
boltlib_path: [PE_BOLTLIB_PATH, Bolt::Config::Modulepath::BOLTLIB_PATH]
|
@@ -514,10 +514,10 @@ module BoltServer
|
|
514
514
|
|
515
515
|
# Fetches the metadata for a single plan
|
516
516
|
#
|
517
|
-
# @param
|
517
|
+
# @param versioned_project [String] the project to fetch the plan from
|
518
518
|
get '/project_plans/:module_name/:plan_name' do
|
519
|
-
return
|
520
|
-
in_bolt_project(params['
|
519
|
+
return MISSING_VERSIONED_PROJECT_RESPONSE if params['versioned_project'].nil?
|
520
|
+
in_bolt_project(params['versioned_project']) do |context|
|
521
521
|
plan_info = pe_plan_info(context[:pal], params[:module_name], params[:plan_name])
|
522
522
|
plan_info = allowed_helper(plan_info, context[:config].project.plans)
|
523
523
|
[200, plan_info.to_json]
|
@@ -541,12 +541,12 @@ module BoltServer
|
|
541
541
|
|
542
542
|
# Fetches the metadata for a single task
|
543
543
|
#
|
544
|
-
# @param
|
544
|
+
# @param bolt_versioned_project [String] the reference to the bolt-project directory to load task metadata from
|
545
545
|
get '/project_tasks/:module_name/:task_name' do
|
546
|
-
return
|
547
|
-
in_bolt_project(params['
|
546
|
+
return MISSING_VERSIONED_PROJECT_RESPONSE if params['versioned_project'].nil?
|
547
|
+
in_bolt_project(params['versioned_project']) do |context|
|
548
548
|
ps_parameters = {
|
549
|
-
'versioned_project' => params['
|
549
|
+
'versioned_project' => params['versioned_project']
|
550
550
|
}
|
551
551
|
task_info = pe_task_info(context[:pal], params[:module_name], params[:task_name], ps_parameters)
|
552
552
|
task_info = allowed_helper(task_info, context[:config].project.tasks)
|
@@ -582,10 +582,10 @@ module BoltServer
|
|
582
582
|
|
583
583
|
# Fetches the list of plans for a project
|
584
584
|
#
|
585
|
-
# @param
|
585
|
+
# @param versioned_project [String] the project to fetch the list of plans from
|
586
586
|
get '/project_plans' do
|
587
|
-
return
|
588
|
-
in_bolt_project(params['
|
587
|
+
return MISSING_VERSIONED_PROJECT_RESPONSE if params['versioned_project'].nil?
|
588
|
+
in_bolt_project(params['versioned_project']) do |context|
|
589
589
|
plans_response = plan_list(context[:pal])
|
590
590
|
|
591
591
|
# Dig in context for the allowlist of plans from project object
|
@@ -618,10 +618,10 @@ module BoltServer
|
|
618
618
|
|
619
619
|
# Fetches the list of tasks for a bolt-project
|
620
620
|
#
|
621
|
-
# @param
|
621
|
+
# @param versioned_project [String] the project to fetch the list of tasks from
|
622
622
|
get '/project_tasks' do
|
623
|
-
return
|
624
|
-
in_bolt_project(params['
|
623
|
+
return MISSING_VERSIONED_PROJECT_RESPONSE if params['versioned_project'].nil?
|
624
|
+
in_bolt_project(params['versioned_project']) do |context|
|
625
625
|
tasks_response = task_list(context[:pal])
|
626
626
|
|
627
627
|
# Dig in context for the allowlist of tasks from project object
|
@@ -639,10 +639,10 @@ module BoltServer
|
|
639
639
|
|
640
640
|
# Implements puppetserver's file_metadatas endpoint for projects.
|
641
641
|
#
|
642
|
-
# @param
|
642
|
+
# @param versioned_project [String] the versioned_project to fetch the file metadatas from
|
643
643
|
get '/project_file_metadatas/:module_name/*' do
|
644
|
-
return
|
645
|
-
in_bolt_project(params['
|
644
|
+
return MISSING_VERSIONED_PROJECT_RESPONSE if params['versioned_project'].nil?
|
645
|
+
in_bolt_project(params['versioned_project']) do |context|
|
646
646
|
file = params[:splat].first
|
647
647
|
metadatas = file_metadatas(context[:pal], params[:module_name], file)
|
648
648
|
[200, metadatas.to_json]
|
@@ -655,14 +655,14 @@ module BoltServer
|
|
655
655
|
|
656
656
|
# Returns a list of targets parsed from a Project inventory
|
657
657
|
#
|
658
|
-
# @param
|
658
|
+
# @param versioned_project [String] the versioned_project to compute the inventory from
|
659
659
|
post '/project_inventory_targets' do
|
660
|
-
return
|
660
|
+
return MISSING_VERSIONED_PROJECT_RESPONSE if params['versioned_project'].nil?
|
661
661
|
content_type :json
|
662
662
|
body = JSON.parse(request.body.read)
|
663
663
|
error = validate_schema(@schemas["connect-data"], body)
|
664
664
|
return [400, error_result(error).to_json] unless error.nil?
|
665
|
-
in_bolt_project(params['
|
665
|
+
in_bolt_project(params['versioned_project']) do |context|
|
666
666
|
if context[:config].inventoryfile &&
|
667
667
|
context[:config].project.inventory_file.to_s !=
|
668
668
|
context[:config].inventoryfile
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bolt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.38.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppet
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-12-
|
11
|
+
date: 2020-12-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -311,6 +311,9 @@ dependencies:
|
|
311
311
|
- - ">="
|
312
312
|
- !ruby/object:Gem::Version
|
313
313
|
version: '1.14'
|
314
|
+
- - "<"
|
315
|
+
- !ruby/object:Gem::Version
|
316
|
+
version: 2.2.0
|
314
317
|
type: :development
|
315
318
|
prerelease: false
|
316
319
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -318,6 +321,9 @@ dependencies:
|
|
318
321
|
- - ">="
|
319
322
|
- !ruby/object:Gem::Version
|
320
323
|
version: '1.14'
|
324
|
+
- - "<"
|
325
|
+
- !ruby/object:Gem::Version
|
326
|
+
version: 2.2.0
|
321
327
|
- !ruby/object:Gem::Dependency
|
322
328
|
name: octokit
|
323
329
|
requirement: !ruby/object:Gem::Requirement
|
@@ -458,12 +464,12 @@ files:
|
|
458
464
|
- lib/bolt/config/transport/remote.rb
|
459
465
|
- lib/bolt/config/transport/ssh.rb
|
460
466
|
- lib/bolt/config/transport/winrm.rb
|
461
|
-
- lib/bolt/config/validator.rb
|
462
467
|
- lib/bolt/error.rb
|
463
468
|
- lib/bolt/executor.rb
|
464
469
|
- lib/bolt/inventory.rb
|
465
470
|
- lib/bolt/inventory/group.rb
|
466
471
|
- lib/bolt/inventory/inventory.rb
|
472
|
+
- lib/bolt/inventory/options.rb
|
467
473
|
- lib/bolt/inventory/target.rb
|
468
474
|
- lib/bolt/logger.rb
|
469
475
|
- lib/bolt/module.rb
|
@@ -551,6 +557,7 @@ files:
|
|
551
557
|
- lib/bolt/transport/winrm/connection.rb
|
552
558
|
- lib/bolt/util.rb
|
553
559
|
- lib/bolt/util/puppet_log_level.rb
|
560
|
+
- lib/bolt/validator.rb
|
554
561
|
- lib/bolt/version.rb
|
555
562
|
- lib/bolt/yarn.rb
|
556
563
|
- lib/bolt_server/acl.rb
|
@@ -1,231 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'bolt/error'
|
4
|
-
|
5
|
-
# This class validates config against a schema, raising an error that includes
|
6
|
-
# details about any invalid configuration.
|
7
|
-
#
|
8
|
-
module Bolt
|
9
|
-
class Config
|
10
|
-
class Validator
|
11
|
-
attr_reader :deprecations, :warnings
|
12
|
-
|
13
|
-
def initialize
|
14
|
-
@errors = []
|
15
|
-
@deprecations = []
|
16
|
-
@warnings = []
|
17
|
-
@path = []
|
18
|
-
end
|
19
|
-
|
20
|
-
# This is the entry method for validating data against the schema.
|
21
|
-
# It loops over each key-value pair in the data hash and validates
|
22
|
-
# the value against the relevant schema definition.
|
23
|
-
#
|
24
|
-
def validate(data, schema, location = nil)
|
25
|
-
@location = location
|
26
|
-
|
27
|
-
validate_keys(data.keys, schema.keys)
|
28
|
-
|
29
|
-
data.each_pair do |key, value|
|
30
|
-
next unless schema.key?(key)
|
31
|
-
|
32
|
-
@path.push(key)
|
33
|
-
|
34
|
-
check_deprecated(key, schema[key], location)
|
35
|
-
validate_value(value, schema[key])
|
36
|
-
ensure
|
37
|
-
@path.pop
|
38
|
-
end
|
39
|
-
|
40
|
-
raise_error
|
41
|
-
end
|
42
|
-
|
43
|
-
# Adds a warning if the given option is deprecated.
|
44
|
-
#
|
45
|
-
def check_deprecated(key, definition, location)
|
46
|
-
if definition.key?(:_deprecation)
|
47
|
-
message = "Option '#{path}' "
|
48
|
-
message += "at #{location} " if location
|
49
|
-
message += "is deprecated. #{definition[:_deprecation]}"
|
50
|
-
@deprecations << { option: key, message: message }
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
# Raises a ValidationError if there are any errors. All error messages
|
55
|
-
# created during validation are concatenated into a single error
|
56
|
-
# message.
|
57
|
-
#
|
58
|
-
private def raise_error
|
59
|
-
return unless @errors.any?
|
60
|
-
|
61
|
-
message = "Invalid configuration"
|
62
|
-
message += " at #{@location}" if @location
|
63
|
-
message += ":\n"
|
64
|
-
message += @errors.map { |error| "\s\s#{error}" }.join("\n")
|
65
|
-
|
66
|
-
raise Bolt::ValidationError, message
|
67
|
-
end
|
68
|
-
|
69
|
-
# Validate an individual value. This performs validation that is
|
70
|
-
# common to all values, including type validation. After validating
|
71
|
-
# the value's type, the value is passed off to an individual
|
72
|
-
# validation method for the value's type.
|
73
|
-
#
|
74
|
-
private def validate_value(value, definition)
|
75
|
-
return if plugin_reference?(value, definition)
|
76
|
-
return unless valid_type?(value, definition)
|
77
|
-
|
78
|
-
case value
|
79
|
-
when Hash
|
80
|
-
validate_hash(value, definition)
|
81
|
-
when Array
|
82
|
-
validate_array(value, definition)
|
83
|
-
when String
|
84
|
-
validate_string(value, definition)
|
85
|
-
when Numeric
|
86
|
-
validate_number(value, definition)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
# Validates a hash value, logging errors for any validations that fail.
|
91
|
-
# This will enumerate each key-value pair in the hash and validate each
|
92
|
-
# value individually.
|
93
|
-
#
|
94
|
-
private def validate_hash(value, definition)
|
95
|
-
properties = definition[:properties] ? definition[:properties].keys : []
|
96
|
-
|
97
|
-
if definition[:properties] && definition[:additionalProperties].nil?
|
98
|
-
validate_keys(value.keys, properties)
|
99
|
-
end
|
100
|
-
|
101
|
-
if definition[:required] && (definition[:required] - value.keys).any?
|
102
|
-
missing = definition[:required] - value.keys
|
103
|
-
@errors << "Value at '#{path}' is missing required keys #{missing.join(', ')}"
|
104
|
-
end
|
105
|
-
|
106
|
-
value.each_pair do |key, val|
|
107
|
-
@path.push(key)
|
108
|
-
|
109
|
-
if properties.include?(key)
|
110
|
-
validate_value(val, definition[:properties][key])
|
111
|
-
elsif definition[:additionalProperties]
|
112
|
-
validate_value(val, definition[:additionalProperties])
|
113
|
-
end
|
114
|
-
ensure
|
115
|
-
@path.pop
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
# Validates an array value, logging errors for any validations that fail.
|
120
|
-
# This will enumerate the items in the array and validate each item
|
121
|
-
# individually.
|
122
|
-
#
|
123
|
-
private def validate_array(value, definition)
|
124
|
-
if definition[:uniqueItems] && value.size != value.uniq.size
|
125
|
-
@errors << "Value at '#{path}' must not include duplicate elements"
|
126
|
-
return
|
127
|
-
end
|
128
|
-
|
129
|
-
return unless definition.key?(:items)
|
130
|
-
|
131
|
-
value.each_with_index do |item, index|
|
132
|
-
@path.push(index)
|
133
|
-
validate_value(item, definition[:items])
|
134
|
-
ensure
|
135
|
-
@path.pop
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
# Validates a string value, logging errors for any validations that fail.
|
140
|
-
#
|
141
|
-
private def validate_string(value, definition)
|
142
|
-
if definition.key?(:enum) && !definition[:enum].include?(value)
|
143
|
-
message = "Value at '#{path}' must be "
|
144
|
-
message += "one of " if definition[:enum].count > 1
|
145
|
-
message += definition[:enum].join(', ')
|
146
|
-
multitype_error(message, value, definition)
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
# Validates a numeric value, logging errors for any validations that fail.
|
151
|
-
#
|
152
|
-
private def validate_number(value, definition)
|
153
|
-
if definition.key?(:minimum) && value < definition[:minimum]
|
154
|
-
@errors << "Value at '#{path}' must be a minimum of #{definition[:minimum]}"
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
# Adds warnings for unknown config options.
|
159
|
-
#
|
160
|
-
private def validate_keys(keys, known_keys)
|
161
|
-
(keys - known_keys).each do |key|
|
162
|
-
message = "Unknown option '#{key}'"
|
163
|
-
message += " at '#{path}'" if @path.any?
|
164
|
-
message += " at #{@location}" if @location
|
165
|
-
message += "."
|
166
|
-
@warnings << message
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
# Returns true if a value is a plugin reference. This also validates whether
|
171
|
-
# a value can be a plugin reference in the first place. If the value is a
|
172
|
-
# plugin reference but cannot be one according to the schema, then this will
|
173
|
-
# log an error.
|
174
|
-
#
|
175
|
-
private def plugin_reference?(value, definition)
|
176
|
-
if value.is_a?(Hash) && value.key?('_plugin')
|
177
|
-
unless definition[:_plugin]
|
178
|
-
@errors << "Value at '#{path}' is a plugin reference, which is unsupported at "\
|
179
|
-
"this location"
|
180
|
-
end
|
181
|
-
|
182
|
-
true
|
183
|
-
else
|
184
|
-
false
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
|
-
# Asserts the type for each option against the type specified in the schema
|
189
|
-
# definition. The schema definition can specify multiple valid types, so the
|
190
|
-
# value needs to only match one of the types to be valid. Returns early if
|
191
|
-
# there is no type in the definition (in practice this shouldn't happen, but
|
192
|
-
# this will safeguard against any dev mistakes).
|
193
|
-
#
|
194
|
-
private def valid_type?(value, definition)
|
195
|
-
return unless definition.key?(:type)
|
196
|
-
|
197
|
-
types = Array(definition[:type])
|
198
|
-
|
199
|
-
if types.include?(value.class)
|
200
|
-
true
|
201
|
-
else
|
202
|
-
if types.include?(TrueClass) || types.include?(FalseClass)
|
203
|
-
types = types - [TrueClass, FalseClass] + ['Boolean']
|
204
|
-
end
|
205
|
-
|
206
|
-
@errors << "Value at '#{path}' must be of type #{types.join(' or ')}"
|
207
|
-
|
208
|
-
false
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
# Adds an error that includes additional helpful information for values
|
213
|
-
# that accept multiple types.
|
214
|
-
#
|
215
|
-
private def multitype_error(message, value, definition)
|
216
|
-
if Array(definition[:type]).count > 1
|
217
|
-
types = Array(definition[:type]) - [value.class]
|
218
|
-
message += " or must be of type #{types.join(' or ')}"
|
219
|
-
end
|
220
|
-
|
221
|
-
@errors << message
|
222
|
-
end
|
223
|
-
|
224
|
-
# Returns the formatted path for the key.
|
225
|
-
#
|
226
|
-
private def path
|
227
|
-
@path.join('.')
|
228
|
-
end
|
229
|
-
end
|
230
|
-
end
|
231
|
-
end
|