puppet 6.0.0 → 6.0.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +4 -4
  3. data/lib/puppet/application/apply.rb +99 -59
  4. data/lib/puppet/application/cert.rb +2 -112
  5. data/lib/puppet/configurer.rb +2 -3
  6. data/lib/puppet/defaults.rb +14 -1
  7. data/lib/puppet/etc.rb +20 -0
  8. data/lib/puppet/module/task.rb +29 -38
  9. data/lib/puppet/parser/catalog_compiler.rb +24 -0
  10. data/lib/puppet/parser/compiler.rb +3 -1
  11. data/lib/puppet/pops/evaluator/deferred_resolver.rb +3 -0
  12. data/lib/puppet/pops/evaluator/runtime3_converter.rb +2 -2
  13. data/lib/puppet/pops/loader/ruby_legacy_function_instantiator.rb +18 -10
  14. data/lib/puppet/pops/loader/task_instantiator.rb +13 -70
  15. data/lib/puppet/pops/parser/heredoc_support.rb +1 -2
  16. data/lib/puppet/pops/parser/lexer2.rb +1 -1
  17. data/lib/puppet/pops/pcore.rb +10 -33
  18. data/lib/puppet/pops/serialization.rb +1 -0
  19. data/lib/puppet/pops/serialization/to_data_converter.rb +9 -1
  20. data/lib/puppet/provider/exec.rb +57 -57
  21. data/lib/puppet/provider/group/aix.rb +1 -15
  22. data/lib/puppet/provider/group/pw.rb +4 -8
  23. data/lib/puppet/provider/group/windows_adsi.rb +7 -4
  24. data/lib/puppet/provider/nameservice.rb +1 -25
  25. data/lib/puppet/provider/nameservice/directoryservice.rb +5 -3
  26. data/lib/puppet/provider/package/portage.rb +2 -2
  27. data/lib/puppet/provider/service/launchd.rb +19 -3
  28. data/lib/puppet/provider/user/aix.rb +48 -2
  29. data/lib/puppet/type/group.rb +62 -18
  30. data/lib/puppet/type/schedule.rb +7 -0
  31. data/lib/puppet/util/execution.rb +14 -1
  32. data/lib/puppet/util/posix.rb +15 -0
  33. data/lib/puppet/util/storage.rb +12 -0
  34. data/lib/puppet/util/windows/adsi.rb +60 -1
  35. data/lib/puppet/util/windows/process.rb +16 -1
  36. data/lib/puppet/util/windows/service.rb +68 -26
  37. data/lib/puppet/version.rb +1 -1
  38. data/lib/puppet_pal.rb +36 -3
  39. data/locales/ja/puppet.po +598 -861
  40. data/locales/puppet.pot +197 -160
  41. data/man/man5/puppet.conf.5 +12 -1
  42. data/man/man8/puppet.8 +1 -1
  43. data/spec/integration/application/apply_spec.rb +4 -1
  44. data/spec/integration/util/windows/adsi_spec.rb +2 -1
  45. data/spec/unit/application/apply_spec.rb +14 -0
  46. data/spec/unit/configurer_spec.rb +11 -0
  47. data/spec/unit/etc_spec.rb +25 -0
  48. data/spec/unit/indirector/catalog/json_spec.rb +9 -3
  49. data/spec/unit/pops/evaluator/runtime3_converter_spec.rb +22 -4
  50. data/spec/unit/pops/loaders/loader_spec.rb +3 -10
  51. data/spec/unit/pops/loaders/loaders_spec.rb +30 -0
  52. data/spec/unit/pops/loaders/module_loaders_spec.rb +7 -7
  53. data/spec/unit/pops/parser/parse_heredoc_spec.rb +16 -0
  54. data/spec/unit/pops/serialization/to_from_hr_spec.rb +9 -0
  55. data/spec/unit/pops/types/task_spec.rb +42 -116
  56. data/spec/unit/provider/group/aix_spec.rb +0 -19
  57. data/spec/unit/provider/group/pw_spec.rb +0 -6
  58. data/spec/unit/provider/group/windows_adsi_spec.rb +34 -35
  59. data/spec/unit/provider/nameservice/directoryservice_spec.rb +2 -2
  60. data/spec/unit/provider/service/launchd_spec.rb +19 -0
  61. data/spec/unit/provider/user/aix_spec.rb +43 -2
  62. data/spec/unit/provider/user/windows_adsi_spec.rb +1 -4
  63. data/spec/unit/puppet_pal_2pec.rb +6 -6
  64. data/spec/unit/puppet_pal_catalog_spec.rb +58 -0
  65. data/spec/unit/task_spec.rb +50 -5
  66. data/spec/unit/type/group_spec.rb +111 -13
  67. data/spec/unit/util/execution_spec.rb +59 -0
  68. data/spec/unit/util/posix_spec.rb +28 -0
  69. data/spec/unit/util/storage_spec.rb +107 -0
  70. data/spec/unit/util/windows/adsi_spec.rb +100 -5
  71. data/spec/unit/util/windows/service_spec.rb +100 -43
  72. metadata +2 -2
