bolt 0.23.0 → 0.24.0

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

Potentially problematic release.


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

Files changed (192) hide show
  1. checksums.yaml +4 -4
  2. data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +5 -2
  3. data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_query.rb +5 -1
  4. data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +5 -8
  5. data/lib/bolt/applicator.rb +11 -8
  6. data/lib/bolt/boltdir.rb +13 -5
  7. data/lib/bolt/catalog.rb +22 -47
  8. data/lib/bolt/config.rb +1 -26
  9. data/lib/bolt/executor.rb +1 -1
  10. data/lib/bolt/outputter.rb +0 -9
  11. data/lib/bolt/outputter/human.rb +29 -14
  12. data/lib/bolt/outputter/json.rb +12 -1
  13. data/lib/bolt/pal.rb +12 -10
  14. data/lib/bolt/target.rb +0 -6
  15. data/lib/bolt/task.rb +53 -10
  16. data/lib/bolt/transport/base.rb +1 -6
  17. data/lib/bolt/transport/local.rb +11 -13
  18. data/lib/bolt/transport/local/shell.rb +2 -2
  19. data/lib/bolt/transport/ssh.rb +16 -11
  20. data/lib/bolt/transport/winrm.rb +8 -11
  21. data/lib/bolt/version.rb +1 -1
  22. data/lib/bolt_ext/schemas/task.json +12 -5
  23. data/libexec/apply_catalog.rb +3 -1
  24. data/libexec/bolt_catalog +4 -0
  25. data/vendored/puppet/lib/puppet.rb +2 -1
  26. data/vendored/puppet/lib/puppet/application/agent.rb +2 -6
  27. data/vendored/puppet/lib/puppet/application/apply.rb +100 -60
  28. data/vendored/puppet/lib/puppet/application/cert.rb +26 -291
  29. data/vendored/puppet/lib/puppet/application/device.rb +0 -5
  30. data/vendored/puppet/lib/puppet/application/lookup.rb +1 -1
  31. data/vendored/puppet/lib/puppet/application/ssl.rb +133 -0
  32. data/vendored/puppet/lib/puppet/application_support.rb +1 -2
  33. data/vendored/puppet/lib/puppet/configurer.rb +34 -50
  34. data/vendored/puppet/lib/puppet/configurer/downloader.rb +1 -1
  35. data/vendored/puppet/lib/puppet/configurer/plugin_handler.rb +1 -1
  36. data/vendored/puppet/lib/puppet/daemon.rb +1 -1
  37. data/vendored/puppet/lib/puppet/defaults.rb +40 -117
  38. data/vendored/puppet/lib/puppet/face/epp.rb +2 -2
  39. data/vendored/puppet/lib/puppet/face/help.rb +21 -7
  40. data/vendored/puppet/lib/puppet/face/node/clean.rb +14 -10
  41. data/vendored/puppet/lib/puppet/feature/base.rb +7 -23
  42. data/vendored/puppet/lib/puppet/feature/eventlog.rb +1 -1
  43. data/vendored/puppet/lib/puppet/file_serving/base.rb +2 -2
  44. data/vendored/puppet/lib/puppet/file_serving/fileset.rb +1 -1
  45. data/vendored/puppet/lib/puppet/file_serving/metadata.rb +2 -2
  46. data/vendored/puppet/lib/puppet/functions.rb +133 -0
  47. data/vendored/puppet/lib/puppet/functions/eyaml_lookup_key.rb +4 -5
  48. data/vendored/puppet/lib/puppet/functions/filter.rb +7 -6
  49. data/vendored/puppet/lib/puppet/functions/new.rb +37 -53
  50. data/vendored/puppet/lib/puppet/functions/warning.rb +1 -1
  51. data/vendored/puppet/lib/puppet/functions/yaml_data.rb +4 -5
  52. data/vendored/puppet/lib/puppet/gettext/config.rb +1 -1
  53. data/vendored/puppet/lib/puppet/graph.rb +0 -2
  54. data/vendored/puppet/lib/puppet/indirector/catalog/json.rb +14 -3
  55. data/vendored/puppet/lib/puppet/indirector/catalog/yaml.rb +0 -16
  56. data/vendored/puppet/lib/puppet/indirector/certificate/file.rb +0 -1
  57. data/vendored/puppet/lib/puppet/indirector/facts/yaml.rb +4 -2
  58. data/vendored/puppet/lib/puppet/indirector/key/file.rb +1 -6
  59. data/vendored/puppet/lib/puppet/indirector/node/exec.rb +1 -3
  60. data/vendored/puppet/lib/puppet/indirector/node/yaml.rb +0 -6
  61. data/vendored/puppet/lib/puppet/indirector/request.rb +1 -1
  62. data/vendored/puppet/lib/puppet/indirector/ssl_file.rb +3 -44
  63. data/vendored/puppet/lib/puppet/indirector/yaml.rb +4 -4
  64. data/vendored/puppet/lib/puppet/info_service/task_information_service.rb +7 -3
  65. data/vendored/puppet/lib/puppet/loaders.rb +1 -0
  66. data/vendored/puppet/lib/puppet/module/task.rb +198 -29
  67. data/vendored/puppet/lib/puppet/module_tool/applications/unpacker.rb +1 -1
  68. data/vendored/puppet/lib/puppet/network/format_support.rb +13 -8
  69. data/vendored/puppet/lib/puppet/network/formats.rb +93 -2
  70. data/vendored/puppet/lib/puppet/network/http/api/indirected_routes.rb +10 -3
  71. data/vendored/puppet/lib/puppet/node/facts.rb +11 -1
  72. data/vendored/puppet/lib/puppet/parser/catalog_compiler.rb +56 -0
  73. data/vendored/puppet/lib/puppet/parser/compiler.rb +3 -1
  74. data/vendored/puppet/lib/puppet/parser/functions.rb +3 -1
  75. data/vendored/puppet/lib/puppet/parser/functions/filter.rb +1 -1
  76. data/vendored/puppet/lib/puppet/parser/functions/generate.rb +1 -1
  77. data/vendored/puppet/lib/puppet/parser/functions/sprintf.rb +12 -1
  78. data/vendored/puppet/lib/puppet/parser/functions/tagged.rb +1 -4
  79. data/vendored/puppet/lib/puppet/parser/scope.rb +1 -1
  80. data/vendored/puppet/lib/puppet/parser/script_compiler.rb +7 -2
  81. data/vendored/puppet/lib/puppet/pops/evaluator/deferred_resolver.rb +5 -3
  82. data/vendored/puppet/lib/puppet/pops/evaluator/runtime3_converter.rb +23 -4
  83. data/vendored/puppet/lib/puppet/pops/evaluator/runtime3_support.rb +3 -4
  84. data/vendored/puppet/lib/puppet/pops/functions/dispatch.rb +4 -0
  85. data/vendored/puppet/lib/puppet/pops/issues.rb +8 -0
  86. data/vendored/puppet/lib/puppet/pops/loader/loader.rb +2 -2
  87. data/vendored/puppet/lib/puppet/pops/loader/loader_paths.rb +3 -1
  88. data/vendored/puppet/lib/puppet/pops/loader/module_loaders.rb +30 -9
  89. data/vendored/puppet/lib/puppet/pops/loader/ruby_legacy_function_instantiator.rb +62 -0
  90. data/vendored/puppet/lib/puppet/pops/loader/static_loader.rb +0 -1
  91. data/vendored/puppet/lib/puppet/pops/loader/task_instantiator.rb +13 -70
  92. data/vendored/puppet/lib/puppet/pops/loaders.rb +19 -29
  93. data/vendored/puppet/lib/puppet/pops/lookup/hiera_config.rb +1 -1
  94. data/vendored/puppet/lib/puppet/pops/model/model_label_provider.rb +4 -1
  95. data/vendored/puppet/lib/puppet/pops/pcore.rb +10 -33
  96. data/vendored/puppet/lib/puppet/pops/serialization.rb +2 -0
  97. data/vendored/puppet/lib/puppet/pops/serialization/from_data_converter.rb +2 -1
  98. data/vendored/puppet/lib/puppet/pops/serialization/to_data_converter.rb +11 -3
  99. data/vendored/puppet/lib/puppet/pops/serialization/to_stringified_converter.rb +226 -0
  100. data/vendored/puppet/lib/puppet/pops/types/p_object_type.rb +3 -0
  101. data/vendored/puppet/lib/puppet/pops/validation/checker4_0.rb +97 -47
  102. data/vendored/puppet/lib/puppet/pops/validation/validator_factory_4_0.rb +7 -8
  103. data/vendored/puppet/lib/puppet/property/keyvalue.rb +70 -8
  104. data/vendored/puppet/lib/puppet/provider/aix_object.rb +483 -0
  105. data/vendored/puppet/lib/puppet/provider/file/windows.rb +1 -1
  106. data/vendored/puppet/lib/puppet/provider/group/aix.rb +51 -112
  107. data/vendored/puppet/lib/puppet/provider/package/gem.rb +1 -1
  108. data/vendored/puppet/lib/puppet/provider/package/pip.rb +1 -1
  109. data/vendored/puppet/lib/puppet/provider/package/puppet_gem.rb +1 -1
  110. data/vendored/puppet/lib/puppet/provider/package/rpm.rb +1 -1
  111. data/vendored/puppet/lib/puppet/provider/package/windows/package.rb +1 -1
  112. data/vendored/puppet/lib/puppet/provider/package/zypper.rb +1 -1
  113. data/vendored/puppet/lib/puppet/provider/service/systemd.rb +1 -1
  114. data/vendored/puppet/lib/puppet/provider/service/windows.rb +37 -40
  115. data/vendored/puppet/lib/puppet/provider/user/aix.rb +142 -254
  116. data/vendored/puppet/lib/puppet/resource.rb +20 -3
  117. data/vendored/puppet/lib/puppet/resource/catalog.rb +2 -12
  118. data/vendored/puppet/lib/puppet/rest/routes.rb +97 -34
  119. data/vendored/puppet/lib/puppet/settings.rb +1 -1
  120. data/vendored/puppet/lib/puppet/settings/file_setting.rb +1 -1
  121. data/vendored/puppet/lib/puppet/ssl/base.rb +1 -9
  122. data/vendored/puppet/lib/puppet/ssl/certificate_request.rb +1 -13
  123. data/vendored/puppet/lib/puppet/ssl/certificate_request_attributes.rb +1 -1
  124. data/vendored/puppet/lib/puppet/ssl/host.rb +114 -232
  125. data/vendored/puppet/lib/puppet/ssl/key.rb +1 -5
  126. data/vendored/puppet/lib/puppet/ssl/oids.rb +1 -1
  127. data/vendored/puppet/lib/puppet/test/test_helper.rb +0 -4
  128. data/vendored/puppet/lib/puppet/transaction/event.rb +3 -7
  129. data/vendored/puppet/lib/puppet/transaction/persistence.rb +1 -1
  130. data/vendored/puppet/lib/puppet/type/exec.rb +18 -16
  131. data/vendored/puppet/lib/puppet/type/file.rb +3 -3
  132. data/vendored/puppet/lib/puppet/type/file/source.rb +20 -7
  133. data/vendored/puppet/lib/puppet/type/group.rb +3 -5
  134. data/vendored/puppet/lib/puppet/type/notify.rb +1 -1
  135. data/vendored/puppet/lib/puppet/type/package.rb +2 -5
  136. data/vendored/puppet/lib/puppet/type/schedule.rb +1 -1
  137. data/vendored/puppet/lib/puppet/type/service.rb +3 -6
  138. data/vendored/puppet/lib/puppet/type/tidy.rb +1 -1
  139. data/vendored/puppet/lib/puppet/type/user.rb +13 -20
  140. data/vendored/puppet/lib/puppet/util.rb +8 -9
  141. data/vendored/puppet/lib/puppet/util/execution.rb +3 -3
  142. data/vendored/puppet/lib/puppet/util/feature.rb +61 -39
  143. data/vendored/puppet/lib/puppet/util/log/destinations.rb +1 -1
  144. data/vendored/puppet/lib/puppet/util/rdoc.rb +1 -1
  145. data/vendored/puppet/lib/puppet/util/run_mode.rb +1 -1
  146. data/vendored/puppet/lib/puppet/util/storage.rb +1 -1
  147. data/vendored/puppet/lib/puppet/util/suidmanager.rb +7 -5
  148. data/vendored/puppet/lib/puppet/util/tag_set.rb +1 -1
  149. data/vendored/puppet/lib/puppet/util/tagging.rb +1 -1
  150. data/vendored/puppet/lib/puppet/util/windows.rb +18 -2
  151. data/vendored/puppet/lib/puppet/util/windows/adsi.rb +154 -205
  152. data/vendored/puppet/lib/puppet/util/windows/service.rb +770 -0
  153. data/vendored/puppet/lib/puppet/util/yaml.rb +41 -5
  154. data/vendored/puppet/lib/puppet/version.rb +1 -1
  155. data/vendored/puppet/lib/puppet_pal.rb +280 -24
  156. metadata +8 -38
  157. data/lib/bolt/catalog/compiler.rb +0 -48
  158. data/lib/bolt/catalog/loaders.rb +0 -19
  159. data/vendored/puppet/lib/puppet/application/ca.rb +0 -11
  160. data/vendored/puppet/lib/puppet/application/certificate.rb +0 -17
  161. data/vendored/puppet/lib/puppet/application/certificate_request.rb +0 -7
  162. data/vendored/puppet/lib/puppet/application/certificate_revocation_list.rb +0 -7
  163. data/vendored/puppet/lib/puppet/face/ca.rb +0 -266
  164. data/vendored/puppet/lib/puppet/face/certificate.rb +0 -167
  165. data/vendored/puppet/lib/puppet/face/certificate_request.rb +0 -56
  166. data/vendored/puppet/lib/puppet/face/certificate_revocation_list.rb +0 -56
  167. data/vendored/puppet/lib/puppet/graph/random_prioritizer.rb +0 -16
  168. data/vendored/puppet/lib/puppet/graph/title_hash_prioritizer.rb +0 -16
  169. data/vendored/puppet/lib/puppet/indirector/certificate/ca.rb +0 -9
  170. data/vendored/puppet/lib/puppet/indirector/certificate/disabled_ca.rb +0 -22
  171. data/vendored/puppet/lib/puppet/indirector/certificate_request/ca.rb +0 -22
  172. data/vendored/puppet/lib/puppet/indirector/certificate_request/disabled_ca.rb +0 -22
  173. data/vendored/puppet/lib/puppet/indirector/certificate_revocation_list/ca.rb +0 -8
  174. data/vendored/puppet/lib/puppet/indirector/certificate_revocation_list/disabled_ca.rb +0 -22
  175. data/vendored/puppet/lib/puppet/indirector/certificate_revocation_list/file.rb +0 -8
  176. data/vendored/puppet/lib/puppet/indirector/certificate_revocation_list/rest.rb +0 -11
  177. data/vendored/puppet/lib/puppet/indirector/certificate_status.rb +0 -4
  178. data/vendored/puppet/lib/puppet/indirector/certificate_status/file.rb +0 -91
  179. data/vendored/puppet/lib/puppet/indirector/certificate_status/rest.rb +0 -11
  180. data/vendored/puppet/lib/puppet/indirector/key/ca.rb +0 -16
  181. data/vendored/puppet/lib/puppet/indirector/key/disabled_ca.rb +0 -22
  182. data/vendored/puppet/lib/puppet/indirector/ldap.rb +0 -86
  183. data/vendored/puppet/lib/puppet/indirector/node/ldap.rb +0 -275
  184. data/vendored/puppet/lib/puppet/provider/aixobject.rb +0 -392
  185. data/vendored/puppet/lib/puppet/provider/cron/crontab.rb +0 -297
  186. data/vendored/puppet/lib/puppet/ssl/certificate_authority.rb +0 -475
  187. data/vendored/puppet/lib/puppet/ssl/certificate_authority/autosign_command.rb +0 -45
  188. data/vendored/puppet/lib/puppet/ssl/certificate_authority/interface.rb +0 -324
  189. data/vendored/puppet/lib/puppet/ssl/certificate_factory.rb +0 -219
  190. data/vendored/puppet/lib/puppet/ssl/certificate_revocation_list.rb +0 -111
  191. data/vendored/puppet/lib/puppet/ssl/inventory.rb +0 -55
  192. data/vendored/puppet/lib/puppet/type/cron.rb +0 -480
