bolt 2.6.0 → 2.11.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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/Puppetfile +4 -3
  3. data/bolt-modules/boltlib/lib/puppet/datatypes/applyresult.rb +2 -0
  4. data/bolt-modules/boltlib/lib/puppet/datatypes/resourceinstance.rb +27 -0
  5. data/bolt-modules/boltlib/lib/puppet/datatypes/result.rb +2 -0
  6. data/bolt-modules/boltlib/lib/puppet/datatypes/resultset.rb +2 -0
  7. data/bolt-modules/boltlib/lib/puppet/datatypes/target.rb +4 -3
  8. data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +1 -1
  9. data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +192 -0
  10. data/bolt-modules/boltlib/lib/puppet/functions/set_resources.rb +122 -0
  11. data/bolt-modules/boltlib/types/planresult.pp +12 -1
  12. data/bolt-modules/file/lib/puppet/functions/file/exists.rb +3 -1
  13. data/bolt-modules/file/lib/puppet/functions/file/join.rb +1 -1
  14. data/bolt-modules/file/lib/puppet/functions/file/read.rb +2 -1
  15. data/bolt-modules/file/lib/puppet/functions/file/readable.rb +3 -1
  16. data/bolt-modules/file/lib/puppet/functions/file/write.rb +3 -1
  17. data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +43 -0
  18. data/lib/bolt/analytics.rb +1 -1
  19. data/lib/bolt/applicator.rb +3 -2
  20. data/lib/bolt/apply_inventory.rb +1 -1
  21. data/lib/bolt/apply_result.rb +1 -1
  22. data/lib/bolt/apply_target.rb +11 -2
  23. data/lib/bolt/bolt_option_parser.rb +27 -7
  24. data/lib/bolt/catalog.rb +32 -3
  25. data/lib/bolt/cli.rb +52 -22
  26. data/lib/bolt/config.rb +51 -27
  27. data/lib/bolt/config/transport/base.rb +3 -3
  28. data/lib/bolt/config/transport/docker.rb +7 -1
  29. data/lib/bolt/config/transport/local.rb +9 -1
  30. data/lib/bolt/config/transport/orch.rb +4 -2
  31. data/lib/bolt/config/transport/remote.rb +2 -0
  32. data/lib/bolt/config/transport/ssh.rb +81 -3
  33. data/lib/bolt/config/transport/winrm.rb +6 -1
  34. data/lib/bolt/executor.rb +38 -0
  35. data/lib/bolt/inventory.rb +2 -1
  36. data/lib/bolt/inventory/group.rb +1 -0
  37. data/lib/bolt/inventory/inventory.rb +9 -0
  38. data/lib/bolt/inventory/target.rb +17 -1
  39. data/lib/bolt/node/output.rb +1 -1
  40. data/lib/bolt/outputter/human.rb +5 -4
  41. data/lib/bolt/outputter/json.rb +1 -1
  42. data/lib/bolt/pal.rb +32 -14
  43. data/lib/bolt/pal/yaml_plan.rb +1 -0
  44. data/lib/bolt/plugin.rb +14 -8
  45. data/lib/bolt/plugin/env_var.rb +2 -1
  46. data/lib/bolt/plugin/module.rb +40 -7
  47. data/lib/bolt/plugin/prompt.rb +1 -1
  48. data/lib/bolt/plugin/puppetdb.rb +5 -2
  49. data/lib/bolt/project.rb +135 -0
  50. data/lib/bolt/puppetdb/config.rb +16 -28
  51. data/lib/bolt/rerun.rb +1 -1
  52. data/lib/bolt/resource_instance.rb +126 -0
  53. data/lib/bolt/result.rb +46 -23
  54. data/lib/bolt/result_set.rb +2 -5
  55. data/lib/bolt/secret.rb +20 -4
  56. data/lib/bolt/shell/bash.rb +27 -14
  57. data/lib/bolt/shell/bash/tmpdir.rb +1 -1
  58. data/lib/bolt/shell/powershell.rb +43 -15
  59. data/lib/bolt/shell/powershell/snippets.rb +1 -1
  60. data/lib/bolt/target.rb +18 -2
  61. data/lib/bolt/transport/base.rb +24 -8
  62. data/lib/bolt/transport/docker.rb +3 -3
  63. data/lib/bolt/transport/docker/connection.rb +11 -7
  64. data/lib/bolt/transport/local/connection.rb +13 -7
  65. data/lib/bolt/transport/orch.rb +5 -1
  66. data/lib/bolt/transport/ssh.rb +6 -2
  67. data/lib/bolt/transport/ssh/connection.rb +26 -1
  68. data/lib/bolt/transport/ssh/exec_connection.rb +110 -0
  69. data/lib/bolt/transport/winrm/connection.rb +10 -2
  70. data/lib/bolt/version.rb +1 -1
  71. data/lib/bolt_server/pe/pal.rb +1 -38
  72. data/lib/bolt_server/transport_app.rb +7 -7
  73. data/lib/bolt_spec/bolt_context.rb +3 -6
  74. data/lib/bolt_spec/plans.rb +78 -8
  75. data/lib/bolt_spec/plans/action_stubs.rb +37 -7
  76. data/lib/bolt_spec/plans/action_stubs/plan_stub.rb +55 -0
  77. data/lib/bolt_spec/plans/mock_executor.rb +62 -2
  78. data/lib/bolt_spec/run.rb +10 -13
  79. metadata +26 -7
  80. data/lib/bolt/boltdir.rb +0 -54
  81. data/lib/bolt/plugin/pkcs7.rb +0 -104
  82. data/lib/bolt/secret/base.rb +0 -41
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ca1602020625348ee5ba03e246feeb9263ca10fdcda10e13bba36eb674dcdfba
4
- data.tar.gz: bdde3554a2406f427f57d02f2e0063e592b7ceba235656c406e9f1ece3899c75
3
+ metadata.gz: ffe8fa93c2b9de411d9c2ef46c6c68deae3e10c7b711451dd1acf12034952b31
4
+ data.tar.gz: 4892ece130a2c4c8d70aa6315df85c13be920c290c2e337dddf2b493180bea58
5
5
  SHA512:
6
- metadata.gz: 13bcc53364488a1f37ebc19821c6429bcbcc055a543f9b581352b03c403793579290af88be0b92416506c3ddd9f23e775c7d9b9a6d2369aed3c38b0ef162da8f
7
- data.tar.gz: ea09c6b92cc08d1cd893c136775070df2f6d79097663c58a96212bfdcf23984fe39fd5690d706f4802a61cd384bfe8139e446914506938605ba6d4771f272d63
6
+ metadata.gz: b95e67238b0af8b5ded1c2f14f1514fb5ab715ae43afbca3247c4f4224d7f403385e0b3f6072babbc90faeb8bcaa4d7cc0b79319258f509340fe765c3e9e13b7
7
+ data.tar.gz: 9e1584102d3b103632e36d9c922df007c9b780d69a088b964ef59ff4162e94abdc12693e61fea42c5256a97ebf245e2dd4222d4019d98db2d658a10b4f15e317
data/Puppetfile CHANGED
@@ -6,7 +6,7 @@ moduledir File.join(File.dirname(__FILE__), 'modules')
6
6
 
7
7
  # Core modules used by 'apply'
8
8
  mod 'puppetlabs-service', '1.2.0'
9
- mod 'puppetlabs-puppet_agent', '3.0.2'
9
+ mod 'puppetlabs-puppet_agent', '3.2.0'
10
10
  mod 'puppetlabs-facts', '1.0.0'
11
11
 
12
12
  # Core types and providers for Puppet 6
@@ -24,7 +24,7 @@ mod 'puppetlabs-zone_core', '1.0.3'
24
24
  # Useful additional modules
25
25
  mod 'puppetlabs-package', '1.1.0'
26
26
  mod 'puppetlabs-puppet_conf', '0.6.0'
27
- mod 'puppetlabs-python_task_helper', '0.4.0'
27
+ mod 'puppetlabs-python_task_helper', '0.4.3'
28
28
  mod 'puppetlabs-reboot', '3.0.0'