@@ -71,19 +71,22 @@ class Puppet::Module
71
71
 
72
72
  { "name" => name, "path" => path }
73
73
  end
74
+ private_class_method :get_file_details
74
75
 
75
- # Find task's required lib files and retrieve paths
76
- # for both 'files' and 'implementation:files' metadata keys
77
- def self.find_files(files, mod)
78
- env = mod.environment.respond_to?(:name) ? mod.environment.name : 'production'
76
+ # Find task's required lib files and retrieve paths for both 'files' and 'implementation:files' metadata keys
77
+ def self.find_extra_files(metadata, envname = nil)
78
+ return [] if metadata.nil?
79
79
 
80
- file_list = files.flat_map do |file|
80
+ files = metadata.fetch('files', []) +
81
+ metadata.fetch('implementations', []).flat_map { |impl| impl.fetch('files', []) }
82
+
83
+ files.uniq.flat_map do |file|
81
84
  module_name, mount, endpath = file.split("/", 3)
82
85
  # If there's a mount directory with no trailing slash this will be nil
83
86
  # We want it to be empty to construct a path
84
87
  endpath ||= ''
85
88
 
86
- pup_module = Puppet::Module.find(module_name, env)
89
+ pup_module = Puppet::Module.find(module_name, envname)
87
90
  if pup_module.nil?
88
91
  msg = _("Could not find module %{module_name} containing task file %{filename}" %
89
92
  {module_name: module_name, filename: endpath})
@@ -114,22 +117,18 @@ class Puppet::Module
114
117
  raise InvalidMetadata.new(msg, 'puppet.tasks/invalid-metadata')
115
118
  end
116
119
  dir_files = Dir.glob("#{path}**/*").select { |f| File.file?(f) }
117
- files = dir_files.map { |f| get_file_details(f, pup_module) }
120
+ dir_files.map { |f| get_file_details(f, pup_module) }
118
121
  else
119
122
  if last_char
120
123
  msg = _("Files specified in task metadata cannot include a trailing slash: %{file}" % { file: file } )
121
124
  raise InvalidMetadata.new(msg, 'puppet.task/invalid-metadata')
122
125
  end
123
- files = get_file_details(path, pup_module)
126
+ get_file_details(path, pup_module)
124
127
  end
125
-
126
- files
127
128
  end
128
- return file_list
129
129
  end
130
+ private_class_method :find_extra_files
130
131
 
131
- # Copied from TaskInstantiator so we can use the Error classes here
132
- # TODO: harmonize on one implementation
133
132
  # Executables list should contain the full path of all possible implementation files
134
133
  def self.find_implementations(name, directory, metadata, executables)
135
134
  basename = name.split('::')[1] || 'init'
@@ -148,7 +147,7 @@ class Puppet::Module
148
147
  msg = _("Task metadata for task %{name} specifies missing implementation %{implementation}" % { name: name, implementation: impl['name'] })
149
148
  raise InvalidTask.new(msg, 'puppet.tasks/missing-implementation', { missing: [impl['name']] } )
150
149
  end
151
- { "name" => impl['name'], "requirements" => impl.fetch('requirements', []), "path" => path }
150
+ { "name" => impl['name'], "path" => path }
152
151
  end
153
152
  return implementations
154
153
  end
@@ -166,7 +165,13 @@ class Puppet::Module
166
165
  raise InvalidTask.new(msg, 'puppet.tasks/multiple-implementations')
167
166
  end
168
167
 
169
- [{ "name" => File.basename(implementations.first), "path" => implementations.first, "requirements" => [] }]
168
+ [{ "name" => File.basename(implementations.first), "path" => implementations.first }]
169
+ end
170
+ private_class_method :find_implementations
171
+
172
+ def self.find_files(name, directory, metadata, executables, envname = nil)
173
+ # PXP agent relies on 'impls' (which is the task file) being first if there is no metadata
174
+ find_implementations(name, directory, metadata, executables) + find_extra_files(metadata, envname)
170
175
  end
