puppet 6.12.0 → 6.13.0

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 (144) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +12 -12
  3. data/README.md +1 -1
  4. data/ext/project_data.yaml +1 -1
  5. data/lib/puppet.rb +22 -7
  6. data/lib/puppet/application/resource.rb +1 -1
  7. data/lib/puppet/configurer.rb +8 -13
  8. data/lib/puppet/defaults.rb +83 -49
  9. data/lib/puppet/environments.rb +26 -18
  10. data/lib/puppet/face/facts.rb +8 -5
  11. data/lib/puppet/file_system/memory_file.rb +6 -0
  12. data/lib/puppet/file_system/memory_impl.rb +13 -0
  13. data/lib/puppet/file_system/windows.rb +7 -10
  14. data/lib/puppet/http.rb +2 -0
  15. data/lib/puppet/http/client.rb +30 -0
  16. data/lib/puppet/http/errors.rb +2 -0
  17. data/lib/puppet/http/service.rb +61 -2
  18. data/lib/puppet/http/service/compiler.rb +86 -0
  19. data/lib/puppet/http/service/file_server.rb +85 -0
  20. data/lib/puppet/http/service/report.rb +4 -8
  21. data/lib/puppet/http/session.rb +8 -1
  22. data/lib/puppet/indirector/catalog/compiler.rb +10 -0
  23. data/lib/puppet/indirector/file_bucket_file/file.rb +1 -1
  24. data/lib/puppet/indirector/json.rb +1 -1
  25. data/lib/puppet/indirector/msgpack.rb +1 -1
  26. data/lib/puppet/network/http/connection.rb +4 -0
  27. data/lib/puppet/network/http/nocache_pool.rb +1 -0
  28. data/lib/puppet/network/http/pool.rb +5 -1
  29. data/lib/puppet/parser/ast/pops_bridge.rb +6 -11
  30. data/lib/puppet/pops/evaluator/access_operator.rb +2 -2
  31. data/lib/puppet/pops/evaluator/evaluator_impl.rb +1 -1
  32. data/lib/puppet/pops/loader/puppet_plan_instantiator.rb +12 -3
  33. data/lib/puppet/pops/parser/evaluating_parser.rb +5 -7
  34. data/lib/puppet/pops/types/p_object_type_extension.rb +10 -0
  35. data/lib/puppet/pops/types/type_calculator.rb +24 -0
  36. data/lib/puppet/pops/validation/checker4_0.rb +1 -1
  37. data/lib/puppet/pops/validation/tasks_checker.rb +5 -1
  38. data/lib/puppet/provider/aix_object.rb +4 -2
  39. data/lib/puppet/provider/group/aix.rb +1 -0
  40. data/lib/puppet/provider/group/groupadd.rb +52 -24
  41. data/lib/puppet/provider/package/apt.rb +14 -3
  42. data/lib/puppet/provider/package/dnfmodule.rb +9 -2
  43. data/lib/puppet/provider/package/dpkg.rb +14 -7
  44. data/lib/puppet/provider/package/fink.rb +20 -3
  45. data/lib/puppet/provider/package/openbsd.rb +13 -1
  46. data/lib/puppet/provider/package/pkg.rb +18 -5
  47. data/lib/puppet/provider/package/yum.rb +9 -5
  48. data/lib/puppet/provider/user/aix.rb +1 -0
  49. data/lib/puppet/provider/user/directoryservice.rb +30 -5
  50. data/lib/puppet/provider/user/useradd.rb +6 -7
  51. data/lib/puppet/reports/store.rb +1 -1
  52. data/lib/puppet/settings.rb +2 -0
  53. data/lib/puppet/ssl/certificate.rb +2 -1
  54. data/lib/puppet/test/test_helper.rb +4 -0
  55. data/lib/puppet/transaction/resource_harness.rb +1 -1
  56. data/lib/puppet/type/group.rb +2 -2
  57. data/lib/puppet/type/package.rb +63 -9
  58. data/lib/puppet/type/user.rb +2 -2
  59. data/lib/puppet/util/log/destinations.rb +1 -1
  60. data/lib/puppet/util/pidlock.rb +26 -6
  61. data/lib/puppet/util/plist.rb +6 -0
  62. data/lib/puppet/util/storage.rb +0 -1
  63. data/lib/puppet/util/yaml.rb +1 -1
  64. data/lib/puppet/version.rb +1 -1
  65. data/locales/puppet.pot +127 -115
  66. data/man/man5/puppet.conf.5 +21 -7
  67. data/man/man8/puppet-agent.8 +1 -1
  68. data/man/man8/puppet-apply.8 +1 -1
  69. data/man/man8/puppet-catalog.8 +1 -1
  70. data/man/man8/puppet-config.8 +1 -1
  71. data/man/man8/puppet-describe.8 +1 -1
  72. data/man/man8/puppet-device.8 +1 -1
  73. data/man/man8/puppet-doc.8 +1 -1
  74. data/man/man8/puppet-epp.8 +1 -1
  75. data/man/man8/puppet-facts.8 +1 -1
  76. data/man/man8/puppet-filebucket.8 +1 -1
  77. data/man/man8/puppet-generate.8 +1 -1
  78. data/man/man8/puppet-help.8 +1 -1
  79. data/man/man8/puppet-key.8 +1 -1
  80. data/man/man8/puppet-lookup.8 +1 -1
  81. data/man/man8/puppet-man.8 +1 -1
  82. data/man/man8/puppet-module.8 +1 -1
  83. data/man/man8/puppet-node.8 +1 -1
  84. data/man/man8/puppet-parser.8 +1 -1
  85. data/man/man8/puppet-plugin.8 +1 -1
  86. data/man/man8/puppet-report.8 +1 -1
  87. data/man/man8/puppet-resource.8 +1 -1
  88. data/man/man8/puppet-script.8 +1 -1
  89. data/man/man8/puppet-ssl.8 +1 -1
  90. data/man/man8/puppet-status.8 +1 -1
  91. data/man/man8/puppet.8 +2 -2
  92. data/spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_md5/should_fetch_if_not_on_the_local_disk.yml +0 -35
  93. data/spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_md5/should_not_update_if_content_on_disk_is_up-to-date.yml +0 -37
  94. data/spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_md5/should_update_if_content_differs_on_disk.yml +0 -37
  95. data/spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_mtime/should_fetch_if_mtime_is_older_on_disk.yml +0 -35
  96. data/spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_mtime/should_fetch_if_no_header_specified.yml +0 -33
  97. data/spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_mtime/should_fetch_if_not_on_the_local_disk.yml +0 -35
  98. data/spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_mtime/should_not_update_if_mtime_is_newer_on_disk.yml +0 -35
  99. data/spec/integration/configurer_spec.rb +26 -7
  100. data/spec/integration/indirector/facts/facter_spec.rb +4 -0
  101. data/spec/unit/application/apply_spec.rb +2 -12
  102. data/spec/unit/application/resource_spec.rb +2 -2
  103. data/spec/unit/configurer/fact_handler_spec.rb +0 -4
  104. data/spec/unit/configurer_spec.rb +0 -3
  105. data/spec/unit/defaults_spec.rb +1 -1
  106. data/spec/unit/environments_spec.rb +57 -28
  107. data/spec/unit/face/facts_spec.rb +24 -20
  108. data/spec/unit/file_system_spec.rb +16 -2
  109. data/spec/unit/http/client_spec.rb +6 -0
  110. data/spec/unit/http/service/compiler_spec.rb +322 -0
  111. data/spec/unit/http/service/file_server_spec.rb +219 -0
  112. data/spec/unit/http/service/report_spec.rb +8 -1
  113. data/spec/unit/http/service_spec.rb +4 -0
  114. data/spec/unit/http/session_spec.rb +31 -0
  115. data/spec/unit/indirector/catalog/compiler_spec.rb +46 -29
  116. data/spec/unit/network/http/connection_spec.rb +23 -1
  117. data/spec/unit/network/http/nocache_pool_spec.rb +3 -3
  118. data/spec/unit/network/http/pool_spec.rb +32 -0
  119. data/spec/unit/node/facts_spec.rb +2 -1
  120. data/spec/unit/node_spec.rb +7 -4
  121. data/spec/unit/pops/serialization/to_from_hr_spec.rb +6 -1
  122. data/spec/unit/pops/validator/validator_spec.rb +7 -2
  123. data/spec/unit/provider/aix_object_spec.rb +16 -2
  124. data/spec/unit/provider/group/groupadd_spec.rb +167 -56
  125. data/spec/unit/provider/package/apt_spec.rb +13 -2
  126. data/spec/unit/provider/package/aptitude_spec.rb +1 -0
  127. data/spec/unit/provider/package/dnfmodule_spec.rb +22 -0
  128. data/spec/unit/provider/package/dpkg_spec.rb +28 -6
  129. data/spec/unit/provider/package/openbsd_spec.rb +17 -0
  130. data/spec/unit/provider/package/pkg_spec.rb +15 -1
  131. data/spec/unit/provider/package/yum_spec.rb +50 -0
  132. data/spec/unit/provider/user/directoryservice_spec.rb +41 -0
  133. data/spec/unit/provider/user/useradd_spec.rb +13 -8
  134. data/spec/unit/puppet_pal_2pec.rb +3 -0
  135. data/spec/unit/puppet_pal_catalog_spec.rb +3 -0
  136. data/spec/unit/puppet_spec.rb +14 -0
  137. data/spec/unit/ssl/certificate_spec.rb +7 -0
  138. data/spec/unit/transaction/persistence_spec.rb +1 -10
  139. data/spec/unit/type/package_spec.rb +8 -0
  140. data/spec/unit/type/user_spec.rb +0 -1
  141. data/spec/unit/util/pidlock_spec.rb +38 -16
  142. data/spec/unit/util/plist_spec.rb +20 -0
  143. data/spec/unit/util/storage_spec.rb +1 -8
  144. metadata +10 -4