@@ -27,16 +27,15 @@ class ValidatorFactory_4_0 < Factory
27
27
  # Configure each issue that should **not** be an error
28
28
  #
29
29
  # Validate as per the current runtime configuration
30
- p[Issues::RT_NO_STORECONFIGS_EXPORT] = Puppet[:storeconfigs] ? :ignore : :warning
31
- p[Issues::RT_NO_STORECONFIGS] = Puppet[:storeconfigs] ? :ignore : :warning
30
+ p[Issues::RT_NO_STORECONFIGS_EXPORT] = Puppet[:storeconfigs] ? :ignore : :warning
31
+ p[Issues::RT_NO_STORECONFIGS] = Puppet[:storeconfigs] ? :ignore : :warning
32
32
 
33
- p[Issues::FUTURE_RESERVED_WORD] = :deprecation
33
+ p[Issues::FUTURE_RESERVED_WORD] = :deprecation
34
34
 
35
- p[Issues::DUPLICATE_KEY] = Puppet[:strict] == :off ? :ignore : Puppet[:strict]
36
- p[Issues::NAME_WITH_HYPHEN] = :error
37
- p[Issues::EMPTY_RESOURCE_SPECIALIZATION] = :ignore
38
- p[Issues::CLASS_NOT_VIRTUALIZABLE] = Puppet[:strict] == :off ? :warning : Puppet[:strict]
39
- p[Issues::ILLEGAL_DEFINITION_LOCATION] = Puppet[:strict] == :off ? :ignore : Puppet[:strict]
35
+ p[Issues::DUPLICATE_KEY] = Puppet[:strict] == :off ? :ignore : Puppet[:strict]
36
+ p[Issues::NAME_WITH_HYPHEN] = :error
37
+ p[Issues::EMPTY_RESOURCE_SPECIALIZATION] = :ignore
38
+ p[Issues::CLASS_NOT_VIRTUALIZABLE] = Puppet[:strict] == :off ? :warning : Puppet[:strict]
40
39
  p
