puppet 6.0.5 → 6.0.7

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 (98) hide show
  1. checksums.yaml +4 -4
  2. data/CODEOWNERS +30 -0
  3. data/Gemfile.lock +14 -14
  4. data/lib/puppet.rb +4 -4
  5. data/lib/puppet/application.rb +1 -1
  6. data/lib/puppet/application/filebucket.rb +6 -1
  7. data/lib/puppet/configurer.rb +6 -6
  8. data/lib/puppet/confine/boolean.rb +45 -0
  9. data/lib/puppet/confine/false.rb +7 -1
  10. data/lib/puppet/confine/true.rb +7 -1
  11. data/lib/puppet/defaults.rb +21 -29
  12. data/lib/puppet/functions/call.rb +2 -1
  13. data/lib/puppet/network/http/connection.rb +15 -5
  14. data/lib/puppet/pops/issues.rb +4 -0
  15. data/lib/puppet/pops/loader/ruby_legacy_function_instantiator.rb +60 -4
  16. data/lib/puppet/pops/model/factory.rb +38 -4
  17. data/lib/puppet/pops/parser/egrammar.ra +2 -2
  18. data/lib/puppet/pops/parser/heredoc_support.rb +17 -7
  19. data/lib/puppet/pops/parser/lexer2.rb +6 -1
  20. data/lib/puppet/pops/parser/locator.rb +106 -86
  21. data/lib/puppet/pops/parser/parser_support.rb +11 -2
  22. data/lib/puppet/pops/types/type_mismatch_describer.rb +1 -1
  23. data/lib/puppet/provider/file/windows.rb +49 -1
  24. data/lib/puppet/provider/group/windows_adsi.rb +4 -1
  25. data/lib/puppet/provider/package/windows.rb +5 -1
  26. data/lib/puppet/provider/service/upstart.rb +16 -6
  27. data/lib/puppet/settings.rb +10 -5
  28. data/lib/puppet/transaction.rb +8 -6
  29. data/lib/puppet/transaction/resource_harness.rb +1 -0
  30. data/lib/puppet/type/exec.rb +27 -5
  31. data/lib/puppet/type/file/mode.rb +6 -1
  32. data/lib/puppet/type/filebucket.rb +12 -8
  33. data/lib/puppet/util/command_line.rb +5 -1
  34. data/lib/puppet/util/log.rb +7 -2
  35. data/lib/puppet/util/pidlock.rb +14 -1
  36. data/lib/puppet/util/windows/process.rb +73 -5
  37. data/lib/puppet/util/windows/security.rb +29 -8
  38. data/lib/puppet/version.rb +1 -1
  39. data/locales/ja/puppet.po +149 -132
  40. data/locales/puppet.pot +197 -148
  41. data/man/man5/puppet.conf.5 +14 -6
  42. data/man/man8/puppet-agent.8 +1 -1
  43. data/man/man8/puppet-apply.8 +1 -1
  44. data/man/man8/puppet-catalog.8 +1 -1
  45. data/man/man8/puppet-config.8 +1 -1
  46. data/man/man8/puppet-describe.8 +1 -1
  47. data/man/man8/puppet-device.8 +1 -1
  48. data/man/man8/puppet-doc.8 +1 -1
  49. data/man/man8/puppet-epp.8 +1 -1
  50. data/man/man8/puppet-facts.8 +1 -1
  51. data/man/man8/puppet-filebucket.8 +6 -2
  52. data/man/man8/puppet-generate.8 +1 -1
  53. data/man/man8/puppet-help.8 +1 -1
  54. data/man/man8/puppet-key.8 +1 -1
  55. data/man/man8/puppet-lookup.8 +1 -1
  56. data/man/man8/puppet-man.8 +1 -1
  57. data/man/man8/puppet-module.8 +1 -1
  58. data/man/man8/puppet-node.8 +1 -1
  59. data/man/man8/puppet-parser.8 +1 -1
  60. data/man/man8/puppet-plugin.8 +1 -1
  61. data/man/man8/puppet-report.8 +1 -1
  62. data/man/man8/puppet-resource.8 +1 -1
  63. data/man/man8/puppet-script.8 +1 -1
  64. data/man/man8/puppet-ssl.8 +1 -1
  65. data/man/man8/puppet-status.8 +1 -1
  66. data/man/man8/puppet.8 +2 -2
  67. data/spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/usee/lib/puppet/parser/functions/bad_func_load2.rb +11 -0
  68. data/spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/usee/lib/puppet/parser/functions/bad_func_load3.rb +11 -0
  69. data/spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/usee/lib/puppet/parser/functions/bad_func_load4.rb +11 -0
  70. data/spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/usee/lib/puppet/parser/functions/bad_func_load5.rb +12 -0
  71. data/spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/usee/lib/puppet/parser/functions/good_func_load.rb +9 -0
  72. data/spec/integration/provider/file/windows_spec.rb +162 -0
  73. data/spec/integration/type/file_spec.rb +0 -19
  74. data/spec/unit/application_spec.rb +8 -1
  75. data/spec/unit/configurer_spec.rb +6 -7
  76. data/spec/unit/confine/false_spec.rb +27 -0
  77. data/spec/unit/confine/true_spec.rb +27 -0
  78. data/spec/unit/defaults_spec.rb +0 -14
  79. data/spec/unit/network/http/connection_spec.rb +1 -1
  80. data/spec/unit/pops/loaders/loaders_spec.rb +70 -3
  81. data/spec/unit/pops/parser/locator_spec.rb +45 -0
  82. data/spec/unit/pops/parser/parse_heredoc_spec.rb +111 -15
  83. data/spec/unit/pops/types/type_mismatch_describer_spec.rb +9 -0
  84. data/spec/unit/provider/group/windows_adsi_spec.rb +7 -1
  85. data/spec/unit/provider/package/windows_spec.rb +12 -1
  86. data/spec/unit/provider/service/systemd_spec.rb +7 -5
  87. data/spec/unit/settings_spec.rb +36 -0
  88. data/spec/unit/transaction/resource_harness_spec.rb +26 -0
  89. data/spec/unit/transaction_spec.rb +29 -0
  90. data/spec/unit/type/exec_spec.rb +47 -0
  91. data/spec/unit/type/filebucket_spec.rb +8 -6
  92. data/spec/unit/util/command_line_spec.rb +23 -2
  93. data/spec/unit/util/execution_spec.rb +2 -2
  94. data/spec/unit/util/log_spec.rb +15 -0
  95. data/spec/unit/util/pidlock_spec.rb +21 -1
  96. data/spec/unit/util/storage_spec.rb +19 -19
  97. metadata +16 -3
  98. data/MAINTAINERS +0 -47