@@ -1,6 +1,5 @@
1
1
  class Puppet::HTTP::Service::Report < Puppet::HTTP::Service
2
2
  API = '/puppet/v3'.freeze
3
- EXCLUDED_FORMATS = [:yaml, :b64_zlib_yaml, :dot]
4
3
 
5
4
  # puppet major version where JSON is enabled by default
6
5
  MAJOR_VERSION_JSON_DEFAULT = 5
@@ -12,17 +11,14 @@ class Puppet::HTTP::Service::Report < Puppet::HTTP::Service
12
11
 
13
12
  def put_report(name, report, environment:, ssl_context: nil)
14
13
  formatter = Puppet::Network::FormatHandler.format_for(Puppet[:preferred_serialization_format])
15
-
16
- model = Puppet::Transaction::Report
17
- network_formats = model.supported_formats - EXCLUDED_FORMATS
18
- mime_types = network_formats.map { |f| model.get_format(f).mime }
14
+ headers = add_puppet_headers('Accept' => get_mime_types(Puppet::Transaction::Report).join(', '))
19
15
 
20
16
  response = @client.put(
21
17
  with_base_url("/report/#{name}"),
22
- headers: add_puppet_headers('ACCEPT' => mime_types.join(', ')),
23
- params: { :environment => environment },
18
+ headers: headers,
19
+ params: { environment: environment },
24
20
  content_type: formatter.mime,
25
- body: formatter.render(report),
21
+ body: serialize(formatter, report),
26
22
  ssl_context: ssl_context
27
23
  )
