bolt 3.15.0 → 3.18.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 (111) hide show
  1. checksums.yaml +4 -4
  2. data/Puppetfile +7 -7
  3. data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +1 -4
  4. data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +1 -4
  5. data/bolt-modules/file/lib/puppet/functions/file/exists.rb +1 -4
  6. data/bolt-modules/file/lib/puppet/functions/file/read.rb +1 -4
  7. data/bolt-modules/file/lib/puppet/functions/file/readable.rb +1 -4
  8. data/lib/bolt/analytics.rb +4 -21
  9. data/lib/bolt/application.rb +627 -0
  10. data/lib/bolt/applicator.rb +7 -6
  11. data/lib/bolt/apply_inventory.rb +3 -3
  12. data/lib/bolt/apply_result.rb +2 -2
  13. data/lib/bolt/bolt_option_parser.rb +27 -3
  14. data/lib/bolt/catalog.rb +10 -11
  15. data/lib/bolt/cli.rb +607 -809
  16. data/lib/bolt/config/modulepath.rb +1 -1
  17. data/lib/bolt/config/options.rb +32 -13
  18. data/lib/bolt/config/transport/base.rb +4 -4
  19. data/lib/bolt/config/transport/docker.rb +2 -2
  20. data/lib/bolt/config/transport/local.rb +2 -2
  21. data/lib/bolt/config/transport/lxd.rb +2 -2
  22. data/lib/bolt/config/transport/orch.rb +2 -2
  23. data/lib/bolt/config/transport/podman.rb +2 -2
  24. data/lib/bolt/config/transport/remote.rb +2 -2
  25. data/lib/bolt/config/transport/ssh.rb +2 -2
  26. data/lib/bolt/config/transport/winrm.rb +2 -2
  27. data/lib/bolt/config.rb +5 -5
  28. data/lib/bolt/container_result.rb +2 -2
  29. data/lib/bolt/error.rb +1 -1
  30. data/lib/bolt/executor.rb +17 -14
  31. data/lib/bolt/fiber_executor.rb +9 -5
  32. data/lib/bolt/inventory/group.rb +4 -4
  33. data/lib/bolt/inventory/inventory.rb +64 -41
  34. data/lib/bolt/inventory/options.rb +1 -1
  35. data/lib/bolt/inventory/target.rb +1 -1
  36. data/lib/bolt/inventory.rb +10 -17
  37. data/lib/bolt/module_installer/installer.rb +2 -2
  38. data/lib/bolt/module_installer/puppetfile/forge_module.rb +1 -1
  39. data/lib/bolt/module_installer/puppetfile/git_module.rb +1 -1
  40. data/lib/bolt/module_installer/puppetfile/module.rb +1 -1
  41. data/lib/bolt/module_installer/puppetfile.rb +27 -13
  42. data/lib/bolt/module_installer/resolver.rb +3 -3
  43. data/lib/bolt/module_installer/specs/forge_spec.rb +1 -1
  44. data/lib/bolt/module_installer/specs/git_spec.rb +1 -1
  45. data/lib/bolt/module_installer/specs.rb +3 -3
  46. data/lib/bolt/module_installer.rb +6 -6
  47. data/lib/bolt/node/errors.rb +1 -1
  48. data/lib/bolt/node/output.rb +1 -1
  49. data/lib/bolt/outputter/human.rb +85 -34
  50. data/lib/bolt/outputter/json.rb +63 -38
  51. data/lib/bolt/outputter/logger.rb +1 -1
  52. data/lib/bolt/outputter/rainbow.rb +1 -1
  53. data/lib/bolt/outputter.rb +4 -4
  54. data/lib/bolt/pal/logging.rb +1 -1
  55. data/lib/bolt/pal/yaml_plan/evaluator.rb +1 -1
  56. data/lib/bolt/pal/yaml_plan/loader.rb +2 -2
  57. data/lib/bolt/pal/yaml_plan/step.rb +11 -11
  58. data/lib/bolt/pal/yaml_plan/transpiler.rb +3 -3
  59. data/lib/bolt/pal/yaml_plan.rb +2 -2
  60. data/lib/bolt/pal.rb +11 -11
  61. data/lib/bolt/plan_creator.rb +88 -29
  62. data/lib/bolt/plan_future.rb +11 -6
  63. data/lib/bolt/plan_result.rb +3 -3
  64. data/lib/bolt/plugin/cache.rb +2 -2
  65. data/lib/bolt/plugin/module.rb +1 -1
  66. data/lib/bolt/plugin.rb +18 -24
  67. data/lib/bolt/project.rb +4 -11
  68. data/lib/bolt/project_manager/config_migrator.rb +1 -1
  69. data/lib/bolt/project_manager/inventory_migrator.rb +1 -1
  70. data/lib/bolt/project_manager/migrator.rb +1 -1
  71. data/lib/bolt/project_manager/module_migrator.rb +5 -5
  72. data/lib/bolt/project_manager.rb +23 -4
  73. data/lib/bolt/puppetdb/config.rb +1 -1
  74. data/lib/bolt/puppetdb.rb +3 -3
  75. data/lib/bolt/result.rb +1 -1
  76. data/lib/bolt/result_set.rb +2 -1
  77. data/lib/bolt/shell/bash.rb +12 -2
  78. data/lib/bolt/shell/powershell.rb +9 -2
  79. data/lib/bolt/shell.rb +2 -2
  80. data/lib/bolt/target.rb +2 -2
  81. data/lib/bolt/transport/base.rb +1 -1
  82. data/lib/bolt/transport/docker/connection.rb +1 -1
  83. data/lib/bolt/transport/docker.rb +2 -2
  84. data/lib/bolt/transport/local/connection.rb +19 -3
  85. data/lib/bolt/transport/local.rb +3 -3
  86. data/lib/bolt/transport/lxd/connection.rb +1 -1
  87. data/lib/bolt/transport/lxd.rb +4 -4
  88. data/lib/bolt/transport/orch/connection.rb +13 -1
  89. data/lib/bolt/transport/orch.rb +2 -2
  90. data/lib/bolt/transport/podman/connection.rb +1 -1
  91. data/lib/bolt/transport/podman.rb +2 -2
  92. data/lib/bolt/transport/remote.rb +2 -2
  93. data/lib/bolt/transport/simple.rb +3 -3
  94. data/lib/bolt/transport/ssh/connection.rb +3 -3
  95. data/lib/bolt/transport/ssh.rb +5 -5
  96. data/lib/bolt/transport/winrm/connection.rb +2 -2
  97. data/lib/bolt/transport/winrm.rb +3 -3
  98. data/lib/bolt/util.rb +8 -31
  99. data/lib/bolt/validator.rb +1 -1
  100. data/lib/bolt/version.rb +1 -1
  101. data/lib/bolt.rb +1 -1
  102. data/lib/bolt_server/file_cache.rb +12 -0
  103. data/lib/bolt_server/schemas/action-apply.json +32 -0
  104. data/lib/bolt_server/schemas/action-apply_prep.json +19 -0
  105. data/lib/bolt_server/transport_app.rb +129 -26
  106. data/lib/bolt_spec/bolt_context.rb +7 -5
  107. data/lib/bolt_spec/plans/mock_executor.rb +3 -3
  108. data/lib/bolt_spec/run.rb +1 -1
  109. data/libexec/bolt_catalog +1 -1
  110. metadata +7 -5
  111. data/lib/bolt/secret.rb +0 -37