171
176
 
172
177
  def self.is_tasks_metadata_filename?(name)
@@ -206,7 +211,7 @@ class Puppet::Module
206
211
  @module_executables = module_executables || []
207
212
  end
208
213
 
209
- def read_metadata(file)
214
+ def self.read_metadata(file)
210
215
  Puppet::Util::Json.load(Puppet::FileSystem.read(file, :encoding => 'utf-8')) if file
211
216
  rescue SystemCallError, IOError => err
212
217
  msg = _("Error reading metadata: %{message}" % {message: err.message})
@@ -216,34 +221,15 @@ class Puppet::Module
216
221
  end
217
222
 
218
223
  def metadata
219
- @metadata ||= read_metadata(@metadata_file)
220
- end
221
-
222
- def implementations
223
- @implementations ||= self.class.find_implementations(@name, @module.tasks_directory, metadata, @module_executables)
224
+ @metadata ||= self.class.read_metadata(@metadata_file)
224
225
  end
225
226
 
226
227
  def files
227
- md = metadata
228
- outer_files = []
229
- impl_lib_files = []
230
- lib_files = []
231
-
232
- unless md.nil?
233
- outer_files = md['files'] if md.key?('files')
234
- # There's definitely a more elegant way to do this...
235
- if md.key?('implementations')
236
- md['implementations'].each { |impl| impl_lib_files << impl['files'] if impl.key?('files') }
237
- end
238
- lib_files = self.class.find_files((impl_lib_files.flatten.uniq + outer_files).uniq, @module)
239
- end
240
- task_file = implementations.map {|imp| { 'name' => imp['name'], 'path' => imp['path'] } }
241
- # PXP agent relies on 'impls' (which is the task file) being first if there is no metadata
242
- task_file + lib_files
228
+ @files ||= self.class.find_files(@name, @module.tasks_directory, metadata, @module_executables, environment_name)
243
229
  end
244
230
 
245
231
  def validate
246
- implementations
232
+ files
247
233
  true
248
234
  end
249
235
 
@@ -252,6 +238,11 @@ class Puppet::Module
252
238
  self.module == other.module
253
239
  end
254
240
 
241
+ def environment_name
242
+ @module.environment.respond_to?(:name) ? @module.environment.name : 'production'
243
+ end
244
+ private :environment_name
245
+
255
246
  def self.new_with_files(pup_module, name, task_files, module_executables)
256
247
  metadata_file = task_files.find { |f| is_tasks_metadata_filename?(f) }
257
248
  Puppet::Module::Task.new(pup_module, name, module_executables, metadata_file)
@@ -29,4 +29,28 @@ class Puppet::Parser::CatalogCompiler < Puppet::Parser::Compiler
29
29
  raise Puppet::Error, message, detail.backtrace
30
30
  end
31
31
 
32
+ # Evaluates all added constructs, and validates the resulting catalog.
33
+ # This can be called whenever a series of evaluation of puppet code strings
34
+ # have reached a stable state (essentially that there are no relationships to
35
+ # non-existing resources).
36
+ #
37
+ # Raises an error if validation fails.
38
+ #
39
+ def compile_additions
40
+ evaluate_additions
41
+ validate
42
+ end
43
+
44
+ # Evaluates added constructs that are lazily evaluated until all of them have been evaluated.
45
+ #
46
+ def evaluate_additions
47
+ evaluate_generators
48
+ finish
49
+ end
50
+
51
+ # Validates the current state of the catalog.
52
+ # Does not cause evaluation of lazy constructs.
53
+ def validate
54
+ validate_catalog(CatalogValidator::FINAL)
55
+ end
32
56
  end
@@ -577,6 +577,7 @@ class Puppet::Parser::Compiler
577
577
  end
578
578
  end
579
579
  end
580
+ protected :evaluate_generators
580
581
 
581
582
  # Find and evaluate our main object, if possible.
582
583
  def evaluate_main
@@ -641,6 +642,7 @@ class Puppet::Parser::Compiler
641
642
 
642
643
  add_resource_metaparams
643
644
  end
645
+ protected :finish
644
646
 
645
647
  def add_resource_metaparams
646
648
  unless main = catalog.resource(:class, :main)
@@ -780,8 +782,8 @@ class Puppet::Parser::Compiler
780
782
  settings_resource = Puppet::Parser::Resource.new('class', SETTINGS, :scope => @topscope)