41
40
  end
42
41
  end
@@ -12,9 +12,20 @@ module Puppet
12
12
  # @todo The node with an important message is not very clear.
13
13
  #
14
14
  class KeyValue < Property
15
+ class << self
16
+ # This is a class-level variable that child properties can override
17
+ # if they wish.
18
+ attr_accessor :log_only_changed_or_new_keys
19
+ end
20
+
21
+ self.log_only_changed_or_new_keys = false
15
22
 
16
23
  def hash_to_key_value_s(hash)
17
- hash.select { |k,v| true }.map { |pair| pair.join(separator) }.join(delimiter)
24
+ if self.class.log_only_changed_or_new_keys
25
+ hash = hash.select { |k, _| @changed_or_new_keys.include?(k) }
26
+ end
27
+
28
+ hash.map { |*pair| pair.join(separator) }.join(delimiter)
18
29
  end
19
30
 
20
31
  def should_to_s(should_value)
@@ -33,11 +44,19 @@ module Puppet
33
44
  @resource[membership] == :inclusive
34
45
  end
35
46
 
36
- def hashify(key_value_array)
37
- #turns string array into a hash
38
- key_value_array.inject({}) do |hash, key_value|
47
+ def hashify_should
48
+ # Puppet casts all should values to arrays. Thus, if the user
49
+ # passed in a hash for our property's should value, the should_value
50
+ # parameter will be a single element array so we just extract our value
51
+ # directly.
52
+ if ! @should.empty? && @should.first.is_a?(Hash)
53
+ return @should.first
54
+ end
55
+
56
+ # Here, should is an array of key/value pairs.
57
+ @should.inject({}) do |hash, key_value|
39
58
  tmp = key_value.split(separator)