@@ -3,13 +3,15 @@
3
3
  require 'open3'
4
4
  require 'fileutils'
5
5
  require 'tempfile'
6
- require 'bolt/node/output'
7
- require 'bolt/util'
6
+ require_relative '../../../bolt/node/output'
7
+ require_relative '../../../bolt/util'
8
8
 
9
9
  module Bolt
10
10
  module Transport
11
11
  class Local < Simple
12
12
  class Connection
13
+ RUBY_ENV_VARS = %w[GEM_PATH GEM_HOME RUBYLIB RUBYLIB_PREFIX RUBYOPT RUBYPATH RUBYSHELL].freeze
14
+
13
15
  attr_accessor :user, :logger, :target
14
16
 
15
17
  def initialize(target)
@@ -68,7 +70,21 @@ module Bolt
68
70
  end
69
71
  end
70
72
 
71
- Open3.popen3(*command)
73
+ # Only do this if bundled-ruby is set to false, not nil
74
+ ruby_env_vars = if target.transport_config['bundled-ruby'] == false
75
+ RUBY_ENV_VARS.each_with_object({}) do |e, acc|
76
+ acc[e] = ENV["BOLT_ORIG_#{e}"] if ENV["BOLT_ORIG_#{e}"]
77
+ end
78
+ end
79
+
80
+ if target.transport_config['bundled-ruby'] == false &&
81
+ Gem.loaded_specs.keys.include?('bundler')
82
+ Bundler.with_unbundled_env do
83
+ Open3.popen3(ruby_env_vars || {}, *command)
84
+ end
85
+ else
86
+ Open3.popen3(ruby_env_vars || {}, *command)
87
+ end
72
88
  end
