bolt 0.8.0 → 0.9.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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/lib/bolt/cli.rb +31 -21
  3. data/lib/bolt/config.rb +1 -0
  4. data/lib/bolt/error.rb +28 -0
  5. data/lib/bolt/executor.rb +10 -6
  6. data/lib/bolt/node.rb +7 -5
  7. data/lib/bolt/node/errors.rb +25 -3
  8. data/lib/bolt/node/orch.rb +7 -16
  9. data/lib/bolt/node/output.rb +17 -0
  10. data/lib/bolt/node/ssh.rb +101 -44
  11. data/lib/bolt/node/winrm.rb +86 -40
  12. data/lib/bolt/outputter/human.rb +65 -8
  13. data/lib/bolt/outputter/json.rb +8 -1
  14. data/lib/bolt/result.rb +74 -124
  15. data/lib/bolt/version.rb +1 -1
  16. data/{vendored/puppet → modules/boltlib}/lib/puppet/functions/file_upload.rb +9 -7
  17. data/{vendored/puppet → modules/boltlib}/lib/puppet/functions/run_command.rb +8 -9
  18. data/{vendored/puppet → modules/boltlib}/lib/puppet/functions/run_plan.rb +8 -12
  19. data/{vendored/puppet → modules/boltlib}/lib/puppet/functions/run_script.rb +12 -8
  20. data/{vendored/puppet → modules/boltlib}/lib/puppet/functions/run_task.rb +10 -10
  21. data/vendored/puppet/lib/puppet/agent.rb +1 -1
  22. data/vendored/puppet/lib/puppet/application/lookup.rb +1 -3
  23. data/vendored/puppet/lib/puppet/configurer.rb +2 -3
  24. data/vendored/puppet/lib/puppet/configurer/plugin_handler.rb +25 -7
  25. data/vendored/puppet/lib/puppet/defaults.rb +9 -1
  26. data/vendored/puppet/lib/puppet/face/epp.rb +4 -2
  27. data/vendored/puppet/lib/puppet/face/module/build.rb +1 -1
  28. data/vendored/puppet/lib/puppet/face/module/list.rb +5 -16
  29. data/vendored/puppet/lib/puppet/face/module/uninstall.rb +14 -3
  30. data/vendored/puppet/lib/puppet/face/plugin.rb +1 -3
  31. data/vendored/puppet/lib/puppet/forge/errors.rb +17 -7
  32. data/vendored/puppet/lib/puppet/functions.rb +8 -6
  33. data/vendored/puppet/lib/puppet/functions/each.rb +10 -4
  34. data/vendored/puppet/lib/puppet/functions/lookup.rb +2 -2
  35. data/vendored/puppet/lib/puppet/functions/map.rb +12 -2
  36. data/vendored/puppet/lib/puppet/functions/slice.rb +2 -3
  37. data/vendored/puppet/lib/puppet/graph/simple_graph.rb +9 -5
  38. data/vendored/puppet/lib/puppet/interface.rb +1 -0
  39. data/vendored/puppet/lib/puppet/module_tool/errors/installer.rb +27 -17
  40. data/vendored/puppet/lib/puppet/module_tool/errors/shared.rb +143 -63
  41. data/vendored/puppet/lib/puppet/module_tool/errors/uninstaller.rb +37 -14
  42. data/vendored/puppet/lib/puppet/module_tool/errors/upgrader.rb +30 -18
  43. data/vendored/puppet/lib/puppet/network/auth_config_parser.rb +8 -8
  44. data/vendored/puppet/lib/puppet/network/http/error.rb +7 -7
  45. data/vendored/puppet/lib/puppet/network/http/rack.rb +2 -2
  46. data/vendored/puppet/lib/puppet/network/http/webrick.rb +1 -1
  47. data/vendored/puppet/lib/puppet/node.rb +10 -0
  48. data/vendored/puppet/lib/puppet/node/facts.rb +9 -0
  49. data/vendored/puppet/lib/puppet/parameter/value_collection.rb +16 -6
  50. data/vendored/puppet/lib/puppet/parser/resource.rb +103 -31
  51. data/vendored/puppet/lib/puppet/pops/evaluator/access_operator.rb +13 -0
  52. data/vendored/puppet/lib/puppet/pops/evaluator/evaluator_impl.rb +22 -6
  53. data/vendored/puppet/lib/puppet/pops/loader/static_loader.rb +1 -1
  54. data/vendored/puppet/lib/puppet/pops/lookup/lookup_adapter.rb +13 -4
  55. data/vendored/puppet/lib/puppet/pops/model/ast_transformer.rb +1 -1
  56. data/vendored/puppet/lib/puppet/pops/parser/eparser.rb +527 -529
  57. data/vendored/puppet/lib/puppet/pops/serialization/abstract_reader.rb +4 -0
  58. data/vendored/puppet/lib/puppet/pops/serialization/abstract_writer.rb +6 -0
  59. data/vendored/puppet/lib/puppet/pops/serialization/extension.rb +1 -0
  60. data/vendored/puppet/lib/puppet/pops/serialization/serializer.rb +2 -1
  61. data/vendored/puppet/lib/puppet/pops/types/execution_result.rb +8 -0
  62. data/vendored/puppet/lib/puppet/pops/types/iterable.rb +2 -0
  63. data/vendored/puppet/lib/puppet/pops/types/p_object_type.rb +3 -0
  64. data/vendored/puppet/lib/puppet/pops/types/p_object_type_extension.rb +6 -0
  65. data/vendored/puppet/lib/puppet/pops/types/p_uri_type.rb +191 -0
  66. data/vendored/puppet/lib/puppet/pops/types/string_converter.rb +17 -0
  67. data/vendored/puppet/lib/puppet/pops/types/type_calculator.rb +5 -0
  68. data/vendored/puppet/lib/puppet/pops/types/type_factory.rb +7 -0
  69. data/vendored/puppet/lib/puppet/pops/types/type_formatter.rb +16 -18
  70. data/vendored/puppet/lib/puppet/pops/types/type_mismatch_describer.rb +15 -5
  71. data/vendored/puppet/lib/puppet/pops/types/type_parser.rb +6 -0
  72. data/vendored/puppet/lib/puppet/pops/types/type_with_members.rb +43 -0
  73. data/vendored/puppet/lib/puppet/pops/types/types.rb +3 -0
  74. data/vendored/puppet/lib/puppet/provider/package/gem.rb +1 -1
  75. data/vendored/puppet/lib/puppet/provider/package/nim.rb +7 -8
  76. data/vendored/puppet/lib/puppet/provider/package/opkg.rb +1 -1
  77. data/vendored/puppet/lib/puppet/provider/package/pkg.rb +6 -4
  78. data/vendored/puppet/lib/puppet/provider/package/pkgutil.rb +3 -3
  79. data/vendored/puppet/lib/puppet/provider/service/init.rb +1 -1
  80. data/vendored/puppet/lib/puppet/syntax_checkers/base64.rb +5 -6
  81. data/vendored/puppet/lib/puppet/transaction.rb +1 -1
  82. data/vendored/puppet/lib/puppet/type.rb +1 -9
  83. data/vendored/puppet/lib/puppet/type/schedule.rb +1 -1
  84. data/vendored/puppet/lib/puppet/util/log.rb +2 -3
  85. data/vendored/puppet/lib/puppet/util/plist.rb +1 -1
  86. data/vendored/puppet/lib/puppet/util/reference.rb +2 -3
  87. data/vendored/puppet/lib/puppet_pal.rb +326 -53
  88. metadata +28 -12
  89. data/lib/bolt/node/result.rb +0 -115
  90. data/vendored/puppet/lib/puppet/configurer/downloader_factory.rb +0 -44
