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, Group, 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.
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 |
# File 'lib/chef-api/resources/base.rb', line 558 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.
536 537 538 |
# File 'lib/chef-api/resources/base.rb', line 536 def associations @associations end |
Class Method Details
.all ⇒ Array<Resource::Base>
393 394 395 |
# File 'lib/chef-api/resources/base.rb', line 393 def all entries end |
.build(attributes = {}, prefix = {}) ⇒ Object
Build a new resource from the given attributes.
275 276 277 |
# File 'lib/chef-api/resources/base.rb', line 275 def build(attributes = {}, prefix = {}) new(attributes, prefix) end |
.classname ⇒ String
The name for this resource, minus the parent module.
448 449 450 |
# File 'lib/chef-api/resources/base.rb', line 448 def classname name.split("::")[1..-1].join("::") end |
.collection(prefix = {}) ⇒ Array<Resource::Base>
The full collection list.
473 474 475 |
# File 'lib/chef-api/resources/base.rb', line 473 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.
118 119 120 121 122 123 124 125 |
# File 'lib/chef-api/resources/base.rb', line 118 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.
526 527 528 |
# File 'lib/chef-api/resources/base.rb', line 526 def connection Thread.current["chefapi.connection"] || ChefAPI.connection end |
.count(prefix = {}) ⇒ Fixnum Also known as: size
The total number of reosurces in the collection.
380 381 382 |
# File 'lib/chef-api/resources/base.rb', line 380 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.
295 296 297 298 299 300 301 302 303 304 |
# File 'lib/chef-api/resources/base.rb', line 295 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.
176 177 178 179 180 181 182 |
# File 'lib/chef-api/resources/base.rb', line 176 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.
215 216 217 218 219 220 221 |
# File 'lib/chef-api/resources/base.rb', line 215 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
231 232 233 |
# File 'lib/chef-api/resources/base.rb', line 231 def destroy_all(prefix = {}) map(&: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
.
366 367 368 369 370 371 372 373 |
# File 'lib/chef-api/resources/base.rb', line 366 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.
316 317 318 |
# File 'lib/chef-api/resources/base.rb', line 316 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.
508 509 510 511 512 513 514 515 516 517 518 519 |
# File 'lib/chef-api/resources/base.rb', line 508 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 CGI.escape(value) end.sub(%r{^/}, "") # Remove leading slash end |
.fetch(id, prefix = {}) ⇒ Resource::Base?
Fetch a single resource in the remote collection.
250 251 252 253 254 255 256 257 258 |
# File 'lib/chef-api/resources/base.rb', line 250 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.
17 18 19 |
# File 'lib/chef-api/resources/base.rb', line 17 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.
408 409 410 411 412 413 |
# File 'lib/chef-api/resources/base.rb', line 408 def from_json(response, prefix = {}) response.delete("json_class") response.delete("chef_type") new(response, prefix) end |
.from_url(url, prefix = {}) ⇒ Object
doc
24 25 26 |
# File 'lib/chef-api/resources/base.rb', line 24 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.
82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/chef-api/resources/base.rb', line 82 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.
436 437 438 |
# File 'lib/chef-api/resources/base.rb', line 436 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.
198 199 200 201 |
# File 'lib/chef-api/resources/base.rb', line 198 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.
141 142 143 144 |
# File 'lib/chef-api/resources/base.rb', line 141 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.
66 67 68 |
# File 'lib/chef-api/resources/base.rb', line 66 def protect(*ids) ids.each { |id| protected_resources << id } end |
.protected_resources ⇒ Object
doc
97 98 99 |
# File 'lib/chef-api/resources/base.rb', line 97 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.
162 163 164 165 |
# File 'lib/chef-api/resources/base.rb', line 162 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.
486 487 488 |
# File 'lib/chef-api/resources/base.rb', line 486 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.
44 45 46 47 48 49 50 |
# File 'lib/chef-api/resources/base.rb', line 44 def schema(&block) if block @schema = Schema.new(&block) else @schema end end |
.to_s ⇒ String
The string representation of this class.
423 424 425 |
# File 'lib/chef-api/resources/base.rb', line 423 def to_s classname end |
.type ⇒ String
The type of this resource.
460 461 462 |
# File 'lib/chef-api/resources/base.rb', line 460 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.
338 339 340 341 342 343 344 345 346 347 |
# File 'lib/chef-api/resources/base.rb', line 338 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.
611 612 613 |
# File 'lib/chef-api/resources/base.rb', line 611 def _attributes @_attributes ||= {}.merge(@schema.attributes) end |
#_prefix ⇒ Object
doc
602 603 604 |
# File 'lib/chef-api/resources/base.rb', line 602 def _prefix @_prefix end |
#attribute?(key) ⇒ Boolean
Determine if this resource has the given attribute.
624 625 626 |
# File 'lib/chef-api/resources/base.rb', line 624 def attribute?(key) _attributes.key?(key.to_sym) end |
#destroy ⇒ self
Remove the resource from the Chef Server.
720 721 722 723 |
# File 'lib/chef-api/resources/base.rb', line 720 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.
864 865 866 867 868 869 870 871 872 873 874 875 |
# File 'lib/chef-api/resources/base.rb', line 864 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.
843 844 845 |
# File 'lib/chef-api/resources/base.rb', line 843 def dirty? new_resource? || !diff.empty? end |
#errors ⇒ ErrorCollection
The collection of errors on the resource.
909 910 911 |
# File 'lib/chef-api/resources/base.rb', line 909 def errors @errors ||= ErrorCollection.new end |
#id ⇒ Object
The unique id for this resource.
595 596 597 |
# File 'lib/chef-api/resources/base.rb', line 595 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.
900 901 902 |
# File 'lib/chef-api/resources/base.rb', line 900 def ignore_attribute?(key) @schema.ignored_attributes.key?(key.to_sym) end |
#inspect ⇒ String
Custom inspect method for easier readability.
950 951 952 953 954 955 956 957 958 959 960 |
# File 'lib/chef-api/resources/base.rb', line 950 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.
820 821 822 |
# File 'lib/chef-api/resources/base.rb', line 820 def new_resource? !self.class.exists?(id, _prefix) end |
#primary_key ⇒ Symbol
The primary key for the resource.
586 587 588 |
# File 'lib/chef-api/resources/base.rb', line 586 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.
636 637 638 639 640 641 642 643 644 |
# File 'lib/chef-api/resources/base.rb', line 636 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.
657 658 659 660 661 662 663 664 665 666 667 668 |
# File 'lib/chef-api/resources/base.rb', line 657 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.
887 888 889 |
# File 'lib/chef-api/resources/base.rb', line 887 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.
708 709 710 711 712 |
# File 'lib/chef-api/resources/base.rb', line 708 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.
681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 |
# File 'lib/chef-api/resources/base.rb', line 681 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.
919 920 921 922 923 924 925 |
# File 'lib/chef-api/resources/base.rb', line 919 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.
932 933 934 |
# File 'lib/chef-api/resources/base.rb', line 932 def to_json(*) JSON.fast_generate(to_hash) end |
#to_s ⇒ String
Custom to_s method for easier readability.
941 942 943 |
# File 'lib/chef-api/resources/base.rb', line 941 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.
734 735 736 737 738 739 740 |
# File 'lib/chef-api/resources/base.rb', line 734 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.
747 748 749 750 751 752 753 |
# File 'lib/chef-api/resources/base.rb', line 747 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.
794 795 796 797 798 799 800 801 802 |
# File 'lib/chef-api/resources/base.rb', line 794 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.
778 779 780 781 782 783 784 785 |
# File 'lib/chef-api/resources/base.rb', line 778 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.
762 763 764 |
# File 'lib/chef-api/resources/base.rb', line 762 def validators @validators ||= @schema.validators end |