73
89
 
74
90
  # This is used by the Bash shell to decide whether to `cd` before
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'bolt/logger'
4
- require 'bolt/transport/simple'
3
+ require_relative '../../bolt/logger'
4
+ require_relative '../../bolt/transport/simple'
5
5
 
6
6
  module Bolt
7
7
  module Transport
@@ -21,4 +21,4 @@ module Bolt
21
21
  end
22
22
  end
23
23
 
24
- require 'bolt/transport/local/connection'
24
+ require_relative 'local/connection'
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'logging'
4
- require 'bolt/node/errors'
4
+ require_relative '../../../bolt/node/errors'
5
5
 
6
6
  module Bolt
7
7
  module Transport
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'bolt/logger'
4
- require 'bolt/node/errors'
5
- require 'bolt/transport/simple'
3
+ require_relative '../../bolt/logger'
4
+ require_relative '../../bolt/node/errors'
5
+ require_relative '../../bolt/transport/simple'
6
6
 
7
7
  module Bolt
8
8
  module Transport
@@ -23,4 +23,4 @@ module Bolt
23
23
  end
24
24
  end
25
25
 
26
- require 'bolt/transport/lxd/connection'
26
+ require_relative 'lxd/connection'
@@ -36,7 +36,8 @@ module Bolt
36
36
  end
37
37
  logger.debug("Creating orchestrator client for #{client_opts}")
38
38
  @client = OrchestratorClient.new(client_opts, true)
39
- @plan_job = start_plan(plan_context)
39
+ @plan_context = plan_context
40
+ @plan_job = start_plan(@plan_context)
40
41
  logger.debug("Started plan #{@plan_job}")
41
42
  @environment = opts["task-environment"]
42
43
  end
@@ -87,6 +88,17 @@ module Bolt
87
88
  def run_task(targets, task, arguments, options)
88
89
  body = build_request(targets, task, arguments, options[:description])
89
90
  @client.run_task(body)
91
+ rescue OrchestratorClient::ApiError => e
92
+ if e.data['kind'] == 'puppetlabs.orchestrator/plan-already-finished'
93
+ @logger.debug("Retrying the task")
94
+ # Instead of recursing, just retry once
95
+ @plan_job = start_plan(@plan_context)
96
+ # Rebuild the request with the new plan job ID
97
+ body = build_request(targets, task, arguments, options[:description])
98
+ @client.run_task(body)
99
+ else
100
+ raise e
101
+ end
90
102
  end
91
103
 
92
104
  def query_inventory(targets)
@@ -4,8 +4,8 @@ require 'base64'
4
4
  require 'find'
5
5
  require 'json'
6
6
  require 'pathname'
7
- require 'bolt/transport/base'
8
- require 'bolt/transport/orch/connection'
7
+ require_relative '../../bolt/transport/base'
8
+ require_relative 'orch/connection'
9
9
 
10
10
  module Bolt
11
11
  module Transport
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'logging'
4
- require 'bolt/node/errors'
4
+ require_relative '../../../bolt/node/errors'
5
5
 