@@ -28,6 +28,7 @@ class Factory
28
28
  BUILD_VISITOR = Visitor.new(self, 'build')
29
29
  INFER_VISITOR = Visitor.new(self, 'infer')
30
30
  INTERPOLATION_VISITOR = Visitor.new(self, 'interpolate')
31
+ MAPOFFSET_VISITOR = Visitor.new(self, 'map_offset')
31
32
 
32
33
  def self.infer(o)
33
34
  if o.instance_of?(Factory)
@@ -89,6 +90,29 @@ class Factory
89
90
  end
90
91
  end
91
92
 
93
+ def map_offset(model, locator)
94
+ MAPOFFSET_VISITOR.visit_this_1(self, model, locator)
95
+ end
96
+
97
+ def map_offset_Object(o, locator)
98
+ o
99
+ end
100
+
101
+ def map_offset_Factory(o, locator)
102
+ map_offset(o.model, locator)
103
+ end
104
+
105
+ def map_offset_Positioned(o, locator)
106
+ # Transpose the local offset, length to global "coordinates"
107
+ global_offset, global_length = locator.to_global(o.offset, o.length)
108
+
109
+ # mutate
110
+ o.instance_variable_set(:'@offset', global_offset)
111
+ o.instance_variable_set(:'@length', global_length)
112
+ # Change locator since the positions were transposed to the global coordinates
113
+ o.instance_variable_set(:'@locator', locator.locator) if locator.is_a? Puppet::Pops::Parser::Locator::SubLocator
114
+ end
115
+
92
116
  # Polymorphic interpolate
93
117
  def interpolate()
94
118
  INTERPOLATION_VISITOR.visit_this_class(self, @model_class, EMPTY_ARRAY)
