bolt 2.29.0 → 2.33.2

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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/Puppetfile +15 -14
  3. data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +1 -1
  4. data/bolt-modules/boltlib/lib/puppet/functions/facts.rb +6 -0
  5. data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_query.rb +2 -2
  6. data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +1 -1
  7. data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +1 -1
  8. data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +1 -1
  9. data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +1 -1
  10. data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +1 -1
  11. data/bolt-modules/boltlib/lib/puppet/functions/write_file.rb +2 -2
  12. data/bolt-modules/out/lib/puppet/functions/out/message.rb +44 -1
  13. data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +3 -0
  14. data/guides/logging.txt +18 -0
  15. data/guides/module.txt +19 -0
  16. data/guides/modulepath.txt +25 -0
  17. data/lib/bolt/bolt_option_parser.rb +48 -9
  18. data/lib/bolt/catalog.rb +1 -1
  19. data/lib/bolt/cli.rb +154 -116
  20. data/lib/bolt/config.rb +13 -1
  21. data/lib/bolt/config/modulepath.rb +30 -0
  22. data/lib/bolt/config/options.rb +32 -13
  23. data/lib/bolt/config/transport/options.rb +2 -2
  24. data/lib/bolt/error.rb +4 -0
  25. data/lib/bolt/executor.rb +13 -13
  26. data/lib/bolt/inventory.rb +10 -9
  27. data/lib/bolt/module_installer.rb +198 -0
  28. data/lib/bolt/{puppetfile → module_installer}/installer.rb +3 -2
  29. data/lib/bolt/module_installer/puppetfile.rb +117 -0
  30. data/lib/bolt/module_installer/puppetfile/forge_module.rb +54 -0
  31. data/lib/bolt/module_installer/puppetfile/git_module.rb +37 -0
  32. data/lib/bolt/module_installer/puppetfile/module.rb +26 -0
  33. data/lib/bolt/module_installer/resolver.rb +76 -0
  34. data/lib/bolt/module_installer/specs.rb +93 -0
  35. data/lib/bolt/module_installer/specs/forge_spec.rb +85 -0
  36. data/lib/bolt/module_installer/specs/git_spec.rb +179 -0
  37. data/lib/bolt/outputter.rb +2 -45
  38. data/lib/bolt/outputter/human.rb +78 -18
  39. data/lib/bolt/outputter/json.rb +22 -7
  40. data/lib/bolt/outputter/logger.rb +2 -2
  41. data/lib/bolt/pal.rb +55 -45
  42. data/lib/bolt/pal/yaml_plan.rb +4 -2
  43. data/lib/bolt/pal/yaml_plan/evaluator.rb +23 -1
  44. data/lib/bolt/pal/yaml_plan/loader.rb +14 -9
  45. data/lib/bolt/plugin.rb +1 -1
  46. data/lib/bolt/plugin/module.rb +1 -1
  47. data/lib/bolt/project.rb +32 -21
  48. data/lib/bolt/project_migrator.rb +80 -0
  49. data/lib/bolt/project_migrator/base.rb +39 -0
  50. data/lib/bolt/project_migrator/config.rb +67 -0
  51. data/lib/bolt/project_migrator/inventory.rb +67 -0
  52. data/lib/bolt/project_migrator/modules.rb +200 -0
  53. data/lib/bolt/result.rb +23 -11
  54. data/lib/bolt/shell/bash.rb +15 -9
  55. data/lib/bolt/shell/powershell.rb +11 -6
  56. data/lib/bolt/transport/base.rb +18 -18
  57. data/lib/bolt/transport/docker.rb +23 -6
  58. data/lib/bolt/transport/orch.rb +23 -14
  59. data/lib/bolt/transport/remote.rb +2 -2
  60. data/lib/bolt/transport/simple.rb +6 -6
  61. data/lib/bolt/transport/ssh/connection.rb +1 -1
  62. data/lib/bolt/util.rb +22 -0
  63. data/lib/bolt/version.rb +1 -1
  64. data/lib/bolt_server/acl.rb +2 -2
  65. data/lib/bolt_server/base_config.rb +3 -3
  66. data/lib/bolt_server/schemas/partials/task.json +17 -2
  67. data/lib/bolt_server/transport_app.rb +92 -12
  68. data/lib/bolt_spec/bolt_context.rb +4 -2
  69. data/lib/bolt_spec/plans.rb +1 -1
  70. data/lib/bolt_spec/plans/action_stubs/command_stub.rb +1 -1
  71. data/lib/bolt_spec/plans/action_stubs/script_stub.rb +1 -1
  72. data/lib/bolt_spec/plans/mock_executor.rb +6 -6
  73. data/lib/bolt_spec/run.rb +1 -1
  74. metadata +29 -10
  75. data/lib/bolt/project_migrate.rb +0 -138
  76. data/lib/bolt/puppetfile.rb +0 -160
  77. data/lib/bolt/puppetfile/module.rb +0 -89
  78. data/lib/bolt_server/pe/pal.rb +0 -67
  79. data/modules/secure_env_vars/plans/init.pp +0 -20
