bolt 2.33.2 → 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.

Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/Puppetfile +1 -1
  3. data/bolt-modules/boltlib/lib/puppet/datatypes/applyresult.rb +1 -0
  4. data/bolt-modules/boltlib/lib/puppet/functions/catch_errors.rb +1 -3
  5. data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +17 -6
  6. data/bolt-modules/boltlib/lib/puppet/functions/parallelize.rb +56 -0
  7. data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +24 -6
  8. data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +27 -8
  9. data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +21 -1
  10. data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +18 -1
  11. data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +24 -6
  12. data/lib/bolt/analytics.rb +27 -8
  13. data/lib/bolt/apply_result.rb +3 -3
  14. data/lib/bolt/bolt_option_parser.rb +48 -16
  15. data/lib/bolt/cli.rb +154 -249
  16. data/lib/bolt/config.rb +188 -55
  17. data/lib/bolt/config/options.rb +147 -87
  18. data/lib/bolt/config/transport/base.rb +10 -19
  19. data/lib/bolt/config/transport/local.rb +1 -7
  20. data/lib/bolt/config/transport/options.rb +10 -68
  21. data/lib/bolt/config/transport/ssh.rb +8 -14
  22. data/lib/bolt/error.rb +33 -3
  23. data/lib/bolt/executor.rb +92 -6
  24. data/lib/bolt/inventory.rb +25 -0
  25. data/lib/bolt/inventory/group.rb +2 -1
  26. data/lib/bolt/inventory/options.rb +130 -0
  27. data/lib/bolt/inventory/target.rb +10 -11
  28. data/lib/bolt/module_installer.rb +21 -13
  29. data/lib/bolt/module_installer/resolver.rb +1 -1
  30. data/lib/bolt/outputter.rb +19 -5
  31. data/lib/bolt/outputter/human.rb +41 -10
  32. data/lib/bolt/outputter/json.rb +1 -1
  33. data/lib/bolt/outputter/logger.rb +1 -1
  34. data/lib/bolt/outputter/rainbow.rb +13 -2
  35. data/lib/bolt/pal.rb +19 -7
  36. data/lib/bolt/pal/yaml_plan.rb +7 -0
  37. data/lib/bolt/plan_creator.rb +160 -0
  38. data/lib/bolt/plugin.rb +42 -13
  39. data/lib/bolt/plugin/cache.rb +76 -0
  40. data/lib/bolt/plugin/module.rb +4 -4
  41. data/lib/bolt/plugin/puppetdb.rb +1 -1
  42. data/lib/bolt/project.rb +59 -40
  43. data/lib/bolt/project_manager.rb +201 -0
  44. data/lib/bolt/{project_migrator/config.rb → project_manager/config_migrator.rb} +51 -5
  45. data/lib/bolt/{project_migrator/inventory.rb → project_manager/inventory_migrator.rb} +5 -5
  46. data/lib/bolt/{project_migrator/base.rb → project_manager/migrator.rb} +2 -2
  47. data/lib/bolt/{project_migrator/modules.rb → project_manager/module_migrator.rb} +5 -3
  48. data/lib/bolt/puppetdb/client.rb +11 -2
  49. data/lib/bolt/puppetdb/config.rb +9 -8
  50. data/lib/bolt/rerun.rb +1 -5
  51. data/lib/bolt/shell/bash.rb +8 -2
  52. data/lib/bolt/shell/powershell.rb +22 -4
  53. data/lib/bolt/target.rb +4 -0
  54. data/lib/bolt/task/run.rb +1 -1
  55. data/lib/bolt/transport/local.rb +13 -0
  56. data/lib/bolt/transport/orch.rb +0 -5
  57. data/lib/bolt/transport/orch/connection.rb +10 -3
  58. data/lib/bolt/transport/remote.rb +1 -1
  59. data/lib/bolt/transport/ssh/exec_connection.rb +6 -2
  60. data/lib/bolt/util.rb +41 -7
  61. data/lib/bolt/validator.rb +226 -0
  62. data/lib/bolt/version.rb +1 -1
  63. data/lib/bolt/yarn.rb +23 -0
  64. data/lib/bolt_server/base_config.rb +3 -1
  65. data/lib/bolt_server/config.rb +3 -1
  66. data/lib/bolt_server/file_cache.rb +2 -0
  67. data/lib/bolt_server/plugin.rb +13 -0
  68. data/lib/bolt_server/plugin/puppet_connect_data.rb +37 -0
  69. data/lib/bolt_server/schemas/connect-data.json +22 -0
  70. data/lib/bolt_server/schemas/partials/task.json +2 -2
  71. data/lib/bolt_server/transport_app.rb +82 -23
  72. data/lib/bolt_spec/plans/mock_executor.rb +4 -1
  73. data/libexec/apply_catalog.rb +1 -1
  74. data/libexec/custom_facts.rb +1 -1
  75. data/libexec/query_resources.rb +1 -1
  76. metadata +22 -13
  77. data/lib/bolt/project_migrator.rb +0 -80