@@ -433,8 +457,7 @@ class Factory
433
457
  @init_hash[KEY_LOCATOR] = locator
434
458
  @init_hash['leading_line_count'] = locator.leading_line_count
435
459
  @init_hash['leading_line_offset'] = locator.leading_line_offset
436
- # Index is held in sublocator's parent locator - needed to be able to reconstruct
437
- @init_hash['line_offsets'] = locator.locator.line_index
460
+ @init_hash['line_offsets'] = locator.line_index # index of lines in sublocated
438
461
  end
439
462
 
440
463
  def build_SelectorEntry(o, matching, value)
@@ -729,8 +752,6 @@ class Factory
729
752
 
730
753
  def self.STRING(*args); new(ConcatenatedString, args); end
731
754
 
732
- def self.SUBLOCATE(token, expr) new(SubLocatedExpression, token, expr); end
733
-
734
755
  def self.LIST(entries); new(LiteralList, entries); end
735
756
 
736
757
  def self.PARAM(name, expr=nil); new(Parameter, name, expr); end
@@ -765,6 +786,19 @@ class Factory
765
786
  o.instance_of?(Factory) && o.model_class <= QualifiedReference ? self : new(QualifiedReference, o)
766
787
  end
767
788
 
789
+ def self.SUBLOCATE(token, expr_factory)
790
+ # expr is a Factory wrapped LiteralString, or ConcatenatedString
791
+ # The token is SUBLOCATED token which has a SubLocator as the token's locator
792
+ # Use the SubLocator to recalculate the offsets and lengths.
793
+ model = expr_factory.model
794
+ locator = token.locator
795
+ expr_factory.map_offset(model, locator)
796
+ model._pcore_all_contents([]) { |element| expr_factory.map_offset(element, locator) }
797
+
798
+ # Returned the factory wrapping the now offset/length transformed expression(s)
799
+ expr_factory
800
+ end
801
+
768
802
  def self.TEXT(expr)
769
803
  new(TextExpression, infer(expr).interpolate)
770
804
  end
@@ -857,8 +857,8 @@ heredoc
857
857
  : HEREDOC sublocated_text { result = Factory.HEREDOC(val[0][:value], val[1]); loc result, val[0] }
858
858
 
859
859
  sublocated_text
860
- : SUBLOCATE string { result = Factory.SUBLOCATE(val[0], val[1]); loc result, val[0] }
861
- | SUBLOCATE dq_string { result = Factory.SUBLOCATE(val[0], val[1]); loc result, val[0] }
860
+ : SUBLOCATE string { result = Factory.SUBLOCATE(val[0], val[1]); }
861
+ | SUBLOCATE dq_string { result = Factory.SUBLOCATE(val[0], val[1]); }
862
862
 
863
863
  epp_expression
864
864
  : EPP_START epp_parameters_list optional_statements { result = Factory.EPP(val[1], val[2]); loc result, val[0] }
@@ -94,11 +94,12 @@ module HeredocSupport
94
94
 
95
95
 
96
96
  # Process captured lines - remove leading, and trailing newline
97
- str = heredoc_text(lines, leading, has_margin, remove_break)
97
+ # get processed string and index of removed margin/leading size per line
98
+ str, margin_per_line = heredoc_text(lines, leading, has_margin, remove_break)
98
99
 
99
100
  # Use a new lexer instance configured with a sub-locator to enable correct positioning
100
101
  sublexer = self.class.new()
101
- locator = Locator::SubLocator.new(locator, heredoc_line, heredoc_offset, leading.length())
102
+ locator = Locator::SubLocator.new(locator, str, heredoc_line, heredoc_offset, has_margin, margin_per_line)
102
103
 
103
104
  # Emit a token that provides the grammar with location information about the lines on which the heredoc
104
105
  # content is based.
@@ -120,23 +121,32 @@ module HeredocSupport
120
121
  raise eof_error
121
122
  end
122
123
 
123
- # Produces the heredoc text string given the individual (unprocessed) lines as an array.
124
+ # Produces the heredoc text string given the individual (unprocessed) lines as an array and array with margin sizes per line
124
125
  # @param lines [Array<String>] unprocessed lines of text in the heredoc w/o terminating line
125
126
  # @param leading [String] the leading text up (up to pipe or other terminating char)
