puppet 3.7.5 → 3.8.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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/ext/build_defaults.yaml +5 -5
  3. data/lib/hiera/puppet_function.rb +15 -4
  4. data/lib/puppet.rb +5 -2
  5. data/lib/puppet/application/agent.rb +5 -0
  6. data/lib/puppet/application/apply.rb +5 -0
  7. data/lib/puppet/application/device.rb +8 -3
  8. data/lib/puppet/application/master.rb +5 -0
  9. data/lib/puppet/defaults.rb +8 -0
  10. data/lib/puppet/error.rb +27 -1
  11. data/lib/puppet/file_system.rb +13 -0
  12. data/lib/puppet/file_system/file19windows.rb +8 -0
  13. data/lib/puppet/file_system/file_impl.rb +4 -0
  14. data/lib/puppet/file_system/memory_impl.rb +4 -0
  15. data/lib/puppet/functions.rb +25 -3
  16. data/lib/puppet/functions/defined.rb +130 -0
  17. data/lib/puppet/functions/hiera_include.rb +1 -1
  18. data/lib/puppet/node/environment.rb +4 -0
  19. data/lib/puppet/parser/compiler.rb +5 -2
  20. data/lib/puppet/parser/functions/defined.rb +26 -1
  21. data/lib/puppet/parser/functions/file.rb +3 -1
  22. data/lib/puppet/parser/templatewrapper.rb +2 -1
  23. data/lib/puppet/pops.rb +5 -0
  24. data/lib/puppet/pops/evaluator/access_operator.rb +25 -5
  25. data/lib/puppet/pops/evaluator/collector_transformer.rb +1 -11
  26. data/lib/puppet/pops/evaluator/compare_operator.rb +43 -0
  27. data/lib/puppet/pops/evaluator/evaluator_impl.rb +43 -28
  28. data/lib/puppet/pops/evaluator/runtime3_support.rb +9 -5
  29. data/lib/puppet/pops/functions/dispatch.rb +6 -1
  30. data/lib/puppet/pops/issue_reporter.rb +42 -16
  31. data/lib/puppet/pops/issues.rb +96 -0
  32. data/lib/puppet/pops/loader/module_loaders.rb +3 -1
  33. data/lib/puppet/pops/loaders.rb +6 -4
  34. data/lib/puppet/pops/migration/migration_checker.rb +45 -0
  35. data/lib/puppet/pops/model/factory.rb +1 -1
  36. data/lib/puppet/pops/model/model_meta.rb +1 -1
  37. data/lib/puppet/pops/parser/egrammar.ra +1 -1
  38. data/lib/puppet/pops/parser/eparser.rb +1 -1
  39. data/lib/puppet/pops/parser/epp_support.rb +18 -9
  40. data/lib/puppet/pops/parser/evaluating_parser.rb +7 -1
  41. data/lib/puppet/pops/parser/heredoc_support.rb +12 -11
  42. data/lib/puppet/pops/parser/interpolation_support.rb +7 -1
  43. data/lib/puppet/pops/parser/lexer2.rb +8 -8
  44. data/lib/puppet/pops/parser/lexer_support.rb +46 -20
  45. data/lib/puppet/pops/parser/parser_support.rb +11 -14
  46. data/lib/puppet/pops/parser/slurp_support.rb +22 -6
  47. data/lib/puppet/pops/types/type_calculator.rb +156 -55
  48. data/lib/puppet/pops/types/type_factory.rb +67 -14
  49. data/lib/puppet/pops/types/type_parser.rb +22 -13
  50. data/lib/puppet/pops/types/types.rb +21 -3
  51. data/lib/puppet/pops/types/types_meta.rb +13 -2
  52. data/lib/puppet/pops/validation.rb +25 -2
  53. data/lib/puppet/pops/validation/checker4_0.rb +25 -5
  54. data/lib/puppet/provider/group/windows_adsi.rb +18 -6
  55. data/lib/puppet/provider/mount/parsed.rb +145 -2
  56. data/lib/puppet/provider/package/pip.rb +4 -5
  57. data/lib/puppet/provider/package/zypper.rb +17 -7
  58. data/lib/puppet/provider/scheduled_task/win32_taskscheduler.rb +35 -10
  59. data/lib/puppet/provider/service/init.rb +7 -0
  60. data/lib/puppet/provider/user/windows_adsi.rb +8 -1
  61. data/lib/puppet/provider/zpool/zpool.rb +7 -2
  62. data/lib/puppet/resource.rb +1 -1
  63. data/lib/puppet/type/group.rb +1 -1
  64. data/lib/puppet/type/mount.rb +14 -3
  65. data/lib/puppet/type/scheduled_task.rb +21 -6
  66. data/lib/puppet/util/log.rb +50 -8
  67. data/lib/puppet/util/log/destinations.rb +23 -2
  68. data/lib/puppet/util/logging.rb +37 -1
  69. data/lib/puppet/util/windows/adsi.rb +36 -11
  70. data/lib/puppet/version.rb +1 -1
  71. data/spec/fixtures/unit/provider/mount/parsed/aix.filesystems +93 -85
  72. data/spec/fixtures/unit/provider/mount/parsed/aix.mount +11 -7
  73. data/spec/integration/parser/collector_spec.rb +7 -0
  74. data/spec/integration/parser/future_compiler_spec.rb +9 -0
  75. data/spec/integration/parser/resource_expressions_spec.rb +3 -0
  76. data/spec/unit/file_system_spec.rb +38 -0
  77. data/spec/unit/functions/defined_spec.rb +291 -0
  78. data/spec/unit/functions/hiera_spec.rb +8 -6
  79. data/spec/unit/functions4_spec.rb +97 -2
  80. data/spec/unit/parser/functions/file_spec.rb +8 -2
  81. data/spec/unit/parser/functions/template_spec.rb +1 -1
  82. data/spec/unit/parser/templatewrapper_spec.rb +1 -1
  83. data/spec/unit/pops/evaluator/access_ops_spec.rb +19 -0
  84. data/spec/unit/pops/evaluator/evaluating_parser_spec.rb +61 -8
  85. data/spec/unit/pops/issues_spec.rb +16 -16
  86. data/spec/unit/pops/loaders/module_loaders_spec.rb +5 -0
  87. data/spec/unit/pops/migration_spec.rb +180 -0
  88. data/spec/unit/pops/parser/lexer2_spec.rb +152 -1
  89. data/spec/unit/pops/parser/parse_heredoc_spec.rb +26 -0
  90. data/spec/unit/pops/transformer/transform_calls_spec.rb +1 -1
  91. data/spec/unit/pops/types/type_calculator_spec.rb +204 -11
  92. data/spec/unit/pops/validation_spec.rb +66 -0
  93. data/spec/unit/provider/group/windows_adsi_spec.rb +65 -1
  94. data/spec/unit/provider/mount/parsed_spec.rb +31 -5
  95. data/spec/unit/provider/package/pip_spec.rb +19 -7
  96. data/spec/unit/provider/package/zypper_spec.rb +25 -14
  97. data/spec/unit/provider/scheduled_task/win32_taskscheduler_spec.rb +312 -70
  98. data/spec/unit/provider/service/base_spec.rb +42 -31
  99. data/spec/unit/provider/service/freebsd_spec.rb +1 -0
  100. data/spec/unit/provider/service/gentoo_spec.rb +1 -0
  101. data/spec/unit/provider/service/init_spec.rb +18 -0
  102. data/spec/unit/provider/service/openbsd_spec.rb +1 -0
  103. data/spec/unit/provider/service/redhat_spec.rb +1 -0
  104. data/spec/unit/provider/user/windows_adsi_spec.rb +21 -0
  105. data/spec/unit/provider/zpool/zpool_spec.rb +47 -10
  106. data/spec/unit/util/log_spec.rb +113 -0
  107. data/spec/unit/util/windows/adsi_spec.rb +106 -26
  108. metadata +10 -2