28
24
 
@@ -6,9 +6,16 @@ class Puppet::HTTP::Session
6
6
  @resolution_exceptions = []
7
7
  end
8
8
 
9
- def route_to(name, ssl_context: nil)
9
+ def route_to(name, url: nil, ssl_context: nil)
10
10
  raise ArgumentError, "Unknown service #{name}" unless Puppet::HTTP::Service.valid_name?(name)
11
11
 
12
+ # short circuit if explicit URL host & port given
13
+ if url && url.host != nil && !url.host.empty?
14
+ service = Puppet::HTTP::Service.create_service(@client, name, url.host, url.port)
15
+ service.connect(ssl_context: ssl_context)
16
+ return service
17
+ end
18
+
12
19
  cached = @resolved_services[name]
13
20
  return cached if cached
14
21
 
@@ -1,3 +1,4 @@
1
+ require 'puppet/environments'
1
2
  require 'puppet/node'
2
3
  require 'puppet/resource/catalog'
3
4
  require 'puppet/indirector/code'
@@ -173,6 +174,7 @@ class Puppet::Resource::Catalog::Compiler < Puppet::Indirector::Code
173
174
  # Initially restricted to files sourced from codedir via puppet:/// uri.
174
175
  def inline_metadata(catalog, checksum_type)
175
176
  environment_path = Pathname.new File.join(Puppet[:environmentpath], catalog.environment, "")