126
127
  # @param has_margin [Boolean] if the left margin should be adjusted as indicated by `leading`
127
128
  # @param remove_break [Boolean] if the line break (\r?\n) at the end of the last line should be removed or not
129
+ # @return [Array] - a tuple with resulting string, and an array with margin size per line
128
130
  #
129
131
  def heredoc_text(lines, leading, has_margin, remove_break)
130
- if has_margin
132
+ if has_margin && leading.length > 0
131
133
  leading_pattern = /^#{Regexp.escape(leading)}/
132
- lines = lines.collect {|s| s.gsub(leading_pattern, '') }
134
+ # TODO: This implementation is not according to the specification, but is kept to be bug compatible.
135
+ # The specification says that leading space up to the margin marker should be removed, but this implementation
136
+ # simply leaves lines that have text in the margin untouched.
137
+ #
138
+ processed_lines = lines.collect {|s| s.gsub(leading_pattern, '') }
139
+ margin_per_line = processed_lines.length.times.map {|x| lines[x].length - processed_lines[x].length }
140
+ lines = processed_lines
141
+ else
142
+ # Array with a 0 per line
143
+ margin_per_line = Array.new(lines.length, 0)
133
144
  end
134
145
  result = lines.join('')
135
146
  result.gsub!(/\r?\n\z/m, '') if remove_break
136
- result
147
+ [result, margin_per_line]
137
148
  end
138
149
 
139
-
140
150
  end
141
151
  end
142
152
  end
@@ -189,7 +189,12 @@ class Lexer2
189
189
  ',' => lambda { emit(TOKEN_COMMA, @scanner.pos) },
190
190
  '[' => lambda do
191
191
  before = @scanner.pos
192
- if (before == 0 || locator.string[locator.char_offset(before)-1,1] =~ /[[:blank:]\r\n]+/)
192
+ # Must check the preceding character to see if it is whitespace.
193
+ # The fastest thing to do is to simply byteslice to get the string ending at the offset before
194
+ # and then check what the last character is. (This is the same as what an locator.char_offset needs
195
+ # to compute, but with less overhead of trying to find out the global offset from a local offset in the
196
+ # case when this is sublocated in a heredoc).
197
+ if before == 0 || @scanner.string.byteslice(0, before)[-1] =~ /[[:blank:]\r\n]+/
193
198
  emit(TOKEN_LISTSTART, before)
194
199
  else
195
200
  emit(TOKEN_LBRACK, before)
@@ -71,6 +71,16 @@ class Locator
71
71
  def line_index()
72
72
  end
73
73
 
74
+ # Common byte based impl that works for all rubies (stringscanner is byte based
75
+ def self.compute_line_index(string)
76
+ scanner = StringScanner.new(string)
77
+ result = [0] # first line starts at 0
78
+ while scanner.scan_until(/\n/)
79
+ result << scanner.pos
80
+ end
81
+ result.freeze
82
+ end
83
+
74
84
  # Produces an URI with path?line=n&pos=n. If origin is unknown the URI is string:?line=n&pos=n
75
85
  def to_uri(ast)
76
86
  f = file
@@ -83,81 +93,8 @@ class Locator
83
93
  URI("#{f}?line=#{line_for_offset(offset).to_s}&pos=#{pos_on_line(offset).to_s}")
84
94
  end
85
95
 