@@ -201,6 +201,7 @@ class TypeParser
201
201
  'semverrange' => TypeFactory.sem_ver_range,
202
202
  'timestamp' => TypeFactory.timestamp,
203
203
  'timespan' => TypeFactory.timespan,
204
+ 'uri' => TypeFactory.uri,
204
205
  }.freeze
205
206
  end
206
207
 
@@ -356,6 +357,11 @@ class TypeParser
356
357
  raise_invalid_parameters_error('Pattern', '1 or more', parameters.size) unless parameters.size >= 1
357
358
  TypeFactory.pattern(*parameters)
358
359
 
360
+ when 'uri'
361
+ # 1 parameter which is a string or a URI
362
+ raise_invalid_parameters_error('URI', '1', parameters.size) unless parameters.size == 1
363
+ TypeFactory.uri(parameters[0])
364
+
359
365
  when 'variant'
360
366
  # 1..m parameters being strings or regular expressions
361
367
  raise_invalid_parameters_error('Variant', '1 or more', parameters.size) unless parameters.size >= 1
@@ -0,0 +1,43 @@
1
+ module Puppet::Pops
2
+ module Types
3
+
4
+ # Interface implemented by a type that has IvocableMembers
5
+ module TypeWithMembers
6
+ # @return [InvocableMember,nil] An invocable member if it exists, or `nil`
7
+ def [](member_name)
8
+ raise NotImplementedError, "'#{self.class.name}' should implement #[]"
9
+ end
10
+ end
11
+
12
+ # Interface implemented by attribute and function members
13
+ module InvocableMember
14
+ # Performs type checking of arguments and invokes the method that corresponds to this
15
+ # method. The result of the invocation is returned
16
+ #
17
+ # @param receiver [Object] The receiver of the call
18
+ # @param scope [Puppet::Parser::Scope] The caller scope
19
+ # @param args [Array] Array of arguments.
20
+ # @return [Object] The result returned by the member function or attribute
21
+ #
22
+ # @api private
23
+ def invoke(receiver, scope, args, &block)
24
+ raise NotImplementedError, "'#{self.class.name}' should implement #invoke"
25
+ end
26
+ end
27
+
28
+ # Plays the same role as an PAttribute in the PObjectType. Provides
29
+ # access to known attr_readers and plain reader methods.
30
+ class AttrReader
31
+ include InvocableMember
32
+
33
+ def initialize(message)
34
+ @message = message.to_sym
35
+ end
36
+
37
+ def invoke(receiver, scope, args, &block)
38
+ receiver.send(@message)
39
+ end
40
+ end
41
+ end
42
+ end
43
+
@@ -1414,6 +1414,8 @@ class PIterableType < PTypeWithContainedType
1414
1414
  o >= 0
1415
1415
  when PIntegerType
1416
1416
  o.finite_range?
1417
+ when PTypeAliasType
1418
+ instance?(o.resolved_type, guard)
1417
1419
  else
1418
1420
  false
1419
1421
  end
@@ -3623,6 +3625,7 @@ require_relative 'p_timestamp_type'
3623
3625
  require_relative 'p_binary_type'
3624
3626
  require_relative 'p_init_type'
3625
3627
  require_relative 'p_object_type_extension'
3628
+ require_relative 'p_uri_type'
3626
3629
  require_relative 'type_set_reference'
3627
3630
  require_relative 'implementation_registry'
3628
3631
  require_relative 'tree_iterators'
@@ -8,7 +8,7 @@ Puppet::Type.type(:package).provide :gem, :parent => Puppet::Provider::Package d
8
8
  specified source is used, also pass `--clear-sources` via `install_options`.
9
9
  If source is present but is not a valid URL, it will be interpreted as the
10
10
  path to a local gem file. If source is not present, the gem will be
11
- installed from the default gem repositories.
11
+ installed from the default gem repositories. Note that to modify this for Windows, it has to be a valid URL.
12
12
 
13
13
  This provider supports the `install_options` and `uninstall_options` attributes,
14
14
  which allow command-line flags to be passed to the gem command.
@@ -97,14 +97,13 @@ Puppet::Type.type(:package).provide :nim, :parent => :aix, :source => :aix do
97
97
  end
98
98
 
99
99
  if (package_type == nil)