@@ -0,0 +1,226 @@
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 Validator
10
+ attr_reader :deprecations, :warnings
11
+
12
+ def initialize
13
+ @errors = []
14
+ @deprecations = []
15
+ @warnings = []
16
+ @path = []
17
+ end
18
+
19
+ # This is the entry method for validating data against the schema.
20
+ #
21
+ def validate(data, schema, location = nil)
22
+ @schema = schema
23
+ @location = location
24
+
25
+ validate_value(data, schema)
26
+
27
+ raise_error
28
+ end
29
+
30
+ # Raises a ValidationError if there are any errors. All error messages
31
+ # created during validation are concatenated into a single error
32
+ # message.
33
+ #
34
+ private def raise_error
35
+ return unless @errors.any?
36
+
37
+ message = "Invalid configuration"
38
+ message += " at #{@location}" if @location
39
+ message += ":\n"
40
+ message += @errors.map { |error| "\s\s#{error}" }.join("\n")
41
+
42
+ raise Bolt::ValidationError, message
43
+ end
44
+
45
+ # Validate an individual value. This performs validation that is
46
+ # common to all values, including type validation. After validating
47
+ # the value's type, the value is passed off to an individual
48
+ # validation method for the value's type.
49
+ #
50
+ private def validate_value(value, definition)
51
+ definition = @schema.dig(:definitions, definition[:_ref]) if definition[:_ref]
52
+
53
+ return if plugin_reference?(value, definition)
54
+ return unless valid_type?(value, definition)
55
+
56
+ case value
57
+ when Hash
58
+ validate_hash(value, definition)
59
+ when Array
60
+ validate_array(value, definition)
61
+ when String
62
+ validate_string(value, definition)
63
+ when Numeric
64
+ validate_number(value, definition)
65
+ end
66
+ end
67
+
68
+ # Validates a hash value, logging errors for any validations that fail.
69
+ # This will enumerate each key-value pair in the hash and validate each
70
+ # value individually.
71
+ #
72
+ private def validate_hash(value, definition)
73
+ properties = definition[:properties] ? definition[:properties].keys : []
74
+
75
+ if definition[:properties] && definition[:additionalProperties].nil?
76
+ validate_keys(value.keys, properties)
77
+ end
78
+
79
+ if definition[:required] && (definition[:required] - value.keys).any?
80
+ missing = definition[:required] - value.keys
81
+ @errors << "Value at '#{path}' is missing required keys #{missing.join(', ')}"
82
+ end
83
+
84
+ value.each_pair do |key, val|
85
+ @path.push(key)
86
+
87
+ if properties.include?(key)
88
+ check_deprecated(key, definition[:properties][key])
89
+ validate_value(val, definition[:properties][key])
90
+ elsif definition[:additionalProperties]
91
+ validate_value(val, definition[:additionalProperties])
92
+ end
93
+ ensure
94
+ @path.pop
95
+ end
96
+ end
97
+
98
+ # Validates an array value, logging errors for any validations that fail.
99
+ # This will enumerate the items in the array and validate each item
100
+ # individually.
101
+ #
102
+ private def validate_array(value, definition)
103
+ if definition[:uniqueItems] && value.size != value.uniq.size
104
+ @errors << "Value at '#{path}' must not include duplicate elements"
105
+ return
106
+ end
107
+
108
+ return unless definition.key?(:items)
109
+
110
+ value.each_with_index do |item, index|
111
+ @path.push(index)
112
+ validate_value(item, definition[:items])
113
+ ensure
114
+ @path.pop
115
+ end
116
+ end
117
+
118
+ # Validates a string value, logging errors for any validations that fail.
119
+ #
120
+ private def validate_string(value, definition)
121
+ if definition.key?(:enum) && !definition[:enum].include?(value)
122
+ message = "Value at '#{path}' must be "
123
+ message += "one of " if definition[:enum].count > 1
124
+ message += definition[:enum].join(', ')
125
+ multitype_error(message, value, definition)
126
+ end
127
+ end
128
+
129
+ # Validates a numeric value, logging errors for any validations that fail.
130
+ #
131
+ private def validate_number(value, definition)
132
+ if definition.key?(:minimum) && value < definition[:minimum]
133
+ @errors << "Value at '#{path}' must be a minimum of #{definition[:minimum]}"
134
+ end
135
+
136
+ if definition.key?(:maximum) && value > definition[:maximum]
137
+ @errors << "Value at '#{path}' must be a maximum of #{definition[:maximum]}"
138
+ end
139
+ end
140
+
141
+ # Adds warnings for unknown config options.
142
+ #
143
+ private def validate_keys(keys, known_keys)
144
+ (keys - known_keys).each do |key|
145
+ message = "Unknown option '#{key}'"
146
+ message += " at '#{path}'" if @path.any?
147
+ message += " at #{@location}" if @location
148
+ message += "."
149
+ @warnings << message
150
+ end
151
+ end
152
+
153
+ # Adds a warning if the given option is deprecated.
154
+ #
155
+ private def check_deprecated(key, definition)
156
+ definition = @schema.dig(:definitions, definition[:_ref]) if definition[:_ref]
157
+
158
+ if definition.key?(:_deprecation)
159
+ message = "Option '#{path}' "
160
+ message += "at #{@location} " if @location
161
+ message += "is deprecated. #{definition[:_deprecation]}"
162
+ @deprecations << { option: key, message: message }
163
+ end
164
+ end
165
+
166
+ # Returns true if a value is a plugin reference. This also validates whether
167
+ # a value can be a plugin reference in the first place. If the value is a
168
+ # plugin reference but cannot be one according to the schema, then this will
169
+ # log an error.
170
+ #
171
+ private def plugin_reference?(value, definition)
172
+ if value.is_a?(Hash) && value.key?('_plugin')
173
+ unless definition[:_plugin]
174
+ @errors << "Value at '#{path}' is a plugin reference, which is unsupported at "\
175
+ "this location"
176
+ end
177
+
178
+ true
179
+ else
180
+ false
181
+ end
182
+ end
183
+
184
+ # Asserts the type for each option against the type specified in the schema
185
+ # definition. The schema definition can specify multiple valid types, so the
186
+ # value needs to only match one of the types to be valid. Returns early if
187
+ # there is no type in the definition (in practice this shouldn't happen, but
188
+ # this will safeguard against any dev mistakes).
189
+ #
190
+ private def valid_type?(value, definition)
191
+ return unless definition.key?(:type)
192
+
193
+ types = Array(definition[:type])
194
+
195
+ if types.include?(value.class)
196
+ true
197
+ else
198
+ if types.include?(TrueClass) || types.include?(FalseClass)
199
+ types = types - [TrueClass, FalseClass] + ['Boolean']
200
+ end
201
+
202
+ @errors << "Value at '#{path}' must be of type #{types.join(' or ')}"
203
+
204
+ false
205
+ end
206
+ end
207
+
208
+ # Adds an error that includes additional helpful information for values
209
+ # that accept multiple types.
210
+ #
211
+ private def multitype_error(message, value, definition)
212
+ if Array(definition[:type]).count > 1
213
+ types = Array(definition[:type]) - [value.class]
214
+ message += " or must be of type #{types.join(' or ')}"
215
+ end
216
+
217
+ @errors << message
218
+ end
219
+
220
+ # Returns the formatted path for the key.
221
+ #
222
+ private def path
223
+ @path.join('.')
224
+ end
225
+ end
226
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bolt
4
- VERSION = '2.33.2'
4
+ VERSION = '2.38.0'
5
5
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fiber'
4
+
5
+ module Bolt
6
+ class Yarn
7
+ attr_reader :fiber, :value, :index
8
+
9
+ def initialize(fiber, index)
10
+ @fiber = fiber
11
+ @index = index
12
+ @value = nil
13
+ end
14
+
15
+ def alive?
16
+ fiber.alive?
17
+ end
18
+
19
+ def resume
20
+ @value = fiber.resume
21
+ end
22
+ end
23
+ end
@@ -7,7 +7,9 @@ module BoltServer
7
7
  class BaseConfig