40
- hash[tmp[0].intern] = tmp[1]
59
+ hash[tmp[0].strip.intern] = tmp[1]
41
60
  hash
42
61
  end
43
62
  end
@@ -53,11 +72,24 @@ module Puppet
53
72
  def should
54
73
  return nil unless @should
55
74
 
56
- members = hashify(@should)
75
+ members = hashify_should
57
76
  current = process_current_hash(retrieve)
58
77
 
59
78
  #shared keys will get overwritten by members
60
- current.merge(members)
79
+ should_value = current.merge(members)
80
+
81
+ # Figure out the keys that will actually change in our Puppet run.
82
+ # This lets us reduce the verbosity of Puppet's logging for instances
83
+ # of this class when we want to.
84
+ #
85
+ # NOTE: We use ||= here because we only need to compute the
86
+ # changed_or_new_keys once (since this property will only be synced once).
87
+ #
88
+ @changed_or_new_keys ||= should_value.keys.select do |key|
89
+ ! current.key?(key) || current[key] != should_value[key]
90
+ end
91
+
92
+ should_value
61
93
  end
62
94
 
63
95
  # @return [String] Returns a default separator of "="
@@ -84,12 +116,42 @@ module Puppet
84
116
 
85
117
  # Returns true if there is no _is_ value, else returns if _is_ is equal to _should_ using == as comparison.