@@ -2,9 +2,9 @@
2
2
  # @api public
3
3
  #
4
4
  module Puppet::Pops::Types::TypeFactory
5
- @type_calculator = Puppet::Pops::Types::TypeCalculator.new()
6
-
7
5
  Types = Puppet::Pops::Types
6
+ @type_calculator = Types::TypeCalculator.singleton
7
+ @undef_t = Types::PUndefType.new
8
8
 
9
9
  # Produces the Integer type
10
10
  # @api public
@@ -66,9 +66,17 @@ module Puppet::Pops::Types::TypeFactory
66
66
  end
67
67
 
68
68
  # Produces the Optional type, i.e. a short hand for Variant[T, Undef]
69
+ # If the given 'optional_type' argument is a String, then it will be
70
+ # converted into a String type that represents that string.
71
+ #
72
+ # @param optional_type [String,PAnyType,nil] the optional type
73
+ # @return [POptionalType] the created type
74
+ #
75
+ # @api public
76
+ #
69
77
  def self.optional(optional_type = nil)
70
78
  t = Types::POptionalType.new
71
- t.optional_type = type_of(optional_type)
79
+ t.optional_type = optional_type.is_a?(String) ? string(optional_type) : type_of(optional_type)
72
80
  t
73
81
  end
74
82
 