8
8
  def config_keys
9
9
  %w[host port ssl-cert ssl-key ssl-ca-cert
10
- ssl-cipher-suites loglevel logfile allowlist projects-dir]
10
+ ssl-cipher-suites loglevel logfile allowlist
11
+ projects-dir environments-codedir
12
+ environmentpath basemodulepath]
11
13
  end
12
14
 
13
15
  def env_keys
@@ -7,7 +7,9 @@ require 'bolt/error'
7
7
  module BoltServer
8
8
  class Config < BoltServer::BaseConfig
9
9
  def config_keys
10
- super + %w[concurrency cache-dir file-server-conn-timeout file-server-uri projects-dir]
10
+ super + %w[concurrency cache-dir file-server-conn-timeout
11
+ file-server-uri projects-dir environments-codedir
12
+ environmentpath basemodulepath]
11
13
  end
12
14
 
13
15
  def env_keys
@@ -63,6 +63,7 @@ module BoltServer
63
63
  end
64
64
 
65
65
  def client
66
+ # rubocop:disable Naming/VariableNumber
66
67
  @client ||= begin
67
68
  uri = URI(@config['file-server-uri'])
68
69
  https = Net::HTTP.new(uri.host, uri.port)
@@ -75,6 +76,7 @@ module BoltServer
75
76
  https.open_timeout = @config['file-server-conn-timeout']