29
29
  mod 'puppetlabs-ruby_task_helper', '0.5.1'
30
30
  mod 'puppetlabs-ruby_plugin_helper', '0.1.0'
@@ -32,7 +32,8 @@ mod 'puppetlabs-ruby_plugin_helper', '0.1.0'
32
32
  # Plugin modules
33
33
  mod 'puppetlabs-aws_inventory', '0.5.0'
34
34
  mod 'puppetlabs-azure_inventory', '0.3.0'
35
- mod 'puppetlabs-gcloud_inventory', '0.1.0'
35
+ mod 'puppetlabs-gcloud_inventory', '0.1.1'
36
+ mod 'puppetlabs-pkcs7', '0.1.0'
36
37
  mod 'puppetlabs-terraform', '0.5.0'
37
38
  mod 'puppetlabs-vault', '0.3.0'
38
39
  mod 'puppetlabs-yaml', '0.2.0'
@@ -17,5 +17,7 @@ Puppet::DataTypes.create_type('ApplyResult') do
17
17
 
18
18
  load_file('bolt/apply_result')
19
19
 
20
+ # Needed for Puppet to recognize Bolt::ApplyResult as a Puppet object when deserializing
21
+ Bolt::ApplyResult.include(Puppet::Pops::Types::PuppetObject)
20
22
  implementation_class Bolt::ApplyResult
21
23
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ Puppet::DataTypes.create_type('ResourceInstance') do
4
+ interface <<-PUPPET
5
+ attributes => {
6
+ 'target' => Target,
7
+ 'type' => Variant[String[1], Type[Resource]],
8
+ 'title' => String[1],
9
+ 'state' => Optional[Hash[String[1], Data]],
10
+ 'desired_state' => Optional[Hash[String[1], Data]],
11
+ 'events' => Optional[Array[Hash[String[1], Data]]]
12
+ },
13
+ functions => {
14
+ add_event => Callable[[Hash[String[1], Data]], [Hash[String[1], Data]]],
15
+ set_state => Callable[[Hash[String[1], Data]], Hash[String[1], Data]],
16
+ overwrite_state => Callable[[Hash[String[1], Data]], Hash[String[1], Data]],
17
+ set_desired_state => Callable[[Hash[String[1], Data]], Hash[String[1], Data]],
18
+ overwrite_desired_state => Callable[[Hash[String[1], Data]], Hash[String[1], Data]],
19
+ reference => Callable[[], String]
20
+ }
21
+ PUPPET
22
+
23
+ load_file('bolt/resource_instance')
24
+ # Needed for Puppet to recognize Bolt::ResourceInstance as a Puppet object when deserializing
25
+ Bolt::ResourceInstance.include(Puppet::Pops::Types::PuppetObject)
26
+ implementation_class Bolt::ResourceInstance
27
+ end
@@ -19,5 +19,7 @@ Puppet::DataTypes.create_type('Result') do
19
19
 
20
20
  load_file('bolt/result')
21
21
 
22
+ # Needed for Puppet to recognize Bolt::Result as a Puppet object when deserializing
23
+ Bolt::Result.include(Puppet::Pops::Types::PuppetObject)
22
24
  implementation_class Bolt::Result
23
25
  end
@@ -25,5 +25,7 @@ Puppet::DataTypes.create_type('ResultSet') do
25
25
 
26
26
  load_file('bolt/result_set')
27
27
 
28
+ # Needed for Puppet to recognize Bolt::ResultSet as a Puppet object when deserializing
29
+ Bolt::ResultSet.include(Puppet::Pops::Types::PuppetObject)
28
30
  implementation_class Bolt::ResultSet
29
31
  end
@@ -8,8 +8,6 @@ Puppet::DataTypes.create_type('Target') do
8
8
  target_implementation_class = Bolt::Target
9
9
  end
10
10
 
11
- require 'bolt/target'
12
-
13
11
  interface <<-PUPPET