100
- #TRANSLATORS Full message: "Unable to find package #{pkg} with version #{version} on lpp_source #{source}"
101
- errmsg = _("Unable to find package '%{pkg}' ") % { pkg: pkg }
102
- if (version_specified)
103
- #TRANSLATORS Full message: "Unable to find package #{pkg} with version #{version} on lpp_source #{source}"
104
- errmsg << _("with version '%{version}' ") % { version: version }
105
- end
106
- #TRANSLATORS Full message: "Unable to find package #{pkg} with version #{version} on lpp_source #{source}"
107
- errmsg << "on lpp_source '#{source}'"
100
+
101
+ errmsg = if version_specified
102
+ _("Unable to find package '%{package}' with version '%{version}' on lpp_source '%{source}'") %
103
+ { package: pkg, version: version, source: source }
104
+ else
105
+ _("Unable to find package '%{package}' on lpp_source '%{source}'") % { package: pkg, source: source }
106
+ end
108
107
  self.fail errmsg
109
108
  end
110
109
 
@@ -22,7 +22,7 @@ Puppet::Type.type(:package).provide :opkg, :source => :opkg, :parent => Puppet::
22
22
  packages << new(hash)
23
23
  hash = {}
24
24
  else
25
- warning(_("Failed to match line %s" % line))
25
+ warning(_("Failed to match line %{line}") % { line: line })
26
26
  end
27
27
  }
28
28
  end
@@ -43,7 +43,8 @@ Puppet::Type.type(:package).provide :pkg, :parent => Puppet::Provider::Package d
43
43
  when '-'
44
44
  {:status => 'known'}
45
45
  else
46
- raise ArgumentError, _('Unknown format %s: %s[%s]') % [self.name, flags, flags[0..0]]
46
+ raise ArgumentError, _('Unknown format %{resource_name}: %{full_flags}[%{bad_flag}]') %
47
+ { resource_name: self.name, full_flags: flags, bad_flag: flags[0..0] }
47
48
  end
48
49
  ).merge(
49
50
  case flags[1..1]
@@ -52,7 +53,8 @@ Puppet::Type.type(:package).provide :pkg, :parent => Puppet::Provider::Package d
52
53
  when '-'
53
54
  {}
54
55
  else
55
- raise ArgumentError, _('Unknown format %s: %s[%s]') % [self.name, flags, flags[1..1]]
56
+ raise ArgumentError, _('Unknown format %{resource_name}: %{full_flags}[%{bad_flag}]') %
57
+ { resource_name: self.name, full_flags: flags, bad_flag: flags[1..1] }
56
58
  end
57
59
  )
58
60
  end
@@ -82,7 +84,7 @@ Puppet::Type.type(:package).provide :pkg, :parent => Puppet::Provider::Package d
82
84
  when /known/
83
85
  {:status => 'known'}
84
86
  else
85
- raise ArgumentError, _('Unknown format %s: %s') % [self.name, state]
87
+ raise ArgumentError, _('Unknown format %{resource_name}: %{state}') % { resource_name: self.name, state: state }
86
88
  end
87
89
  end
88
90
 
@@ -101,7 +103,7 @@ Puppet::Type.type(:package).provide :pkg, :parent => Puppet::Provider::Package d
101
103
  {:publisher => $1, :name => $2, :ensure => $3}.merge pkg_state($4).merge(ufoxi_flag($5))
102
104
 
103
105
  else
104
- raise ArgumentError, _('Unknown line format %s: %s') % [self.name, line]
106
+ raise ArgumentError, _('Unknown line format %{resource_name}: %{parse_line}') % { resource_name: self.name, parse_line: line }
105
107
  end).merge({:provider => self.name})
106
108
  end
107
109
 
@@ -70,7 +70,7 @@ Puppet::Type.type(:package).provide :pkgutil, :parent => :sun, :source => :sun d
70
70
  if line =~ /\s*(\S+)\s+(\S+)\s+(.*)/
71
71
  { :alias => $1, :name => $2, :avail => $3 }
72
72
  else
73
- Puppet.warning _("Cannot match %s") % line
73
+ Puppet.warning _("Cannot match %{line}") % { line: line }
74
74
  end
75
75
  end.reject { |h| h.nil? }
76
76
  end
@@ -88,7 +88,7 @@ Puppet::Type.type(:package).provide :pkgutil, :parent => :sun, :source => :sun d
88
88
  output = output.split("\n")
89
89
 
90
90
  if output[-1] == "Not in catalog"
91
- Puppet.warning _("Package not in pkgutil catalog: %s") % hash[:justme]
91
+ Puppet.warning _("Package not in pkgutil catalog: %{package}") % { package: hash[:justme] }
92
92
  return nil
93
93
  end
94
94
 
@@ -142,7 +142,7 @@ Puppet::Type.type(:package).provide :pkgutil, :parent => :sun, :source => :sun d
142
142
 
143
143
  return hash
144
144
  else
145
- Puppet.warning _("Cannot match %s") % line
145
+ Puppet.warning _("Cannot match %{line}") % { line: line }
146
146
  return nil
147
147
  end
148
148
  end
@@ -171,7 +171,7 @@ Puppet::Type.type(:service).provide :init, :parent => :base do
171
171
 
172
172
  def texecute(type, command, fof = true, squelch = false, combine = true)
173
173
  if type == :start && Facter.value(:osfamily) == "Solaris"
174
- command = ["/usr/bin/ctrun -l none", command].flatten.join(" ")
174
+ command = ["/usr/bin/ctrun -l child", command].flatten.join(" ")
175
175
  end
176
176
  super(type, command, fof, squelch, combine)
177
177
  end
@@ -24,12 +24,11 @@ class Puppet::SyntaxCheckers::Base64 < Puppet::Plugins::SyntaxCheckers::SyntaxCh
24
24
  # simply skips all non base64 characters
25
25
  Base64.strict_decode64(cleaned_text)
26
26
  rescue => e