@@ -91,21 +99,50 @@ module Puppet::Pops::Types::TypeFactory
91
99
  end
92
100
 
93
101
  # Produces the Struct type, either a non parameterized instance representing
94
- # all structs (i.e. all hashes) or a hash with a given set of keys of String
95
- # type (names), bound to a value of a given type. Type may be a Ruby Class, a
96
- # Puppet Type, or an instance from which the type is inferred.
102
+ # all structs (i.e. all hashes) or a hash with entries where the key is
103
+ # either a literal String, an Enum with one entry, or a String representing exactly one value.
104
+ # The key type may also be wrapped in a NotUndef or an Optional.
97
105
  #
98
- def self.struct(name_type_hash = {})
106
+ # The value can be a ruby class, a String (interpreted as the name of a ruby class) or
107
+ # a Type.
108
+ #
109
+ # @param hash [Hash<Object, Object>] key => value hash
110
+ # @return [PStructType] the created Struct type
111
+ #
112
+ def self.struct(hash = {})
113
+ tc = @type_calculator
99
114
  t = Types::PStructType.new
100
- name_type_hash.map do |name, type|
101
- elem = Types::PStructElement.new
102
- if name.is_a?(String) && name.empty?
103
- raise ArgumentError, "An empty String can not be used where a String[1, default] is expected"
115
+ t.elements = hash.map do |key_type, value_type|
116
+ value_type = type_of(value_type)
117
+ raise ArgumentError, 'Struct element value_type must be a Type' unless value_type.is_a?(Types::PAnyType)
118
+
119
+ # TODO: Should have stricter name rule
120
+ if key_type.is_a?(String)
121
+ raise ArgumentError, 'Struct element key cannot be an empty String' if key_type.empty?
122
+ key_type = string(key_type)
123
+ # Must make key optional if the value can be Undef
124
+ key_type = optional(key_type) if tc.assignable?(value_type, @undef_t)
125
+ else
126
+ # assert that the key type is one of String[1], NotUndef[String[1]] and Optional[String[1]]
127
+ case key_type
128
+ when Types::PNotUndefType
129
+ # We can loose the NotUndef wrapper here since String[1] isn't optional anyway
130
+ key_type = key_type.type
131
+ s = key_type
132
+ when Types::POptionalType
133
+ s = key_type.optional_type
134
+ else
135
+ s = key_type
136
+ end
137
+ unless (s.is_a?(Puppet::Pops::Types::PStringType) || s.is_a?(Puppet::Pops::Types::PEnumType)) && s.values.size == 1 && !s.values[0].empty?
138
+ raise ArgumentError, 'Unable to extract a non-empty literal string from Struct member key type' if key_type.empty?
139
+ end
104
140
  end
105
- elem.name = name
106
- elem.type = type_of(type)
141
+ elem = Types::PStructElement.new
142
+ elem.key_type = key_type
143
+ elem.value_type = value_type
107
144
  elem
108
- end.each {|elem| t.addElements(elem) }
145
+ end
109
146
  t
110
147
  end
111
148
 
@@ -351,6 +388,22 @@ module Puppet::Pops::Types::TypeFactory
351
388
  type
352
389
  end
353
390
 