177
+ environment_path = Puppet::Environments::Directories.real_path(environment_path)
176
178
  list_of_resources = catalog.resources.find_all { |res| res.type == "File" }
177
179
 
178
180
  # TODO: get property/parameter defaults if entries are nil in the resource
@@ -393,9 +395,17 @@ class Puppet::Resource::Catalog::Compiler < Puppet::Indirector::Code
393
395
 
394
396
  # Initialize our server fact hash; we add these to each client, and they
395
397
  # won't change while we're running, so it's safe to cache the values.
398
+ #
399
+ # See also set_server_facts in Puppet::Server::Compiler in puppetserver.
396
400
  def set_server_facts
397
401
  @server_facts = {}
398
402
 
403
+ # Add our server Puppet Enterprise version, if available.
404
+ pe_version_file = '/opt/puppetlabs/server/pe_version'
405
+ if File.readable?(pe_version_file) and !File.zero?(pe_version_file)
406
+ @server_facts['pe_serverversion'] = File.read(pe_version_file).chomp
407
+ end
408
+
399
409
  # Add our server version to the fact list
400
410
  @server_facts["serverversion"] = Puppet.version.to_s
401
411
 
@@ -250,7 +250,7 @@ module Puppet::FileBucketFile
250
250
  # @return [void]
251
251
  # @api private
252
252
  def copy_bucket_file_to_contents_file(contents_file, bucket_file)
253
- Puppet::Util.replace_file(contents_file, 0440) do |of|
253
+ Puppet::FileSystem.replace_file(contents_file, 0440) do |of|
254
254
  # PUP-1044 writes all of the contents
255
255
  bucket_file.stream() do |src|
256
256
  FileUtils.copy_stream(src, of)
@@ -14,7 +14,7 @@ class Puppet::Indirector::JSON < Puppet::Indirector::Terminus
14
14
  filename = path(request.key)
15
15
  FileUtils.mkdir_p(File.dirname(filename))
16
16
 
17
- Puppet::Util.replace_file(filename, 0660) {|f| f.print to_json(request.instance).force_encoding(Encoding::BINARY) }
17
+ Puppet::FileSystem.replace_file(filename, 0660) {|f| f.print to_json(request.instance).force_encoding(Encoding::BINARY) }
18
18
  rescue TypeError => detail
19
19
  Puppet.log_exception(detail, _("Could not save %{json} %{request}: %{detail}") % { json: self.name, request: request.key, detail: detail })
20
20
  end
@@ -21,7 +21,7 @@ class Puppet::Indirector::Msgpack < Puppet::Indirector::Terminus
21
21
  filename = path(request.key)
22
22
  FileUtils.mkdir_p(File.dirname(filename))
23
23
 
24
- Puppet::Util.replace_file(filename, 0660) {|f| f.print to_msgpack(request.instance) }
24
+ Puppet::FileSystem.replace_file(filename, 0660) {|f| f.print to_msgpack(request.instance) }
25
25
  rescue TypeError => detail
26
26
  Puppet.log_exception(detail, _("Could not save %{name} %{request}: %{detail}") % { name: self.name, request: request.key, detail: detail })
27
27
  end
@@ -213,6 +213,10 @@ module Puppet::Network::HTTP
213
213
  current_request[header] = value
214
214
  end
215
215
  when 429, 503
216
+ if connection.started?
217
+ Puppet.debug("Closing connection for #{current_site}")
218
+ connection.finish
219
+ end
216
220
  response = handle_retry_after(current_response)
217
221
  else
218
222
  response = current_response
@@ -15,6 +15,7 @@ class Puppet::Network::HTTP::NoCachePool < Puppet::Network::HTTP::BasePool
15
15
  begin
16
16
  yield http
17
17
  ensure
18
+ return unless http.started?
18
19
  Puppet.debug("Closing connection for #{site}")
19
20
  http.finish
20
21
  end
@@ -33,7 +33,7 @@ class Puppet::Network::HTTP::Pool < Puppet::Network::HTTP::BasePool
33
33
  reuse = false
34
34
  raise detail
35
35
  ensure
36
- if reuse
36
+ if reuse && http.started?
37
37
  release(site, verifier, http)
38
38
  else
39
39
  close_connection(site, http)
@@ -56,13 +56,17 @@ class Puppet::Network::HTTP::Pool < Puppet::Network::HTTP::BasePool
56
56
  end
57
57
 
58
58
  # Safely close a persistent connection.