86
118
  # @return [Boolean] whether the property is in sync or not.
87
- #
88
119
  def insync?(is)
89
120
  return true unless is
90
121
 
91
122
  (is == self.should)
92
123
  end
124
+
125
+ # We only accept an array of key/value pairs (strings), a single
126
+ # key/value pair (string) or a Hash as valid values for our property.
127
+ # Note that for an array property value, the 'value' passed into the
128
+ # block corresponds to the array element.
129
+ validate do |value|
130
+ unless value.is_a?(String) || value.is_a?(Hash)
131
+ raise ArgumentError, _("The %{name} property must be specified as a hash or an array of key/value pairs (strings)!") % { name: name }
132
+ end
133
+
134
+ next if value.is_a?(Hash)
135
+
136
+ unless value.include?("#{separator}")
137
+ raise ArgumentError, _("Key/value pairs must be separated by '%{separator}'") % {separator: separator}
138
+ end
139
+ end
140
+
141
+ # The validate step ensures that our passed-in value is
142
+ # either a String or a Hash. If our value's a string,
143
+ # then nothing else needs to be done. Otherwise, we need
144
+ # to stringify the hash's keys and values to match our
145
+ # internal representation of the property's value.
146
+ munge do |value|
147
+ next value if value.is_a?(String)
148
+
149
+ munged_value = value.to_a.map! do |hash_key, hash_value|
150
+ [hash_key.to_s.strip.to_sym, hash_value.to_s]
151
+ end
152
+
153
+ Hash[munged_value]
154
+ end
93
155
  end
94
156
  end
95
157
  end