391
+ # Produces a type for NotUndef[T]
392
+ # The given 'inst_type' can be a string in which case it will be converted into
393
+ # the type String[inst_type].
394
+ #
395
+ # @param inst_type [Type,String] the type to qualify
396
+ # @return [Puppet::Pops::Types::PNotUndefType] the NotUndef type
397
+ #
398
+ # @api public
399
+ #
400
+ def self.not_undef(inst_type = nil)
401
+ type = Types::PNotUndefType.new()
402
+ inst_type = string(inst_type) if inst_type.is_a?(String)
403
+ type.type = inst_type
404
+ type
405
+ end
406
+
354
407
  # Produces a type for Type[T]
355
408
  # @api public
356
409
  #
@@ -13,6 +13,7 @@ class Puppet::Pops::Types::TypeParser
13
13
  def initialize
14
14
  @parser = Puppet::Pops::Parser::Parser.new()
15
15
  @type_transformer = Puppet::Pops::Visitor.new(nil, "interpret", 0, 0)
16
+ @undef_t = TYPES.undef
16
17
  end
17
18
 
18
19
  # Produces a *puppet type* based on the given string.
@@ -163,6 +164,9 @@ class Puppet::Pops::Types::TypeParser
163
164
  when "undef"
164
165
  TYPES.undef()
165
166
 
167
+ when "notundef"
168
+ TYPES.not_undef()
169
+
166
170
  when "default"
167
171
  TYPES.default()
168
172
 
@@ -344,8 +348,9 @@ class Puppet::Pops::Types::TypeParser
344
348
  when "struct"
345
349
  # 1..m parameters being types (last two optionally integer or literal default
346
350
  raise_invalid_parameters_error("Struct", "1", parameters.size) unless parameters.size == 1
347
- assert_struct_parameter(parameters[0])
348
- TYPES.struct(parameters[0])
351
+ h = parameters[0]
352
+ raise_invalid_type_specification_error unless h.is_a?(Hash)
353
+ TYPES.struct(h)
349
354
 
350
355
  when "integer"
351
356
  if parameters.size == 1
@@ -400,12 +405,25 @@ class Puppet::Pops::Types::TypeParser
400
405
  if parameters.size != 1
401
406
  raise_invalid_parameters_error("Optional", 1, parameters.size)
402
407
  end
403
- assert_type(parameters[0])
404
- TYPES.optional(parameters[0])
408
+ param = parameters[0]
409
+ assert_type(param) unless param.is_a?(String)
410
+ TYPES.optional(param)
405
411
 
406
412
  when "any", "data", "catalogentry", "boolean", "scalar", "undef", "numeric", "default"
407
413
  raise_unparameterized_type_error(parameterized_ast.left_expr)
408
414
 
415
+ when "notundef"
416
+ case parameters.size
417
+ when 0
418
+ TYPES.not_undef
419
+ when 1
420
+ param = parameters[0]
421
+ assert_type(param) unless param.is_a?(String)
422
+ TYPES.not_undef(param)
423
+ else
424
+ raise_invalid_parameters_error("NotUndef", "0 to 1", parameters.size)
425
+ end
426
+
409
427
  when "type"
410
428
  if parameters.size != 1
411
429
  raise_invalid_parameters_error("Type", 1, parameters.size)
@@ -438,15 +456,6 @@ class Puppet::Pops::Types::TypeParser
438
456
  raise_invalid_type_specification_error unless TYPES.is_range_parameter?(t)
439
457
  end
440
458
 
441
- def assert_struct_parameter(h)
442
- raise_invalid_type_specification_error unless h.is_a?(Hash)
443
- h.each do |k,v|
444
- # TODO: Should have stricter name rule
445
- raise_invalid_type_specification_error unless k.is_a?(String) && !k.empty?
446
- assert_type(v)
447
- end
448
- end
449
-
450
459
  def raise_invalid_type_specification_error
451
460
  raise Puppet::ParseError,
452
461
  "The expression <#{@string}> is not a valid type specification."
@@ -46,6 +46,18 @@ module Puppet::Pops
46
46
  end
47
47
  end
48
48
 
49
+ class PNotUndefType < PAnyType
50
+ module ClassModule
51
+ def hash
52
+ [self.class, type].hash
53
+ end
54
+
55
+ def ==(o)
56
+ self.class == o.class && type == o.type
57
+ end
58
+ end
59
+ end
60
+
49
61
  class PType < PAnyType
50
62
  module ClassModule
51
63
  def hash
@@ -220,11 +232,17 @@ module Puppet::Pops
220
232
  class PStructElement < TypeModelObject