59
+ # Don't try to close a connection that's already closed.
59
60
  #
60
61
  # @api private
61
62
  def close_connection(site, http)
63
+ return false unless http.started?
62
64
  Puppet.debug("Closing connection for #{site}")
63
65
  http.finish
66
+ true
64
67
  rescue => detail
65
68
  Puppet.log_exception(detail, _("Failed to close connection for %{site}: %{detail}") % { site: site, detail: detail })
69
+ nil
66
70
  end
67
71
 
68
72
  # Borrow and take ownership of a persistent connection. If a new
@@ -13,12 +13,6 @@ class Puppet::Parser::AST::PopsBridge
13
13
  # expression.
14
14
  #
15
15
  class Expression < Puppet::Parser::AST::Leaf
16
-
17
- def initialize args
18
- super
19
- @@evaluator ||= Puppet::Pops::Parser::EvaluatingParser.new()
20
- end
21
-
22
16
  def to_s
23
17
  Puppet::Pops::Model::ModelTreeDumper.new.dump(@value)
24
18
  end
@@ -29,8 +23,9 @@ class Puppet::Parser::AST::PopsBridge
29
23
  end
30
24
 
31
25
  def evaluate(scope)
32
- object = @@evaluator.evaluate(scope, @value)
33
- @@evaluator.convert_to_3x(object, scope)
26
+ evaluator = Puppet::Pops::Parser::EvaluatingParser.singleton
27
+ object = evaluator.evaluate(scope, @value)
28
+ evaluator.convert_to_3x(object, scope)
34
29
  end
35
30
 
36
31
  # Adapts to 3x where top level constructs needs to have each to iterate over children. Short circuit this
@@ -87,7 +82,6 @@ class Puppet::Parser::AST::PopsBridge
87
82
  @program_model = program_model
88
83
  @context = context
89
84
  @ast_transformer ||= Puppet::Pops::Model::AstTransformer.new(@context[:file])
90
- @@evaluator ||= Puppet::Pops::Parser::EvaluatingParser.new()
91
85
  end
92
86
 
93
87
  # This is the 3x API, the 3x AST searches through all code to find the instructions that can be instantiated.
@@ -122,7 +116,7 @@ class Puppet::Parser::AST::PopsBridge
122
116
  end
123
117
 
124
118
  def evaluate(scope)
125
- @@evaluator.evaluate(scope, program_model)
119
+ Puppet::Pops::Parser::EvaluatingParser.singleton.evaluate(scope, program_model)
126
120
  end
127
121
 
128
122
  # Adapts to 3x where top level constructs needs to have each to iterate over children. Short circuit this
@@ -178,8 +172,9 @@ class Puppet::Parser::AST::PopsBridge
178
172
  #
179
173
  scope = obtain_scope
180
174
  if scope
175
+ evaluator = Puppet::Pops::Parser::EvaluatingParser.singleton
181
176
  typed_parameters.each do |p|
182
- result[p.name] = @@evaluator.evaluate(scope, p.type_expr)
177
+ result[p.name] = evaluator.evaluate(scope, p.type_expr)
183
178
  end
184
179
  end
185
180
  result
@@ -20,14 +20,14 @@ class AccessOperator
20
20
  @semantic = access_expression
21
21
  end
22
22
 
23
- def access (o, scope, *keys)
23
+ def access(o, scope, *keys)
24
24
  @@access_visitor.visit_this_2(self, o, scope, keys)
25
25
  end
26
26
 
27
27
  protected
28
28
 
29
29
  def access_Object(o, scope, keys)
30
- type = Puppet::Pops::Types::TypeCalculator.infer(o)
30
+ type = Puppet::Pops::Types::TypeCalculator.infer_callable_methods_t(o)
31
31
  if type.is_a?(Puppet::Pops::Types::TypeWithMembers)
32
32
  access_func = type['[]']
33
33
  return access_func.invoke(o, scope, keys) unless access_func.nil?
@@ -959,7 +959,7 @@ class EvaluatorImpl
959
959
  name = name.value # the string function name
960
960
 
961
961
  obj = receiver[0]
962
- receiver_type = Types::TypeCalculator.infer(obj)
962
+ receiver_type = Types::TypeCalculator.infer_callable_methods_t(obj)
963
963
  if receiver_type.is_a?(Types::TypeWithMembers)
964
964
  member = receiver_type[name]