27
- if (cleaned_text.bytes.to_a.size * 8) % 6 != 0
28
- msg2 = _("padding is not correct")
29
- else
30
- msg2 = _("contains letters outside strict base 64 range (or whitespace)")
31
- end
32
- msg = _("Base64 syntax checker: Cannot parse invalid Base64 string - %{msg2}") % { msg2: msg2 }
27
+ msg = if (cleaned_text.bytes.to_a.size * 8) % 6 != 0
28
+ _("Base64 syntax checker: Cannot parse invalid Base64 string - padding is not correct")
29
+ else
30
+ _("Base64 syntax checker: Cannot parse invalid Base64 string - contains letters outside strict base 64 range (or whitespace)")
31
+ end
33
32
 
34
33
  # TODO: improve the pops API to allow simpler diagnostic creation while still maintaining capabilities
35
34
  # and the issue code. (In this case especially, where there is only a single error message being issued).
@@ -177,7 +177,7 @@ class Puppet::Transaction
177
177
  else
178
178
  resource.info _("Starting to evaluate the resource") if Puppet[:evaltrace] and @catalog.host_config?
179
179
  seconds = thinmark { block.call(resource) }
180
- resource.info _("Evaluated in %0.2f seconds") % seconds if Puppet[:evaltrace] and @catalog.host_config?
180
+ resource.info _("Evaluated in %{seconds} seconds") % { seconds: "%0.2f" % seconds } if Puppet[:evaltrace] and @catalog.host_config?
181
181
  end
182
182
  end
183
183
 
@@ -1289,9 +1289,7 @@ class Type
1289
1289
  end
1290
1290
 
1291
1291
  newmetaparam(:audit) do
1292
- desc "(This metaparameter is deprecated and will be ignored in a future release.)
1293
-
1294
- Marks a subset of this resource's unmanaged attributes for auditing. Accepts an
1292
+ desc "Marks a subset of this resource's unmanaged attributes for auditing. Accepts an
1295
1293
  attribute name, an array of attribute names, or `all`.
1296
1294
 
1297
1295
  Auditing a resource attribute has two effects: First, whenever a catalog
@@ -1312,12 +1310,6 @@ class Type
1312
1310
  and the second run will log the edit made by Puppet.)"
1313
1311
 
1314
1312
  validate do |list|
1315
- if Puppet.settings[:strict] != :off
1316
- # Only warn if `audit` metaparam came from a manifest
1317
- if file && line
1318
- puppet_deprecation_warning(_("The `audit` metaparameter is deprecated and will be ignored in a future release."), { :line => line, :file => file })
1319
- end
1320
- end
1321
1313
  list = Array(list).collect {|p| p.to_sym}
1322
1314
  unless list == [:all]
1323
1315
  list.each do |param|
@@ -371,7 +371,7 @@ module Puppet
371
371
  values.each { |value|
372
372
  unless value.is_a?(String) and
373
373
  (value =~ /^[0-6]$/ or value =~ /^(Mon|Tues?|Wed(?:nes)?|Thu(?:rs)?|Fri|Sat(?:ur)?|Sun)(day)?$/i)
374
- raise ArgumentError, _("%s is not a valid day of the week") % value
374
+ raise ArgumentError, _("%{value} is not a valid day of the week") % { value: value }
375
375
  end
376
376
  }
377
377
  end
@@ -168,10 +168,9 @@ class Puppet::Util::Log
168
168
  def Log.coerce_string(str)
169
169
  return Puppet::Util::CharacterEncoding.convert_to_utf_8(str) if str.valid_encoding?
170
170
 
171
- annotated_string = _("Received a Log attribute with invalid encoding:")
172
- annotated_string << Puppet::Util::CharacterEncoding.convert_to_utf_8(str.dump) << "\n"
173
171
  # We only select the last 10 callers in the stack to avoid being spammy
174
- annotated_string << _("Backtrace:") << "\n" << caller[0..10].join("\n")
172
+ _("Received a Log attribute with invalid encoding:%{log_message}\nBacktrace:\n%{backtrace}") %
173
+ { log_message: Puppet::Util::CharacterEncoding.convert_to_utf_8(str.dump), backtrace: caller[0..10].join("\n") }
175
174
  end
176
175
 
177
176
  public
@@ -45,7 +45,7 @@ module Puppet::Util::Plist
45
45
  {:failonfail => true, :combine => true})
46
46
  return parse_plist(plist)
47
47
  rescue Puppet::ExecutionFailure => detail
48
- Puppet.warning(_("Cannot read file %{file_path}; Puppet is skipping it.\n") % { file_path: file_path } + _("Details: %{detail}") % { detail: detail })
48
+ Puppet.warning(_("Cannot read file %{file_path}; Puppet is skipping it.\nDetails: %{detail}") % { file_path: file_path, detail: detail })
49
49
  end
50
50
  end
51
51
  return nil
@@ -13,8 +13,7 @@ class Puppet::Util::Reference
13
13
  instance_load(:reference, 'puppet/reference')
14
14
 
15
15
  def self.footer
16
- #TRANSLATORS message accompanied by date of generation
17
- "\n\n----------------\n\n*" + _("This page autogenerated on ") + "#{Time.now}*\n"
16
+ "\n\n----------------\n\n" + _("*This page autogenerated on %{current_time}*\n") % { current_time: Time.now.to_s }
18
17
  end
19
18
 
20
19
  def self.modes
@@ -113,7 +112,7 @@ class Puppet::Util::Reference
113
112
  # First the header
114
113
  text = markdown_header(@title, 1)
115
114
  #TRANSLATORS message accompanied by date of generation
116
- text << _("\n\n**This page is autogenerated; any changes will get overwritten** *(last generated on ") + "#{Time.now.to_s})*\n\n"
115
+ text << _("\n\n**This page is autogenerated; any changes will get overwritten** *(last generated on %{current_time})*\n\n") % { current_time: Time.now.to_s }
117
116
 
118
117
  text << @header
119
118
 