781
783
 
782
784
  @catalog.add_resource(settings_resource)
783
-
784
785
  settings_type.evaluate_code(settings_resource)
786
+ settings_resource.instance_variable_set(:@evaluated, true) # Prevents settings from being reevaluated
785
787
 
786
788
  scope = @topscope.class_scope(settings_type)
787
789
  scope.merge_settings(environment.name)
@@ -96,6 +96,9 @@ class DeferredResolver
96
96
  elsif x.is_a?(Puppet::Pops::Types::PSensitiveType::Sensitive)
97
97
  # rewrap in a new Sensitive after resolving any nested deferred values
98
98
  Puppet::Pops::Types::PSensitiveType::Sensitive.new(resolve(x.unwrap))
99
+ elsif x.is_a?(Puppet::Pops::Types::PBinaryType::Binary)
100
+ # use the ASCII-8BIT string that it wraps
101
+ x.binary_buffer
99
102
  else
100
103
  x
101
104
  end
@@ -105,8 +105,8 @@ class Runtime3Converter
105
105
  end
106
106
 
107
107
  def convert_Symbol(o, scope, undef_value)
108
- o == :undef ? undef_value : o
109
- #o == :undef && !@inner ? undef_value : o
108
+ return o unless o == :undef
109
+ !@inner ? undef_value : nil
110
110
  end
111
111
 
112
112
  def convert_PAnyType(o, scope, undef_value)
@@ -20,17 +20,25 @@ class Puppet::Pops::Loader::RubyLegacyFunctionInstantiator
20
20
  loader_for_function = loader.private_loader
21
21
  here = get_binding(loader_for_function)
22
22
 
23
- # This will to do the 3x loading and define the "function_<name>" and "real_function_<name>" methods
24
- # in the anonymous module used to hold function definitions.
25
- #
26
- func_info = eval(ruby_code_string, here, source_ref, 1)
23
+ # Avoid reloading the function if already loaded via one of the APIs that trigger 3x function loading
24
+ # Check if function is already loaded the 3x way (and obviously not the 4x way since we would not be here in the
25
+ # first place.
26
+ environment = Puppet.lookup(:current_environment)
27
+ func_info = Puppet::Parser::Functions.environment_module(environment).get_function_info(typed_name.name.to_sym)
28
+ if func_info.nil?
29
+ # This will to do the 3x loading and define the "function_<name>" and "real_function_<name>" methods
30
+ # in the anonymous module used to hold function definitions.
31
+ #
32
+ func_info = eval(ruby_code_string, here, source_ref, 1)
27
33
 
28
- unless func_info.is_a?(Hash)
29
- raise ArgumentError, _("The code loaded from %{source_ref} did not produce the expected 3x function info Hash when evaluated. Got '%{klass}'") % { source_ref: source_ref, klass: created.class }
30
- end
31
- unless func_info[:name] == "function_#{typed_name.name()}"
32
- raise ArgumentError, _("The code loaded from %{source_ref} produced mis-matched name, expected 'function_%{type_name}', got %{created_name}") % {
33
- source_ref: source_ref, type_name: typed_name.name, created_name: func_info[:name] }
34
+ # Validate what was loaded
35
+ unless func_info.is_a?(Hash)
36
+ raise ArgumentError, _("The code loaded from %{source_ref} did not produce the expected 3x function info Hash when evaluated. Got '%{klass}'") % { source_ref: source_ref, klass: created.class }
37
+ end
38
+ unless func_info[:name] == "function_#{typed_name.name()}"
39
+ raise ArgumentError, _("The code loaded from %{source_ref} produced mis-matched name, expected 'function_%{type_name}', got %{created_name}") % {
40
+ source_ref: source_ref, type_name: typed_name.name, created_name: func_info[:name] }
41
+ end
34
42
  end
35
43
 
36
44
  created = Puppet::Functions::Function3x.create_function(typed_name.name(), func_info, loader_for_function)
@@ -1,58 +1,9 @@
1
1
  # The TypeDefinitionInstantiator instantiates a type alias or a type definition
2
2
  #
3
+ require 'puppet/module/task'
3
4
  module Puppet::Pops
4
5
  module Loader
5
6
  class TaskInstantiator