@@ -1,138 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Bolt
4
- class ProjectMigrate
5
- attr_reader :path, :project_file, :backup_dir, :outputter, :inventory_file, :config_file
6
-
7
- # This init mostly makes testing easier
8
- def initialize(path, outputter, configured_inventory = nil)
9
- @path = Pathname.new(path).expand_path
10
- @project_file = @path + 'bolt-project.yaml'
11
- @config_file = @path + 'bolt.yaml'
12
- @backup_dir = @path + '.bolt-bak'
13
- @inventory_file = configured_inventory || @path + 'inventory.yaml'
14
- @outputter = outputter
15
- end
16
-
17
- def migrate_project
18
- inv_ok = inventory_1_to_2(inventory_file, outputter) if inventory_file.file?
19
- config_ok = bolt_yaml_to_bolt_project(inventory_file, outputter)
20
- inv_ok && config_ok ? 0 : 1
21
- end
22
-
23
- # This could be made public and used elsewhere if the need arises
24
- private def backup_file(origin_path)
25
- unless File.exist?(origin_path)
26
- outputter.print_message "Could not find file #{origin_path}, skipping backup."
27
- return
28
- end
29
-
30
- date = Time.new.strftime("%Y%m%d_%H%M%S%L")
31
- FileUtils.mkdir_p(backup_dir)
32
-
33
- filename = File.basename(origin_path)
34
- backup_path = File.join(backup_dir, "#{filename}.#{date}.bak")
35
-
36
- outputter.print_message "Backing up #{filename} from #{origin_path} to #{backup_path}"
37
-
38
- begin
39
- FileUtils.cp(origin_path, backup_path)
40
- rescue StandardError => e
41
- raise Bolt::FileError.new("#{e.message}; unable to create backup of #{filename}.", origin_path)
42
- end
43
- end
44
-
45
- private def bolt_yaml_to_bolt_project(inventory_file, outputter)
46
- # If bolt-project.yaml already exists
47
- if project_file.file?
48
- outputter.print_message "bolt-project.yaml already exists in Bolt "\
49
- "project at #{path}. Skipping project file update."
50
-
51
- # If bolt.yaml doesn't exist
52
- elsif !config_file.file?
53
- outputter.print_message "Could not find bolt.yaml in project at "\
54
- "#{path}. Skipping project file update."
55
-
56
- else
57
- config_data = Bolt::Util.read_optional_yaml_hash(config_file, 'config')
58
- transport_data, project_data = config_data.partition do |k, _|
59
- Bolt::Config::INVENTORY_OPTIONS.keys.include?(k)
60
- end.map(&:to_h)
61
-
62
- if transport_data.any?
63
- if File.exist?(inventory_file)
64
- inventory_data = Bolt::Util.read_yaml_hash(inventory_file, 'inventory')
65
- merged = Bolt::Util.deep_merge(transport_data, inventory_data['config'] || {})
66
- inventory_data['config'] = merged
67
- backup_file(inventory_file)
68
- else
69
- FileUtils.touch(inventory_file)
70
- inventory_data = { 'config' => transport_data }
71
- end
72
-
73
- backup_file(config_file)
74
-
75
- begin
76
- outputter.print_message "Moving transportation configuration options "\
77
- "'#{transport_data.keys.join(', ')}' from bolt.yaml to inventory.yaml"
78
- File.write(inventory_file, inventory_data.to_yaml)
79
- File.write(config_file, project_data.to_yaml)
80
- rescue StandardError => e
81
- raise Bolt::FileError.new("#{e.message}; unable to write inventory.", inventory_file)
82
- end
83
- end
84
-
85
- outputter.print_message "Renaming bolt.yaml to bolt-project.yaml"
86
- FileUtils.mv(config_file, project_file)
87
- outputter.print_message "Successfully updated project. Please add a "\
88
- "'name' key to bolt-project.yaml to use project-level tasks and plans. "\
89
- "Learn more about projects by running 'bolt guide project'."
90
- # If nothing errored, this succeeded
91
- true
92
- end
93
- end
94
-
95
- private def inventory_1_to_2(inventory_file, outputter)
96
- data = Bolt::Util.read_yaml_hash(inventory_file, 'inventory')
97
- data.delete('version') if data['version'] != 2
98
- migrated = migrate_group(data)
99
-
100
- ok = if migrated
101
- backup_file(inventory_file)
102
- File.write(inventory_file, data.to_yaml)
103
- end
104
-
105
- result = if migrated && ok
106
- "Successfully migrated Bolt inventory to the latest version."
107
- elsif !migrated
108
- "Bolt inventory is already on the latest version. Skipping inventory update."
109
- else
110
- "Could not migrate Bolt inventory to the latest version. See "\
111
- "https://puppet.com/docs/bolt/latest/inventory_file_v2.html to manually update."
112
- end
113
- outputter.print_message(result)
114
- ok
115
- end
116
-
117
- # Walks an inventory hash and replaces all 'nodes' keys with 'targets' keys
118
- # and all 'name' keys nested in a 'targets' hash with 'uri' keys. Data is
119
- # modified in place.
120
- private def migrate_group(group)
121
- migrated = false
122
- if group.key?('nodes')
123
- migrated = true
124
- targets = group['nodes'].map do |target|
125
- target['uri'] = target.delete('name') if target.is_a?(Hash)
126
- target
127
- end
128
- group.delete('nodes')
129
- group['targets'] = targets
130
- end
131
- (group['groups'] || []).each do |subgroup|
132
- migrated_group = migrate_group(subgroup)
133
- migrated ||= migrated_group
134
- end
135
- migrated
136
- end
137
- end
138
- end
@@ -1,160 +0,0 @@
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 = mod.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
@@ -1,89 +0,0 @@
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, mod['version_requirement'])
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 &&
46
- @owner == other.owner &&
47
- @name == other.name &&
48
- versions_intersect?(other)
49
- end
50
- alias == eql?
51
-
52
- # Returns true if the versions of two modules intersect. Used to determine
53
- # if an installed module satisfies the version requirement of another.
54
- #
55
- def versions_intersect?(other)
56
- range = ::SemanticPuppet::VersionRange.parse(@version || '')
57
- other_range = ::SemanticPuppet::VersionRange.parse(other.version || '')
58
-
59
- range.intersection(other_range) != ::SemanticPuppet::VersionRange::EMPTY_RANGE
60
- end
61
-
62
- # Hashes the module.
63
- #
64
- def hash
65
- [@owner, @name].hash
66
- end
67
-
68
- # Returns a hash representation similar to the module
69
- # declaration.
70
- #
71
- def to_hash
72
- {
73
- 'name' => title,
74
- 'version_requirement' => version
75
- }.compact
76
- end
77
-
78
- # Returns the Puppetfile specification for the module.
79
- #
80
- def to_spec
81
- if @version
82
- "mod #{title.inspect}, #{@version.inspect}"
83
- else
84
- "mod #{title.inspect}"
85
- end
86
- end
87
- end
88
- end
89
- end
@@ -1,67 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bolt/pal'
4
- require 'bolt/util'
5
-
6
- module BoltServer
7
- module PE
8
- class PAL < Bolt::PAL
9
- # PE_BOLTLIB_PATH is intended to function exactly like the BOLTLIB_PATH used
10
- # in Bolt::PAL. Paths and variable names are similar to what exists in
11
- # Bolt::PAL, but with a 'PE' prefix.
12
- PE_BOLTLIB_PATH = '/opt/puppetlabs/server/apps/bolt-server/pe-bolt-modules'
13
-
14
- # For now at least, we maintain an entirely separate codedir from
15
- # puppetserver by default, so that filesync can work properly. If filesync
16
- # is not used, this can instead match the usual puppetserver codedir.
17
- # See the `orchestrator.bolt.codedir` tk config setting.
18
- DEFAULT_BOLT_CODEDIR = '/opt/puppetlabs/server/data/orchestration-services/code'
19
-
20
- # This function is nearly identical to Bolt::Pal's `with_puppet_settings` with the
21
- # one difference that we set the codedir to point to actual code, rather than the
22
- # tmpdir. We only use this funtion inside the PEBolt::PAL initializer so that Puppet
23
- # is correctly configured to pull environment configuration correctly. If we don't
24
- # set codedir in this way: when we try to load and interpolate the modulepath it
25
- # won't correctly load.
26
- def with_pe_pal_init_settings(codedir, environmentpath, basemodulepath)
27
- Dir.mktmpdir('pe-bolt') do |dir|
28
- cli = []
29
- Puppet::Settings::REQUIRED_APP_SETTINGS.each do |setting|
30
- dir = setting == :codedir ? codedir : dir
31
- cli << "--#{setting}" << dir
32
- end
33
- cli << "--environmentpath" << environmentpath
34
- cli << "--basemodulepath" << basemodulepath
35
- Puppet.settings.send(:clear_everything_for_tests)
36
- Puppet.initialize_settings(cli)
37
- yield
38
- # Ensure the puppet settings go back to what bolt expects after
39
- # we finish with the settings we need for PEBolt::PAL init.
40
- with_puppet_settings { |_| nil }
41
- end
42
- end
43
-
44
- def initialize(plan_executor_config, environment_name, hiera_config = nil, max_compiles = nil)
45
- # Bolt::PAL#initialize takes the modulepath as its first argument, but we
46
- # want to customize it later, so we pass an empty value:
47
- super([], hiera_config, max_compiles)
48
-
49
- codedir = plan_executor_config['codedir'] || DEFAULT_BOLT_CODEDIR
50
- environmentpath = plan_executor_config['environmentpath'] || "#{codedir}/environments"
51
- basemodulepath = plan_executor_config['basemodulepath'] || "#{codedir}/modules:/opt/puppetlabs/puppet/modules"
52
-
53
- with_pe_pal_init_settings(codedir, environmentpath, basemodulepath) do
54
- environment = Puppet.lookup(:environments).get!(environment_name)
55
- # A new modulepath is created from scratch (rather than using super's @modulepath)
56
- # so that we can have full control over all the entries in modulepath. In the future
57
- # it's likely we will need to preceed _both_ Bolt::PAL::BOLTLIB_PATH _and_
58
- # Bolt::PAL::MODULES_PATH which would be more complex if we tried to use @modulepath since
59
- # we need to append our modulepaths and exclude modules shiped in bolt gem code
60
- modulepath_dirs = environment.modulepath
61
- @user_modulepath = modulepath_dirs
62
- @modulepath = [PE_BOLTLIB_PATH, Bolt::PAL::BOLTLIB_PATH, *modulepath_dirs]
63
- end
64
- end
65
- end
66
- end
67
- end