@@ -16,59 +16,301 @@ require 'puppet/parser/script_compiler'
16
16
  # end
17
17
  # # The result is the value 6
18
18
  #
19
- # @example Making a run_plan call
19
+ # @example Calling a function
20
20
  # require 'puppet_pal'
21
21
  # result = Puppet::Pal.in_tmp_environment('pal_env', modulepath: ['/tmp/testmodules']) do |pal|
22
- # pal.run_plan('mymodule::myplan', plan_args: { 'arg1' => 10, 'arg2' => '20 })
22
+ # pal.call_function('mymodule::myfunction', 10, 20)
23
23
  # end
24
- # # The result is what 'mymodule::myplan' returns
24
+ # # The result is what 'mymodule::myfunction' returns
25
25
  #
26
- module Puppet::Pal
26
+ module Puppet
27
+ module Pal
27
28
 
28
- # Evaluates a Puppet Language script string.
29
- # @param code_string [String] a snippet of Puppet Language source code
30
- # @return [Object] what the Puppet Language code_string evaluates to
31
- #
32
- def self.evaluate_script_string(code_string)
33
- Puppet[:tasks] = true
34
- Puppet[:code] = code_string
35
- main
29
+ # @param compiler [Puppet::Pal::Compiler] a configured compiler as obtained in the callback from `with_script_compiler`
30
+
31
+ class Compiler
32
+ attr_reader :internal_compiler
33
+ protected :internal_compiler
34
+
35
+ attr_reader :internal_evaluator
36
+ protected :internal_evaluator
37
+
38
+ def initialize(internal_compiler)
39
+ @internal_compiler = internal_compiler
40
+ @internal_evaluator = Puppet::Pops::Parser::EvaluatingParser.new
41
+ end
42
+
43
+ # Calls a function given by name with arguments specified in an `Array`, and optionally accepts a code block.
44
+ # @param function_name [String] the name of the function to call
45
+ # @param args [Object] the arguments to the function
46
+ # @param block [Proc] an optional callable block that is given to the called function
47
+ # @return [Object] what the called function returns
48
+ #
49
+ def call_function(function_name, *args, &block)
50
+ # TRANSLATORS: do not translate variable name strings in these assertions
51
+ Pal::assert_non_empty_string(function_name, 'function_name', false)
52
+ Pal::assert_type(Pal::T_ANY_ARRAY, args, 'args', false)
53
+ internal_evaluator.evaluator.external_call_function(function_name, args, topscope, &block)
54
+ end
55
+
56
+ # Returns an Array[Puppet::Pops::Types::PCallableType] describing the given function's signatures, or empty array if function not found.
57
+ # @param function_name [String] the name of the function to get a signature for
58
+ # @return [Array<Puppet::Pops::Types::PCallableType>] an array of callable signatures, or an empty array if function not found
59
+ def function_signatures(function_name)
60
+ loader = internal_compiler.loaders.private_environment_loader
61
+ if func = loader.load(:function, function_name)
62
+ t = func.class.dispatcher.to_type
63
+ return t.is_a?(Puppet::Pops::Types::PVariantType) ? t.types : [t]
64
+ end
65
+ # Could not find function
66
+ Puppet::Pops::EMPTY_ARRAY
67
+ end
68
+
69
+ # Evaluates a string of puppet language code in top scope.
70
+ # A "source_file" reference to a source can be given - if not an actual file name, by convention the name should
71
+ # be bracketed with < > to indicate it is something symbolic; for example `<commandline>` if the string was given on the
72
+ # command line.
73
+ #
74
+ # If the given `puppet_code` is `nil` or an empty string, `nil` is returned, otherwise the result of evaluating the
75
+ # puppet language string. The given string must form a complete and valid expression/statement as an error is raised
76
+ # otherwise. That is, it is not possible to divide a compound expression by line and evaluate each line individually.
77
+ #
78
+ # @param puppet_code [String, nil] the puppet language code to evaluate, must be a complete expression/statement
79
+ # @param source_file [String, nil] an optional reference to a source (a file or symbolic name/location)
80
+ # @return [Object] what the `puppet_code` evaluates to
81
+ #
82
+ def evaluate_string(puppet_code, source_file = nil)
83
+ return nil if puppet_code.nil? || puppet_code == ''
84
+ unless puppet_code.is_a?(String)
85
+ raise ArgumentError, _("The argument 'puppet_code' must be a String, got %{type}") % { type: puppet_code.class }
86
+ end
87
+ evaluate(parse_string(puppet_code, source_file))
88
+ end
89
+
90
+ # Evaluates a puppet language file in top scope.
91
+ # The file must exist and contain valid puppet language code or an error is raised.
92
+ #
93
+ # @param file [Path, String] an absolute path to a file with puppet language code, must exist
94
+ # @return [Object] what the last evaluated expression in the file evaluated to
95
+ #
96
+ def evaluate_file(file)
97
+ evaluate(parse_file(file))
98
+ end
99
+
100
+ # Evaluates an AST obtained from `parse_string` or `parse_file` in topscope.
101
+ # If the ast is a `Puppet::Pops::Model::Program` (what is returned from the `parse` methods, any definitions
102
+ # in the program (that is, any function, plan, etc. that is defined will be made available for use).
103
+ #
104
+ # @param ast [Puppet::Pops::Model::PopsObject] typically the returned `Program` from the parse methods, but can be any `Expression`
105
+ # @returns [Object] whatever the ast evaluates to
106
+ #
107
+ def evaluate(ast)
108
+ if ast.is_a?(Puppet::Pops::Model::Program)
109
+ loaders = Puppet.lookup(:loaders)
110
+ loaders.instantiate_definitions(ast, loaders.public_environment_loader)
111
+ end
112
+ internal_evaluator.evaluate(topscope, ast)
113
+ end
114
+
115
+ # Produces a literal value if the AST obtained from `parse_string` or `parse_file` does not require any actual evaluation.
116
+ # This method is useful if obtaining an AST that represents literal values; string, integer, float, boolean, regexp, array, hash;
117
+ # for example from having read this from the command line or as values in some file.
118
+ #
119
+ # @param ast [Puppet::Pops::Model::PopsObject] typically the returned `Program` from the parse methods, but can be any `Expression`
120
+ # @returns [Object] whatever the literal value the ast evaluates to
121
+ #
122
+ def evaluate_literal(ast)
123
+ catch :not_literal do
124
+ return Puppet::Pops::Evaluator::LiteralEvaluator.new().literal(ast)
125
+ end
126
+ # TRANSLATORS, the 'ast' is the name of a parameter, do not translate
127
+ raise ArgumentError, _("The given 'ast' does not represent a literal value")
128
+ end
129
+
130
+ # Parses and validates a puppet language string and returns an instance of Puppet::Pops::Model::Program on success.
131
+ # If the content is not valid an error is raised.
132
+ #
133
+ # @param code_string [String] a puppet language string to parse and validate
134
+ # @param source_file [String] an optional reference to a file or other location in angled brackets
135
+ # @return [Puppet::Pops::Model::Program] returns a `Program` instance on success
136
+ #
137
+ def parse_string(code_string, source_file = nil)
138
+ unless code_string.is_a?(String)
139
+ raise ArgumentError, _("The argument 'code_string' must be a String, got %{type}") % { type: code_string.class }
140
+ end
141
+ internal_evaluator.parse_string(code_string, source_file)
142
+ end
143
+
144
+ # Parses and validates a puppet language file and returns an instance of Puppet::Pops::Model::Program on success.
145
+ # If the content is not valid an error is raised.
146
+ #
147
+ # @param file [String] a file with puppet language content to parse and validate
148
+ # @return [Puppet::Pops::Model::Program] returns a `Program` instance on success
149
+ #
150
+ def parse_file(file)
151
+ unless file.is_a?(String)
152
+ raise ArgumentError, _("The argument 'file' must be a String, got %{type}") % { type: puppet_code.class }
153
+ end
154
+ internal_evaluator.parse_file(file)
155
+ end
156
+
157
+ # Parses a puppet data type given in String format and returns that type, or raises an error.
158
+ # A type is needed in calls to `new` to create an instance of the data type, or to perform type checking
159
+ # of values - typically using `type.instance?(obj)` to check if `obj` is an instance of the type.
160
+ #
161
+ # @example Verify if obj is an instance of a data type
162
+ # # evaluates to true
163
+ # pal.type('Enum[red, blue]').instance?("blue")
164
+ #
165
+ # @example Create an instance of a data type
166
+ # # using an already create type
167
+ # t = pal.type('Car')
168
+ # pal.create(t, 'color' => 'black', 'make' => 't-ford')
169
+ #
170
+ # # letting 'new_object' parse the type from a string
171
+ # pal.create('Car', 'color' => 'black', 'make' => 't-ford')
172
+ #
173
+ # @param type_string [String] a puppet language data type
174
+ # @return [Puppet::Pops::Types::PAnyType] the data type
175
+ #
176
+ def type(type_string)
177
+ Puppet::Pops::Types::TypeParser.singleton.parse(type_string)
178
+ end
179
+
180
+ # Creates a new instance of a given data type.
181
+ # @param data_type [String, Puppet::Pops::Types::PAnyType] the data type as a data type or in String form.
182
+ # @param arguments [Object] one or more arguments to the called `new` function
183
+ # @return [Object] an instance of the given data type,
184
+ # or raises an error if it was not possible to parse data type or create an instance.
185
+ #
186
+ def create(data_type, *arguments)
187
+ t = data_type.is_a?(String) ? type(data_type) : data_type
188
+ unless t.is_a?(Puppet::Pops::Types::PAnyType)
189
+ raise ArgumentError, _("Given data_type value is not a data type, got '%{type}'") % {type: t.class}
190
+ end
191
+ call_function('new', t, *arguments)
192
+ end
193
+
194
+ private
195
+
196
+ def topscope
197
+ internal_compiler.topscope
198
+ end
36
199
  end