@@ -0,0 +1,483 @@
1
+ # Common code for AIX user/group providers.
2
+ class Puppet::Provider::AixObject < Puppet::Provider
3
+ desc "Generic AIX resource provider"
4
+
5
+ # Class representing a MappedObject, which can either be an
6
+ # AIX attribute or a Puppet property. This class lets us
7
+ # write something like:
8
+ #
9
+ # attribute = mappings[:aix_attribute][:uid]
10
+ # attribute.name
11
+ # attribute.convert_property_value(uid)
12
+ #
13
+ # property = mappings[:puppet_property][:id]
14
+ # property.name
15
+ # property.convert_attribute_value(id)
16
+ #
17
+ # NOTE: This is an internal class specific to AixObject. It is
18
+ # not meant to be used anywhere else. That's why we do not have
19
+ # any validation code in here.
20
+ #
21
+ # NOTE: See the comments in the class-level mappings method to
22
+ # understand what we mean by pure and impure conversion functions.
23
+ #
24
+ # NOTE: The 'mapping' code, including this class, could possibly
25
+ # be moved to a separate module so that it can be re-used in some
26
+ # of our other providers. See PUP-9082.
27
+ class MappedObject
28
+ attr_reader :name
29
+
30
+ def initialize(name, conversion_fn, conversion_fn_code)
31
+ @name = name
32
+ @conversion_fn = conversion_fn
33
+ @conversion_fn_code = conversion_fn_code
34
+
35
+ return unless pure_conversion_fn?
36
+
37
+ # Our conversion function is pure, so we can go ahead
38
+ # and define it. This way, we can use this MappedObject
39
+ # at the class-level as well as at the instance-level.
40
+ define_singleton_method(@conversion_fn) do |value|
41
+ @conversion_fn_code.call(value)
42
+ end
43
+ end
44
+
45
+ def pure_conversion_fn?
46
+ @conversion_fn_code.arity == 1
47
+ end
48
+
49
+ # Sets our MappedObject's provider. This only makes sense
50
+ # if it has an impure conversion function. We will call this
51
+ # in the instance-level mappings method after the provider
52
+ # instance has been created to define our conversion function.
53
+ # Note that a MappedObject with an impure conversion function
54
+ # cannot be used at the class level.
55
+ def set_provider(provider)
56
+ define_singleton_method(@conversion_fn) do |value|
57
+ @conversion_fn_code.call(provider, value)
58
+ end
59
+ end
60
+ end
61
+
62
+ class << self
63
+ #-------------
64
+ # Mappings
65
+ # ------------
66
+
67
+ def mappings
68
+ return @mappings if @mappings
69
+
70
+ @mappings = {}
71
+ @mappings[:aix_attribute] = {}
72
+ @mappings[:puppet_property] = {}
73
+
74
+ @mappings
75
+ end
76
+
77
+ # Add a mapping from a Puppet property to an AIX attribute. The info must include:
78
+ #
79
+ # * :puppet_property -- The puppet property corresponding to this attribute
80
+ # * :aix_attribute -- The AIX attribute corresponding to this attribute. Defaults
81
+ # to puppet_property if this is not provided.
82
+ # * :property_to_attribute -- A lambda that converts a Puppet Property to an AIX attribute
83
+ # value. Defaults to the identity function if not provided.
84
+ # * :attribute_to_property -- A lambda that converts an AIX attribute to a Puppet property.
85
+ # Defaults to the identity function if not provided.
86
+ #
87
+ # NOTE: The lambdas for :property_to_attribute or :attribute_to_property can be 'pure'
88
+ # or 'impure'. A 'pure' lambda is one that needs only the value to do the conversion,
89
+ # while an 'impure' lambda is one that requires the provider instance along with the
90
+ # value. 'Pure' lambdas have the interface 'do |value| ...' while 'impure' lambdas have
91
+ # the interface 'do |provider, value| ...'.
92
+ #
93
+ # NOTE: 'Impure' lambdas are useful in case we need to generate more specific error
94
+ # messages or pass-in instance-specific command-line arguments.
95
+ def mapping(info = {})
96
+ identity_fn = lambda { |x| x }
97
+ info[:aix_attribute] ||= info[:puppet_property]
98
+ info[:property_to_attribute] ||= identity_fn
99
+ info[:attribute_to_property] ||= identity_fn
100
+
101
+ mappings[:aix_attribute][info[:puppet_property]] = MappedObject.new(
102
+ info[:aix_attribute],
103
+ :convert_property_value,
104
+ info[:property_to_attribute]
105
+ )
106
+ mappings[:puppet_property][info[:aix_attribute]] = MappedObject.new(
107
+ info[:puppet_property],
108
+ :convert_attribute_value,
109
+ info[:attribute_to_property]
110
+ )
111
+ end
112
+
113
+ # Creates a mapping from a purely numeric Puppet property to
114
+ # an attribute
115
+ def numeric_mapping(info = {})
116
+ property = info[:puppet_property]
117
+
118
+ # We have this validation here b/c not all numeric properties
119
+ # handle this at the property level (e.g. like the UID). Given
120
+ # that, we might as well go ahead and do this validation for all
121
+ # of our numeric properties. Doesn't hurt.
122
+ info[:property_to_attribute] = lambda do |value|
123
+ unless value.is_a?(Integer)
124
+ raise ArgumentError, _("Invalid value %{value}: %{property} must be an Integer!") % { value: value, property: property }
125
+ end
126
+
127
+ value.to_s
128
+ end
129
+
130
+ # AIX will do the right validation to ensure numeric attributes
131
+ # can't be set to non-numeric values, so no need for the extra clutter.
132
+ info[:attribute_to_property] = lambda do |value|
133
+ value.to_i
134
+ end
135
+
136
+ mapping(info)
137
+ end
138
+
139
+ #-------------
140
+ # Useful Class Methods
141
+ # ------------
142
+
143
+ # Defines the getter and setter methods for each Puppet property that's mapped
144
+ # to an AIX attribute. We define only a getter for the :attributes property.
145
+ #
146
+ # Provider subclasses should call this method after they've defined all of
147
+ # their <puppet_property> => <aix_attribute> mappings.
148
+ def mk_resource_methods
149
+ # Define the Getter methods for each of our properties + the attributes
150
+ # property
151
+ properties = [:attributes]
152
+ properties += mappings[:aix_attribute].keys
153
+ properties.each do |property|
154
+ # Define the getter
155
+ define_method(property) do
156
+ get(property)
157
+ end
158
+
159
+ # We have a custom setter for the :attributes property,
160
+ # so no need to define it.
161
+ next if property == :attributes
162
+
163
+ # Define the setter
164
+ define_method("#{property}=".to_sym) do |value|
165
+ set(property, value)
166
+ end
167
+ end
168
+ end
169
+
170
+ # This helper splits a list separated by sep into its corresponding
171
+ # items. Note that a key precondition here is that none of the items
172
+ # in the list contain sep.
173
+ #
174
+ # Let A be the return value. Then one of our postconditions is:
175
+ # A.join(sep) == list
176
+ #
177
+ # NOTE: This function is only used by the parse_colon_separated_list
178
+ # function below. It is meant to be an inner lambda. The reason it isn't
179
+ # here is so we avoid having to create a proc. object for the split_list
180
+ # lambda each time parse_colon_separated_list is invoked. This will happen
181
+ # quite often since it is used at the class level and at the instance level.
182
+ # Since this function is meant to be an inner lambda and thus not exposed
183
+ # anywhere else, we do not have any unit tests for it. These test cases are
184
+ # instead covered by the unit tests for parse_colon_separated_list
185
+ def split_list(list, sep)
186
+ return [""] if list.empty?
187
+
188
+ list.split(sep, -1)
189
+ end
190
+
191
+ # Parses a colon-separated list. Example includes something like:
192
+ # <item1>:<item2>:<item3>:<item4>
193
+ #
194
+ # Returns an array of the parsed items, e.g.
195
+ # [ <item1>, <item2>, <item3>, <item4> ]
196
+ #
197
+ # Note that colons inside items are escaped by #!
198
+ def parse_colon_separated_list(colon_list)
199
+ # ALGORITHM:
200
+ # Treat the colon_list as a list separated by '#!:' We will get
201
+ # something like:
202
+ # [ <chunk1>, <chunk2>, ... <chunkn> ]
203
+ #
204
+ # Each chunk is now a list separated by ':' and none of the items
205
+ # in each chunk contains an escaped ':'. Now, split each chunk on
206
+ # ':' to get:
207
+ # [ [<piece11>, ..., <piece1n>], [<piece21>, ..., <piece2n], ... ]
208
+ #
209
+ # Now note that <item1> = <piece11>, <item2> = <piece12> in our original
210
+ # list, and that <itemn> = <piece1n>#!:<piece21>. This is the main idea
211
+ # behind what our inject method is trying to do at the end, except that
212
+ # we replace '#!:' with ':' since the colons are no longer escaped.
213
+ chunks = split_list(colon_list, '#!:')
214
+ chunks.map! { |chunk| split_list(chunk, ':') }
215
+
216
+ chunks.inject do |accum, chunk|
217
+ left = accum.pop
218
+ right = chunk.shift
219
+
220
+ accum.push("#{left}:#{right}")
221
+ accum += chunk
222
+
223
+ accum
224
+ end
225
+ end
226
+
227
+ # Parses the AIX objects from the command output, returning an array of
228
+ # hashes with each hash having the following schema:
229
+ # {
230
+ # :name => <object_name>
231
+ # :attributes => <object_attributes>
232
+ # }
233
+ #
234
+ # Output should be of the form
235
+ # #name:<attr1>:<attr2> ...
236
+ # <name>:<value1>:<value2> ...
237
+ # #name:<attr1>:<attr2> ...
238
+ # <name>:<value1>:<value2> ...
239
+ #
240
+ # NOTE: We need to parse the colon-formatted output in case we have
241
+ # space-separated attributes (e.g. 'gecos'). ":" characters are escaped
242
+ # with a "#!".
243
+ def parse_aix_objects(output)
244
+ # Object names cannot begin with '#', so we are safe to
245
+ # split individual users this way. We do not have to worry
246
+ # about an empty list either since there is guaranteed to be
247
+ # at least one instance of an AIX object (e.g. at least one
248
+ # user or one group on the system).
249
+ _, *objects = output.chomp.split(/^#/)
250
+
251
+ objects.map! do |object|
252
+ attributes_line, values_line = object.chomp.split("\n")
253
+
254
+ attributes = parse_colon_separated_list(attributes_line.chomp)
255
+ attributes.map!(&:to_sym)
256
+
257
+ values = parse_colon_separated_list(values_line.chomp)
258
+
259
+ attributes_hash = Hash[attributes.zip(values)]
260
+
261
+ object_name = attributes_hash.delete(:name)
262
+
263
+ Hash[[[:name, object_name.to_s], [:attributes, attributes_hash]]]
264
+ end
265
+
266
+ objects
267
+ end
268
+
269
+ # Lists all instances of the given object, taking in an optional set
270
+ # of ia_module arguments. Returns an array of hashes, each hash
271
+ # having the schema
272
+ # {
273
+ # :name => <object_name>
274
+ # :id => <object_id>
275
+ # }
276
+ def list_all(ia_module_args = [])
277
+ cmd = [command(:list), '-c', *ia_module_args, '-a', 'id', 'ALL']
278
+ parse_aix_objects(execute(cmd)).to_a.map do |object|
279
+ name = object[:name]
280
+ id = object[:attributes].delete(:id)
281
+
282
+ Hash[[[:name, name,],[:id, id]]]
283
+ end
284
+ end
285
+
286
+ #-------------
287
+ # Provider API
288
+ # ------------
289
+
290
+ def instances
291
+ list_all.to_a.map! do |object|
292
+ new({ :name => object[:name] })
293
+ end
294
+ end
295
+ end
296
+
297
+ # Instantiate our mappings. These need to be at the instance-level
298
+ # since some of our mapped objects may have impure conversion functions
299
+ # that need our provider instance.
300
+ def mappings
301
+ return @mappings if @mappings
302
+
303
+ @mappings = {}
304
+ self.class.mappings.each do |type, mapped_objects|
305
+ @mappings[type] = {}
306
+ mapped_objects.each do |input, mapped_object|
307
+ if mapped_object.pure_conversion_fn?
308
+ # Our mapped_object has a pure conversion function so we
309
+ # can go ahead and use it as-is.
310
+ @mappings[type][input] = mapped_object
311
+ next
312
+ end
313
+
314
+ # Otherwise, we need to dup it and set its provider to our
315
+ # provider instance. The dup is necessary so that we do not
316
+ # touch the class-level mapped object.
317
+ @mappings[type][input] = mapped_object.dup
318
+ @mappings[type][input].set_provider(self)
319
+ end
320
+ end
321
+
322
+ @mappings
323
+ end
324
+
325
+ # Converts the given attributes hash to CLI args.
326
+ def attributes_to_args(attributes)
327
+ attributes.map do |attribute, value|
328
+ "#{attribute}=#{value}"
329
+ end
330
+ end
331
+
332
+ def ia_module_args
333
+ return [] unless @resource[:ia_load_module]
334
+ ["-R", @resource[:ia_load_module].to_s]
335
+ end
336
+
337
+ def lscmd
338
+ [self.class.command(:list), '-c'] + ia_module_args + [@resource[:name]]
339
+ end
340
+
341
+ def addcmd(attributes)
342
+ attribute_args = attributes_to_args(attributes)
343
+ [self.class.command(:add)] + ia_module_args + attribute_args + [@resource[:name]]
344
+ end
345
+
346
+ def deletecmd
347
+ [self.class.command(:delete)] + ia_module_args + [@resource[:name]]
348
+ end
349
+
350
+ def modifycmd(new_attributes)
351
+ attribute_args = attributes_to_args(new_attributes)
352
+ [self.class.command(:modify)] + ia_module_args + attribute_args + [@resource[:name]]
353
+ end
354
+
355
+ # Modifies the AIX object by setting its new attributes.
356
+ def modify_object(new_attributes)
357
+ execute(modifycmd(new_attributes))
358
+ object_info(true)
359
+ end
360
+
361
+ # Gets a Puppet property's value from object_info
362
+ def get(property)
363
+ return :absent unless exists?
364
+ object_info[property] || :absent
365
+ end
366
+
367
+ # Sets a mapped Puppet property's value.
368
+ def set(property, value)
369
+ aix_attribute = mappings[:aix_attribute][property]
370
+ modify_object(
371
+ { aix_attribute.name => aix_attribute.convert_property_value(value) }
372
+ )
373
+ rescue Puppet::ExecutionFailure => detail
374
+ raise Puppet::Error, _("Could not set %{property} on %{resource}[%{name}]: %{detail}") % { property: property, resource: @resource.class.name, name: @resource.name, detail: detail }, detail.backtrace
375
+ end
376
+
377
+ # This routine validates our new attributes property value to ensure
378
+ # that it does not contain any Puppet properties.
379
+ def validate_new_attributes(new_attributes)
380
+ # Gather all of the <puppet property>, <aix attribute> conflicts to print
381
+ # them all out when we create our error message. This makes it easy for the
382
+ # user to update their manifest based on our error message.
383
+ conflicts = {}
384
+ mappings[:aix_attribute].each do |property, aix_attribute|
385
+ next unless new_attributes.key?(aix_attribute.name)
386
+
387
+ conflicts[:properties] ||= []
388
+ conflicts[:properties].push(property)
389
+
390
+ conflicts[:attributes] ||= []
391
+ conflicts[:attributes].push(aix_attribute.name)
392
+ end
393
+
394
+ return if conflicts.empty?
395
+
396
+ properties, attributes = conflicts.keys.map do |key|
397
+ conflicts[key].map! { |name| "'#{name}'" }.join(', ')
398
+ end
399
+
400
+ detail = _("attributes is setting the %{properties} properties via. the %{attributes} attributes, respectively! Please specify these property values in the resource declaration instead.") % { properties: properties, attributes: attributes }
401
+
402
+ raise Puppet::Error, _("Could not set attributes on %{resource}[%{name}]: %{detail}") % { resource: @resource.class.name, name: @resource.name, detail: detail }
403
+ end
404
+
405
+ # Modifies the attribute property. Note we raise an error if the user specified
406
+ # an AIX attribute corresponding to a Puppet property.
407
+ def attributes=(new_attributes)
408
+ validate_new_attributes(new_attributes)
409
+ modify_object(new_attributes)
410
+ rescue Puppet::ExecutionFailure => detail
411
+ raise Puppet::Error, _("Could not set attributes on %{resource}[%{name}]: %{detail}") % { resource: @resource.class.name, name: @resource.name, detail: detail }, detail.backtrace
412
+ end
413
+
414
+ # Collects the current property values of all mapped properties +
415
+ # the attributes property.
416
+ def object_info(refresh = false)
417
+ return @object_info if @object_info && ! refresh
418
+ @object_info = nil
419
+
420
+ begin
421
+ output = execute(lscmd)
422
+ rescue Puppet::ExecutionFailure
423
+ Puppet.debug(_("aix.object_info(): Could not find %{resource}[%{name}]") % { resource: @resource.class.name, name: @resource.name })
424
+
425
+ return @object_info
426
+ end
427
+
428
+ # If lscmd succeeds, then output will contain our object's information.
429
+ # Thus, .parse_aix_objects will always return a single element array.
430
+ aix_attributes = self.class.parse_aix_objects(output).first[:attributes]
431
+ aix_attributes.each do |attribute, value|
432
+ @object_info ||= {}
433
+
434
+ # If our attribute has a Puppet property, then we store that. Else, we store it as part
435
+ # of our :attributes property hash
436
+ if (property = mappings[:puppet_property][attribute])
437
+ @object_info[property.name] = property.convert_attribute_value(value)
438
+ else
439
+ @object_info[:attributes] ||= {}
440
+ @object_info[:attributes][attribute] = value
441
+ end
442
+ end
443
+
444
+ @object_info
445
+ end
446
+
447
+ #-------------
448
+ # Methods that manage the ensure property
449
+ # ------------
450
+
451
+ # Check that the AIX object exists
452
+ def exists?
453
+ ! object_info.nil?
454
+ end
455
+
456
+ # Creates a new instance of the resource
457
+ def create
458
+ attributes = @resource.should(:attributes) || {}
459
+ validate_new_attributes(attributes)
460
+
461
+ mappings[:aix_attribute].each do |property, aix_attribute|
462
+ property_should = @resource.should(property)
463
+ next if property_should.nil?
464
+ attributes[aix_attribute.name] = aix_attribute.convert_property_value(property_should)
465
+ end
466
+
467
+ execute(addcmd(attributes))
468
+ rescue Puppet::ExecutionFailure => detail
469
+ raise Puppet::Error, _("Could not create %{resource} %{name}: %{detail}") % { resource: @resource.class.name, name: @resource.name, detail: detail }, detail.backtrace
470
+ end
471
+
472
+ # Deletes this instance resource
473
+ def delete
474
+ execute(deletecmd)
475
+
476
+ # Recollect the object info so that our current properties reflect
477
+ # the actual state of the system. Otherwise, puppet resource reports
478
+ # the wrong info. at the end. Note that this should return nil.
479
+ object_info(true)
480
+ rescue Puppet::ExecutionFailure => detail
481
+ raise Puppet::Error, _("Could not delete %{resource} %{name}: %{detail}") % { resource: @resource.class.name, name: @resource.name, detail: detail }, detail.backtrace
482
+ end
483
+ end