965
965
  unless member.nil?
@@ -19,8 +19,17 @@ class PuppetPlanInstantiator
19
19
 
20
20
  # parse and validate
21
21
  result = parser.parse_string(pp_code_string, source_ref)
22
- # Only one function is allowed (and no other definitions)
23
- case result.definitions.size
22
+
23
+ # The parser attaches all definitions, including those nested in apply
24
+ # blocks, to the Program object. Node definitions in apply blocks are
25
+ # perfectly legal and don't count as the file containing multiple
26
+ # definitions for this purpose. By this point, we've already validated that
27
+ # there are no node definitions *outside* apply blocks, so we simply ignore
28
+ # them here.
29
+ definitions = result.definitions.reject { |definition| definition.is_a?(Puppet::Pops::Model::NodeDefinition) }
30
+
31
+ # Only one plan is allowed (and no other definitions)
32
+ case definitions.size
24
33
  when 0
25
34
  raise ArgumentError, _("The code loaded from %{source_ref} does not define the plan '%{plan_name}' - it is empty.") % { source_ref: source_ref, plan_name: typed_name.name }
26
35
  when 1
@@ -28,7 +37,7 @@ class PuppetPlanInstantiator
28
37
  else
29
38
  raise ArgumentError, _("The code loaded from %{source_ref} must contain only the plan '%{plan_name}' - it has additional definitions.") % { source_ref: source_ref, plan_name: typed_name.name }
30
39
  end
31
- the_plan_definition = result.definitions[0]
40
+ the_plan_definition = definitions[0]
32
41
 
33
42
  unless the_plan_definition.is_a?(Model::PlanDefinition)
34
43
  raise ArgumentError, _("The code loaded from %{source_ref} does not define the plan '%{plan_name}' - no plan found.") % { source_ref: source_ref, plan_name: typed_name.name }
@@ -15,7 +15,6 @@ class EvaluatingParser
15
15
  end
16
16
 
17
17
  def parse_string(s, file_source = nil)
18
- @file_source = file_source
19
18
  clear()
20
19
  # Handling of syntax error can be much improved (in general), now it bails out of the parser
21
20
  # and does not have as rich information (when parsing a string), need to update it with the file source
@@ -24,20 +23,19 @@ class EvaluatingParser
24
23
  # Also a possible improvement (if the YAML parser returns positions) is to provide correct output of position.
25
24
  #
26
25
  begin
27
- assert_and_report(parser.parse_string(s, file_source)).model
26
+ assert_and_report(parser.parse_string(s, file_source), file_source).model
28
27
  rescue Puppet::ParseErrorWithIssue => e
29
28
  raise e
30
29
  rescue Puppet::ParseError => e
31
30
  # TODO: This is not quite right, why does not the exception have the correct file?
32
- e.file = @file_source unless e.file.is_a?(String) && !e.file.empty?
31
+ e.file = file_source unless e.file.is_a?(String) && !e.file.empty?
33
32
  raise e
34
33
  end
35
34
  end
36
35
 
37
36
  def parse_file(file)
38
- @file_source = file
39
37
  clear()
40
- assert_and_report(parser.parse_file(file)).model
38
+ assert_and_report(parser.parse_file(file), file).model
41
39
  end
42
40
 
43
41
  def evaluate_string(scope, s, file_source = nil)
@@ -96,10 +94,10 @@ class EvaluatingParser
96
94
  Validation::ValidatorFactory_4_0.new().validator(acceptor)
97
95
  end
98
96
 
99
- def assert_and_report(parse_result)
97
+ def assert_and_report(parse_result, file_source)
100
98
  return nil unless parse_result
101
99
  if parse_result['source_ref'].nil? || parse_result['source_ref'] == ''
102
- parse_result['source_ref'] = @file_source
100
+ parse_result['source_ref'] = file_source
103
101
  end
104
102
  validation_result = validate(parse_result.model)
105
103
 
@@ -153,6 +153,16 @@ class PObjectTypeExtension < PAnyType
153
153
  @base_type.simple_name
154
154
  end
155
155
 
156
+ # @api private
157
+ def implementation_class(create = true)
158
+ @base_type.implementation_class(create)
159
+ end
160
+
161
+ # @api private
162
+ def parameter_info(impl_class)
163
+ @base_type.parameter_info(impl_class)
164
+ end
165
+
156
166
  protected
157
167
 