76
77
  https
77
78
  end
79
+ # rubocop:enable Naming/VariableNumber
78
80
  end
79
81
 
80
82
  def request_file(path, params, file)
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bolt/error'
4
+
5
+ module BoltServer
6
+ class Plugin
7
+ class PluginNotSupported < Bolt::Error
8
+ def initialize(msg, plugin_name)
9
+ super(msg, 'bolt/plugin-not-supported', { "plugin_name" => plugin_name })
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BoltServer
4
+ class Plugin
5
+ class PuppetConnectData
6
+ def initialize(data, **_opts)
7
+ @data = data
8
+ end
9
+
10
+ def name
11
+ 'puppet_connect_data'
12
+ end
13
+
14
+ def hooks
15
+ %i[resolve_reference validate_resolve_reference]
16
+ end
17
+
18
+ def resolve_reference(opts)
19
+ key = opts['key']
20
+
21
+ @data.dig(key, 'value')
22
+ end
23
+
24
+ def validate_resolve_reference(opts)
25
+ unless opts['key']
26
+ raise Bolt::ValidationError,
27
+ "puppet_connect_data plugin requires that 'key' be specified"
28
+ end
29
+
30
+ unless @data.key?(opts['key'])
31
+ raise Bolt::ValidationError,
32
+ "puppet_connect_data plugin tried to lookup key '#{opts['key']}' but no value was found"
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,22 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-04/schema#",
3
+ "title": "project_inventory_targets connect plugin data",
4
+ "description": "POST project_inventory_targets connect plugin data",
5
+ "type": "object",
6
+ "properties": {
7
+ "puppet_connect_data": {
8
+ "type": "object",
9
+ "patternProperties": {
10
+ ".*": {
11
+ "type": "object",
12
+ "properties": {
13
+ "value": {}
14
+ },
15
+ "required": ["value"]
16
+ }
17
+ },
18
+ "additionalProperties": false
19
+ }
20
+ },
21
+ "required": ["puppet_connect_data"]
22
+ }
@@ -64,7 +64,7 @@
64
64
  "description": "Environment the task is in",