86
- # A Sublocator locates a concrete locator (subspace) in a virtual space.
87
- # The `leading_line_count` is the (virtual) number of lines preceding the first line in the concrete locator.
88
- # The `leading_offset` is the (virtual) byte offset of the first byte in the concrete locator.
89
- # The `leading_line_offset` is the (virtual) offset / margin in characters for each line.
90
- #
91
- # This illustrates characters in the sublocator (`.`) inside the subspace (`X`):
92
- #
93
- # 1:XXXXXXXX
94
- # 2:XXXX.... .. ... ..
95
- # 3:XXXX. . .... ..
96
- # 4:XXXX............
97
- #
98
- # This sublocator would be configured with leading_line_count = 1,
99
- # leading_offset=8, and leading_line_offset=4
100
- #
101
- # Note that leading_offset must be the same for all lines and measured in characters.
102
- #
103
- class SubLocator < Locator
104
- attr_reader :locator
105
- attr_reader :leading_line_count
106
- attr_reader :leading_offset
107
- attr_reader :leading_line_offset
108
-
109
- def self.sub_locator(string, file, leading_line_count, leading_offset, leading_line_offset)
110
- self.new(Locator.locator(string, file),
111
- leading_line_count,
112
- leading_offset,
113
- leading_line_offset)
114
- end
115
-
116
- def initialize(locator, leading_line_count, leading_offset, leading_line_offset)
117
- @locator = locator
118
- @leading_line_count = leading_line_count
119
- @leading_offset = leading_offset
120
- @leading_line_offset = leading_line_offset
121
- end
122
-
123
- def file
124
- @locator.file
125
- end
126
-
127
- def string
128
- @locator.string
129
- end
130
-
131
- # Given offset is offset in the subspace
132
- def line_for_offset(offset)
133
- @locator.line_for_offset(offset) + @leading_line_count
134
- end
135
-
136
- # Given offset is offset in the subspace
137
- def offset_on_line(offset)
138
- @locator.offset_on_line(offset) + @leading_line_offset
139
- end
140
-
141
- # Given offset is offset in the subspace
142
- def char_offset(offset)
143
- effective_line = @locator.line_for_offset(offset)
144
- locator.char_offset(offset) + (effective_line * @leading_line_offset) + @leading_offset
145
- end
146
-
147
- # Given offsets are offsets in the subspace
148
- def char_length(offset, end_offset)
149
- effective_line = @locator.line_for_offset(end_offset) - @locator.line_for_offset(offset)
150
- locator.char_length(offset, end_offset) + (effective_line * @leading_line_offset)
151
- end
152
-
153
- def pos_on_line(offset)
154
- offset_on_line(offset) +1
155
- end
156
- end
157
-
158
96
  class AbstractLocator < Locator
159
97
  attr_accessor :line_index
160
- attr_accessor :string
161
98
  attr_reader :string
162
99
  attr_reader :file
163
100
 
@@ -169,8 +106,7 @@ class Locator
169
106
  @file = file.freeze
170
107
  @prev_offset = nil
171
108
  @prev_line = nil
172
- @line_index = line_index
173
- compute_line_index if line_index.nil?
109
+ @line_index = line_index.nil? ? Locator.compute_line_index(@string) : line_index
174
110
  end
175
111
 
176
112
  # Returns the position on line (first position on a line is 1)
@@ -235,16 +171,6 @@ class Locator
235
171
  self.class == o.class && string == o.string && file == o.file && line_index == o.line_index
236
172
  end
237
173
 
238
- # Common impl for 18 and 19 since scanner is byte based
239
- def compute_line_index
240
- scanner = StringScanner.new(string)
241
- result = [0] # first line starts at 0
242
- while scanner.scan_until(/\n/)
243
- result << scanner.pos
244
- end
245
- self.line_index = result.freeze
246
- end
247
-
248
174
  # Returns the line number (first line is 1) for the given offset
249
175
  def line_for_offset(offset)
250
176
  if @prev_offset == offset
@@ -264,6 +190,100 @@ class Locator
264
190
  end
265
191
  end
266
192
 