158
168
  # Checks that the given `param_values` hash contains all keys present in the `parameters` of
@@ -123,6 +123,30 @@ class TypeCalculator
123
123
  singleton.infer(o)
124
124
  end
125
125
 
126
+ # Infers a type if given object may have callable members, else returns nil.
127
+ # Caller must check for nil or if returned type supports members.
128
+ # This is a much cheaper call than doing a call to the general infer(o) method.
129
+ #
130
+ # @api private
131
+ def self.infer_callable_methods_t(o)
132
+ # If being a value that cannot have Pcore based methods callable from Puppet Language
133
+ if (o.is_a?(String) ||
134
+ o.is_a?(Numeric) ||
135
+ o.is_a?(TrueClass) ||
136
+ o.is_a?(FalseClass) ||
137
+ o.is_a?(Regexp) ||
138
+ o.instance_of?(Array) ||
139
+ o.instance_of?(Hash) ||
140
+ Types::PUndefType::DEFAULT.instance?(o)
141
+ )
142
+ return nil
143
+ end
144
+ # For other objects (e.g. PObjectType instances, and runtime types) full inference needed, since that will
145
+ # cover looking into the runtime type registry.
146
+ #
147
+ infer(o)
148
+ end
149
+
126
150
  # @api public
127
151
  def self.generalize(o)
128
152
  singleton.generalize(o)
@@ -93,7 +93,7 @@ class Checker4_0 < Evaluator::LiteralEvaluator
93
93
  o = container(idx)
94
94
  idx -= 1
95
95
  case o
96
- when NilClass, Model::HostClassDefinition, Model::Program
96
+ when NilClass, Model::ApplyExpression, Model::HostClassDefinition, Model::Program
97
97
  # ok, stop scanning parents
98
98
  when Model::BlockExpression
99
99
  c = container(idx)
@@ -40,7 +40,11 @@ class TasksChecker < Checker4_0
40
40
  end
41
41
 
42
42
  def check_NodeDefinition(o)
43
- illegalTasksExpression(o)
43
+ if in_ApplyExpression?
44
+ super(o)
45
+ else
46
+ illegalTasksExpression(o)
47
+ end
44
48
  end
45
49
 
46
50
  def check_RelationshipExpression(o)
@@ -330,8 +330,10 @@ class Puppet::Provider::AixObject < Puppet::Provider
330
330
  end
331
331
 
332
332
  def ia_module_args
333
- return [] unless @resource[:ia_load_module]
334
- ["-R", @resource[:ia_load_module].to_s]
333
+ raise ArgumentError, _("Cannot have both 'forcelocal' and 'ia_load_module' at the same time!") if @resource[:ia_load_module] && @resource[:forcelocal]
334
+ return ["-R", @resource[:ia_load_module].to_s] if @resource[:ia_load_module]
335
+ return ["-R", "files"] if @resource[:forcelocal]
336
+ []
335
337
  end
336
338
 
337
339
  def lscmd
@@ -18,6 +18,7 @@ Puppet::Type.type(:group).provide :aix, :parent => Puppet::Provider::AixObject d
18
18
  # Provider features
19
19
  has_features :manages_aix_lam
20
20
  has_features :manages_members
21
+ has_features :manages_local_users_and_groups
21
22
 
22
23
  class << self
23
24
  # Used by the AIX user provider. Returns a hash of:
@@ -2,9 +2,7 @@ require 'puppet/provider/nameservice/objectadd'
2
2
  require 'puppet/util/libuser'
3
3
 
4
4
  Puppet::Type.type(:group).provide :groupadd, :parent => Puppet::Provider::NameService::ObjectAdd do
5
- desc "Group management via `groupadd` and its ilk. The default for most platforms.
6
-
7
- "
5
+ desc "Group management via `groupadd` and its ilk. The default for most platforms."
8
6
 
9
7
  commands :add => "groupadd", :delete => "groupdel", :modify => "groupmod"
10
8
 
@@ -16,7 +14,9 @@ Puppet::Type.type(:group).provide :groupadd, :parent => Puppet::Provider::NameSe
16
14
 
17
15
  optional_commands :localadd => "lgroupadd", :localdelete => "lgroupdel", :localmodify => "lgroupmod"
18
16
 
19
- has_feature :libuser if Puppet.features.libuser?
17
+ has_feature :manages_local_users_and_groups, :manages_members if Puppet.features.libuser?
18
+
19
+ options :members, :flag => '-M', :method => :mem
20
20
 