221
233
  module ClassModule
222
234
  def hash
223
- [self.class, type, name].hash
235
+ [self.class, value_type, key_type].hash
236
+ end
237
+
238
+ def name
239
+ k = key_type
240
+ k = k.optional_type if k.is_a?(POptionalType)
241
+ k.values[0]
224
242
  end
225
243
 
226
244
  def ==(o)
227
- self.class == o.class && type == o.type && name == o.name
245
+ self.class == o.class && value_type == o.value_type && key_type == o.key_type
228
246
  end
229
247
  end
230
248
  end
@@ -233,7 +251,7 @@ module Puppet::Pops
233
251
  class PStructType < PAnyType
234
252
  module ClassModule
235
253
  def hashed_elements_derived
236
- @_hashed ||= elements.reduce({}) {|memo, e| memo[e.name] = e.type; memo }
254
+ @_hashed ||= elements.reduce({}) {|memo, e| memo[e.name] = e; memo }
237
255
  @_hashed
238
256
  end
239
257
 
@@ -27,6 +27,15 @@ module Puppet::Pops::Types
27
27
  class PAnyType < TypeModelObject
28
28
  end
29
29
 
30
+ # A type that is assignable from the same types as its contained `type` except the
31
+ # types assignable from {Puppet::Pops::Types::PUndefType}
32
+ #
33
+ # @api public
34
+ #
35
+ class PNotUndefType < PAnyType
36
+ contains_one_uni 'type', PAnyType
37
+ end
38
+
30
39
  # The type of types.
31
40
  # @api public
32
41
  #
@@ -135,8 +144,10 @@ module Puppet::Pops::Types
135
144
  # @api public
136
145
  #
137
146
  class PStructElement < TypeModelObject
138
- has_attr 'name', String, :lowerBound => 1
139
- contains_one_uni 'type', PAnyType
147
+ # key_type must be either String[1] or Optional[String[1]] and the String[1] must
148
+ # have a values collection with exactly one element
149
+ contains_one_uni 'key_type', PAnyType, :lowerBound => 1
150
+ contains_one_uni 'value_type', PAnyType
140
151
  end
141
152
 
142
153
  # @api public
@@ -93,11 +93,12 @@ module Puppet::Pops::Validation
93
93
  @@severity_hash = {:ignore => true, :warning => true, :error => true, :deprecation => true }
94
94
 
95
95
  # Creates a new instance where all issues are diagnosed as :error unless overridden.
96
+ # @param [Symbol] specifies default severity if :error is not wanted as the default
96
97
  # @api public
97
98
  #
98
- def initialize
99
+ def initialize(default_severity = :error)
99
100
  # If diagnose is not set, the default is returned by the block
100
- @severities = Hash.new :error
101
+ @severities = Hash.new default_severity
101
102
  end
102
103
 
103
104
  # Returns the severity of the given issue.
@@ -227,6 +228,28 @@ module Puppet::Pops::Validation
227
228
  # debugging or similar - this to catch internal problems reported as higher level issues.
228
229
  @exception = exception
229
230
  end
231
+
232
+ # Two diagnostics are considered equal if the have the same issue, location and severity
233
+ # (arguments and exception are ignored)
234
+ #
235
+ def ==(o)
236
+ self.class == o.class &&
237
+ same_position?(o) &&
238
+ issue.issue_code == o.issue.issue_code &&
239
+ file == o.file &&
240
+ severity == o.severity
241
+ end
242
+ alias eql? ==
243
+
244
+ # Position is equal if the diagnostic is not located or if referring to the same offset
245
+ def same_position?(o)
246
+ source_pos.nil? && o.source_pos.nil? || source_pos.offset == o.source_pos.offset
247
+ end
248
+ private :same_position?
249
+
250
+ def hash
251
+ @hash ||= [file, source_pos.offset, issue.issue_code, severity].hash
252
+ end
230
253
  end
231
254
 
232
255
  # Formats a diagnostic for output.
@@ -14,6 +14,8 @@ class Puppet::Pops::Validation::Checker4_0
14
14
  Model = Puppet::Pops::Model
15
15
 
16
16
  attr_reader :acceptor
17
+ attr_reader :migration_checker
18
+
17
19
  # Initializes the validator with a diagnostics producer. This object must respond to