193
+ # A Sublocator locates a concrete locator (subspace) in a virtual space.
194
+ # The `leading_line_count` is the (virtual) number of lines preceding the first line in the concrete locator.
195
+ # The `leading_offset` is the (virtual) byte offset of the first byte in the concrete locator.
196
+ # The `leading_line_offset` is the (virtual) offset / margin in characters for each line.
197
+ #
198
+ # This illustrates characters in the sublocator (`.`) inside the subspace (`X`):
199
+ #
200
+ # 1:XXXXXXXX
201
+ # 2:XXXX.... .. ... ..
202
+ # 3:XXXX. . .... ..
203
+ # 4:XXXX............
204
+ #
205
+ # This sublocator would be configured with leading_line_count = 1,
206
+ # leading_offset=8, and leading_line_offset=4
207
+ #
208
+ # Note that leading_offset must be the same for all lines and measured in characters.
209
+ #
210
+ # A SubLocator is only used during parsing as the parser will translate the local offsets/lengths to
211
+ # the parent locator when a sublocated expression is reduced. Do not call the methods
212
+ # `char_offset` or `char_length` as those methods will raise an error.
213
+ #
214
+ class SubLocator < AbstractLocator
215
+ attr_reader :locator
216
+ attr_reader :leading_line_count
217
+ attr_reader :leading_offset
218
+ attr_reader :has_margin
219
+ attr_reader :margin_per_line
220
+
221
+ def initialize(locator, str, leading_line_count, leading_offset, has_margin, margin_per_line)
222
+ super(str, locator.file)
223
+ @locator = locator
224
+ @leading_line_count = leading_line_count
225
+ @leading_offset = leading_offset
226
+ @has_margin = has_margin
227
+ @margin_per_line = margin_per_line
228
+
229
+ # Since lines can have different margin - accumulated margin per line must be computed
230
+ # and since this accumulated margin adjustment is needed more than once; both for start offset,
231
+ # and for end offset (to compute global length) it is computed up front here.
232
+ # The accumulated_offset holds the sum of all removed margins before a position on line n (line index is 1-n,
233
+ # and (unused) position 0 is always 0).
234
+ # The last entry is duplicated since there will be the line "after last line" that would otherwise require
235
+ # conditional logic.
236
+ #
237
+ @accumulated_margin = margin_per_line.reduce([0]) {|memo, val| memo << memo[-1] + val; memo }
238
+ @accumulated_margin << @accumulated_margin[-1]
239
+ end
240
+
241
+ def file
242
+ @locator.file
243
+ end
244
+
245
+ # Returns array with transposed (local) offset and (local) length. The transposed values
246
+ # take the margin into account such that it is added to the content to the right
247
+ #
248
+ # Using X to denote margin and where end of line is explicitly shown as \n:
249
+ # ```
250
+ # XXXXabc\n
251
+ # XXXXdef\n
252
+ # ```
253
+ # A local offset of 0 is translated to the start of the first heredoc line, and a length of 1 is adjusted to
254
+ # 5 - i.e to cover "XXXXa". A local offset of 1, with length 1 would cover "b".
255
+ # A local offset of 4 and length 1 would cover "XXXXd"
256
+ #
257
+ # It is possible that lines have different margin and that is taken into account.
258
+ #
259
+ def to_global(offset, length)
260
+ # simple case, no margin
261
+ return [offset + @leading_offset, length] unless @has_margin
262
+
263
+ # compute local start and end line
264
+ start_line = line_for_offset(offset)
265
+ end_line = line_for_offset(offset+length)
266
+
267
+ # complex case when there is a margin
268
+ transposed_offset = offset == 0 ? @leading_offset : offset + @leading_offset + @accumulated_margin[start_line]
269
+ transposed_length = length +
270
+ @accumulated_margin[end_line] - @accumulated_margin[start_line] + # the margins between start and end (0 is line 1)
271
+ (offset_on_line(offset) == 0 ? margin_per_line[start_line - 1] : 0) # include start's margin in position 0
272
+ [transposed_offset, transposed_length]
273
+ end
274
+
275
+ # Do not call this method
276
+ def char_offset(offset)
277
+ raise "Should not be called"
278
+ end
279
+
280
+ # Do not call this method
281
+ def char_length(offset, end_offset)
282
+ raise "Should not be called"
283
+ end
284
+
285
+ end
286
+
267
287
  class LocatorForChars < AbstractLocator
268
288
 
269
289
  def offset_on_line(offset)
@@ -311,7 +331,7 @@ class Locator
311
331
  # Ruby 19 is multibyte but has no character position methods, must use byteslice
312
332
  def offset_on_line(offset)
313
333
  line_offset = line_index[ line_for_offset(offset)-1 ]
314
- string.byteslice(line_offset, offset-line_offset).length
334
+ @string.byteslice(line_offset, offset-line_offset).length
315
335
  end
316
336
 
317
337
  # Returns the character offset for a given byte offset
@@ -114,8 +114,17 @@ class Parser
114
114
  pos = nil
115
115
  if token != 0
116
116
  file = value[:file]