65
65
  "type": "string"
66
66
  },
67
- "project": {
67
+ "versioned_project": {
68
68
  "description": "Project the task is in",
69
69
  "type": "string"
70
70
  }
@@ -77,7 +77,7 @@
77
77
  },
78
78
  {
79
79
  "required": [
80
- "project"
80
+ "versioned_project"
81
81
  ]
82
82
  }
83
83
  ],
@@ -8,6 +8,8 @@ require 'bolt/inventory'
8
8
  require 'bolt/project'
9
9
  require 'bolt/target'
10
10
  require 'bolt_server/file_cache'
11
+ require 'bolt_server/plugin'
12
+ require 'bolt_server/plugin/puppet_connect_data'
11
13
  require 'bolt/task/puppet_server'
12
14
  require 'json'
13
15
  require 'json-schema'
@@ -35,6 +37,7 @@ module BoltServer
35
37
  action-upload_file
36
38
  transport-ssh
37
39
  transport-winrm
40
+ connect-data
38
41
  ].freeze
39
42
 
40
43
  # PE_BOLTLIB_PATH is intended to function exactly like the BOLTLIB_PATH used
@@ -48,6 +51,10 @@ module BoltServer
48
51
  # See the `orchestrator.bolt.codedir` tk config setting.
49
52
  DEFAULT_BOLT_CODEDIR = '/opt/puppetlabs/server/data/orchestration-services/code'
50
53
 
54
+ MISSING_VERSIONED_PROJECT_RESPONSE = [
55
+ 400, Bolt::ValidationError.new('`versioned_project` is a required argument').to_json
56
+ ].freeze
57
+
51
58
  def initialize(config)
52
59
  @config = config