6
- def self.load_metadata(loader, metadata)
7
- if metadata.nil?
8
- EMPTY_HASH
9
- else
10
- json_text = loader.get_contents(metadata)
11
- begin
12
- Puppet::Util::Json.load(json_text).freeze || EMPTY_HASH
13
- rescue Puppet::Util::Json::ParseError => ex
14
- raise Puppet::ParseError.new(ex.message, metadata)
15
- end
16
- end
17
- end
18
-
19
- def self.validate_implementations(typed_name, directory, metadata, executables)
20
- name = typed_name.name
21
- basename = typed_name.name_parts[1] || 'init'
22
- # If 'implementations' is defined, it needs to mention at least one
23
- # implementation, and everything it mentions must exist.
24
- if metadata.key?('implementations')
25
- if metadata['implementations'].is_a?(Array)
26
- metadata['implementations'].map do |impl|
27
- path = executables.find { |real_impl| File.basename(real_impl) == impl['name'] }
28
- if path
29
- { "name" => impl['name'], "requirements" => impl.fetch('requirements', []), "path" => path }
30
- else
31
- raise ArgumentError, _("Task metadata for task %{name} specifies missing implementation %{implementation}") %
32
- { name: name, implementation: impl['name'] }
33
- end
34
- end
35
- else
36
- # If 'implementations' is the wrong type, we just pass it through and
37
- # let the task type definition reject it.
38
- metadata['implementations']
39
- end
40
- # If implementations isn't defined, then we use executables matching the
41
- # task name, and only one may exist.
42
- else
43
- implementations = executables.select { |impl| File.basename(impl, '.*') == basename }
44
- if implementations.empty?
45
- raise ArgumentError, _('No source besides task metadata was found in directory %{directory} for task %{name}') %
46
- { name: name, directory: directory }
47
- elsif implementations.length > 1
48
- raise ArgumentError, _("Multiple executables were found in directory %{directory} for task %{name}; define 'implementations' in metadata to differentiate between them") %
49
- { name: name, directory: implementations[0] }
50
- end
51
-
52
- [{ "name" => File.basename(implementations.first), "path" => implementations.first, "requirements" => [] }]
53
- end
54
- end
55
-
56
7
  def self.create(loader, typed_name, source_refs)
57
8
  name = typed_name.name
58
9
  basename = typed_name.name_parts[1] || 'init'
@@ -60,31 +11,16 @@ class TaskInstantiator
60
11
  metadata_files, executables = source_refs.partition { |source_ref| source_ref.end_with?('.json') }
61
12
  metadata_file = metadata_files.find { |source_ref| File.basename(source_ref, '.json') == basename }
62
13
 
63
- metadata = load_metadata(loader, metadata_file)
14
+ metadata = Puppet::Module::Task.read_metadata(metadata_file) || {}
64
15
 
65
- implementation_metadata = validate_implementations(typed_name, dirname, metadata, executables)
16
+ files = Puppet::Module::Task.find_files(name, dirname, metadata, executables)
66
17
 
67
- arguments = {
68
- 'name' => name,
69
- 'implementations' => implementation_metadata
70
- }
18
+ task = { 'name' => name, 'metadata' => metadata, 'files' => files }
71
19
 
72
20
  begin
73
- metadata.each_pair do |key, value|
74
- if %w[parameters output].include?(key)
75
- ps = {}
76
- value.each_pair do |k, v|
77
- pd = v.dup
78
- t = v['type']
79
- pd['type'] = t.nil? ? Types::TypeFactory.data : Types::TypeParser.singleton.parse(t)
80
- ps[k] = pd
81
- end
82
- value = ps
83
- end
84
- arguments[key] = value unless arguments.key?(key)
85
- end
21
+ task['parameters'] = convert_types(metadata['parameters'])
86
22
 
87
- Types::TypeFactory.task.from_hash(arguments)
23
+ Types::TypeFactory.task.from_hash(task)
88
24
  rescue Types::TypeAssertionError => ex
89
25
  # Not strictly a parser error but from the users perspective, the file content didn't parse properly. The
90
26
  # ParserError also conveys file info (even though line is unknown)
@@ -92,6 +28,13 @@ class TaskInstantiator
92
28
  raise Puppet::ParseError.new(msg, metadata_file)
93
29
  end
94
30
  end
31
+
32
+ def self.convert_types(args)
33
+ args.each_with_object({}) do |(k, v), hsh|
34
+ hsh[k] = v['type'].nil? ? Types::TypeFactory.data : Types::TypeParser.singleton.parse(v['type'])
35
+ end if args
36
+ end
37
+ private_class_method :convert_types
95
38
  end