117
- line = value[:line]
118
- pos = value[:pos]
117
+ locator = value.locator
118
+ if locator.is_a?(Puppet::Pops::Parser::Locator::SubLocator)
119
+ # The error occurs when doing sub-parsing and the token must be transformed
120
+ # Transpose the local offset, length to global "coordinates"
121
+ global_offset, _ = locator.to_global(value.offset, value.length)
122
+ line = locator.locator.line_for_offset(global_offset)
123
+ pos = locator.locator.pos_on_line(global_offset)
124
+ else
125
+ line = value[:line]
126
+ pos = value[:pos]
127
+ end
119
128
  else
120
129
  # At end of input, use what the lexer thinks is the source file
121
130
  file = lexer.file
@@ -753,7 +753,7 @@ module Types
753
753
  end
754
754
 
755
755
  def describe_POptionalType(expected, original, actual, path)
756
- return EMPTY_ARRAY if actual.is_a?(PUndefType)
756
+ return EMPTY_ARRAY if actual.is_a?(PUndefType) || expected.optional_type.nil?
757
757
  internal_describe(expected.optional_type, original.is_a?(PTypeAliasType) ? original : expected, actual, path)
758
758
  end
759
759
 
@@ -71,7 +71,9 @@ Puppet::Type.type(:file).provide :windows do
71
71
 
72
72
  def mode=(value)
73
73
  begin
74
- set_mode(value.to_i(8), resource[:path])
74
+ managing_owner = !resource[:owner].nil?
75
+ managing_group = !resource[:group].nil?
76
+ set_mode(value.to_i(8), resource[:path], true, managing_owner, managing_group)
75
77
  rescue => detail
76
78
  error = Puppet::Error.new(_("failed to set mode %{mode} on %{path}: %{message}") % { mode: mode, path: resource[:path], message: detail.message })
77
79
  error.set_backtrace detail.backtrace
@@ -86,6 +88,52 @@ Puppet::Type.type(:file).provide :windows do
86
88
  end
87
89
  end
88
90
 
91
+ # munge the windows group permissions if the user or group are set to SYSTEM
92
+ #
93
+ # when SYSTEM user is the group or user and the resoure is not managing them then treat
94
+ # the resource as insync if System has FullControl access.
95
+ #
96
+ # @param [String] current - the current mode returned by the resource
97
+ # @param [String] should - what the mode should be
98
+ #
99
+ # @return [String, nil] munged mode or nil if the resource should be out of sync
100
+ def munge_windows_system_group(current, should)
101
+ [
102
+ {
103
+ 'type' => 'group',
104
+ 'resource' => resource[:group],
105
+ 'set_to_user' => group,
106
+ 'fullcontrol' => "070".to_i(8),
107
+ 'remove_mask' => "707".to_i(8),
108
+ 'should_mask' => (should[0].to_i(8) & "070".to_i(8)),
109
+ },
110
+ {
111
+ 'type' => 'owner',
112
+ 'resource' => resource[:owner],
113
+ 'set_to_user' => owner,
114
+ 'fullcontrol' => "700".to_i(8),
115
+ 'remove_mask' => "077".to_i(8),
116
+ 'should_mask' => (should[0].to_i(8) & "700".to_i(8)),
117
+ }
118
+ ].each do |mode_part|
119
+ if mode_part['resource'].nil? && (mode_part['set_to_user'] == Puppet::Util::Windows::SID::LocalSystem)
120
+ if (current.to_i(8) & mode_part['fullcontrol']) == mode_part['fullcontrol']
121
+ # Since the group is LocalSystem, and the permissions are FullControl,
122
+ # replace the value returned with the value expected. This will treat
123
+ # this specific situation as "insync"
124
+ current = ( (current.to_i(8) & mode_part['remove_mask']) | mode_part['should_mask'] ).to_s(8).rjust(4, '0')
125
+ else
126
+ # If the SYSTEM account does _not_ have FullControl in this scenario, we should
127
+ # force the resource out of sync no matter what.
128
+ #TRANSLATORS 'SYSTEM' is a Windows name and should not be translated
129
+ Puppet.debug _("%{resource_name}: %{mode_part_type} set to SYSTEM. SYSTEM permissions cannot be set below FullControl ('7')") % { resource_name: resource[:name], mode_part_type: mode_part['type']}
130
+ return nil
131
+ end
132
+ end
133
+ end
134
+ current
135
+ end
136
+
89
137
  attr_reader :file
90
138
  private
91
139
  def file