Class: ChefAPI::Resource::Base
- Inherits:
-
Object
- Object
- ChefAPI::Resource::Base
- Extended by:
- Enumerable
- Defined in:
- lib/chef-api/resources/base.rb
Direct Known Subclasses
Client, Cookbook, CookbookVersion, DataBag, DataBagItem, Environment, Node, Organization, PartialSearch, Principal, Role, Search, User
Instance Attribute Summary collapse
-
#associations ⇒ Hash
readonly
The list of associations.
Class Method Summary collapse
-
.all ⇒ Array<Resource::Base>
Return an array of all resources in the collection.
-
.build(attributes = {}, prefix = {}) ⇒ Object
Build a new resource from the given attributes.
-
.classname ⇒ String
The name for this resource, minus the parent module.
-
.collection(prefix = {}) ⇒ Array<Resource::Base>
The full collection list.
-
.collection_path(value = UNSET) ⇒ Symbol, String
Get or set the name of the remote resource collection.
-
.connection ⇒ ChefAPI::Connection
The current connection object.
-
.count(prefix = {}) ⇒ Fixnum
(also: size)
The total number of reosurces in the collection.
-
.create(attributes = {}, prefix = {}) ⇒ Resource::Base
Create a new resource and save it to the Chef Server, raising any exceptions that might occur.
-
.delete(id, prefix = {}) ⇒ true
Delete the remote resource from the Chef Sserver.
-
.destroy(id, prefix = {}) ⇒ Base?
Destroy a record with the given id.
-
.destroy_all(prefix = {}) ⇒ Array<Base>
Delete all remote resources of the given type from the Chef Server.
-
.each(prefix = {}, &block) ⇒ Object
(Lazy) iterate over each item in the collection, yielding the fully- built resource object.
-
.exists?(id, prefix = {}) ⇒ Boolean
Check if the given resource exists on the Chef Server.
-
.expanded_collection_path(prefix = {}) ⇒ String
Expand the collection path, “interpolating” any parameters.
-
.fetch(id, prefix = {}) ⇒ Resource::Base?
Fetch a single resource in the remote collection.
-
.from_file(path) ⇒ Object
Load the given resource from it’s on-disk equivalent.
-
.from_json(response, prefix = {}) ⇒ Resource::Base
Construct the object from a JSON response.
- .from_url(url, prefix = {}) ⇒ Object
-
.has_many(method, options = {}) ⇒ Object
Create a nested relationship collection.
-
.inspect ⇒ String
The detailed string representation of this class, including the full schema definition.
-
.list(prefix = {}) ⇒ Array<String>
Get the “list” of items in this resource.
-
.post(body, prefix = {}) ⇒ String
Make an authenticated HTTP POST request using the connection object.
-
.protect(*ids) ⇒ Object
Protect one or more resources from being altered by the user.
- .protected_resources ⇒ Object
-
.put(id, body, prefix = {}) ⇒ String
Perform a PUT request to the Chef Server against the given resource or resource identifier.
-
.resource_path(id, prefix = {}) ⇒ String
The path to an individual resource.
-
.schema(&block) ⇒ Schema
Get or set the schema for the remote resource.
-
.to_s ⇒ String
The string representation of this class.
-
.type ⇒ String
The type of this resource.
-
.update(id, attributes = {}, prefix = {}) ⇒ Resource::Base
Perform a PUT request to the Chef Server for the current resource, updating the given parameters.
Instance Method Summary collapse
-
#_attributes ⇒ Hash<Symbol, Object>
The list of attributes on this resource.
- #_prefix ⇒ Object
-
#attribute?(key) ⇒ Boolean
Determine if this resource has the given attribute.
-
#destroy ⇒ self
Remove the resource from the Chef Server.
-
#diff ⇒ Hash
Calculate a differential of the attributes on the local resource with it’s remote Chef Server counterpart.
-
#dirty? ⇒ Boolean
Check if the local resource is in sync with the remote Chef Server.
-
#errors ⇒ ErrorCollection
The collection of errors on the resource.
-
#id ⇒ Object
The unique id for this resource.
-
#ignore_attribute?(key) ⇒ Boolean
Determine if a given attribute should be ignored.
-
#initialize(attributes = {}, prefix = {}) {|_self| ... } ⇒ Base
constructor
Initialize a new resource with the given attributes.
-
#inspect ⇒ String
Custom inspect method for easier readability.
-
#new_resource? ⇒ Boolean
Check if this resource exists on the remote Chef Server.
-
#primary_key ⇒ Symbol
The primary key for the resource.
-
#protected? ⇒ Boolean
Determine if this current resource is protected.
-
#reload! ⇒ self
Reload (or reset) this object using the values currently stored on the remote server.
-
#resource_path ⇒ String
The URL for this resource on the Chef Server.
-
#save ⇒ Boolean
Commit the resource and any changes to the remote Chef Server.
-
#save! ⇒ Boolean
Commit the resource and any changes to the remote Chef Server.
-
#to_hash ⇒ Hash
The hash representation of this resource.
-
#to_json ⇒ String
The JSON serialization of this resource.
-
#to_s ⇒ String
Custom to_s method for easier readability.
-
#update(attributes = {}) ⇒ self
Update a subset of attributes on the current resource.
-
#update_attribute(key, value) ⇒ Object
Update a single attribute in the attributes hash.
-
#valid? ⇒ Boolean
Determine if the current resource is valid.
-
#validate! ⇒ Boolean
Run all of this resource’s validations, raising an exception if any validations fail.
-
#validators ⇒ Array<~Validator::Base>
The list of validators for this resource.
Constructor Details
#initialize(attributes = {}, prefix = {}) {|_self| ... } ⇒ Base
Initialize a new resource with the given attributes. These attributes are merged with the default values from the schema. Any attributes that aren’t defined in the schema are silently ignored for security purposes.
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 |
# File 'lib/chef-api/resources/base.rb', line 556 def initialize(attributes = {}, prefix = {}) @schema = self.class.schema.dup @schema.load_flavor(self.class.connection.flavor) @associations = {} @_prefix = prefix # Define a getter and setter method for each attribute in the schema _attributes.each do |key, value| define_singleton_method(key) { _attributes[key] } define_singleton_method("#{key}=") { |value| update_attribute(key, value) } end attributes.each do |key, value| unless ignore_attribute?(key) update_attribute(key, value) end end yield self if block_given? end |
Instance Attribute Details
#associations ⇒ Hash (readonly)
The list of associations.
534 535 536 |
# File 'lib/chef-api/resources/base.rb', line 534 def associations @associations end |
Class Method Details
.all ⇒ Array<Resource::Base>
391 392 393 |
# File 'lib/chef-api/resources/base.rb', line 391 def all entries end |
.build(attributes = {}, prefix = {}) ⇒ Object
Build a new resource from the given attributes.
273 274 275 |
# File 'lib/chef-api/resources/base.rb', line 273 def build(attributes = {}, prefix = {}) new(attributes, prefix) end |
.classname ⇒ String
The name for this resource, minus the parent module.
446 447 448 |
# File 'lib/chef-api/resources/base.rb', line 446 def classname name.split('::')[1..-1].join('::') end |
.collection(prefix = {}) ⇒ Array<Resource::Base>
The full collection list.
471 472 473 |
# File 'lib/chef-api/resources/base.rb', line 471 def collection(prefix = {}) connection.get((prefix)) end |
.collection_path(value = UNSET) ⇒ Symbol, String
Get or set the name of the remote resource collection. This is most likely the remote API endpoint (such as /clients
), without the leading slash.
116 117 118 119 120 121 122 123 |
# File 'lib/chef-api/resources/base.rb', line 116 def collection_path(value = UNSET) if value != UNSET @collection_path = value.to_s else @collection_path || raise(ArgumentError, "collection_path not set for #{self.class}") end end |
.connection ⇒ ChefAPI::Connection
The current connection object.
524 525 526 |
# File 'lib/chef-api/resources/base.rb', line 524 def connection Thread.current['chefapi.connection'] || ChefAPI.connection end |
.count(prefix = {}) ⇒ Fixnum Also known as: size
The total number of reosurces in the collection.
378 379 380 |
# File 'lib/chef-api/resources/base.rb', line 378 def count(prefix = {}) collection(prefix).size end |
.create(attributes = {}, prefix = {}) ⇒ Resource::Base
Create a new resource and save it to the Chef Server, raising any exceptions that might occur. This method will save the resource back to the Chef Server, raising any validation errors that occur.
293 294 295 296 297 298 299 300 301 302 |
# File 'lib/chef-api/resources/base.rb', line 293 def create(attributes = {}, prefix = {}) resource = build(attributes, prefix) unless resource.new_resource? raise Error::ResourceAlreadyExists.new end resource.save! resource end |
.delete(id, prefix = {}) ⇒ true
Delete the remote resource from the Chef Sserver.
174 175 176 177 178 179 180 |
# File 'lib/chef-api/resources/base.rb', line 174 def delete(id, prefix = {}) path = resource_path(id, prefix) connection.delete(path) true rescue Error::HTTPNotFound true end |
.destroy(id, prefix = {}) ⇒ Base?
Destroy a record with the given id.
213 214 215 216 217 218 219 |
# File 'lib/chef-api/resources/base.rb', line 213 def destroy(id, prefix = {}) resource = fetch(id, prefix) return nil if resource.nil? resource.destroy resource end |
.destroy_all(prefix = {}) ⇒ Array<Base>
Delete all remote resources of the given type from the Chef Server
229 230 231 |
# File 'lib/chef-api/resources/base.rb', line 229 def destroy_all(prefix = {}) map { |resource| resource.destroy } end |
.each(prefix = {}, &block) ⇒ Object
(Lazy) iterate over each item in the collection, yielding the fully- built resource object. This method, coupled with the Enumerable module, provides us with other methods like first
and map
.
364 365 366 367 368 369 370 371 |
# File 'lib/chef-api/resources/base.rb', line 364 def each(prefix = {}, &block) collection(prefix).each do |resource, path| response = connection.get(path) result = from_json(response, prefix) block.call(result) if block end end |
.exists?(id, prefix = {}) ⇒ Boolean
Check if the given resource exists on the Chef Server.
314 315 316 |
# File 'lib/chef-api/resources/base.rb', line 314 def exists?(id, prefix = {}) !fetch(id, prefix).nil? end |
.expanded_collection_path(prefix = {}) ⇒ String
Expand the collection path, “interpolating” any parameters. This syntax is heavily borrowed from Rails and it will make more sense by looking at an example.
506 507 508 509 510 511 512 513 514 515 516 517 |
# File 'lib/chef-api/resources/base.rb', line 506 def (prefix = {}) collection_path.gsub(/:\w+/) do |param| key = param.delete(':') value = prefix[key] || prefix[key.to_sym] if value.nil? raise Error::MissingURLParameter.new(param: key) end URI.escape(value) end.sub(/^\//, '') # Remove leading slash end |
.fetch(id, prefix = {}) ⇒ Resource::Base?
Fetch a single resource in the remote collection.
248 249 250 251 252 253 254 255 256 |
# File 'lib/chef-api/resources/base.rb', line 248 def fetch(id, prefix = {}) return nil if id.nil? path = resource_path(id, prefix) response = connection.get(path) from_json(response, prefix) rescue Error::HTTPNotFound nil end |
.from_file(path) ⇒ Object
Load the given resource from it’s on-disk equivalent. This action only makes sense for some resources, and must be defined on a per-resource basis, since the logic varies between resources.
15 16 17 |
# File 'lib/chef-api/resources/base.rb', line 15 def from_file(path) raise Error::AbstractMethod.new(method: 'Resource::Base#from_file') end |
.from_json(response, prefix = {}) ⇒ Resource::Base
Construct the object from a JSON response. This method actually just delegates to the new
method, but it removes some marshall data and whatnot from the response first.
406 407 408 409 410 411 |
# File 'lib/chef-api/resources/base.rb', line 406 def from_json(response, prefix = {}) response.delete('json_class') response.delete('chef_type') new(response, prefix) end |
.from_url(url, prefix = {}) ⇒ Object
22 23 24 |
# File 'lib/chef-api/resources/base.rb', line 22 def from_url(url, prefix = {}) from_json(connection.get(url), prefix) end |
.has_many(method, options = {}) ⇒ Object
Create a nested relationship collection. The associated collection is cached on the class, reducing API requests.
80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/chef-api/resources/base.rb', line 80 def has_many(method, = {}) class_name = [:class_name] || "Resource::#{Util.camelize(method).sub(/s$/, '')}" rest_endpoint = [:rest_endpoint] || method class_eval <<-EOH, __FILE__, __LINE__ + 1 def #{method} associations[:#{method}] ||= Resource::CollectionProxy.new(self, #{class_name}, '#{rest_endpoint}') end EOH end |
.inspect ⇒ String
The detailed string representation of this class, including the full schema definition.
434 435 436 |
# File 'lib/chef-api/resources/base.rb', line 434 def inspect "#{classname}(#{schema.attributes.keys.join(', ')})" end |
.list(prefix = {}) ⇒ Array<String>
Get the “list” of items in this resource. This list contains the primary keys of all of the resources in this collection. This method is useful in CLI applications, because it only makes a single API request to gather this data.
196 197 198 199 |
# File 'lib/chef-api/resources/base.rb', line 196 def list(prefix = {}) path = (prefix) connection.get(path).keys.sort end |
.post(body, prefix = {}) ⇒ String
Make an authenticated HTTP POST request using the connection object. This method returns a new object representing the response from the server, which should be merged with an existing object’s attributes to reflect the newest state of the resource.
139 140 141 142 |
# File 'lib/chef-api/resources/base.rb', line 139 def post(body, prefix = {}) path = (prefix) connection.post(path, body) end |
.protect(*ids) ⇒ Object
Protect one or more resources from being altered by the user. This is useful if there’s an admin client or magical cookbook that you don’t want users to modify.
64 65 66 |
# File 'lib/chef-api/resources/base.rb', line 64 def protect(*ids) ids.each { |id| protected_resources << id } end |
.protected_resources ⇒ Object
95 96 97 |
# File 'lib/chef-api/resources/base.rb', line 95 def protected_resources @protected_resources ||= [] end |
.put(id, body, prefix = {}) ⇒ String
Perform a PUT request to the Chef Server against the given resource or resource identifier. The resource will be partially updated (this method doubles as PATCH) with the given parameters.
160 161 162 163 |
# File 'lib/chef-api/resources/base.rb', line 160 def put(id, body, prefix = {}) path = resource_path(id, prefix) connection.put(path, body) end |
.resource_path(id, prefix = {}) ⇒ String
The path to an individual resource.
484 485 486 |
# File 'lib/chef-api/resources/base.rb', line 484 def resource_path(id, prefix = {}) [(prefix), id].join('/') end |
.schema(&block) ⇒ Schema
Get or set the schema for the remote resource. You probably only want to call schema once with a block, because it will overwrite the existing schema (meaning entries are not merged). If a block is given, a new schema object is created, otherwise the current one is returned.
42 43 44 45 46 47 48 |
# File 'lib/chef-api/resources/base.rb', line 42 def schema(&block) if block @schema = Schema.new(&block) else @schema end end |
.to_s ⇒ String
The string representation of this class.
421 422 423 |
# File 'lib/chef-api/resources/base.rb', line 421 def to_s classname end |
.type ⇒ String
The type of this resource.
458 459 460 |
# File 'lib/chef-api/resources/base.rb', line 458 def type Util.underscore(name.split('::').last).gsub('_', ' ') end |
.update(id, attributes = {}, prefix = {}) ⇒ Resource::Base
Perform a PUT request to the Chef Server for the current resource, updating the given parameters. The parameters may be a full or partial resource update, as supported by the Chef Server.
336 337 338 339 340 341 342 343 344 345 |
# File 'lib/chef-api/resources/base.rb', line 336 def update(id, attributes = {}, prefix = {}) resource = fetch(id, prefix) unless resource raise Error::ResourceNotFound.new(type: type, id: id) end resource.update(attributes).save resource end |
Instance Method Details
#_attributes ⇒ Hash<Symbol, Object>
The list of attributes on this resource.
609 610 611 |
# File 'lib/chef-api/resources/base.rb', line 609 def _attributes @_attributes ||= {}.merge(@schema.attributes) end |
#_prefix ⇒ Object
600 601 602 |
# File 'lib/chef-api/resources/base.rb', line 600 def _prefix @_prefix end |
#attribute?(key) ⇒ Boolean
Determine if this resource has the given attribute.
622 623 624 |
# File 'lib/chef-api/resources/base.rb', line 622 def attribute?(key) _attributes.has_key?(key.to_sym) end |
#destroy ⇒ self
Remove the resource from the Chef Server.
718 719 720 721 |
# File 'lib/chef-api/resources/base.rb', line 718 def destroy self.class.delete(id, _prefix) self end |
#diff ⇒ Hash
This is a VERY expensive operation - use it sparringly!
Calculate a differential of the attributes on the local resource with it’s remote Chef Server counterpart.
862 863 864 865 866 867 868 869 870 871 872 873 |
# File 'lib/chef-api/resources/base.rb', line 862 def diff diff = {} remote = self.class.fetch(id, _prefix) || self.class.new({}, _prefix) remote._attributes.each do |key, value| unless _attributes[key] == value diff[key] = { local: _attributes[key], remote: value } end end diff end |
#dirty? ⇒ Boolean
Check if the local resource is in sync with the remote Chef Server. When a remote resource is updated, ChefAPI has no way of knowing it’s cached resources are dirty unless additional requests are made against the remote Chef Server and diffs are compared.
841 842 843 |
# File 'lib/chef-api/resources/base.rb', line 841 def dirty? new_resource? || !diff.empty? end |
#errors ⇒ ErrorCollection
The collection of errors on the resource.
907 908 909 |
# File 'lib/chef-api/resources/base.rb', line 907 def errors @errors ||= ErrorCollection.new end |
#id ⇒ Object
The unique id for this resource.
593 594 595 |
# File 'lib/chef-api/resources/base.rb', line 593 def id _attributes[primary_key] end |
#ignore_attribute?(key) ⇒ Boolean
Determine if a given attribute should be ignored. Ignored attributes are defined at the schema level and are frozen.
898 899 900 |
# File 'lib/chef-api/resources/base.rb', line 898 def ignore_attribute?(key) @schema.ignored_attributes.has_key?(key.to_sym) end |
#inspect ⇒ String
Custom inspect method for easier readability.
948 949 950 951 952 953 954 955 956 957 958 |
# File 'lib/chef-api/resources/base.rb', line 948 def inspect attrs = (_prefix).merge(_attributes).map do |key, value| if value.is_a?(String) "#{key}: #{Util.truncate(value, length: 50).inspect}" else "#{key}: #{value.inspect}" end end "#<#{self.class.classname} #{attrs.join(', ')}>" end |
#new_resource? ⇒ Boolean
Check if this resource exists on the remote Chef Server. This is useful when determining if a resource should be saved or updated, since a resource must exist before it can be saved.
818 819 820 |
# File 'lib/chef-api/resources/base.rb', line 818 def new_resource? !self.class.exists?(id, _prefix) end |
#primary_key ⇒ Symbol
The primary key for the resource.
584 585 586 |
# File 'lib/chef-api/resources/base.rb', line 584 def primary_key @schema.primary_key end |
#protected? ⇒ Boolean
Determine if this current resource is protected. Resources may be protected by name or by a Proc. A protected resource is one that should not be modified (i.e. created/updated/deleted) by the user. An example of a protected resource is the pivotal key or the chef-webui client.
634 635 636 637 638 639 640 641 642 |
# File 'lib/chef-api/resources/base.rb', line 634 def protected? @protected ||= self.class.protected_resources.any? do |thing| if thing.is_a?(Proc) thing.call(self) else id == thing end end end |
#reload! ⇒ self
This will remove any custom values you have set on the resource!
Reload (or reset) this object using the values currently stored on the remote server. This method will also clear any cached collection proxies so they will be reloaded the next time they are requested. If the remote record does not exist, no attributes are modified.
655 656 657 658 659 660 661 662 663 664 665 666 |
# File 'lib/chef-api/resources/base.rb', line 655 def reload! associations.clear remote = self.class.fetch(id, _prefix) return self if remote.nil? remote._attributes.each do |key, value| update_attribute(key, value) end self end |
#resource_path ⇒ String
The URL for this resource on the Chef Server.
885 886 887 |
# File 'lib/chef-api/resources/base.rb', line 885 def resource_path self.class.resource_path(id, _prefix) end |
#save ⇒ Boolean
Commit the resource and any changes to the remote Chef Server. Any errors are gracefully handled and added to the resource’s error collection for handling.
706 707 708 709 710 |
# File 'lib/chef-api/resources/base.rb', line 706 def save save! rescue false end |
#save! ⇒ Boolean
Commit the resource and any changes to the remote Chef Server. Any errors will raise an exception in the main thread and the resource will not be committed back to the Chef Server.
Any response errors (such as server-side responses) that ChefAPI failed to account for in validations will also raise an exception.
679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 |
# File 'lib/chef-api/resources/base.rb', line 679 def save! validate! response = if new_resource? self.class.post(to_json, _prefix) else self.class.put(id, to_json, _prefix) end # Update our local copy with any partial information that was returned # from the server, ignoring an "bad" attributes that aren't defined in # our schema. response.each do |key, value| update_attribute(key, value) if attribute?(key) end true end |
#to_hash ⇒ Hash
The hash representation of this resource. All attributes are serialized and any values that respond to to_hash
are also serialized.
917 918 919 920 921 922 923 |
# File 'lib/chef-api/resources/base.rb', line 917 def to_hash {}.tap do |hash| _attributes.each do |key, value| hash[key] = value.respond_to?(:to_hash) ? value.to_hash : value end end end |
#to_json ⇒ String
The JSON serialization of this resource.
930 931 932 |
# File 'lib/chef-api/resources/base.rb', line 930 def to_json JSON.fast_generate(to_hash) end |
#to_s ⇒ String
Custom to_s method for easier readability.
939 940 941 |
# File 'lib/chef-api/resources/base.rb', line 939 def to_s "#<#{self.class.classname} #{primary_key}: #{id.inspect}>" end |
#update(attributes = {}) ⇒ self
Update a subset of attributes on the current resource. This is a handy way to update multiple attributes at once.
732 733 734 735 736 737 738 |
# File 'lib/chef-api/resources/base.rb', line 732 def update(attributes = {}) attributes.each do |key, value| update_attribute(key, value) end self end |
#update_attribute(key, value) ⇒ Object
Update a single attribute in the attributes hash.
745 746 747 748 749 750 751 |
# File 'lib/chef-api/resources/base.rb', line 745 def update_attribute(key, value) unless attribute?(key.to_sym) raise Error::UnknownAttribute.new(attribute: key) end _attributes[key.to_sym] = value end |
#valid? ⇒ Boolean
Determine if the current resource is valid. This relies on the validations defined in the schema at initialization.
792 793 794 795 796 797 798 799 800 |
# File 'lib/chef-api/resources/base.rb', line 792 def valid? errors.clear validators.each do |validator| validator.validate(self) end errors.empty? end |
#validate! ⇒ Boolean
Run all of this resource’s validations, raising an exception if any validations fail.
776 777 778 779 780 781 782 783 |
# File 'lib/chef-api/resources/base.rb', line 776 def validate! unless valid? sentence = errors..join(', ') raise Error::InvalidResource.new(errors: sentence) end true end |
#validators ⇒ Array<~Validator::Base>
The list of validators for this resource. This is primarily set and managed by the underlying schema clean room.
760 761 762 |
# File 'lib/chef-api/resources/base.rb', line 760 def validators @validators ||= @schema.validators end |