37
200
 
38
- # Evaluates a Puppet Language script (.pp) file.
39
- # @param manifest_file [String] a file with Puppet Language source code
40
- # @return [Object] what the Puppet Language manifest_file contents evaluates to
41
- #
42
- def self.evaluate_script_manifest(manifest_file)
43
- assert_non_empty_string(manifest_file, _("manifest file"))
44
- Puppet[:tasks] = true
45
- main(manifest_file)
201
+ class ScriptCompiler < Compiler
202
+ # Returns the signature callable of the given plan (the arguments it accepts, and the data type it returns)
203
+ # @param plan_name [String] the name of the plan to get the signature of
204
+ # @return [Puppet::Pops::Types::PCallableType, nil] returns a callable data type, or nil if plan is not found
205
+ #
206
+ def plan_signature(plan_name)
207
+ loader = internal_compiler.loaders.private_environment_loader
208
+ if func = loader.load(:plan, plan_name)
209
+ return func.class.dispatcher.to_type
210
+ end
211
+ # Could not find plan
212
+ nil
213
+ end
46
214
  end
47
215
 
48
- # Runs the given named plan passing arguments by name in a hash.
49
- # @param plan_name [String] the name of the plan to run
50
- # @param plan_args [Hash] arguments to the plan - a map of plan parameter name to value, defaults to empty hash
51
- # @param manifest_file [String] a Puppet Language file to load and evaluate before running the plan, mutually exclusive with code_string
52
- # @param code_string [String] a Puppet Language source string to load and evaluate before running the plan, mutually exclusive with manifest_file
53
- # @return [Object] returns what the evaluated plan returns
216
+ # Defines a context in which multiple operations in an env with a script compiler can be performed in a given block.
217
+ # The calls that takes place to PAL inside of the given block are all with the same instance of the compiler.
218
+ # The parameter `configured_by_env` makes it possible to either use the configuration in the environment, or specify
219
+ # `manifest_file` or `code_string` manually. If neither is given, an empty `code_string` is used.
220
+ #
221
+ # @example define a script compiler without any initial logic
222
+ # pal.with_script_compiler do | compiler |
223
+ # # do things with compiler
224
+ # end
225
+ #
226
+ # @example define a script compiler with a code_string containing initial logic
227
+ # pal.with_script_compiler(code_string: '$myglobal_var = 42') do | compiler |
228
+ # # do things with compiler
229
+ # end
230
+ #
231
+ # @param configured_by_env [Boolean] when true the environment's settings are used, otherwise the given `manifest_file` or `code_string`
232
+ # @param manifest_file [String] a Puppet Language file to load and evaluate before calling the given block, mutually exclusive with `code_string`
233
+ # @param code_string [String] a Puppet Language source string to load and evaluate before calling the given block, mutually exclusive with `manifest_file`
234
+ # @param facts [Hash] optional map of fact name to fact value - if not given will initialize the facts (which is a slow operation)
235
+ # If given at the environment level, the facts given here are merged with higher priority.
236
+ # @param variables [Hash] optional map of fully qualified variable name to value. If given at the environment level, the variables
237
+ # given here are merged with higher priority.
238
+ # @param block [Proc] the block performing operations on compiler
239
+ # @return [Object] what the block returns
240
+ # @yieldparam [Puppet::Pal::ScriptCompiler] compiler, a ScriptCompiler to perform operations on.
54
241
  #