14
12
  attributes => {
15
13
  uri => { type => Optional[String[1]], kind => given_or_derived },
@@ -20,7 +18,8 @@ Puppet::DataTypes.create_type('Target') do
20
18
  vars => { type => Optional[Hash[String[1], Data]], kind => given_or_derived },
21
19
  facts => { type => Optional[Hash[String[1], Data]], kind => given_or_derived },
22
20
  features => { type => Optional[Array[String[1]]], kind => given_or_derived },
23
- plugin_hooks => { type => Optional[Hash[String[1], Data]], kind => given_or_derived }
21
+ plugin_hooks => { type => Optional[Hash[String[1], Data]], kind => given_or_derived },
22
+ resources => { type => Optional[Hash[String[1], ResourceInstance]], kind => given_or_derived }
24
23
  },
25
24
  functions => {
26
25
  host => Callable[[], Optional[String]],
@@ -33,5 +32,7 @@ Puppet::DataTypes.create_type('Target') do
33
32
  }
34
33
  PUPPET
35
34
 
35
+ # Needed for Puppet to recognize targets as Puppet objects when deserializing
36
+ target_implementation_class.include(Puppet::Pops::Types::PuppetObject)
36
37
  implementation_class target_implementation_class
37
38
  end
@@ -117,7 +117,7 @@ Puppet::Functions.create_function(:run_plan, Puppet::Functions::InternalFunction
117
117
  # undef/nil
118
118
  result = catch(:return) do
119
119
  scope.with_global_scope do |global_scope|
120
- closure.call_by_name_with_scope(global_scope, params, true)
120
+ executor.run_plan(global_scope, closure, params)
121
121
  end
122
122
  nil
123
123
  end&.value
@@ -0,0 +1,192 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bolt/error'
4
+ require 'bolt/pal'
5
+ require 'bolt/task'
6
+
7
+ # Runs a given instance of a `Task` with target-specific parameters on the given set of targets and
8
+ # returns the result from each. This function differs from {run_task} by accepting a block that returns
9
+ # a `Hash` of target-specific parameters that are passed to the task. This can be used to send parameters
10
+ # based on a target's attributes, such as its `facts`, or to use conditional logic to determine the
11
+ # parameters a task should receive for a specific target.
12
+ #
13
+ # This function does nothing if the list of targets is empty.
14
+ #
15
+ # > **Note:** Not available in apply block
16
+ #
17
+ # > **Note:** Not available to targets using the pcp transport
18
+ Puppet::Functions.create_function(:run_task_with) do
19
+ # Run a task with target-specific parameters.
20
+ # @param task_name The task to run.
21
+ # @param targets A pattern identifying zero or more targets. See {get_targets} for accepted patterns.
22
+ # @param options A hash of additional options.
23
+ # @option options [Boolean] _catch_errors Whether to catch raised errors.
24
+ # @option options [Boolean] _noop Run the task in noop mode if available.
25
+ # @option options [String] _run_as User to run as using privilege escalation.
26
+ # @param block A block that returns a `Hash` of target-specific parameters for the task.
27
+ # @return A list of results, one entry per target.
28
+ # @example Run a task with target-specific parameters as root
29
+ # run_task_with('my_task', $targets, '_run_as' => 'root') |$t| {
30
+ # { 'param1' => $t.vars['var1'],
31
+ # 'param2' => $t.vars['var2'] }
32
+ # }
33
+ dispatch :run_task_with do
34
+ param 'String[1]', :task_name
35
+ param 'Boltlib::TargetSpec', :targets
36
+ optional_param 'Hash[String[1], Any]', :options
37
+ required_block_param 'Callable[Target]', :block
38
+ return_type 'ResultSet'
39
+ end
40
+
41
+ # Run a task with target-specific parameters, logging the provided description.
42
+ # @param task_name The task to run.
43
+ # @param targets A pattern identifying zero or more targets. See {get_targets} for accepted patterns.
44
+ # @param description A description to be output when calling this function.
45
+ # @param options A hash of additional options.
46
+ # @option options [Boolean] _catch_errors Whether to catch raised errors.
47
+ # @option options [Boolean] _noop Run the task in noop mode if available.
48
+ # @option options [String] _run_as User to run as using privilege escalation.
49
+ # @param block A block that returns a `Hash` of target-specific parameters for the task.
50
+ # @return A list of results, one entry per target.
51
+ # @example Run a task with target-specific parameters and a description
52
+ # run_task_with('my_task', $targets, 'Update system packages') |$t| {
53
+ # { 'param1' => $t.vars['var1'],
54
+ # 'param2' => $t.vars['var2'] }
55
+ # }
56
+ dispatch :run_task_with_with_description do
57
+ param 'String[1]', :task_name
58
+ param 'Boltlib::TargetSpec', :targets
59
+ param 'Optional[String]', :description
60
+ optional_param 'Hash[String[1], Any]', :options
61
+ required_block_param 'Callable[Target]', :block
62
+ return_type 'ResultSet'
63
+ end
64
+
65
+ def run_task_with(task_name, targets, options = {}, &block)
66
+ run_task_with_with_description(task_name, targets, nil, options, &block)
67
+ end
68
+
69
+ def run_task_with_with_description(task_name, targets, description, options = {})
70
+ unless Puppet[:tasks]
71
+ raise Puppet::ParseErrorWithIssue
72
+ .from_issue_and_stack(
73
+ Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING,
74
+ action: 'run_task_with'
75
+ )
76
+ end
77
+
78
+ executor = Puppet.lookup(:bolt_executor)
79
+ inventory = Puppet.lookup(:bolt_inventory)
80
+ error_set = []
81
+
82
+ # Report to analytics
83
+ executor.report_function_call(self.class.name)
84
+ executor.report_bundled_content('Task', task_name)
85
+
86
+ # Keep valid metaparameters, discarding everything else
87
+ options = options.select { |k, _v| k.start_with?('_') }
88
+ .transform_keys { |k| k.sub(/^_/, '').to_sym }
89
+
90
+ options[:description] = description if description
91
+
92
+ # Get all the targets
93
+ targets = Array(inventory.get_targets(targets))
94
+
95
+ # If all targets use the 'pcp' transport, use a fake task instead of loading the local definition
96
+ # Otherwise, load the local task definition
97
+ if (pcp_only = targets.any? && targets.all? { |t| t.transport == 'pcp' })
98
+ task = Bolt::Task.new(task_name, {}, [{ 'name' => '', 'path' => '' }])
99
+ else
100
+ task_signature = Puppet::Pal::ScriptCompiler.new(closure_scope.compiler).task_signature(task_name)
101
+
102
+ if task_signature.nil?
103
+ raise Bolt::Error.unknown_task(task_name)
104
+ end
105
+
106
+ task = Bolt::Task.from_task_signature(task_signature)
107
+ end
108
+
109
+ # Map the targets to their specific parameters and merge with the defaults
110
+ target_mapping = targets.each_with_object({}) do |target, mapping|
111
+ params = yield(target)
112
+
113
+ # Parameters returned from the block should be a Hash. If they're not, create a failing
114
+ # Result for the target that will later be added to the ResultSet.
115
+ unless params.is_a?(Hash)
116
+ exception = with_stack(
117
+ :TYPE_MISMATCH,
118
+ "Block must return a Hash of parameters, received #{params.class}"
119
+ )
120
+ error_set << Bolt::Result.from_exception(target, exception, action: 'task')
121
+ next
122
+ end
123
+
124
+ # If parameters are mismatched, create a failing result for the target that will later
125
+ # be added to the ResultSet.
126
+ unless pcp_only
127
+ params = task.parameter_defaults.merge(params)
128
+
129
+ type_match = task_signature.runnable_with?(params) do |mismatch_message|
130
+ exception = with_stack(:TYPE_MISMATCH, mismatch_message)
131
+ error_set << Bolt::Result.from_exception(target, exception, action: 'task')
132
+ end
133
+
134
+ next unless type_match
135
+ end
136
+
137
+ # If there is a type mismatch between the type Data and the type of the params, create
138
+ # a failing result for the target that will later be added to the ResultSet.
139
+ unless Puppet::Pops::Types::TypeFactory.data.instance?(params)
140
+ params_t = Puppet::Pops::Types::TypeCalculator.infer_set(params)
141
+ desc = Puppet::Pops::Types::TypeMismatchDescriber.singleton.describe_mismatch(
142
+ 'Task parameters are not of type Data. run_task_with()',
143
+ Puppet::Pops::Types::TypeFactory.data, params_t
144
+ )
145
+ exception = with_stack(:TYPE_NOT_DATA, desc)
146
+ error_set << Bolt::Result.from_exception(target, exception, action: 'task')
147
+ next
148
+ end
149
+
150
+ # Wrap parameters marked with '"sensitive": true' in the task metadata with a
151
+ # Sensitive wrapper type. This way it's not shown in logs.
152
+ if (param_spec = task.parameters)
153
+ params.each do |k, v|
154
+ if param_spec[k] && param_spec[k]['sensitive']
155
+ params[k] = Puppet::Pops::Types::PSensitiveType::Sensitive.new(v)
156
+ end
157
+ end
158
+ end
159
+
160
+ mapping[target] = task.parameter_defaults.merge(params)
161
+ end
162
+
163
+ # Add a noop parameter if the function was called with the noop metaparameter.
164
+ if options[:noop]
165
+ if task.supports_noop
166
+ target_mapping.each_value { |params| params['_noop'] = true }
167
+ else
168
+ raise with_stack(:TASK_NO_NOOP, 'Task does not support noop')
169
+ end
170
+ end
171
+
172
+ if targets.empty?
173
+ Bolt::ResultSet.new([])
174
+ else
175
+ # Combine the results from the task run with any failing results that were
176
+ # generated earlier when creating the target mapping
177
+ task_result = executor.run_task_with(target_mapping, task, options)
178
+ result = Bolt::ResultSet.new(task_result.results + error_set)
179
+
180
+ if !result.ok && !options[:catch_errors]
181
+ raise Bolt::RunFailure.new(result, 'run_task', task_name)
182
+ end
183
+
184
+ result
185
+ end
186
+ end
187
+
188
+ def with_stack(kind, msg)
189
+ issue = Puppet::Pops::Issues.issue(kind) { msg }
190
+ Puppet::ParseErrorWithIssue.from_issue_and_stack(issue)
191
+ end
192
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bolt/error'
4
+
5
+ # Sets one or more ResourceInstances on a Target. This function does not apply or modify
6
+ # resources on a target.
7
+ #
8
+ # For more information about resources see [the
9
+ # documentation](https://puppet.com/docs/puppet/latest/lang_resources.html).
10
+ #
11
+ # > **Note:** The `ResourceInstance` data type is under active development and is subject to
12
+ # change. You can read more about the data type in the [experimental features
13
+ # documentation](experimental_features.md#resourceinstance-data-type).
14
+ #
15
+ # > **Note:** Not available in apply block
16
+ Puppet::Functions.create_function(:set_resources) do
17
+ # Set multiple resources
18
+ # @param target The `Target` object to add resources to. See {get_targets}.
19
+ # @param resources The resources to set on the target.
20
+ # @return The added `ResourceInstance` objects.
21
+ # @example Add multiple resources to a target with an array of `ResourceInstance` objects.
22
+ # $resource1 = ResourceInstance.new(
23
+ # 'target' => $target,
24
+ # 'type' => 'file',
25
+ # 'title' => '/etc/puppetlabs',
26
+ # 'state' => { 'ensure' => 'present' }
27
+ # )
28
+ # $resource2 = ResourceInstance.new(
29
+ # 'target' => $target,
30
+ # 'type' => 'package',
31
+ # 'title' => 'openssl',
32
+ # 'state' => { 'ensure' => 'installed' }
33
+ # )
34
+ # $target.set_resources([$resource1, $resource2])
35
+ # @example Add resources retrieved with [`get_resources`](#get_resources) to a target.
36
+ # $target.apply_prep
37
+ # $resources = $target.get_resources(Package).first['resources']
38
+ # $target.set_resources($resources)
39
+ dispatch :set_resources do
40
+ param 'Target', :target
41
+ param 'Array[Variant[Hash, ResourceInstance]]', :resources
42
+ return_type 'Array[ResourceInstance]'
43
+ end
44
+
45
+ # Set a single resource
46
+ # @param target The `Target` object to add resources to. See {get_targets}.
47
+ # @param resource The resource to set on the target.
48
+ # @return The added `ResourceInstance` object.
49
+ # @example Add a single resource to a target with a resource data hash.
50
+ # $resource = {
51
+ # 'type' => 'file',
52
+ # 'title' => '/etc/puppetlabs',
53
+ # 'state' => { 'ensure' => 'present' }
54
+ # }
55
+ # $target.set_resources($resource)
56
+ dispatch :set_resource do
57
+ param 'Target', :target
58
+ param 'Variant[Hash, ResourceInstance]', :resource
59
+ return_type 'Array[ResourceInstance]'
60
+ end
61
+
62
+ def set_resources(target, resources)
63
+ unless Puppet[:tasks]
64
+ raise Puppet::ParseErrorWithIssue
65
+ .from_issue_and_stack(
66
+ Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING,
67
+ action: 'set_resources'
68
+ )
69
+ end
70
+
71
+ inventory = Puppet.lookup(:bolt_inventory)
72
+ executor = Puppet.lookup(:bolt_executor)
73
+ executor.report_function_call(self.class.name)
74
+
75
+ inventory_target = inventory.get_target(target)
76
+
77
+ resources.uniq.map do |resource|
78
+ if resource.is_a?(Hash)
79
+ # ResourceInstance expects a Target object, so either get a specified target from
80
+ # the inventory or use the target this function was called on.
81
+ resource_target = if resource.key?('target')
82
+ inventory.get_target(resource['target'])
83
+ else
84
+ inventory_target
85
+ end
86
+
87
+ # Observed state from get_resources() is under the 'parameters' key
88
+ resource_state = resource['state'] || resource['parameters']
89
+
90
+ init_hash = {
91
+ 'target' => resource_target,
92
+ 'type' => resource['type'],
93
+ 'title' => resource['title'],
94
+ 'state' => resource_state,
95
+ 'desired_state' => resource['desired_state'],
96
+ 'events' => resource['events']
97
+ }
98
+
99
+ # Calling Bolt::ResourceInstance.new or Bolt::ResourceInstance.from_asserted_hash
100
+ # will not perform any validation on the parameters. Instead, we need to use the
101
+ # Puppet constructor to initialize the object, which will first validate the parameters
102
+ # and then call Bolt::ResourceInstance.from_asserted_hash internally. To do this we
103
+ # first need to get the Puppet datatype and then call the new function on that type.
104
+ type = Puppet::Pops::Types::TypeParser.singleton.parse('ResourceInstance')
105
+ resource = call_function('new', type, init_hash)
106
+ end
107
+
108
+ unless resource.target == inventory_target
109
+ file, line = Puppet::Pops::PuppetStack.top_of_stack
110
+ raise Bolt::ValidationError, "Cannot set resource #{resource.reference} for target "\
111
+ "#{resource.target} on target #{inventory_target}. "\
112
+ "#{Puppet::Util::Errors.error_location(file, line)}"
113
+ end
114
+
115
+ inventory_target.set_resource(resource)
116
+ end
117
+ end
118
+
119
+ def set_resource(target, resource)
120
+ set_resources(target, [resource])
121
+ end
122
+ end
@@ -2,4 +2,15 @@
2
2
  # should be used as the return type of functions that run plans and return the
3
3
  # results.
4
4
 
5
- type Boltlib::PlanResult = Variant[Boolean, Numeric, String, Undef, Error, Result, ResultSet, Target, Array[Boltlib::PlanResult], Hash[String, Boltlib::PlanResult]]
5
+ type Boltlib::PlanResult = Variant[Boolean,
6
+ Numeric,
7
+ String,
8
+ Undef,
9
+ Error,
10
+ Result,
11
+ ApplyResult,
12
+ ResultSet,
13
+ Target,
14
+ ResourceInstance,
15
+ Array[Boltlib::PlanResult],
16
+ Hash[String, Boltlib::PlanResult]]