6
6
  module Bolt
7
7
  module Transport
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'json'
4
4
  require 'shellwords'
5
- require 'bolt/transport/base'
5
+ require_relative '../../bolt/transport/base'
6
6
 
7
7
  module Bolt
8
8
  module Transport
@@ -16,4 +16,4 @@ module Bolt
16
16
  end
17
17
  end
18
18
 
19
- require 'bolt/transport/podman/connection'
19
+ require_relative 'podman/connection'
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'bolt/task'
4
- require 'bolt/transport/base'
3
+ require_relative '../../bolt/task'
4
+ require_relative '../../bolt/transport/base'
5
5
 
6
6
  module Bolt
7
7
  module Transport
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'logging'
4
- require 'bolt/result'
5
- require 'bolt/shell'
6
- require 'bolt/transport/base'
4
+ require_relative '../../bolt/result'
5
+ require_relative '../../bolt/shell'
6
+ require_relative '../../bolt/transport/base'
7
7
 
8
8
  module Bolt
9
9
  module Transport
@@ -2,9 +2,9 @@
2
2
 
3
3
  require 'logging'
4
4
  require 'shellwords'
5
- require 'bolt/node/errors'
6
- require 'bolt/node/output'
7
- require 'bolt/util'
5
+ require_relative '../../../bolt/node/errors'
6
+ require_relative '../../../bolt/node/output'
7
+ require_relative '../../../bolt/util'
8
8
 
9
9
  module Bolt
10
10
  module Transport
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'bolt/logger'
4
- require 'bolt/node/errors'
5
- require 'bolt/transport/simple'
3
+ require_relative '../../bolt/logger'
4
+ require_relative '../../bolt/node/errors'
5
+ require_relative '../../bolt/transport/simple'
6
6
 
7
7
  module Bolt
8
8
  module Transport
@@ -44,5 +44,5 @@ module Bolt
44
44
  end
45
45
  end
46
46
 
47
- require 'bolt/transport/ssh/connection'
48
- require 'bolt/transport/ssh/exec_connection'
47
+ require_relative 'ssh/connection'
48
+ require_relative 'ssh/exec_connection'
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'bolt/node/errors'
4
- require 'bolt/node/output'
3
+ require_relative '../../../bolt/node/errors'
4
+ require_relative '../../../bolt/node/output'
5
5
 
6
6
  module Bolt
7
7
  module Transport
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'bolt/node/errors'
4
- require 'bolt/transport/base'
3
+ require_relative '../../bolt/node/errors'
4
+ require_relative '../../bolt/transport/base'
5
5
 
6
6
  module Bolt
7
7
  module Transport
@@ -30,4 +30,4 @@ module Bolt
30
30
  end
31
31
  end
32
32
 
33
- require 'bolt/transport/winrm/connection'
33
+ require_relative 'winrm/connection'
data/lib/bolt/util.rb CHANGED
@@ -103,7 +103,7 @@ module Bolt
103
103
  # absolute path or Puppet module syntax lookup. Returns the path to the
104
104
  # file if found, or nil.
105
105
  #
106
- def find_file_from_scope(file, scope, fallback = false)
106
+ def find_file_from_scope(file, scope)
107
107
  # If we got an absolute path, just return that.
108
108
  return file if Pathname.new(file).absolute?
109
109
 
@@ -112,44 +112,21 @@ module Bolt
112
112
  mod_path = scope.compiler.environment.module(module_name)&.path
113
113
 
114
114
  # Search the module for the file, falling back to new-style paths if enabled.
115
- find_file_in_module(mod_path, file_pattern, fallback) if mod_path
115
+ search_module(mod_path, file_pattern) if mod_path
116
116
  end
117
117
 
118
- # This method is used by Bolt to find files when provided a
119
- # module-style path without loading Puppet. It takes the absolute path to
120
- # the module root and the module-style path minus the module name and
121
- # searches subdirectories in the module in order of precedence.
118
+ # This searches a module for files under 'files/' or 'scripts/', falling
119
+ # back to the new style of file loading. It takes the absolute path to the
120
+ # module root and the relative path provided by the user.
122
121
  #