55
- def self.run_plan(plan_name,
56
- plan_args: {},
57
- manifest_file: nil,
58
- code_string: nil
242
+ def self.with_script_compiler(
243
+ configured_by_env: false,
244
+ manifest_file: nil,
245
+ code_string: nil,
246
+ facts: nil,
247
+ variables: nil,
248
+ &block
59
249
  )
60
- # TRANSLATORS: do not translate variable name string in these assertions
250
+ # TRANSLATORS: do not translate variable name strings in these assertions
61
251
  assert_mutually_exclusive(manifest_file, code_string, 'manifest_file', 'code_string')
62
252
  assert_non_empty_string(manifest_file, 'manifest_file', true)
63
253
  assert_non_empty_string(code_string, 'code_string', true)
254
+ assert_type(T_BOOLEAN, configured_by_env, "configured_by_env", false)
255
+
256
+ if configured_by_env
257
+ unless manifest_file.nil? && code_string.nil?
258
+ # TRANSLATORS: do not translate the variable names in this error message
259
+ raise ArgumentError, _("manifest_file or code_string cannot be given when configured_by_env is true")
260
+ end
261
+ # Use the manifest setting
262
+ manifest_file = Puppet[:manifest]
263
+ else
264
+ # An "undef" code_string is the only way to override Puppet[:manifest] & Puppet[:code] settings since an
265
+ # empty string is taken as Puppet[:code] not being set.
266
+ #
267
+ if manifest_file.nil? && code_string.nil?
268
+ code_string = 'undef'
269
+ end
270
+ end
64
271
 
65
272
  Puppet[:tasks] = true
273
+ # After the assertions, if code_string is non nil - it has the highest precedence
66
274
  Puppet[:code] = code_string unless code_string.nil?
67
- main(manifest_file) do | compiler |
68
- compiler.topscope.call_function('run_plan', [plan_name, plan_args])
275
+ overrides = {}
276
+ unless facts.nil?
277
+ overrides[:pal_facts] = Puppet.lookup(:pal_facts).merge(facts)
278
+ end
279
+ unless variables.nil?
280
+ overrides[:pal_variables] = Puppet.lookup(:pal_variables).merge(variables)
281
+ end
282
+ Puppet.override(overrides, "PAL::with_script_compiler") do # TRANSLATORS: Do not translate, symbolic name
283
+ # If manifest_file is nil, the #main method will use the env configured manifest
284
+ # do things in block while a Script Compiler is in effect
285
+ main(manifest_file, &block)
286
+ end
287
+ end
288
+
289
+ # Evaluates a Puppet Language script string.
290
+ # @param code_string [String] a snippet of Puppet Language source code
291
+ # @return [Object] what the Puppet Language code_string evaluates to
292
+ # @deprecated Use {#with_script_compiler} and then evaluate_string on the given compiler - to be removed in 1.0 version
293
+ #
294
+ def self.evaluate_script_string(code_string)
295
+ # prevent the default loading of Puppet[:manifest] which is the environment's manifest-dir by default settings
296
+ # by setting code_string to 'undef'
297
+ with_script_compiler do |compiler|
298
+ compiler.evaluate_string(code_string)
69
299
  end
70
300
  end
71
301
 
302
+ # Evaluates a Puppet Language script (.pp) file.
303
+ # @param manifest_file [String] a file with Puppet Language source code
304
+ # @return [Object] what the Puppet Language manifest_file contents evaluates to
305
+ # @deprecated Use {#with_script_compiler} and then evaluate_file on the given compiler - to be removed in 1.0 version
306
+ #
307
+ def self.evaluate_script_manifest(manifest_file)
308
+ with_script_compiler do |compiler|
309
+ compiler.evaluate_file(manifest_file)
310
+ end
311
+ end
312
+
313
+
72
314
  # Defines the context in which to perform puppet operations (evaluation, etc)
73
315
  # The code to evaluate in this context is given in a block.
74
316
  #
@@ -76,6 +318,7 @@ module Puppet::Pal
76
318
  # @param modulepath [Array<String>] an array of directory paths containing Puppet modules, may be empty, defaults to empty array
77
319
  # @param settings_hash [Hash] a hash of settings - currently not used for anything, defaults to empty hash
78
320
  # @param facts [Hash] optional map of fact name to fact value - if not given will initialize the facts (which is a slow operation)
321
+ # @param variables [Hash] optional map of fully qualified variable name to value
79
322
  # @return [Object] returns what the given block returns
80
323
  # @yieldparam [Puppet::Pal] context, a context that responds to Puppet::Pal methods
81
324
  #
@@ -96,9 +339,10 @@ module Puppet::Pal
96
339
 
97
340
  env = Puppet::Node::Environment.create(env_name, modulepath)
98
341
 
99
- with_loaded_environment(
342
+ in_environment_context(
100
343
  Puppet::Environments::Static.new(env), # The tmp env is the only known env
101
- env, facts, variables, &block)
344
+ env, facts, variables, &block
345
+ )
102
346
  end
103
347
 
104
348
  # Defines the context in which to perform puppet operations (evaluation, etc)
@@ -121,6 +365,7 @@ module Puppet::Pal
121
365
  # @param envpath [String] a path of directories in which there are environments to search for `env_name` (mutually exclusive with `env_dir`).
122
366
  # Should be a single directory, or several directories separated with platform specific `File::PATH_SEPARATOR` character.
123
367
  # @param facts [Hash] optional map of fact name to fact value - if not given will initialize the facts (which is a slow operation)
368
+ # @param variables [Hash] optional map of fully qualified variable name to value
124
369
  # @return [Object] returns what the given block returns
125
370
  # @yieldparam [Puppet::Pal] context, a context that responds to Puppet::Pal methods
126
371
  #
@@ -181,28 +426,27 @@ module Puppet::Pal
181
426
  # not have the same effective modulepath).
182
427
  environments = Puppet::Environments::StaticDirectory.new(env_name, env_path, env) # The env being used is the only one...
183
428
  end
184
- with_loaded_environment(environments, env, facts, variables, &block)
429
+ in_environment_context(environments, env, facts, variables, &block)
185
430
  end
186
431
 
187
432
  private
188
433
 
189
- def self.with_loaded_environment(environments, env, facts, variables, &block)
190
- env.each_plugin_directory do |dir|
191
- $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
192
- end
193
-
194
- # Puppet requires Facter, which initializes its lookup paths. Reset Facter to
195
- # pickup the new $LOAD_PATH.
196
- Facter.reset
197
-
434
+ # Prepares the puppet context with pal information - and delegates to the block
435
+ # No set up is performed at this step - it is delayed until it is known what the
436
+ # operation is going to be (for example - using a ScriptCompiler).
437
+ #
438
+ def self.in_environment_context(environments, env, facts, variables, &block)
439
+ # Create a default node to use (may be overridden later)
198
440
  node = Puppet::Node.new(Puppet[:node_name_value], :environment => env)
199
441
 
200
442
  Puppet.override(
201
- environments: environments, # The env being used is the only one...
202
- current_node: node, # to allow it to be picked up instead of created
203
- variables: variables
443
+ environments: environments, # The env being used is the only one...
444
+ pal_env: env, # provide as convenience
445
+ pal_current_node: node, # to allow it to be picked up instead of created
446
+ pal_variables: variables, # common set of variables across several inner contexts
447
+ pal_facts: facts # common set of facts across several inner contexts (or nil)
204
448
  ) do
205
- prepare_node_facts(node, facts)
449
+ # DELAY: prepare_node_facts(node, facts)
206
450
  return block.call(self)
207
451
  end
208
452
  end
@@ -243,8 +487,24 @@ module Puppet::Pal
243
487
  end
244
488
  end
245
489
 
246
- def self.main(manifest = nil, facts = nil, &block)
247
- node = Puppet.lookup(:current_node)
490
+ # The main routine for script compiler
491
+ # Picks up information from the puppet context and configures a script compiler which is given to
492
+ # the provided block
493
+ #
494
+ def self.main(manifest = nil, &block)
495
+ # Configure the load path
496
+ env = Puppet.lookup(:pal_env)
497
+ env.each_plugin_directory do |dir|
498
+ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
499
+ end
500
+
501
+ # Puppet requires Facter, which initializes its lookup paths. Reset Facter to
502
+ # pickup the new $LOAD_PATH.
503
+ Facter.reset
504
+
505
+ node = Puppet.lookup(:pal_current_node)
506
+ prepare_node_facts(node, Puppet.lookup(:pal_facts))
507
+
248
508
  configured_environment = node.environment || Puppet.lookup(:current_environment)
249
509
 
250
510
  apply_environment = manifest ?
@@ -287,9 +547,13 @@ module Puppet::Pal
287
547
  # create the $settings:: variables
288
548
  topscope.merge_settings(node.environment.name, false)
289
549
 
290
- add_variables(topscope, Puppet.lookup(:variables))
550
+ add_variables(topscope, Puppet.lookup(:pal_variables))
291
551
 
292
- compiler.compile(&block)
552
+ # compiler.compile(&block)
553
+ compiler.compile do | internal_compiler |
554
+ # wrap the internal compiler to prevent it from leaking in the PAL API
555
+ block.call(ScriptCompiler.new(internal_compiler)) unless !block_given?
556
+ end
293
557
 
294
558
  rescue Puppet::ParseErrorWithIssue, Puppet::Error => detail
295
559
  # already logged and handled by the compiler for these two cases
@@ -304,6 +568,8 @@ module Puppet::Pal
304
568
 
305
569
  T_STRING = Puppet::Pops::Types::PStringType::NON_EMPTY
306
570
  T_STRING_ARRAY = Puppet::Pops::Types::TypeFactory.array_of(T_STRING)
571
+ T_ANY_ARRAY = Puppet::Pops::Types::TypeFactory.array_of_any
572
+ T_BOOLEAN = Puppet::Pops::Types::PBooleanType::DEFAULT
307
573
 
308
574
  def self.assert_type(type, value, what, allow_nil=false)
309
575
  Puppet::Pops::Types::TypeAsserter.assert_instance_of(nil, type, value, allow_nil) { _('Puppet Pal: %{what}') % {what: what} }
@@ -323,4 +589,11 @@ module Puppet::Pal
323
589
  end
324
590
  end
325
591
 
592
+ def self.assert_block_given(block)
593
+ if block.nil?
594
+ raise ArgumentError, _("A block must be given")
595
+ end
596
+ end
597
+ end
326
598
  end
599
+