18
20
  # `:will_accept?` and `:accept`.
19
21
  #
@@ -28,6 +30,9 @@ class Puppet::Pops::Validation::Checker4_0
28
30
  @@idem_visitor ||= Puppet::Pops::Visitor.new(self, "idem", 0, 0)
29
31
 
30
32
  @acceptor = diagnostics_producer
33
+
34
+ # Use null migration checker unless given in context
35
+ @migration_checker ||= (Puppet.lookup(:migration_checker) { Puppet::Pops::Migration::MigrationChecker.new() })
31
36
  end
32
37
 
33
38
  # Validates the entire model by visiting each model element and calling `check`.
@@ -182,12 +187,18 @@ class Puppet::Pops::Validation::Checker4_0
182
187
 
183
188
  def check_AttributesOperation(o)
184
189
  # Append operator use is constrained
185
- parent = o.eContainer
186
- parent = parent.eContainer unless parent.nil?
187
- unless parent.is_a?(Model::ResourceExpression)
188
- acceptor.accept(Issues::UNSUPPORTED_OPERATOR_IN_CONTEXT, o, :operator=>'* =>')
190
+ parent1 = o.eContainer
191
+ case parent1
192
+ when Model::AbstractResource
193
+ when Model::CollectExpression
194
+ else
195
+ # protect against just testing a snippet that has no parent, error message will be a bit strange
196
+ # but it is not for a real program.
197
+ parent2 = parent1.nil? ? o : parent1.eContainer
198
+ unless parent2.is_a?(Model::AbstractResource)
199
+ acceptor.accept(Issues::UNSUPPORTED_OPERATOR_IN_CONTEXT, parent2, :operator=>'* =>')
200
+ end
189
201
  end
190
-
191
202
  rvalue(o.expr)
192
203
  end
193
204
 
@@ -203,6 +214,7 @@ class Puppet::Pops::Validation::Checker4_0
203
214
  break # only flag the first
204
215
  end
205
216
  end
217
+ migration_checker.report_array_last_in_block(o.statements[-1])
206
218
  end
207
219
 
208
220
  def check_CallNamedFunctionExpression(o)
@@ -361,6 +373,14 @@ class Puppet::Pops::Validation::Checker4_0
361
373
  o.values.each {|v| rvalue(v) }
362
374
  end
363
375
 
376
+ def check_LiteralFloat(o)
377
+ migration_checker.report_ambiguous_float(o)
378
+ end
379
+
380
+ def check_LiteralInteger(o)
381
+ migration_checker.report_ambiguous_integer(o)
382
+ end
383
+
364
384
  def check_NodeDefinition(o)
365
385
  # Check that hostnames are valid hostnames (or regular expressions)
366
386
  hostname(o.host_matches, o)
@@ -9,6 +9,11 @@ Puppet::Type.type(:group).provide :windows_adsi do
9
9
 
10
10
  has_features :manages_members
11
11
 
12
+ def initialize(value={})
13
+ super(value)
14
+ @deleted = false
15
+ end
16
+
12
17
  def members_insync?(current, should)
13
18
  return false unless current
14
19
 
@@ -17,12 +22,17 @@ Puppet::Type.type(:group).provide :windows_adsi do
17
22
 
18
23
  # Cannot use munge of the group property to canonicalize @should
19
24
  # since the default array_matching comparison is not commutative
20
- should_empty = should.nil? or should.empty?
21
-
22
- return false if current.empty? != should_empty
23
25
 
24
26
  # dupes automatically weeded out when hashes built
25
- Puppet::Util::Windows::ADSI::Group.name_sid_hash(current) == Puppet::Util::Windows::ADSI::Group.name_sid_hash(should)
27
+ current_users = Puppet::Util::Windows::ADSI::Group.name_sid_hash(current)
28
+ specified_users = Puppet::Util::Windows::ADSI::Group.name_sid_hash(should)
29
+
30
+ if @resource[:auth_membership]
31
+ current_users == specified_users
32
+ else
33
+ return true if specified_users.empty?
34
+ (specified_users.keys.to_a & current_users.keys.to_a) == specified_users.keys.to_a
35
+ end
26
36
  end
27
37
 
28
38
  def members_to_s(users)