123
- def find_file_in_module(module_path, module_file, fallback = false)
124
- # If the first part of the path is 'scripts' or 'files', the path may
125
- # be a new-style file location and should fall back to the new path.
126
- subdir_or_file = split_path(module_file).first
127
- case subdir_or_file
128
- # For any subdirs that may indicate the user passed a new-style path,
129
- # first look in 'mymod/files/<relative_path>' (old-style) then fall
130
- # back to 'mymod/<relative_path>' (new-style) if enabled.
131
- when 'scripts', 'files'
132
- search_module(module_path, module_file, fallback)
133
- else
134
- # If the path definitely isn't new-style, only look in the 'files/'
135
- # directory.
136
- search_module(module_path, module_file)
137
- end
138
- end
139
-
140
- # This searches a module for files under 'files/' or 'scripts/',
141
- # optionally falling back to the new style of file loading. It takes the
142
- # absolute path to the module root, the relative path provided by the
143
- # user, and whether to fall back to the new-style script loading if the
144
- # file isn't found in 'files/'.
145
- #
146
- private def search_module(module_path, module_file, fallback = false)
122
+ def search_module(module_path, module_file)
147
123
  if File.exist?(File.join(module_path, 'files', module_file))
148
124
  File.join(module_path, 'files', module_file)
149
- elsif File.exist?(File.join(module_path, module_file)) && fallback
125
+ elsif File.exist?(File.join(module_path, module_file))
150
126
  File.join(module_path, module_file)
151
127
  end
152
128
  end
129
+ alias find_file_in_module search_module
153
130
 
154
131
  # Copied directly from puppet/lib/puppet/parser/files.rb
155
132
  #
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'bolt/error'
3
+ require_relative '../bolt/error'
4
4
 
5
5
  # This class validates config against a schema, raising an error that includes
6
6
  # details about any invalid configuration.
data/lib/bolt/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bolt
4
- VERSION = '3.15.0'
4
+ VERSION = '3.18.0'
5
5
  end
data/lib/bolt.rb CHANGED
@@ -4,5 +4,5 @@
4
4
  require 'logging_extensions/logging'
5
5
 
6
6
  module Bolt
7
- require 'bolt/executor'
7
+ require_relative 'bolt/executor'
8
8
  end
@@ -182,5 +182,17 @@ module BoltServer
182
182
  end
183
183
  end
184
184
  end
185
+
186
+ def get_cached_project_file(versioned_project, file_name)
187
+ file_dir = create_cache_dir(versioned_project)
188
+ file_path = File.join(file_dir, file_name)
189
+ serial_execute { File.read(file_path) if File.exist?(file_path) }
190
+ end
191
+
192
+ def cache_project_file(versioned_project, file_name, data)
193
+ file_dir = create_cache_dir(versioned_project)
194
+ file_path = File.join(file_dir, file_name)
195
+ serial_execute { File.open(file_path, 'w') { |f| f.write(data) } }
196
+ end
185
197
  end
186
198
  end
@@ -0,0 +1,32 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-04/schema#",
3
+ "title": "apply request",
4
+ "description": "POST <transport>/apply request schema",
5
+ "type": "object",
6
+ "properties": {
7
+ "versioned_project": {
8
+ "type": "string",
9
+ "description": "Project from which to load code"
10
+ },
11
+ "parameters": {
12
+ "type": "object",
13
+ "properties": {
14
+ "catalog": {
15
+ "type": "object",
16
+ "description": "Compiled catalog to apply"
17
+ },
18
+ "apply_options": {
19
+ "type": "object",
20
+ "description": "Options for application of a catalog"
21
+ }
22
+ }
23
+ },
24
+ "job_id": {
25
+ "type": "integer",
26
+ "description": "job-id associated with request"
27
+ },
28
+ "target": { "$ref": "partial:target-any" }
29
+ },
30
+ "required": ["target", "versioned_project", "parameters", "job_id"],
31
+ "additionalProperties": false
32
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-04/schema#",
3
+ "title": "apply_prep request",
4
+ "description": "POST <transport>/apply_prep request schema",
5
+ "type": "object",
6
+ "properties": {
7
+ "versioned_project": {
8
+ "type": "String",
9
+ "description": "Project from which to load code"
10
+ },
11
+ "target": { "$ref": "partial:target-any" },
12
+ "job_id": {
13
+ "type": "integer",
14
+ "description": "job-id associated with request"
15
+ }
16
+ },
17
+ "required": ["target", "versioned_project", "job_id"],
18
+ "additionalProperties": false
19
+ }
@@ -43,6 +43,8 @@ module BoltServer
43
43
  transport-ssh