96
39
  end
97
40
  end
@@ -98,8 +98,7 @@ module HeredocSupport
98
98
 
99
99
  # Use a new lexer instance configured with a sub-locator to enable correct positioning
100
100
  sublexer = self.class.new()
101
- locator = Locator::SubLocator.sub_locator(str,
102
- locator.file, heredoc_line, heredoc_offset, leading.length())
101
+ locator = Locator::SubLocator.new(locator, heredoc_line, heredoc_offset, leading.length())
103
102
 
104
103
  # Emit a token that provides the grammar with location information about the lines on which the heredoc
105
104
  # content is based.
@@ -189,7 +189,7 @@ class Lexer2
189
189
  ',' => lambda { emit(TOKEN_COMMA, @scanner.pos) },
190
190
  '[' => lambda do
191
191
  before = @scanner.pos
192
- if (before == 0 || @scanner.string[locator.char_offset(before)-1,1] =~ /[[:blank:]\r\n]+/)
192
+ if (before == 0 || locator.string[locator.char_offset(before)-1,1] =~ /[[:blank:]\r\n]+/)
193
193
  emit(TOKEN_LISTSTART, before)
194
194
  else
195
195
  emit(TOKEN_LBRACK, before)
@@ -39,39 +39,16 @@ module Pcore
39
39
  # Fully qualified name of the task
40
40
  name => { type => Pattern[/\\A[a-z][a-z0-9_]*(?:::[a-z][a-z0-9_]*)*\\z/] },
41
41
 
42
- # List of implementations with requirements
43
- implementations => { type => Array[Struct[name => String, path => String, Optional[requirements] => Array[String]], 1] },
44
-
45
- # Task description
46
- description => { type => Optional[String], value => undef },
47
-
48
- # Puppet Task version
49
- puppet_task_version => { type => Integer, value => 1 },
50
-
51
- # Type, description, and sensitive property of each parameter
52
- parameters => {
53
- type => Optional[Hash[
54
- Pattern[/\\A[a-z][a-z0-9_]*\\z/],
55
- Struct[
56
- Optional[description] => String,
57
- Optional[sensitive] => Boolean,
58
- type => Type]]],
59
- value => undef
60
- },
61
-
62
- # Type, description, and sensitive property of each output
63
- output => {
64
- type => Optional[Hash[
65
- Pattern[/\\A[a-z][a-z0-9_]*\\z/],
66
- Struct[
67
- Optional[description] => String,
68
- Optional[sensitive] => Boolean,
69
- type => Type]]],
70
- value => undef
71
- },
72
-
73
- supports_noop => { type => Boolean, value => false },
74
- input_method => { type => Optional[String] },
42
+ # List of file references referenced by metadata and their paths on disk.
43
+ # If there are no implementations listed in metadata, the first file is always
44
+ # the task executable.
45
+ files => { type => Array[Struct[name => String, path => String]] },
46
+
47
+ # Task metadata
48
+ metadata => { type => Hash[String, Any] },
49
+
50
+ # Map parameter names to their parsed data type
51
+ parameters => { type => Optional[Hash[Pattern[/\\A[a-z][a-z0-9_]*\\z/], Type]], value => undef },
75
52
  }
76
53
  }
77
54
  PUPPET
@@ -19,6 +19,7 @@ module Serialization
19
19
 
20
20
  # Type key used for symbols
21
21
  PCORE_TYPE_SENSITIVE = 'Sensitive'.freeze
22
+ PCORE_TYPE_BINARY = 'Binary'.freeze
22
23
 
23
24
  # Type key used for symbols
24
25
  PCORE_TYPE_SYMBOL = 'Symbol'.freeze
@@ -70,7 +70,15 @@ module Serialization
70
70
 
71
71
  def to_data(value)
72
72
  if value.nil? || Types::PScalarDataType::DEFAULT.instance?(value)
73
- value
73
+ if @rich_data && value.is_a?(String) && value.encoding == Encoding::ASCII_8BIT
74
+ # Transform the binary string to rich Binary
75
+ {
76
+ PCORE_TYPE_KEY => PCORE_TYPE_BINARY,
77
+ PCORE_VALUE_KEY => Puppet::Pops::Types::PBinaryType::Binary.from_binary_string(value).to_s
78
+ }
79
+ else
80
+ value
81
+ end
74
82
  elsif :default == value
75
83
  if @rich_data
76
84
  { PCORE_TYPE_KEY => PCORE_TYPE_DEFAULT }