@@ -49,7 +59,7 @@ Puppet::Type.type(:group).provide :windows_adsi do
49
59
  end
50
60
 
51
61
  def members=(members)
52
- group.set_members(members)
62
+ group.set_members(members, @resource[:auth_membership])
53
63
  end
54
64
 
55
65
  def create
@@ -65,11 +75,13 @@ Puppet::Type.type(:group).provide :windows_adsi do
65
75
 
66
76
  def delete
67
77
  Puppet::Util::Windows::ADSI::Group.delete(@resource[:name])
78
+
79
+ @deleted = true
68
80
  end
69
81
 
70
82
  # Only flush if we created or modified a group, not deleted
71
83
  def flush
72
- @group.commit if @group
84
+ @group.commit if @group && !@deleted
73
85
  end
74
86
 
75
87
  def gid
@@ -4,6 +4,7 @@ require 'puppet/provider/mount'
4
4
  fstab = nil
5
5
  case Facter.value(:osfamily)
6
6
  when "Solaris"; fstab = "/etc/vfstab"
7
+ when "AIX"; fstab = "/etc/filesystems"
7
8
  else
8
9
  fstab = "/etc/fstab"
9
10
  end
@@ -25,7 +26,12 @@ Puppet::Type.type(:mount).provide(
25
26
  @fields = [:device, :name, :fstype, :options, :dump, :pass]
26
27
  end
27
28
 
28
- text_line :comment, :match => /^\s*#/
29
+ if Facter.value(:osfamily) == "AIX"
30
+ # * is the comment character on AIX /etc/filesystems
31
+ text_line :comment, :match => /^\s*\*/
32
+ else
33
+ text_line :comment, :match => /^\s*#/
34
+ end
29
35
  text_line :blank, :match => /^\s*$/
30
36
 
31
37
  optional_fields = @fields - [:device, :name, :blockdevice]
@@ -36,7 +42,144 @@ Puppet::Type.type(:mount).provide(
36
42
  field_pattern = '(\s*(?>\S+))'
37
43
  text_line :incomplete, :match => /^(?!#{field_pattern}{#{mandatory_fields.length}})/
38
44
 
39
- record_line self.name, :fields => @fields, :separator => /\s+/, :joiner => "\t", :optional => optional_fields
45
+ case Facter.value(:osfamily)
46
+ when "AIX"
47
+ # The only field that is actually ordered is :name. See `man filesystems` on AIX
48
+ @fields = [:name, :account, :boot, :check, :dev, :free, :mount, :nodename,
49
+ :options, :quota, :size, :type, :vfs, :vol, :log]
50
+ self.line_separator = "\n"
51
+ # Override lines and use scan instead of split, because we DON'T want to
52
+ # remove the separators
53
+ def self.lines(text)
54
+ lines = text.split("\n")
55
+ filesystem_stanza = false
56
+ filesystem_index = 0
57
+ ret = Array.new
58
+ lines.each_with_index do |line,i|
59
+ if line.match(%r{^\S+:})
60
+ # Begin new filesystem stanza and save the index
61
+ ret[filesystem_index] = filesystem_stanza.join("\n") if filesystem_stanza
62
+ filesystem_stanza = Array(line)
63
+ filesystem_index = i
64
+ # Eat the preceeding blank line
65
+ ret[i-1] = nil if i > 0 and ret[i-1] and ret[i-1].match(%r{^\s*$})
66
+ nil
67
+ elsif line.match(%r{^(\s*\*.*|\s*)$})
68
+ # Just a comment or blank line; add in place
69
+ ret[i] = line
70
+ else
71
+ # Non-comments or blank lines must be part of a stanza
72
+ filesystem_stanza << line
73
+ end
74
+ end
75
+ # Add the final stanza to the return
76
+ ret[filesystem_index] = filesystem_stanza.join("\n") if filesystem_stanza
77
+ ret = ret.compact.flatten
78
+ ret.reject { |line| line.match(/^\* HEADER/) }
79
+ end
80
+ def self.header
81
+ super.gsub(/^#/,'*')
82
+ end
83
+
84
+ record_line self.name,
85
+ :fields => @fields,
86
+ :separator => /\n/,
87
+ :block_eval => :instance do
88
+
89
+ def post_parse(result)
90
+ property_map = {
91
+ :dev => :device,
92
+ :nodename => :nodename,
93
+ :options => :options,
94
+ :vfs => :fstype,
95
+ }
96
+ # Result is modified in-place instead of being returned; icky!
97
+ memo = result.dup
98
+ result.clear
99
+ # Save the line for later, just in case it is unparsable
100
+ result[:line] = @fields.collect do |field|
101
+ memo[field] if memo[field] != :absent
102
+ end.compact.join("\n")
103
+ result[:record_type] = memo[:record_type]
104
+ special_options = Array.new
105
+ result[:name] = memo[:name].sub(%r{:\s*$},'').strip
106
+ memo.each do |_,k_v|
107
+ if k_v and k_v.is_a?(String) and k_v.match("=")
108
+ attr_name, attr_value = k_v.split("=",2).map(&:strip)
109
+ if attr_map_name = property_map[attr_name.to_sym]
110
+ # These are normal "options" options (see `man filesystems`)
111
+ result[attr_map_name] = attr_value
112
+ else
113
+ # These /etc/filesystem attributes have no mount resource simile,
114
+ # so are added to the "options" property for puppet's sake
115
+ special_options << "#{attr_name}=#{attr_value}"
116
+ end
117
+ if result[:nodename]
118
+ result[:device] = "#{result[:nodename]}:#{result[:device]}"
119
+ result.delete(:nodename)
120
+ end
121
+ end
122
+ end
123
+ result[:options] = [result[:options],special_options.sort].flatten.compact.join(',')
124
+ if ! result[:device]
125
+ result[:device] = :absent
126
+ Puppet.err "Prefetch: Mount[#{result[:name]}]: Field 'device' is missing"
127
+ end
128
+ if ! result[:fstype]
129
+ result[:fstype] = :absent
130
+ Puppet.err "Prefetch: Mount[#{result[:name]}]: Field 'fstype' is missing"
131
+ end
132
+ end
133
+ def to_line(result)
134
+ output = Array.new
135
+ output << "#{result[:name]}:"
136
+ if result[:device] and result[:device].match(%r{^/})
137
+ output << "\tdev\t\t= #{result[:device]}"
138
+ elsif result[:device] and result[:device] != :absent
139
+ if ! result[:device].match(%{^.+:/})
140
+ # Just skip this entry; it was malformed to begin with
141
+ Puppet.err "Mount[#{result[:name]}]: Field 'device' must be in the format of <absolute path> or <host>:<absolute path>"
142
+ return result[:line]
143
+ end
144
+ nodename, path = result[:device].split(":")
145
+ output << "\tdev\t\t= #{path}"
146
+ output << "\tnodename\t= #{nodename}"
147
+ else
148
+ # Just skip this entry; it was malformed to begin with
149
+ Puppet.err "Mount[#{result[:name]}]: Field 'device' is required"
150
+ return result[:line]
151
+ end
152
+ if result[:fstype] and result[:fstype] != :absent
153
+ output << "\tvfs\t\t= #{result[:fstype]}"
154
+ else
155
+ # Just skip this entry; it was malformed to begin with
156
+ Puppet.err "Mount[#{result[:name]}]: Field 'device' is required"
157
+ return result[:line]
158
+ end
159
+ if result[:options]
160
+ options = result[:options].split(',')
161
+ special_options = options.select do |x|
162
+ x.match('=') and
163
+ ["account", "boot", "check", "free", "mount", "size", "type",
164
+ "vol", "log", "quota"].include? x.split('=').first
165
+ end
166
+ options = options - special_options
167
+ special_options.sort.each do |x|
168
+ k, v = x.split("=")
169
+ output << "\t#{k}\t\t= #{v}"
170
+ end
171
+ output << "\toptions\t\t= #{options.join(",")}" unless options.empty?
172
+ end
173
+ if result[:line] and result[:line].split("\n").sort == output.sort
174
+ return "\n#{result[:line]}"
175
+ else
176
+ return "\n#{output.join("\n")}"
177
+ end
178
+ end
179
+ end
180
+ else
181
+ record_line self.name, :fields => @fields, :separator => /\s+/, :joiner => "\t", :optional => optional_fields
182
+ end
40
183
 
41
184
  # Every entry in fstab is :unmounted until we can prove different
42
185
  def self.prefetch_hook(target_records)