44
44
  transport-winrm
45
45
  connect-data
46
+ action-apply_prep
47
+ action-apply
46
48
  ].freeze
47
49
 
48
50
  # PE_BOLTLIB_PATH is intended to function exactly like the BOLTLIB_PATH used
@@ -75,6 +77,12 @@ module BoltServer
75
77
  # This is needed until the PAL is threadsafe.
76
78
  @pal_mutex = Mutex.new
77
79
 
80
+ # Avoid redundant plugin tarbal construction
81
+ @plugin_mutex = Mutex.new
82
+
83
+ # Avoid redundant project_task metadata construction
84
+ @task_metadata_mutex = Mutex.new
85
+
78
86
  @logger = Bolt::Logger.logger(self)
79
87
 
80
88
  super(nil)
@@ -118,12 +126,7 @@ module BoltServer
118
126
  end
119
127
  end
120
128
 
121
- def run_task(target, body)
122
- validate_schema(@schemas["action-run_task"], body)
123
-
124
- task_data = body['task']
125
- task = Bolt::Task::PuppetServer.new(task_data['name'], task_data['metadata'], task_data['files'], @file_cache)
126
- parameters = body['parameters'] || {}
129
+ def task_helper(target, task, parameters)
127
130
  # Wrap parameters marked with '"sensitive": true' in the task metadata with a
128
131
  # Sensitive wrapper type. This way it's not shown in logs.
129
132
  if (param_spec = task.parameters)
@@ -142,6 +145,102 @@ module BoltServer
142
145
  end
143
146
  end
144
147
 