21
21
  def exists?
22
22
  return !!localgid if @resource.forcelocal?
@@ -28,25 +28,9 @@ Puppet::Type.type(:group).provide :groupadd, :parent => Puppet::Provider::NameSe
28
28
  get(:gid)
29
29
  end
30
30
 
31
- def findgroup(key, value)
32
- group_file = "/etc/group"
33
- group_keys = ['group_name', 'password', 'gid', 'user_list']
34
- index = group_keys.index(key)
35
- File.open(group_file) do |f|
36
- f.each_line do |line|
37
- group = line.split(":")
38
- if group[index] == value
39
- f.close
40
- return group
41
- end
42
- end
43
- end
44
- false
45
- end
46
-
47
31
  def localgid
48
- group = findgroup('group_name', resource[:name])
49
- return group[2] if group
32
+ group = findgroup(:group_name, resource[:name])
33
+ return group[:gid] if group
50
34
  false
51
35
  end
52
36
 
@@ -56,7 +40,7 @@ Puppet::Type.type(:group).provide :groupadd, :parent => Puppet::Provider::NameSe
56
40
  # to ensure consistent behaviour of the useradd provider when
57
41
  # using both useradd and luseradd
58
42
  if not @resource.allowdupe? and @resource.forcelocal?
59
- if @resource.should(:gid) and findgroup('gid', @resource.should(:gid).to_s)
43
+ if @resource.should(:gid) and findgroup(:gid, @resource.should(:gid).to_s)
60
44
  raise(Puppet::Error, _("GID %{resource} already exists, use allowdupe to force group creation") % { resource: @resource.should(:gid).to_s })
61
45
  end
62
46
  elsif @resource.allowdupe? and not @resource.forcelocal?
@@ -65,6 +49,11 @@ Puppet::Type.type(:group).provide :groupadd, :parent => Puppet::Provider::NameSe
65
49
  []
66
50
  end
67
51
 
52
+ def create
53
+ super
54
+ set(:members, @resource[:members]) if @resource[:members]
55
+ end
56
+
68
57
  def addcmd
69
58
  if @resource.forcelocal?
70
59
  cmd = [command(:localadd)]
@@ -86,12 +75,18 @@ Puppet::Type.type(:group).provide :groupadd, :parent => Puppet::Provider::NameSe
86
75
  end
87
76
 
88
77
  def modifycmd(param, value)
89
- if @resource.forcelocal?
78
+ if @resource.forcelocal? || @resource[:members]
90
79
  cmd = [command(:localmodify)]
91
80
  @custom_environment = Puppet::Util::Libuser.getenv
92
81
  else
93
82
  cmd = [command(:modify)]
94
83
  end
84
+
85
+ if param == :members
86
+ value = members_to_s(value)
87
+ purge_members if @resource[:auth_membership] && !members.empty?
88
+ end
89
+
95
90
  cmd << flag(param) << value
96
91
  # TODO the group type only really manages gid, so there are currently no
97
92
  # tests for this behavior
@@ -109,4 +104,37 @@ Puppet::Type.type(:group).provide :groupadd, :parent => Puppet::Provider::NameSe
109
104
  [command(:delete), @resource[:name]]
110
105
  end
111
106
  end
107
+
108
+ def members_insync?(current, should)
109
+ current.uniq.sort == @resource.parameter(:members).actual_should(current, should)
110
+ end
111
+
112
+ def members_to_s(current)
113
+ return '' if current.nil? || !current.kind_of?(Array)
114
+ current.join(',')
115
+ end
116
+
117
+ def purge_members
118
+ localmodify('-m', members_to_s(members), @resource.name)
119
+ end
120
+
121
+ def member_valid?(user)
122
+ !!Etc.getpwnam(user)
123
+ end
124
+
125
+ private
126
+
127
+ def findgroup(key, value)
128
+ group_file = "/etc/group"
129
+ group_keys = [:group_name, :password, :gid, :user_list]
130
+ index = group_keys.index(key)
131
+ @group_content ||= File.read(group_file)
132
+ @group_content.each_line do |line|
133
+ group = line.split(":")
134
+ if group[index] == value
135
+ return Hash[group_keys.zip(group)]
136
+ end
137
+ end
138
+ false
139
+ end
112
140
  end