53
60
  @schemas = Hash[REQUEST_SCHEMAS.map do |basename|
@@ -231,9 +238,9 @@ module BoltServer
231
238
  #
232
239
  # WARNING: THIS FUNCTION SHOULD ONLY BE CALLED INSIDE A SYNCHRONIZED PAL MUTEX
233
240
  def modulepath_from_environment(environment_name)
234
- codedir = DEFAULT_BOLT_CODEDIR
235
- environmentpath = "#{codedir}/environments"
236
- basemodulepath = "#{codedir}/modules:/opt/puppetlabs/puppet/modules"
241
+ codedir = @config['environments-codedir'] || DEFAULT_BOLT_CODEDIR
242
+ environmentpath = @config['environmentpath'] || "#{codedir}/environments"
243
+ basemodulepath = @config['basemodulepath'] || "#{codedir}/modules:/opt/puppetlabs/puppet/modules"
237
244
  modulepath_dirs = nil
238
245
  with_pe_pal_init_settings(codedir, environmentpath, basemodulepath) do
239
246
  environment = Puppet.lookup(:environments).get!(environment_name)
@@ -261,13 +268,16 @@ module BoltServer
261
268
  end
262
269
  end
263
270
 
264
- def in_bolt_project(bolt_project)
265
- return [400, '`project_ref` is a required argument'] if bolt_project.nil?
266
- project_dir = File.join(@config['projects-dir'], bolt_project)
267
- return [400, "`project_ref`: #{project_dir} does not exist"] unless Dir.exist?(project_dir)
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
+ project = Bolt::Project.create_project(project_dir)
275
+ Bolt::Config.from_project(project, { log: { 'bolt-debug.log' => 'disable' } })
276
+ end
277
+
278
+ def in_bolt_project(versioned_project)
268
279
  @pal_mutex.synchronize do
269
- project = Bolt::Project.create_project(project_dir)
270
- bolt_config = Bolt::Config.from_project(project, { log: { 'bolt-debug.log' => 'disable' } })
280
+ bolt_config = config_from_project(versioned_project)
271
281
  modulepath_object = Bolt::Config::Modulepath.new(
272
282
  bolt_config.modulepath,
273
283
  boltlib_path: [PE_BOLTLIB_PATH, Bolt::Config::Modulepath::BOLTLIB_PATH]
@@ -278,8 +288,6 @@ module BoltServer
278
288
  config: bolt_config
279
289
  }
280
290
  yield context
281
- rescue Bolt::Error => e
282
- [400, e.to_json]
283
291
  end
284
292
  end
285
293
 
@@ -506,13 +514,16 @@ module BoltServer
506
514
 
507
515
  # Fetches the metadata for a single plan
508
516
  #
509
- # @param project_ref [String] the project to fetch the plan from
517
+ # @param versioned_project [String] the project to fetch the plan from
510
518
  get '/project_plans/:module_name/:plan_name' do
511
- in_bolt_project(params['project_ref']) do |context|
519
+ return MISSING_VERSIONED_PROJECT_RESPONSE if params['versioned_project'].nil?
520
+ in_bolt_project(params['versioned_project']) do |context|
512
521
  plan_info = pe_plan_info(context[:pal], params[:module_name], params[:plan_name])
513
522
  plan_info = allowed_helper(plan_info, context[:config].project.plans)
514
523
  [200, plan_info.to_json]
515
524
  end
525
+ rescue Bolt::Error => e
526
+ [400, e.to_json]
516
527
  end
517
528
 
518
529
  # Fetches the metadata for a single task
@@ -530,16 +541,19 @@ module BoltServer
530
541
 
531
542
  # Fetches the metadata for a single task
532
543
  #
533
- # @param bolt_project_ref [String] the reference to the bolt-project directory to load task metadata from
544
+ # @param bolt_versioned_project [String] the reference to the bolt-project directory to load task metadata from
534
545
  get '/project_tasks/:module_name/:task_name' do
535
- in_bolt_project(params['project_ref']) do |context|
546
+ return MISSING_VERSIONED_PROJECT_RESPONSE if params['versioned_project'].nil?
547
+ in_bolt_project(params['versioned_project']) do |context|
536
548
  ps_parameters = {
537
- 'project' => params['project_ref']
549
+ 'versioned_project' => params['versioned_project']
538
550
  }
539
551
  task_info = pe_task_info(context[:pal], params[:module_name], params[:task_name], ps_parameters)
540
552
  task_info = allowed_helper(task_info, context[:config].project.tasks)
541
553
  [200, task_info.to_json]
542
554
  end
555
+ rescue Bolt::Error => e
556
+ [400, e.to_json]
543
557
  end
544
558
 
545
559
  # Fetches the list of plans for an environment, optionally fetching all metadata for each plan
@@ -568,9 +582,10 @@ module BoltServer
568
582
 
569
583
  # Fetches the list of plans for a project
570
584
  #
571
- # @param project_ref [String] the project to fetch the list of plans from
585
+ # @param versioned_project [String] the project to fetch the list of plans from
572
586
  get '/project_plans' do
573
- in_bolt_project(params['project_ref']) do |context|
587
+ return MISSING_VERSIONED_PROJECT_RESPONSE if params['versioned_project'].nil?
588
+ in_bolt_project(params['versioned_project']) do |context|
574
589
  plans_response = plan_list(context[:pal])
575
590
 
576
591
  # Dig in context for the allowlist of plans from project object
@@ -582,6 +597,8 @@ module BoltServer
582
597
  # to bolt-server smaller/simpler.
583
598
  [200, plans_response.to_json]
584
599
  end
600
+ rescue Bolt::Error => e
601
+ [400, e.to_json]
585
602
  end
586
603
 
587
604
  # Fetches the list of tasks for an environment
@@ -601,9 +618,10 @@ module BoltServer
601
618
 
602
619
  # Fetches the list of tasks for a bolt-project
603
620
  #
604
- # @param project_ref [String] the project to fetch the list of tasks from
621
+ # @param versioned_project [String] the project to fetch the list of tasks from
605
622
  get '/project_tasks' do
606
- in_bolt_project(params['project_ref']) do |context|
623
+ return MISSING_VERSIONED_PROJECT_RESPONSE if params['versioned_project'].nil?
624
+ in_bolt_project(params['versioned_project']) do |context|
607
625
  tasks_response = task_list(context[:pal])
608
626
 
609
627
  # Dig in context for the allowlist of tasks from project object
@@ -615,28 +633,69 @@ module BoltServer
615
633
  # to bolt-server smaller/simpler.
616
634
  [200, tasks_response.to_json]
617
635
  end
636
+ rescue Bolt::Error => e
637
+ [400, e.to_json]
618
638
  end
619
639
 
620
640
  # Implements puppetserver's file_metadatas endpoint for projects.
621
641
  #
622
- # @param project_ref [String] the project_ref to fetch the file metadatas from
642
+ # @param versioned_project [String] the versioned_project to fetch the file metadatas from
623
643
  get '/project_file_metadatas/:module_name/*' do
624
- in_bolt_project(params['project_ref']) do |context|
644
+ return MISSING_VERSIONED_PROJECT_RESPONSE if params['versioned_project'].nil?
645
+ in_bolt_project(params['versioned_project']) do |context|
625
646
  file = params[:splat].first
626
647
  metadatas = file_metadatas(context[:pal], params[:module_name], file)
627
648
  [200, metadatas.to_json]
628
649
  end
650
+ rescue Bolt::Error => e
651
+ [400, e.to_json]
629
652
  rescue ArgumentError => e
630
653
  [400, e.message]
631
654
  end
632
655
 
656
+ # Returns a list of targets parsed from a Project inventory
657
+ #
658
+ # @param versioned_project [String] the versioned_project to compute the inventory from
659
+ post '/project_inventory_targets' do
660
+ return MISSING_VERSIONED_PROJECT_RESPONSE if params['versioned_project'].nil?
661
+ content_type :json
662
+ body = JSON.parse(request.body.read)
663
+ error = validate_schema(@schemas["connect-data"], body)
664
+ return [400, error_result(error).to_json] unless error.nil?
665
+ in_bolt_project(params['versioned_project']) do |context|
666
+ if context[:config].inventoryfile &&
667
+ context[:config].project.inventory_file.to_s !=
668
+ context[:config].inventoryfile
669
+ raise Bolt::ValidationError, "Project inventory must be defined in the " \
670
+ "inventory.yaml file at the root of the project directory"
671
+ end
672
+
673
+ Bolt::Util.validate_file('inventory file', context[:config].project.inventory_file)
674
+
675
+ begin
676
+ connect_plugin = BoltServer::Plugin::PuppetConnectData.new(body['puppet_connect_data'])
677
+ plugins = Bolt::Plugin.setup(context[:config], context[:pal], load_plugins: false)
678
+ plugins.add_plugin(connect_plugin)
679
+ inventory = Bolt::Inventory.from_config(context[:config], plugins)
680
+ target_list = inventory.get_targets('all').map { |targ| targ.to_h.merge({ 'transport' => targ.transport }) }
681
+ rescue Bolt::Plugin::PluginError::LoadingDisabled => e
682
+ msg = "Cannot load plugin #{e.details['plugin_name']}: plugin not supported"
683
+ raise BoltServer::Plugin::PluginNotSupported.new(msg, e.details['plugin_name'])
684
+ end
685
+
686
+ [200, target_list.to_json]
687
+ end
688
+ rescue Bolt::Error => e
689
+ [500, e.to_json]
690
+ end
691
+
633
692
  error 404 do
634
693
  err = Bolt::Error.new("Could not find route #{request.path}",
635
694
  'boltserver/not-found')
636
695
  [404, err.to_json]
637
696
  end
638
697
 
639
- error 500 do
698
+ error StandardError do
640
699
  e = env['sinatra.error']
641
700
  err = Bolt::Error.new("500: Unknown error: #{e.message}",
642
701
  'boltserver/server-error')