148
+ def run_task(target, body)
149
+ validate_schema(@schemas["action-run_task"], body)
150
+
151
+ task_data = body['task']
152
+ task = Bolt::Task::PuppetServer.new(task_data['name'], task_data['metadata'], task_data['files'], @file_cache)
153
+ task_helper(target, task, body['parameters'] || {})
154
+ end
155
+
156
+ def extract_install_task(target)
157
+ unless target.plugin_hooks['puppet_library']['task']
158
+ raise BoltServer::RequestError,
159
+ "Target must have 'task' plugin hook"
160
+ end
161
+ install_task = target.plugin_hooks['puppet_library']['task'].split('::', 2)
162
+ install_task << 'init' if install_task.count == 1
163
+ install_task
164
+ end
165
+
166
+ # This helper is responsible for computing or retrieving from the cache a plugin tarball. There are
167
+ # two supported plugin types 'fact_plugins', and 'all_plugins'. Note that this is cached based on
168
+ # versioned_project as there are no plugins in the "builtin content" directory
169
+ def plugin_tarball(versioned_project, tarball_type)
170
+ tarball_types = %w[fact_plugins all_plugins]
171
+ unless tarball_types.include?(tarball_type)
172
+ raise ArgumentError,
173
+ "tarball_type must be one of: #{tarball_types.join(', ')}"
174
+ end
175
+ # lock this so that in the case an apply/apply_prep with multiple targets hits this endpoint
176
+ # the tarball computation only happens once (all the other targets will just need to read the cached data)
177
+ @plugin_mutex.synchronize do
178
+ if (tarball = @file_cache.get_cached_project_file(versioned_project, tarball_type))
179
+ tarball
180
+ else
181
+ new_tarball = build_project_plugins_tarball(versioned_project) do |mod|
182
+ search_dirs = []
183
+ search_dirs << mod.plugins if mod.plugins?
184
+ search_dirs << mod.pluginfacts if mod.pluginfacts?
185
+ if tarball_type == 'all_plugins'
186
+ search_dirs << mod.files if mod.files?
187
+ search_dirs << mod.scripts if mod.scripts?
188
+ type_files = "#{mod.path}/types"
189
+ search_dirs << type_files if File.exist?(type_files)
190
+ end
191
+ search_dirs
192
+ end
193
+ @file_cache.cache_project_file(versioned_project, tarball_type, new_tarball)
194
+ new_tarball
195
+ end
196
+ end
197
+ end
198
+
199
+ # This helper is responsible for computing or retrieving task metadata for a project.
200
+ # It expects task name in segments and uses the combination of task name and versioned_project
201
+ # as a unique identifier for caching in addition to the job_id. The job id is added to protect against
202
+ # a case where the buildtin content is update (where the apply_helpers would be managed)
203
+ def project_task_metadata(versioned_project, task_name_segments, job_id)
204
+ cached_file_name = "#{task_name_segments.join('_')}_#{job_id}"
205
+ # lock this so that in the case an apply/apply_prep with multiple targets hits this endpoint the
206
+ # metadata computation will only be computed once, then the cache will be read.
207
+ @task_metadata_mutex.synchronize do
208
+ if (metadata = @file_cache.get_cached_project_file(versioned_project, cached_file_name))
209
+ JSON.parse(metadata)
210
+ else
211
+ new_metadata = in_bolt_project(versioned_project) do |context|
212
+ ps_parameters = {
213
+ 'versioned_project' => versioned_project
214
+ }
215
+ pe_task_info(context[:pal], *task_name_segments, ps_parameters)
216
+ end
217
+ @file_cache.cache_project_file(versioned_project, cached_file_name, new_metadata.to_json)
218
+ new_metadata
219
+ end
220
+ end
221
+ end
222
+
223
+ def apply_prep(target, body)
224
+ validate_schema(@schemas["action-apply_prep"], body)
225
+ plugins_tarball = plugin_tarball(body['versioned_project'], 'fact_plugins')
226
+ install_task_segments = extract_install_task(target.first)
227
+ task_data = project_task_metadata(body['versioned_project'], install_task_segments, body["job_id"])
228
+ task = Bolt::Task::PuppetServer.new(task_data['name'], task_data['metadata'], task_data['files'], @file_cache)
229
+ install_task_result = task_helper(target, task, target.first.plugin_hooks['puppet_library']['parameters'] || {})
230
+ return install_task_result unless install_task_result.ok
231
+ task_data = project_task_metadata(body['versioned_project'], %w[apply_helpers custom_facts], body["job_id"])
232
+ task = Bolt::Task::PuppetServer.new(task_data['name'], task_data['metadata'], task_data['files'], @file_cache)
233
+ task_helper(target, task, { 'plugins' => plugins_tarball })
234
+ end
235
+
236
+ def apply(target, body)
237
+ validate_schema(@schemas["action-apply"], body)
238
+ plugins_tarball = plugin_tarball(body['versioned_project'], 'all_plugins')
239
+ task_data = project_task_metadata(body['versioned_project'], %w[apply_helpers apply_catalog], body["job_id"])
240
+ task = Bolt::Task::PuppetServer.new(task_data['name'], task_data['metadata'], task_data['files'], @file_cache)
241
+ task_helper(target, task, body['parameters'].merge({ 'plugins' => plugins_tarball }))
242
+ end
243
+
145
244
  def run_command(target, body)
146
245
  validate_schema(@schemas["action-run_command"], body)
147
246
  command = body['command']
@@ -370,13 +469,26 @@ module BoltServer
370
469
  pal.in_bolt_compiler do
371
470
  mod = Puppet.lookup(:current_environment).module(module_name)
372
471
  raise BoltServer::RequestError, "module_name: '#{module_name}' does not exist" unless mod
