bolt 2.26.0 → 2.27.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/lib/bolt/analytics.rb +4 -0
- data/lib/bolt/applicator.rb +5 -4
- data/lib/bolt/bolt_option_parser.rb +41 -3
- data/lib/bolt/cli.rb +100 -115
- data/lib/bolt/config.rb +9 -1
- data/lib/bolt/config/options.rb +19 -0
- data/lib/bolt/pal.rb +17 -0
- data/lib/bolt/project.rb +35 -0
- data/lib/bolt/puppetfile.rb +160 -0
- data/lib/bolt/puppetfile/installer.rb +43 -0
- data/lib/bolt/puppetfile/module.rb +66 -0
- data/lib/bolt/rerun.rb +1 -1
- data/lib/bolt/result.rb +15 -0
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/base_config.rb +1 -1
- data/lib/bolt_server/config.rb +1 -1
- data/lib/bolt_server/transport_app.rb +125 -26
- 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: 851cd71cf3bbdd91a522f123db238205ca21875c7d1c9bf1eefe4850399ffb5d
|
4
|
+
data.tar.gz: 4ec2ca689f05d37ccbc1cd3962edae53dfcda79c0a58facd9a2f228af5df914c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3d595d36833a70860c7c997fff4cc55ce682dd3b3b9c4fc5e7b36067cbb73a665e65d473097993963f8409eb5a06a25b5b834de9dff6f42ad8e244972ab0ac4e
|
7
|
+
data.tar.gz: d136cd117711e3a103480b7cae52661e8ad3beef5023552f3bbaa8235e8f795410e13a1955290c7313fb6f7fced1485b275b8430a0d5c81297e11e1ff911f458
|
data/lib/bolt/analytics.rb
CHANGED
@@ -93,6 +93,10 @@ module Bolt
|
|
93
93
|
def self.write_config(filename, config)
|
94
94
|
FileUtils.mkdir_p(File.dirname(filename))
|
95
95
|
File.write(filename, config.to_yaml)
|
96
|
+
rescue StandardError => e
|
97
|
+
Bolt::Logger.warn_once('unwriteable_file', "Could not write analytics configuration to #{filename}.")
|
98
|
+
# This will get caught by build_client and create a NoopClient
|
99
|
+
raise e
|
96
100
|
end
|
97
101
|
|
98
102
|
class Client
|
data/lib/bolt/applicator.rb
CHANGED
@@ -75,23 +75,24 @@ module Bolt
|
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
|
-
def compile(target,
|
78
|
+
def compile(target, scope)
|
79
79
|
# This simplified Puppet node object is what .local uses to determine the
|
80
80
|
# certname of the target
|
81
81
|
node = Puppet::Node.from_data_hash('name' => target.name,
|
82
82
|
'parameters' => { 'clientcert' => target.name })
|
83
83
|
trusted = Puppet::Context::TrustedInformation.local(node)
|
84
|
-
|
84
|
+
target_data = {
|
85
85
|
name: target.name,
|
86
86
|
facts: @inventory.facts(target).merge('bolt' => true),
|
87
87
|
variables: @inventory.vars(target),
|
88
88
|
trusted: trusted.to_h
|
89
89
|
}
|
90
|
+
catalog_request = scope.merge(target: target_data)
|
90
91
|
|
91
92
|
bolt_catalog_exe = File.join(libexec, 'bolt_catalog')
|
92
93
|
old_path = ENV['PATH']
|
93
94
|
ENV['PATH'] = "#{RbConfig::CONFIG['bindir']}#{File::PATH_SEPARATOR}#{old_path}"
|
94
|
-
out, err, stat = Open3.capture3('ruby', bolt_catalog_exe, 'compile', stdin_data:
|
95
|
+
out, err, stat = Open3.capture3('ruby', bolt_catalog_exe, 'compile', stdin_data: catalog_request.to_json)
|
95
96
|
ENV['PATH'] = old_path
|
96
97
|
|
97
98
|
# If bolt_catalog does not return valid JSON, we should print stderr to
|
@@ -192,7 +193,7 @@ module Bolt
|
|
192
193
|
plan_vars: plan_vars,
|
193
194
|
# This data isn't available on the target config hash
|
194
195
|
config: @inventory.transport_data_get
|
195
|
-
}
|
196
|
+
}.freeze
|
196
197
|
description = options[:description] || 'apply catalog'
|
197
198
|
|
198
199
|
required_modules = options[:required_modules].nil? ? nil : Array(options[:required_modules])
|
@@ -64,6 +64,15 @@ module Bolt
|
|
64
64
|
when 'guide'
|
65
65
|
{ flags: OPTIONS[:global] + %w[format],
|
66
66
|
banner: GUIDE_HELP }
|
67
|
+
when 'module'
|
68
|
+
case action
|
69
|
+
when 'install'
|
70
|
+
{ flags: OPTIONS[:global] + %w[configfile force project],
|
71
|
+
banner: MODULE_INSTALL_HELP }
|
72
|
+
else
|
73
|
+
{ flags: OPTIONS[:global],
|
74
|
+
banner: MODULE_HELP }
|
75
|
+
end
|
67
76
|
when 'plan'
|
68
77
|
case action
|
69
78
|
when 'convert'
|
@@ -341,6 +350,35 @@ module Bolt
|
|
341
350
|
Show the list of targets an action would run on.
|
342
351
|
HELP
|
343
352
|
|
353
|
+
MODULE_HELP = <<~HELP
|
354
|
+
NAME
|
355
|
+
module
|
356
|
+
|
357
|
+
USAGE
|
358
|
+
bolt module <action> [options]
|
359
|
+
|
360
|
+
DESCRIPTION
|
361
|
+
Install the project's modules
|
362
|
+
|
363
|
+
ACTIONS
|
364
|
+
install Install the project's modules
|
365
|
+
HELP
|
366
|
+
|
367
|
+
MODULE_INSTALL_HELP = <<~HELP
|
368
|
+
NAME
|
369
|
+
install
|
370
|
+
|
371
|
+
USAGE
|
372
|
+
bolt module install [options]
|
373
|
+
|
374
|
+
DESCRIPTION
|
375
|
+
Install the project's modules.
|
376
|
+
|
377
|
+
Module declarations are loaded from the project's configuration
|
378
|
+
file. Bolt will automatically resolve all module dependencies,
|
379
|
+
generate a Puppetfile, and install the modules.
|
380
|
+
HELP
|
381
|
+
|
344
382
|
PLAN_HELP = <<~HELP
|
345
383
|
NAME
|
346
384
|
plan
|
@@ -828,7 +866,7 @@ module Bolt
|
|
828
866
|
"This option is experimental.") do |exec|
|
829
867
|
@options[:'copy-command'] = exec
|
830
868
|
end
|
831
|
-
define('--connect-timeout TIMEOUT', Integer, 'Connection timeout (defaults vary)') do |timeout|
|
869
|
+
define('--connect-timeout TIMEOUT', Integer, 'Connection timeout in seconds (defaults vary)') do |timeout|
|
832
870
|
@options[:'connect-timeout'] = timeout
|
833
871
|
end
|
834
872
|
define('--[no-]tty', 'Request a pseudo TTY on targets that support it') do |tty|
|
@@ -864,9 +902,9 @@ module Bolt
|
|
864
902
|
define('--modules MODULES',
|
865
903
|
'A comma-separated list of modules to install from the Puppet Forge',
|
866
904
|
'when initializing a project. Resolves and installs all dependencies.') do |modules|
|
867
|
-
@options[:modules] = modules.split(',')
|
905
|
+
@options[:modules] = modules.split(',').map { |mod| { 'name' => mod } }
|
868
906
|
end
|
869
|
-
define('--force', '
|
907
|
+
define('--force', 'Force a destructive action') do |_force|
|
870
908
|
@options[:force] = true
|
871
909
|
end
|
872
910
|
|
data/lib/bolt/cli.rb
CHANGED
@@ -40,7 +40,8 @@ module Bolt
|
|
40
40
|
'group' => %w[show],
|
41
41
|
'project' => %w[init migrate],
|
42
42
|
'apply' => %w[],
|
43
|
-
'guide' => %w[]
|
43
|
+
'guide' => %w[],
|
44
|
+
'module' => %w[install] }.freeze
|
44
45
|
|
45
46
|
attr_reader :config, :options
|
46
47
|
|
@@ -210,10 +211,14 @@ module Bolt
|
|
210
211
|
end
|
211
212
|
|
212
213
|
def validate(options)
|
213
|
-
unless
|
214
|
+
# Disables the 'module' subcommand unless the module feature flag is set.
|
215
|
+
commands = COMMANDS.dup
|
216
|
+
commands.delete('module') unless ENV['BOLT_MODULE_FEATURE']
|
217
|
+
|
218
|
+
unless commands.include?(options[:subcommand])
|
214
219
|
raise Bolt::CLIError,
|
215
220
|
"Expected subcommand '#{options[:subcommand]}' to be one of " \
|
216
|
-
"#{
|
221
|
+
"#{commands.keys.join(', ')}"
|
217
222
|
end
|
218
223
|
|
219
224
|
actions = COMMANDS[options[:subcommand]]
|
@@ -353,7 +358,7 @@ module Bolt
|
|
353
358
|
# Initialize inventory and targets. Errors here are better to catch early.
|
354
359
|
# options[:target_args] will contain a string/array version of the targetting options this is passed to plans
|
355
360
|
# options[:targets] will contain a resolved set of Target objects
|
356
|
-
unless %w[project puppetfile secret
|
361
|
+
unless %w[guide module project puppetfile secret].include?(options[:subcommand]) ||
|
357
362
|
%w[convert new show].include?(options[:action])
|
358
363
|
update_targets(options)
|
359
364
|
end
|
@@ -443,12 +448,17 @@ module Bolt
|
|
443
448
|
when 'run'
|
444
449
|
code = run_plan(options[:object], options[:task_options], options[:target_args], options)
|
445
450
|
end
|
451
|
+
when 'module'
|
452
|
+
case options[:action]
|
453
|
+
when 'install'
|
454
|
+
code = install_project_modules
|
455
|
+
end
|
446
456
|
when 'puppetfile'
|
447
457
|
case options[:action]
|
448
458
|
when 'generate-types'
|
449
459
|
code = generate_types
|
450
460
|
when 'install'
|
451
|
-
code = install_puppetfile(config.puppetfile_config, config.puppetfile, config.modulepath)
|
461
|
+
code = install_puppetfile(config.puppetfile_config, config.puppetfile, config.modulepath.first)
|
452
462
|
end
|
453
463
|
when 'secret'
|
454
464
|
code = Bolt::Secret.execute(plugins, outputter, options)
|
@@ -798,36 +808,38 @@ module Bolt
|
|
798
808
|
old_config = project + 'bolt.yaml'
|
799
809
|
config = project + 'bolt-project.yaml'
|
800
810
|
puppetfile = project + 'Puppetfile'
|
801
|
-
|
811
|
+
moduledir = project + 'modules'
|
812
|
+
|
813
|
+
# Warn the user if the project directory already exists. We don't error
|
814
|
+
# here since users might not have installed any modules yet. If both
|
815
|
+
# bolt.yaml and bolt-project.yaml exist, this will just warn about
|
816
|
+
# bolt-project.yaml and subsequent Bolt actions will warn about both files
|
817
|
+
# existing.
|
818
|
+
if config.exist?
|
819
|
+
@logger.warn "Found existing project directory at #{project}. Skipping file creation."
|
820
|
+
elsif old_config.exist?
|
821
|
+
@logger.warn "Found existing #{old_config.basename} at #{project}. "\
|
822
|
+
"#{old_config.basename} is deprecated, please rename to #{config.basename}."
|
823
|
+
end
|
802
824
|
|
803
|
-
# If modules were specified, first check if there is already a Puppetfile
|
804
|
-
# directory, erroring if there is. If there is no
|
805
|
-
#
|
806
|
-
#
|
807
|
-
# dependencies are caught early and do not create a project directory.
|
825
|
+
# If modules were specified, first check if there is already a Puppetfile
|
826
|
+
# at the project directory, erroring if there is. If there is no
|
827
|
+
# Puppetfile, install the specified modules. The module installer will
|
828
|
+
# resolve dependencies, generate a Puppetfile, and install the modules.
|
808
829
|
if options[:modules]
|
809
830
|
if puppetfile.exist?
|
810
831
|
raise Bolt::CLIError,
|
811
|
-
"Found existing Puppetfile at #{puppetfile}, unable to initialize
|
812
|
-
"
|
813
|
-
else
|
814
|
-
puppetfile_specs = resolve_puppetfile_specs
|
832
|
+
"Found existing Puppetfile at #{puppetfile}, unable to initialize "\
|
833
|
+
"project with modules."
|
815
834
|
end
|
835
|
+
|
836
|
+
install_modules(puppetfile, {}, moduledir, options[:modules])
|
816
837
|
end
|
817
838
|
|
818
|
-
#
|
819
|
-
#
|
820
|
-
#
|
821
|
-
|
822
|
-
# both files existing
|
823
|
-
if config.exist?
|
824
|
-
@logger.warn "Found existing project directory at #{project}. Skipping file creation."
|
825
|
-
# This won't get called if bolt-project.yaml exists
|
826
|
-
elsif old_config.exist?
|
827
|
-
@logger.warn "Found existing #{old_config.basename} at #{project}. "\
|
828
|
-
"#{old_config.basename} is deprecated, please rename to #{config.basename}."
|
829
|
-
# Bless the project directory as a...wait for it...project
|
830
|
-
else
|
839
|
+
# If either bolt.yaml or bolt-project.yaml exist, the user has already
|
840
|
+
# been warned and we can just finish project creation. Otherwise, create a
|
841
|
+
# bolt-project.yaml with the project name in it.
|
842
|
+
unless config.exist? || old_config.exist?
|
831
843
|
begin
|
832
844
|
content = { 'name' => name }
|
833
845
|
File.write(config.to_path, content.to_yaml)
|
@@ -837,109 +849,82 @@ module Bolt
|
|
837
849
|
end
|
838
850
|
end
|
839
851
|
|
840
|
-
# Write the generated Puppetfile to the fancy new project
|
841
|
-
if puppetfile_specs
|
842
|
-
File.write(puppetfile, puppetfile_specs.join("\n"))
|
843
|
-
outputter.print_message "Successfully created Puppetfile at #{puppetfile}"
|
844
|
-
# Install the modules from our shiny new Puppetfile
|
845
|
-
if install_puppetfile({}, puppetfile, modulepath)
|
846
|
-
outputter.print_message "Successfully installed #{options[:modules].join(', ')}"
|
847
|
-
else
|
848
|
-
raise Bolt::CLIError, "Could not install #{options[:modules].join(', ')}"
|
849
|
-
end
|
850
|
-
end
|
851
|
-
|
852
852
|
0
|
853
853
|
end
|
854
854
|
|
855
|
-
#
|
856
|
-
#
|
857
|
-
def
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
options[:modules].each do |mod_name|
|
863
|
-
model.add_module(
|
864
|
-
PuppetfileResolver::Puppetfile::ForgeModule.new(mod_name).tap { |mod| mod.version = :latest }
|
865
|
-
)
|
866
|
-
end
|
867
|
-
|
868
|
-
# Make sure the Puppetfile model is valid
|
869
|
-
unless model.valid?
|
870
|
-
raise Bolt::ValidationError,
|
871
|
-
"Unable to resolve dependencies for #{options[:modules].join(', ')}"
|
855
|
+
# Installs modules declared in the project configuration file.
|
856
|
+
#
|
857
|
+
def install_project_modules
|
858
|
+
if config.project.modules.nil?
|
859
|
+
outputter.print_message "Project configuration file '#{config.project.project_file}' "\
|
860
|
+
"does not specify any module dependencies. Nothing to do."
|
861
|
+
return 0
|
872
862
|
end
|
873
863
|
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
cache: nil,
|
880
|
-
ui: nil,
|
881
|
-
module_paths: [],
|
882
|
-
allow_missing_modules: true
|
864
|
+
install_modules(
|
865
|
+
config.puppetfile,
|
866
|
+
config.puppetfile_config,
|
867
|
+
config.project.path + '.modules',
|
868
|
+
config.project.modules
|
883
869
|
)
|
870
|
+
end
|
884
871
|
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
872
|
+
# Installs modules declared in the project configuration file.
|
873
|
+
#
|
874
|
+
def install_modules(puppetfile_path, config, moduledir, modules)
|
875
|
+
require 'bolt/puppetfile'
|
876
|
+
require 'bolt/puppetfile/installer'
|
877
|
+
|
878
|
+
puppetfile = Bolt::Puppetfile.new(modules)
|
879
|
+
|
880
|
+
# If the Puppetfile exists, check if it includes specs for each declared
|
881
|
+
# module, erroring if there are any missing. Otherwise, resolve the
|
882
|
+
# module dependencies and write a new Puppetfile. Users can forcibly
|
883
|
+
# overwrite an existing Puppetfile with the '--force' option.
|
884
|
+
if puppetfile_path.exist? && !options[:force]
|
885
|
+
outputter.print_message "Parsing existing Puppetfile at #{puppetfile_path}"
|
886
|
+
existing = Bolt::Puppetfile.parse(puppetfile_path)
|
887
|
+
|
888
|
+
unless existing.modules.superset? puppetfile.modules
|
889
|
+
missing_modules = puppetfile.modules - existing.modules
|
890
|
+
|
891
|
+
raise Bolt::Error.new(
|
892
|
+
"Puppetfile #{puppetfile_path} is missing specifications for modules: "\
|
893
|
+
"#{missing_modules.map(&:title).join(', ')}. This may not be a Puppetfile "\
|
894
|
+
"managed by Bolt. To forcibly overwrite the Puppetfile, run with the "\
|
895
|
+
"'--force' option.",
|
896
|
+
'bolt/missing-module-specs'
|
897
|
+
)
|
893
898
|
end
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
"Unknown module name#{plural} #{names.join(', ')}"
|
900
|
-
end
|
901
|
-
|
902
|
-
# Filter the dependency graph to only include module specifications
|
903
|
-
spec_graph = result.specifications.select do |_name, spec|
|
904
|
-
spec.instance_of? PuppetfileResolver::Models::ModuleSpecification
|
899
|
+
else
|
900
|
+
outputter.print_message "Resolving module dependencies, this may take a moment"
|
901
|
+
puppetfile.resolve
|
902
|
+
outputter.print_message "Writing Puppetfile at #{puppetfile_path}"
|
903
|
+
puppetfile.write(puppetfile_path, force: true)
|
905
904
|
end
|
906
905
|
|
907
|
-
|
908
|
-
|
909
|
-
"mod '#{spec.owner}-#{spec.name}', '#{spec.version}'"
|
910
|
-
end
|
911
|
-
end
|
906
|
+
outputter.print_message "Syncing modules from #{puppetfile_path} to #{moduledir}"
|
907
|
+
ok = Bolt::Puppetfile::Installer.new(config).install(puppetfile_path, moduledir)
|
912
908
|
|
913
|
-
|
914
|
-
|
915
|
-
require 'bolt/r10k_log_proxy'
|
909
|
+
# Automatically generate types after installing modules.
|
910
|
+
pal.generate_types
|
916
911
|
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
root: puppetfile.dirname.to_s,
|
921
|
-
puppetfile: puppetfile.to_s,
|
922
|
-
moduledir: moduledir
|
923
|
-
}
|
912
|
+
outputter.print_puppetfile_result(ok, puppetfile_path, moduledir)
|
913
|
+
ok ? 0 : 1
|
914
|
+
end
|
924
915
|
|
925
|
-
|
926
|
-
|
927
|
-
|
916
|
+
# Loads a Puppetfile and installs its modules.
|
917
|
+
#
|
918
|
+
def install_puppetfile(config, puppetfile, moduledir)
|
919
|
+
require 'bolt/puppetfile/installer'
|
928
920
|
|
929
|
-
|
930
|
-
R10K::Logging.instance_variable_set(:@outputter, Bolt::R10KLogProxy.new)
|
921
|
+
ok = Bolt::Puppetfile::Installer.new(config).install(puppetfile, moduledir)
|
931
922
|
|
932
|
-
|
933
|
-
|
934
|
-
# Automatically generate types after installing modules
|
935
|
-
pal.generate_types
|
923
|
+
# Automatically generate types after installing modules.
|
924
|
+
pal.generate_types
|
936
925
|
|
937
|
-
|
938
|
-
|
939
|
-
raise Bolt::FileError.new("Could not find a Puppetfile at #{puppetfile}", puppetfile)
|
940
|
-
end
|
941
|
-
rescue R10K::Error => e
|
942
|
-
raise PuppetfileError, e
|
926
|
+
outputter.print_puppetfile_result(ok, puppetfile, moduledir)
|
927
|
+
ok ? 0 : 1
|
943
928
|
end
|
944
929
|
|
945
930
|
def pal
|
data/lib/bolt/config.rb
CHANGED
@@ -64,7 +64,7 @@ module Bolt
|
|
64
64
|
end
|
65
65
|
|
66
66
|
data = load_defaults(project).push(
|
67
|
-
filepath:
|
67
|
+
filepath: configfile,
|
68
68
|
data: conf,
|
69
69
|
logs: logs,
|
70
70
|
deprecations: []
|
@@ -344,6 +344,14 @@ module Bolt
|
|
344
344
|
end
|
345
345
|
|
346
346
|
private def update_logs(logs)
|
347
|
+
begin
|
348
|
+
if logs['bolt-debug.log'] && logs['bolt-debug.log'] != 'disable'
|
349
|
+
FileUtils.touch(File.expand_path('bolt-debug.log', @project.path))
|
350
|
+
end
|
351
|
+
rescue StandardError
|
352
|
+
logs.delete('bolt-debug.log')
|
353
|
+
end
|
354
|
+
|
347
355
|
logs.each_with_object({}) do |(key, val), acc|
|
348
356
|
# Remove any disabled logs
|
349
357
|
next if val == 'disable'
|
data/lib/bolt/config/options.rb
CHANGED
@@ -227,6 +227,24 @@ module Bolt
|
|
227
227
|
_example: ["~/.puppetlabs/bolt/modules", "~/.puppetlabs/bolt/site-modules"],
|
228
228
|
_default: ["project/modules", "project/site-modules", "project/site"]
|
229
229
|
},
|
230
|
+
"modules" => {
|
231
|
+
description: "A list of module dependencies for the project. Each dependency is a map of data specifying "\
|
232
|
+
"the module to install. To install the project's module dependencies, run the `bolt module "\
|
233
|
+
"install` command.",
|
234
|
+
type: Array,
|
235
|
+
items: {
|
236
|
+
type: Hash,
|
237
|
+
required: ["name"],
|
238
|
+
properties: {
|
239
|
+
"name" => {
|
240
|
+
description: "The name of the module.",
|
241
|
+
type: String
|
242
|
+
}
|
243
|
+
}
|
244
|
+
},
|
245
|
+
_plugin: false,
|
246
|
+
_example: [{ "name" => "puppetlabs-mysql" }, { "name" => "puppetlabs-apache" }]
|
247
|
+
},
|
230
248
|
"name" => {
|
231
249
|
description: "The name of the Bolt project. When this option is configured, the project is considered a "\
|
232
250
|
"[Bolt project](experimental_features.md#bolt-projects), allowing Bolt to load content from "\
|
@@ -476,6 +494,7 @@ module Bolt
|
|
476
494
|
inventoryfile
|
477
495
|
log
|
478
496
|
modulepath
|
497
|
+
modules
|
479
498
|
name
|
480
499
|
plans
|
481
500
|
plugin_hooks
|
data/lib/bolt/pal.rb
CHANGED
@@ -141,6 +141,19 @@ module Bolt
|
|
141
141
|
end
|
142
142
|
end
|
143
143
|
|
144
|
+
def detect_project_conflict(project, environment)
|
145
|
+
return unless project && project.load_as_module?
|
146
|
+
# The environment modulepath has stripped out non-existent directories,
|
147
|
+
# so we don't need to check for them
|
148
|
+
modules = environment.modulepath.flat_map do |path|
|
149
|
+
Dir.children(path).select { |name| Puppet::Module.is_module_directory?(name, path) }
|
150
|
+
end
|
151
|
+
if modules.include?(project.name)
|
152
|
+
Bolt::Logger.warn_once("project shadows module",
|
153
|
+
"The project '#{project.name}' shadows an existing module of the same name")
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
144
157
|
# Runs a block in a PAL script compiler configured for Bolt. Catches
|
145
158
|
# exceptions thrown by the block and re-raises them ensuring they are
|
146
159
|
# Bolt::Errors since the script compiler block will squash all exceptions.
|
@@ -151,6 +164,10 @@ module Bolt
|
|
151
164
|
# Only load the project if it a) exists, b) has a name it can be loaded with
|
152
165
|
Puppet.override(bolt_project: @project,
|
153
166
|
yaml_plan_instantiator: Bolt::PAL::YamlPlan::Loader) do
|
167
|
+
# Because this has the side effect of loading and caching the list
|
168
|
+
# of modules, it must happen *after* we have overridden
|
169
|
+
# bolt_project or the project will be ignored
|
170
|
+
detect_project_conflict(@project, Puppet.lookup(:environments).get('bolt'))
|
154
171
|
pal.with_script_compiler(set_local_facts: false) do |compiler|
|
155
172
|
alias_types(compiler)
|
156
173
|
register_resource_types(Puppet.lookup(:loaders)) if @resource_types
|
data/lib/bolt/project.rb
CHANGED
@@ -50,6 +50,20 @@ module Bolt
|
|
50
50
|
def self.create_project(path, type = 'option', logs = [])
|
51
51
|
fullpath = Pathname.new(path).expand_path
|
52
52
|
|
53
|
+
if type == 'user'
|
54
|
+
begin
|
55
|
+
# This is already expanded if the type is user
|
56
|
+
FileUtils.mkdir_p(path)
|
57
|
+
rescue StandardError
|
58
|
+
logs << { warn: "Could not create default project at #{path}. Continuing without a writeable project. "\
|
59
|
+
"Log and rerun files will not be written." }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
if type == 'option' && !File.directory?(path)
|
64
|
+
raise Bolt::Error.new("Could not find project at #{path}", "bolt/project-error")
|
65
|
+
end
|
66
|
+
|
53
67
|
if !Bolt::Util.windows? && type != 'environment' && fullpath.world_writable?
|
54
68
|
raise Bolt::Error.new(
|
55
69
|
"Project directory '#{fullpath}' is world-writable which poses a security risk. Set "\
|
@@ -149,6 +163,10 @@ module Bolt
|
|
149
163
|
@data['plans']
|
150
164
|
end
|
151
165
|
|
166
|
+
def modules
|
167
|
+
@data['modules']
|
168
|
+
end
|
169
|
+
|
152
170
|
def validate
|
153
171
|
if name
|
154
172
|
if name !~ Bolt::Module::MODULE_NAME_REGEX
|
@@ -170,6 +188,23 @@ module Bolt
|
|
170
188
|
raise Bolt::ValidationError, "'#{conf}' in bolt-project.yaml must be an array"
|
171
189
|
end
|
172
190
|
end
|
191
|
+
|
192
|
+
if @data['modules']
|
193
|
+
unless @data['modules'].is_a?(Array)
|
194
|
+
raise Bolt::ValidationError, "'modules' in bolt-project.yaml must be an array"
|
195
|
+
end
|
196
|
+
|
197
|
+
@data['modules'].each do |mod|
|
198
|
+
next if mod.is_a?(Hash)
|
199
|
+
raise Bolt::ValidationError, "Module declaration #{mod.inspect} must be a hash"
|
200
|
+
end
|
201
|
+
|
202
|
+
unknown_keys = data['modules'].flat_map(&:keys).uniq - ['name']
|
203
|
+
if unknown_keys.any?
|
204
|
+
@logs << { warn: "Module declarations in bolt-project.yaml only support a name key. Ignoring "\
|
205
|
+
"unsupported keys: #{unknown_keys.join(', ')}." }
|
206
|
+
end
|
207
|
+
end
|
173
208
|
end
|
174
209
|
|
175
210
|
def check_deprecated_file
|
@@ -0,0 +1,160 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/error'
|
4
|
+
require 'bolt/puppetfile/module'
|
5
|
+
|
6
|
+
# This class manages the logical contents of a Puppetfile. It includes methods
|
7
|
+
# for parsing a Puppetfile and its modules, resolving module dependencies,
|
8
|
+
# and writing a Puppetfile.
|
9
|
+
#
|
10
|
+
module Bolt
|
11
|
+
class Puppetfile
|
12
|
+
attr_reader :modules
|
13
|
+
|
14
|
+
def initialize(modules = [])
|
15
|
+
@modules = Set.new
|
16
|
+
add_modules(modules)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Loads a Puppetfile and parses its module specifications, returning a
|
20
|
+
# Bolt::Puppetfile object with the modules set.
|
21
|
+
#
|
22
|
+
def self.parse(path)
|
23
|
+
require 'puppetfile-resolver'
|
24
|
+
require 'puppetfile-resolver/puppetfile/parser/r10k_eval'
|
25
|
+
|
26
|
+
begin
|
27
|
+
parsed = ::PuppetfileResolver::Puppetfile::Parser::R10KEval.parse(File.read(path))
|
28
|
+
rescue StandardError => e
|
29
|
+
raise Bolt::Error.new(
|
30
|
+
"Unable to parse Puppetfile #{path}: #{e.message}",
|
31
|
+
'bolt/puppetfile-parsing'
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
unless parsed.valid?
|
36
|
+
raise Bolt::ValidationError,
|
37
|
+
"Unable to parse Puppetfile #{path}"
|
38
|
+
end
|
39
|
+
|
40
|
+
modules = parsed.modules.map do |mod|
|
41
|
+
Bolt::Puppetfile::Module.new(mod.owner, mod.name, mod.version)
|
42
|
+
end
|
43
|
+
|
44
|
+
new(modules)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Writes a Puppetfile that includes specifications for each of the
|
48
|
+
# modules.
|
49
|
+
#
|
50
|
+
def write(path, force: false)
|
51
|
+
if File.exist?(path) && !force
|
52
|
+
raise Bolt::FileError.new(
|
53
|
+
"Cannot overwrite existing Puppetfile at #{path}. To forcibly overwrite, "\
|
54
|
+
"run with the '--force' option.",
|
55
|
+
path
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
File.open(path, 'w') do |file|
|
60
|
+
file.puts '# This Puppetfile is managed by Bolt. Do not edit.'
|
61
|
+
modules.each { |mod| file.puts mod.to_spec }
|
62
|
+
file.puts
|
63
|
+
end
|
64
|
+
rescue SystemCallError => e
|
65
|
+
raise Bolt::FileError.new(
|
66
|
+
"#{e.message}: unable to write Puppetfile.",
|
67
|
+
path
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Resolves module dependencies using the puppetfile-resolver library. The
|
72
|
+
# resolver will return a document model including all module dependencies
|
73
|
+
# and the latest version that can be installed for each. The document model
|
74
|
+
# is parsed and turned into a Set of Bolt::Puppetfile::Module objects.
|
75
|
+
#
|
76
|
+
def resolve
|
77
|
+
require 'puppetfile-resolver'
|
78
|
+
|
79
|
+
# Build the document model from the modules.
|
80
|
+
model = PuppetfileResolver::Puppetfile::Document.new('')
|
81
|
+
|
82
|
+
@modules.each do |mod|
|
83
|
+
model.add_module(
|
84
|
+
PuppetfileResolver::Puppetfile::ForgeModule.new(mod.title).tap do |tap|
|
85
|
+
tap.version = :latest
|
86
|
+
end
|
87
|
+
)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Make sure the Puppetfile model is valid.
|
91
|
+
unless model.valid?
|
92
|
+
raise Bolt::ValidationError,
|
93
|
+
"Unable to resolve dependencies for modules: #{@modules.map(&:title).join(', ')}"
|
94
|
+
end
|
95
|
+
|
96
|
+
# Create the resolver using the Puppetfile model. nil disables Puppet
|
97
|
+
# version restrictions.
|
98
|
+
resolver = PuppetfileResolver::Resolver.new(model, nil)
|
99
|
+
|
100
|
+
# Configure and resolve the dependency graph, catching any errors
|
101
|
+
# raised by puppetfile-resolver and re-raising them as Bolt errors.
|
102
|
+
begin
|
103
|
+
result = resolver.resolve(
|
104
|
+
cache: nil,
|
105
|
+
ui: nil,
|
106
|
+
module_paths: [],
|
107
|
+
allow_missing_modules: true
|
108
|
+
)
|
109
|
+
rescue StandardError => e
|
110
|
+
raise Bolt::Error.new(e.message, 'bolt/puppetfile-resolver-error')
|
111
|
+
end
|
112
|
+
|
113
|
+
# Validate that the modules exist.
|
114
|
+
missing_graph = result.specifications.select do |_name, spec|
|
115
|
+
spec.instance_of? PuppetfileResolver::Models::MissingModuleSpecification
|
116
|
+
end
|
117
|
+
|
118
|
+
if missing_graph.any?
|
119
|
+
titles = model.modules.each_with_object({}) do |mod, acc|
|
120
|
+
acc[mod.name] = mod.title
|
121
|
+
end
|
122
|
+
|
123
|
+
names = titles.values_at(*missing_graph.keys)
|
124
|
+
plural = names.count == 1 ? '' : 's'
|
125
|
+
|
126
|
+
raise Bolt::Error.new(
|
127
|
+
"Unknown module name#{plural} #{names.join(', ')}",
|
128
|
+
'bolt/unknown-modules'
|
129
|
+
)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Filter the dependency graph to only include module specifications. This
|
133
|
+
# will only remove the Puppet version specification, which is not needed.
|
134
|
+
specs = result.specifications.select do |_name, spec|
|
135
|
+
spec.instance_of? PuppetfileResolver::Models::ModuleSpecification
|
136
|
+
end
|
137
|
+
|
138
|
+
@modules = specs.map do |_name, spec|
|
139
|
+
Bolt::Puppetfile::Module.new(spec.owner, spec.name, spec.version.to_s)
|
140
|
+
end.to_set
|
141
|
+
end
|
142
|
+
|
143
|
+
# Adds to the set of modules.
|
144
|
+
#
|
145
|
+
def add_modules(modules)
|
146
|
+
modules.each do |mod|
|
147
|
+
case mod
|
148
|
+
when Bolt::Puppetfile::Module
|
149
|
+
@modules << mod
|
150
|
+
when Hash
|
151
|
+
@modules << Bolt::Puppetfile::Module.from_hash(mod)
|
152
|
+
else
|
153
|
+
raise Bolt::ValidationError, "Module must be a Bolt::Puppetfile::Module or Hash."
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
@modules
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'r10k/cli'
|
4
|
+
require 'bolt/r10k_log_proxy'
|
5
|
+
require 'bolt/error'
|
6
|
+
|
7
|
+
# This class is used to install modules from a Puppetfile to a module directory.
|
8
|
+
#
|
9
|
+
module Bolt
|
10
|
+
class Puppetfile
|
11
|
+
class Installer
|
12
|
+
def initialize(config = {})
|
13
|
+
@config = config
|
14
|
+
end
|
15
|
+
|
16
|
+
def install(path, moduledir)
|
17
|
+
unless File.exist?(path)
|
18
|
+
raise Bolt::FileError.new(
|
19
|
+
"Could not find a Puppetfile at #{path}",
|
20
|
+
path
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
r10k_opts = {
|
25
|
+
root: File.dirname(path),
|
26
|
+
puppetfile: path.to_s,
|
27
|
+
moduledir: moduledir.to_s
|
28
|
+
}
|
29
|
+
|
30
|
+
settings = R10K::Settings.global_settings.evaluate(@config)
|
31
|
+
R10K::Initializers::GlobalInitializer.new(settings).call
|
32
|
+
install_action = R10K::Action::Puppetfile::Install.new(r10k_opts, nil)
|
33
|
+
|
34
|
+
# Override the r10k logger with a proxy to our own logger
|
35
|
+
R10K::Logging.instance_variable_set(:@outputter, Bolt::R10KLogProxy.new)
|
36
|
+
|
37
|
+
install_action.call
|
38
|
+
rescue R10K::Error => e
|
39
|
+
raise PuppetfileError, e
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/error'
|
4
|
+
|
5
|
+
# This class represents a module specification. It used by the Bolt::Puppetfile
|
6
|
+
# class to have a consistent API for accessing a module's attributes.
|
7
|
+
#
|
8
|
+
module Bolt
|
9
|
+
class Puppetfile
|
10
|
+
class Module
|
11
|
+
attr_reader :owner, :name, :version
|
12
|
+
|
13
|
+
def initialize(owner, name, version = nil)
|
14
|
+
@owner = owner
|
15
|
+
@name = name
|
16
|
+
@version = version
|
17
|
+
end
|
18
|
+
|
19
|
+
# Creates a new module from a hash.
|
20
|
+
#
|
21
|
+
def self.from_hash(mod)
|
22
|
+
unless mod['name'].is_a?(String)
|
23
|
+
raise Bolt::ValidationError,
|
24
|
+
"Module name must be a String, not #{mod['name'].inspect}"
|
25
|
+
end
|
26
|
+
|
27
|
+
owner, name = mod['name'].tr('/', '-').split('-', 2)
|
28
|
+
|
29
|
+
unless owner && name
|
30
|
+
raise Bolt::ValidationError, "Module name #{mod['name']} must include both the owner and module name."
|
31
|
+
end
|
32
|
+
|
33
|
+
new(owner, name)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns the module's title.
|
37
|
+
#
|
38
|
+
def title
|
39
|
+
"#{@owner}-#{@name}"
|
40
|
+
end
|
41
|
+
|
42
|
+
# Checks two modules for equality.
|
43
|
+
#
|
44
|
+
def eql?(other)
|
45
|
+
self.class == other.class && @owner == other.owner && @name == other.name
|
46
|
+
end
|
47
|
+
alias == eql?
|
48
|
+
|
49
|
+
# Hashes the module.
|
50
|
+
#
|
51
|
+
def hash
|
52
|
+
[@owner, @name].hash
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns the Puppetfile specification for the module.
|
56
|
+
#
|
57
|
+
def to_spec
|
58
|
+
if @version
|
59
|
+
"mod #{title.inspect}, #{@version.inspect}"
|
60
|
+
else
|
61
|
+
"mod #{title.inspect}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/bolt/rerun.rb
CHANGED
data/lib/bolt/result.rb
CHANGED
@@ -66,9 +66,24 @@ module Bolt
|
|
66
66
|
'details' => { 'exit_code' => exit_code } }
|
67
67
|
end
|
68
68
|
|
69
|
+
if value.key?('_error')
|
70
|
+
unless value['_error'].is_a?(Hash) && value['_error'].key?('msg')
|
71
|
+
value['_error'] = {
|
72
|
+
'msg' => "Invalid error returned from task #{task}: #{value['_error'].inspect}. Error "\
|
73
|
+
"must be an object with a msg key.",
|
74
|
+
'kind' => 'bolt/invalid-task-error',
|
75
|
+
'details' => { 'original_error' => value['_error'] }
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
value['_error']['kind'] ||= 'bolt/error'
|
80
|
+
value['_error']['details'] ||= {}
|
81
|
+
end
|
82
|
+
|
69
83
|
if value.key?('_sensitive')
|
70
84
|
value['_sensitive'] = Puppet::Pops::Types::PSensitiveType::Sensitive.new(value['_sensitive'])
|
71
85
|
end
|
86
|
+
|
72
87
|
new(target, value: value, action: 'task', object: task)
|
73
88
|
end
|
74
89
|
|
data/lib/bolt/version.rb
CHANGED
data/lib/bolt_server/config.rb
CHANGED
@@ -7,7 +7,7 @@ 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]
|
10
|
+
super + %w[concurrency cache-dir file-server-conn-timeout file-server-uri projects-dir]
|
11
11
|
end
|
12
12
|
|
13
13
|
def env_keys
|
@@ -5,6 +5,7 @@ require 'addressable/uri'
|
|
5
5
|
require 'bolt'
|
6
6
|
require 'bolt/error'
|
7
7
|
require 'bolt/inventory'
|
8
|
+
require 'bolt/project'
|
8
9
|
require 'bolt/target'
|
9
10
|
require 'bolt_server/file_cache'
|
10
11
|
require 'bolt/task/puppet_server'
|
@@ -191,20 +192,44 @@ module BoltServer
|
|
191
192
|
end
|
192
193
|
|
193
194
|
def in_pe_pal_env(environment)
|
194
|
-
if environment.nil?
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
195
|
+
return [400, '`environment` is a required argument'] if environment.nil?
|
196
|
+
@pal_mutex.synchronize do
|
197
|
+
pal = BoltServer::PE::PAL.new({}, environment)
|
198
|
+
yield pal
|
199
|
+
rescue Puppet::Environments::EnvironmentNotFound
|
200
|
+
[400, {
|
201
|
+
"class" => 'bolt/unknown-environment',
|
202
|
+
"message" => "Environment #{environment} not found"
|
203
|
+
}.to_json]
|
204
|
+
rescue Bolt::Error => e
|
205
|
+
[400, e.to_json]
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def in_bolt_project(bolt_project)
|
210
|
+
return [400, '`project_ref` is a required argument'] if bolt_project.nil?
|
211
|
+
project_dir = File.join(@config['projects-dir'], bolt_project)
|
212
|
+
return [400, "`project_ref`: #{project_dir} does not exist"] unless Dir.exist?(project_dir)
|
213
|
+
@pal_mutex.synchronize do
|
214
|
+
project = Bolt::Project.create_project(project_dir)
|
215
|
+
bolt_config = Bolt::Config.from_project(project, {})
|
216
|
+
pal = Bolt::PAL.new(bolt_config.modulepath, nil, nil, nil, nil, nil, bolt_config.project)
|
217
|
+
module_path = [
|
218
|
+
BoltServer::PE::PAL::PE_BOLTLIB_PATH,
|
219
|
+
Bolt::PAL::BOLTLIB_PATH,
|
220
|
+
*bolt_config.modulepath,
|
221
|
+
Bolt::PAL::MODULES_PATH
|
222
|
+
]
|
223
|
+
# CODEREVIEW: I *think* this is the only thing we need to make different between bolt's PAL. Is it acceptable
|
224
|
+
# to hack this? Modulepath is currently a readable attribute, could we make it writeable?
|
225
|
+
pal.instance_variable_set(:@modulepath, module_path)
|
226
|
+
context = {
|
227
|
+
pal: pal,
|
228
|
+
config: bolt_config
|
229
|
+
}
|
230
|
+
yield context
|
231
|
+
rescue Bolt::Error => e
|
232
|
+
[400, e.to_json]
|
208
233
|
end
|
209
234
|
end
|
210
235
|
|
@@ -221,14 +246,12 @@ module BoltServer
|
|
221
246
|
plan_info
|
222
247
|
end
|
223
248
|
|
224
|
-
def build_puppetserver_uri(file_identifier, module_name,
|
249
|
+
def build_puppetserver_uri(file_identifier, module_name, parameters)
|
225
250
|
segments = file_identifier.split('/', 3)
|
226
251
|
if segments.size == 1
|
227
252
|
{
|
228
253
|
'path' => "/puppet/v3/file_content/tasks/#{module_name}/#{file_identifier}",
|
229
|
-
'params' =>
|
230
|
-
'environment' => environment
|
231
|
-
}
|
254
|
+
'params' => parameters
|
232
255
|
}
|
233
256
|
else
|
234
257
|
module_segment, mount_segment, name_segment = *segments
|
@@ -241,14 +264,12 @@ module BoltServer
|
|
241
264
|
when 'lib'
|
242
265
|
"/puppet/v3/file_content/plugins/#{name_segment}"
|
243
266
|
end,
|
244
|
-
'params' =>
|
245
|
-
'environment' => environment
|
246
|
-
}
|
267
|
+
'params' => parameters
|
247
268
|
}
|
248
269
|
end
|
249
270
|
end
|
250
271
|
|
251
|
-
def pe_task_info(pal, module_name, task_name,
|
272
|
+
def pe_task_info(pal, module_name, task_name, parameters)
|
252
273
|
# Handle case where task name is simply module name with special `init` task
|
253
274
|
task_name = if task_name == 'init' || task_name.nil?
|
254
275
|
module_name
|
@@ -261,7 +282,7 @@ module BoltServer
|
|
261
282
|
'filename' => file_hash['name'],
|
262
283
|
'sha256' => Digest::SHA256.hexdigest(File.read(file_hash['path'])),
|
263
284
|
'size_bytes' => File.size(file_hash['path']),
|
264
|
-
'uri' => build_puppetserver_uri(file_hash['name'], module_name,
|
285
|
+
'uri' => build_puppetserver_uri(file_hash['name'], module_name, parameters)
|
265
286
|
}
|
266
287
|
end
|
267
288
|
{
|
@@ -271,6 +292,21 @@ module BoltServer
|
|
271
292
|
}
|
272
293
|
end
|
273
294
|
|
295
|
+
def allowed_helper(metadata, allowlist)
|
296
|
+
allowed = allowlist.nil? || allowlist.include?(metadata['name']) ? true : false
|
297
|
+
metadata.merge({ 'allowed' => allowed })
|
298
|
+
end
|
299
|
+
|
300
|
+
def task_list(pal)
|
301
|
+
tasks = pal.list_tasks
|
302
|
+
tasks.map { |task_name, _description| { 'name' => task_name } }
|
303
|
+
end
|
304
|
+
|
305
|
+
def plan_list(pal)
|
306
|
+
plans = pal.list_plans.flatten
|
307
|
+
plans.map { |plan_name| { 'name' => plan_name } }
|
308
|
+
end
|
309
|
+
|
274
310
|
get '/' do
|
275
311
|
200
|
276
312
|
end
|
@@ -401,12 +437,40 @@ module BoltServer
|
|
401
437
|
end
|
402
438
|
end
|
403
439
|
|
440
|
+
# Fetches the metadata for a single plan
|
441
|
+
#
|
442
|
+
# @param project_ref [String] the project to fetch the plan from
|
443
|
+
get '/project_plans/:module_name/:plan_name' do
|
444
|
+
in_bolt_project(params['project_ref']) do |context|
|
445
|
+
plan_info = pe_plan_info(context[:pal], params[:module_name], params[:plan_name])
|
446
|
+
plan_info = allowed_helper(plan_info, context[:config].project.plans)
|
447
|
+
[200, plan_info.to_json]
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
404
451
|
# Fetches the metadata for a single task
|
405
452
|
#
|
406
453
|
# @param environment [String] the environment to fetch the task from
|
407
454
|
get '/tasks/:module_name/:task_name' do
|
408
455
|
in_pe_pal_env(params['environment']) do |pal|
|
409
|
-
|
456
|
+
ps_parameters = {
|
457
|
+
'environment' => params['environment']
|
458
|
+
}
|
459
|
+
task_info = pe_task_info(pal, params[:module_name], params[:task_name], ps_parameters)
|
460
|
+
[200, task_info.to_json]
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
# Fetches the metadata for a single task
|
465
|
+
#
|
466
|
+
# @param bolt_project_ref [String] the reference to the bolt-project directory to load task metadata from
|
467
|
+
get '/project_tasks/:module_name/:task_name' do
|
468
|
+
in_bolt_project(params['project_ref']) do |context|
|
469
|
+
ps_parameters = {
|
470
|
+
'project' => params['project_ref']
|
471
|
+
}
|
472
|
+
task_info = pe_task_info(context[:pal], params[:module_name], params[:task_name], ps_parameters)
|
473
|
+
task_info = allowed_helper(task_info, context[:config].project.tasks)
|
410
474
|
[200, task_info.to_json]
|
411
475
|
end
|
412
476
|
end
|
@@ -435,13 +499,30 @@ module BoltServer
|
|
435
499
|
end
|
436
500
|
end
|
437
501
|
|
502
|
+
# Fetches the list of plans for a project
|
503
|
+
#
|
504
|
+
# @param project_ref [String] the project to fetch the list of plans from
|
505
|
+
get '/project_plans' do
|
506
|
+
in_bolt_project(params['project_ref']) do |context|
|
507
|
+
plans_response = plan_list(context[:pal])
|
508
|
+
|
509
|
+
# Dig in context for the allowlist of plans from project object
|
510
|
+
plans_response.map! { |metadata| allowed_helper(metadata, context[:config].project.plans) }
|
511
|
+
|
512
|
+
# We structure this array of plans to be an array of hashes so that it matches the structure
|
513
|
+
# returned by the puppetserver API that serves data like this. Structuring the output this way
|
514
|
+
# makes switching between puppetserver and bolt-server easier, which makes changes to switch
|
515
|
+
# to bolt-server smaller/simpler.
|
516
|
+
[200, plans_response.to_json]
|
517
|
+
end
|
518
|
+
end
|
519
|
+
|
438
520
|
# Fetches the list of tasks for an environment
|
439
521
|
#
|
440
522
|
# @param environment [String] the environment to fetch the list of tasks from
|
441
523
|
get '/tasks' do
|
442
524
|
in_pe_pal_env(params['environment']) do |pal|
|
443
|
-
|
444
|
-
tasks_response = tasks.map { |task_name, _description| { 'name' => task_name } }.to_json
|
525
|
+
tasks_response = task_list(pal).to_json
|
445
526
|
|
446
527
|
# We structure this array of tasks to be an array of hashes so that it matches the structure
|
447
528
|
# returned by the puppetserver API that serves data like this. Structuring the output this way
|
@@ -451,6 +532,24 @@ module BoltServer
|
|
451
532
|
end
|
452
533
|
end
|
453
534
|
|
535
|
+
# Fetches the list of tasks for a bolt-project
|
536
|
+
#
|
537
|
+
# @param project_ref [String] the project to fetch the list of tasks from
|
538
|
+
get '/project_tasks' do
|
539
|
+
in_bolt_project(params['project_ref']) do |context|
|
540
|
+
tasks_response = task_list(context[:pal])
|
541
|
+
|
542
|
+
# Dig in context for the allowlist of tasks from project object
|
543
|
+
tasks_response.map! { |metadata| allowed_helper(metadata, context[:config].project.tasks) }
|
544
|
+
|
545
|
+
# We structure this array of tasks to be an array of hashes so that it matches the structure
|
546
|
+
# returned by the puppetserver API that serves data like this. Structuring the output this way
|
547
|
+
# makes switching between puppetserver and bolt-server easier, which makes changes to switch
|
548
|
+
# to bolt-server smaller/simpler.
|
549
|
+
[200, tasks_response.to_json]
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
454
553
|
error 404 do
|
455
554
|
err = Bolt::Error.new("Could not find route #{request.path}",
|
456
555
|
'boltserver/not-found')
|
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.27.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-08
|
11
|
+
date: 2020-09-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -498,6 +498,9 @@ files:
|
|
498
498
|
- lib/bolt/puppetdb.rb
|
499
499
|
- lib/bolt/puppetdb/client.rb
|
500
500
|
- lib/bolt/puppetdb/config.rb
|
501
|
+
- lib/bolt/puppetfile.rb
|
502
|
+
- lib/bolt/puppetfile/installer.rb
|
503
|
+
- lib/bolt/puppetfile/module.rb
|
501
504
|
- lib/bolt/r10k_log_proxy.rb
|
502
505
|
- lib/bolt/rerun.rb
|
503
506
|
- lib/bolt/resource_instance.rb
|