373
- mod.file(file)
472
+ # First, look in the 'old' location <module>/files/<path>.
473
+ # If not found, and the path starts with `files` or `scripts`, munge
474
+ # the path and look inside that directory.
475
+ if (abs_path = mod.file(file))
476
+ abs_path
477
+ else
478
+ subdir, relative_path = file.split(File::SEPARATOR, 2)
479
+ case subdir
480
+ when 'files'
481
+ mod.file(relative_path)
482
+ when 'scripts'
483
+ mod.script(relative_path)
484
+ end
485
+ end
374
486
  end
375
487
  end
376
488
 
377
489
  unless abs_file_path
378
490
  raise BoltServer::RequestError,
379
- "file: '#{file}' does not exist inside the module's 'files' directory"
491
+ "file: '#{file}' does not exist inside #{module_name} 'files' or 'scripts' directories"
380
492
  end
381
493
 
382
494
  fileset = Puppet::FileServing::Fileset.new(abs_file_path, 'recurse' => 'yes')
@@ -476,6 +588,8 @@ module BoltServer
476
588
  run_task
477
589
  run_script
478
590
  upload_file
591
+ apply
592
+ apply_prep
479
593
  ].freeze
480
594
 
481
595
  def make_ssh_target(target_hash)
@@ -499,7 +613,8 @@ module BoltServer
499
613
  'config' => {
500
614
  'transport' => 'ssh',
501
615
  'ssh' => opts.slice(*Bolt::Config::Transport::SSH.options)
502
- }
616
+ },
617
+ 'plugin_hooks' => target_hash['plugin_hooks']
503
618
  }
504
619
 
505
620
  inventory = Bolt::Inventory.empty
@@ -537,7 +652,8 @@ module BoltServer
537
652
  'config' => {
538
653
  'transport' => 'winrm',
539
654
  'winrm' => opts.slice(*Bolt::Config::Transport::WinRM.options)
540
- }
655
+ },
656
+ 'plugin_hooks' => target_hash['plugin_hooks']
541
657
  }
542
658
 
543
659
  inventory = Bolt::Inventory.empty
@@ -730,7 +846,7 @@ module BoltServer
730
846
  }
731
847
 
732
848
  connect_plugin = BoltServer::Plugin::PuppetConnectData.new(body['puppet_connect_data'])
733
- plugins = Bolt::Plugin.setup(context[:config], context[:pal], load_plugins: false)
849
+ plugins = Bolt::Plugin.new(context[:config], context[:pal], load_plugins: false)
734
850
  plugins.add_plugin(connect_plugin)
735
851
  %w[aws_inventory azure_inventory gcloud_inventory].each do |plugin_name|
736
852
  plugins.add_module_plugin(plugin_name) if plugins.known_plugin?(plugin_name)
@@ -756,12 +872,7 @@ module BoltServer
756
872
  raise BoltServer::RequestError, "'versioned_project' is a required argument" if params['versioned_project'].nil?
757
873
  content_type :json
758
874
 
759
- plugins_tarball = build_project_plugins_tarball(params['versioned_project']) do |mod|
760
- search_dirs = []
761
- search_dirs << mod.plugins if mod.plugins?
762
- search_dirs << mod.pluginfacts if mod.pluginfacts?
763
- search_dirs
764
- end
875
+ plugins_tarball = plugin_tarball(params['versioned_project'], 'fact_plugins')
765
876
 
766
877
  [200, plugins_tarball.to_json]
767
878
  end
@@ -773,15 +884,7 @@ module BoltServer
773
884
  raise BoltServer::RequestError, "'versioned_project' is a required argument" if params['versioned_project'].nil?
774
885
  content_type :json
775
886
 
776
- plugins_tarball = build_project_plugins_tarball(params['versioned_project']) do |mod|
777
- search_dirs = []
778
- search_dirs << mod.plugins if mod.plugins?
779
- search_dirs << mod.pluginfacts if mod.pluginfacts?
780
- search_dirs << mod.files if mod.files?
781
- type_files = "#{mod.path}/types"
782
- search_dirs << type_files if File.exist?(type_files)
783
- search_dirs
784
- end
887
+ plugins_tarball = plugin_tarball(params['versioned_project'], 'all_plugins')
785
888
 
786
889
  [200, plugins_tarball.to_json]
787
890
  end