# File lib/chef/node.rb, line 295 def primary_runlist @primary_runlist end
class Chef::Node
Constants
- NULL_ARG
Attributes
RunContext will set itself as #run_context via this setter when initialized. This is needed so DSL::IncludeAttribute (in particular, include_recipe) can access the #run_context to determine if an attributes file has been seen yet.
Public Class Methods
# File lib/chef/node.rb, line 581 def self.build(node_name) node = new node.name(node_name) node.chef_environment(Chef::Config[:environment]) unless Chef::Config[:environment].nil? || Chef::Config[:environment].chomp.empty? node end
# File lib/chef/node.rb, line 573 def self.find_or_create(node_name) load(node_name) rescue Net::HTTPServerException => e raise unless e.response.code == "404" node = build(node_name) node.create end
# File lib/chef/node.rb, line 519 def self.from_hash(o) return o if o.kind_of? Chef::Node node = new node.name(o["name"]) node.policy_name = o["policy_name"] if o.has_key?("policy_name") node.policy_group = o["policy_group"] if o.has_key?("policy_group") unless node.policy_group.nil? node.chef_environment(o["policy_group"]) else node.chef_environment(o["chef_environment"]) end if o.has_key?("attributes") node.normal_attrs = o["attributes"] end node.automatic_attrs = Mash.new(o["automatic"]) if o.has_key?("automatic") node.normal_attrs = Mash.new(o["normal"]) if o.has_key?("normal") node.default_attrs = Mash.new(o["default"]) if o.has_key?("default") node.override_attrs = Mash.new(o["override"]) if o.has_key?("override") if o.has_key?("run_list") node.run_list.reset!(o["run_list"]) elsif o.has_key?("recipes") o["recipes"].each { |r| node.recipes << r } end node end
# File lib/chef/node.rb, line 560 def self.list(inflate = false) if inflate response = Hash.new Chef::Search::Query.new.search(:node) do |n| n = Chef::Node.from_hash(n) response[n.name] = n unless n.nil? end response else Chef::ServerAPI.new(Chef::Config[:chef_server_url]).get("nodes") end end
# File lib/chef/node.rb, line 550 def self.list_by_environment(environment, inflate = false) if inflate response = Hash.new Chef::Search::Query.new.search(:node, "chef_environment:#{environment}") { |n| response[n.name] = n unless n.nil? } response else Chef::ServerAPI.new(Chef::Config[:chef_server_url]).get("environments/#{environment}/nodes") end end
Load a node by name
# File lib/chef/node.rb, line 589 def self.load(name) from_hash(Chef::ServerAPI.new(Chef::Config[:chef_server_url]).get("nodes/#{name}")) end
Create a new Chef::Node object.
# File lib/chef/node.rb, line 71 def initialize(chef_server_rest: nil, logger: nil) @chef_server_rest = chef_server_rest @name = nil @logger = logger || Chef::Log.with_child(subsystem: "node") @chef_environment = "_default" @primary_runlist = Chef::RunList.new @override_runlist = Chef::RunList.new @policy_name = nil @policy_group = nil @attributes = Chef::Node::Attribute.new({}, {}, {}, {}, self) @run_state = {} end
Public Instance Methods
# File lib/chef/node.rb, line 645 def <=>(other) name <=> other.name end
# File lib/chef/node.rb, line 637 def ==(other) if other.kind_of?(self.class) name == other.name else false end end
Return an attribute of this node. Returns nil if the attribute is not found.
# File lib/chef/node.rb, line 195 def [](attrib) attributes[attrib] end
Apply the default and overrides attributes from the expansion passed in, which came from roles.
# File lib/chef/node.rb, line 439 def apply_expansion_attributes(expansion) loaded_environment = if chef_environment == "_default" Chef::Environment.new.tap { |e| e.name("_default") } else Chef::Environment.load(chef_environment) end attributes.env_default = loaded_environment.default_attributes attributes.env_override = loaded_environment.override_attributes attribute.role_default = expansion.default_attrs attributes.role_override = expansion.override_attrs end
Return true if this Node has a given attribute, false if not. Takes either a symbol or a string.
Only works on the top level. Preferred way is to use the normal [] style lookup and call attribute?()
# File lib/chef/node.rb, line 246 def attribute?(attrib) attributes.attribute?(attrib) end
# File lib/chef/node.rb, line 187 def attributes @attributes end
# File lib/chef/node.rb, line 233 def automatic_attrs attributes.automatic end
# File lib/chef/node.rb, line 237 def automatic_attrs=(new_values) attributes.automatic = new_values end
# File lib/chef/node.rb, line 129 def chef_environment(arg = nil) set_or_return( :chef_environment, arg, { :regex => /^[\-[:alnum:]_]+$/, :kind_of => String } ) end
# File lib/chef/node.rb, line 137 def chef_environment=(environment) chef_environment(environment) end
# File lib/chef/node.rb, line 102 def chef_server_rest # for saving node data we use validate_utf8: false which will not # raise an exception on bad utf8 data, but will replace the bad # characters and render valid JSON. @chef_server_rest ||= Chef::ServerAPI.new( Chef::Config[:chef_server_url], client_name: Chef::Config[:node_name], signing_key_filename: Chef::Config[:client_key], validate_utf8: false ) end
Consumes the combined #run_list
and other attributes in attrs
# File lib/chef/node.rb, line 348 def consume_attributes(attrs) normal_attrs_to_merge = consume_run_list(attrs) normal_attrs_to_merge = consume_chef_environment(normal_attrs_to_merge) # FIXME(log): should be trace logger.debug("Applying attributes from json file") self.normal_attrs = Chef::Mixin::DeepMerge.merge(normal_attrs, normal_attrs_to_merge) tags # make sure they're defined end
#chef_environment when set in -j JSON will take precedence over -E ENVIRONMENT. Ideally, IMO, the order of precedence should be (lowest to
highest): config_file -j JSON -E ENVIRONMENT
so that users could reuse their JSON and override the #chef_environment configured within it with -E ENVIRONMENT. Because command line options are merged with Chef::Config there is currently no way to distinguish between an environment set via config from an environment set via command line.
# File lib/chef/node.rb, line 394 def consume_chef_environment(attrs) attrs = attrs ? attrs.dup : {} if env = attrs.delete("chef_environment") chef_environment(env) end attrs end
Consume data from ohai and Attributes provided as JSON on the command line.
# File lib/chef/node.rb, line 326 def consume_external_attrs(ohai_data, json_cli_attrs) # FIXME(log): should be trace logger.debug("Extracting run list from JSON attributes provided on command line") consume_attributes(json_cli_attrs) self.automatic_attrs = ohai_data platform, version = Chef::Platform.find_platform_and_version(self) # FIXME(log): should be trace logger.debug("Platform is #{platform} version #{version}") automatic[:platform] = platform automatic[:platform_version] = version automatic[:chef_guid] = Chef::Config[:chef_guid] automatic[:name] = name automatic[:chef_environment] = chef_environment end
# File lib/chef/node.rb, line 343 def consume_ohai_data(ohai_data) self.automatic_attrs = Chef::Mixin::DeepMerge.merge(automatic_attrs, ohai_data) end
Extracts the run list from attrs
and applies it. Returns the
remaining attributes
# File lib/chef/node.rb, line 372 def consume_run_list(attrs) attrs = attrs ? attrs.dup : {} if new_run_list = attrs.delete("recipes") || attrs.delete("run_list") if attrs.key?("recipes") || attrs.key?("run_list") raise Chef::Exceptions::AmbiguousRunlistSpecification, "please set the node's run list using the 'run_list' attribute only." end logger.info("Setting the run_list to #{new_run_list} from CLI options") run_list(new_run_list) end attrs end
Create the node via the REST API
# File lib/chef/node.rb, line 619 def create chef_server_rest.post("nodes", data_for_save) self rescue Net::HTTPServerException => e # Chef Server before 12.3 rejects node JSON with 'policy_name' or # 'policy_group' keys, but 'policy_name' will be detected first. # Backcompat can be removed in 13.0 if e.response.code == "400" && e.response.body.include?("Invalid key policy_name") chef_server_rest.post("nodes", data_for_save_without_policyfile_attrs) else raise end end
Set a default of this node, but auto-vivify any Mashes that might be missing
# File lib/chef/node.rb, line 207 def default attributes.default end
# File lib/chef/node.rb, line 225 def default_attrs=(new_values) attributes.default = new_values end
Remove this node via the REST API
# File lib/chef/node.rb, line 594 def destroy chef_server_rest.delete("nodes/#{name}") end
# File lib/chef/node.rb, line 468 def display_hash display = {} display["name"] = name display["chef_environment"] = chef_environment display["automatic"] = automatic_attrs display["normal"] = normal_attrs display["default"] = attributes.combined_default display["override"] = attributes.combined_override display["run_list"] = run_list.run_list_items display end
Yield each key of the top level to the block.
# File lib/chef/node.rb, line 251 def each(&block) attributes.each(&block) end
Iterates over each attribute, passing the attribute and value to the block.
# File lib/chef/node.rb, line 256 def each_attribute(&block) attributes.each_attribute(&block) end
Expands the node's run list and sets the default and override attributes. Also applies stored attributes (from json provided on the command line)
Returns the fully-expanded list of recipes, a RunListExpansion.
# File lib/chef/node.rb, line 421 def expand!(data_source = "server") expansion = run_list.expand(chef_environment, data_source) raise Chef::Exceptions::MissingRole, expansion if expansion.errors? tags # make sure they're defined automatic_attrs[:recipes] = expansion.recipes.with_duplicate_names automatic_attrs[:expanded_run_list] = expansion.recipes.with_fully_qualified_names_and_version_constraints automatic_attrs[:roles] = expansion.roles apply_expansion_attributes(expansion) automatic_attrs[:chef_environment] = chef_environment expansion end
# File lib/chef/node.rb, line 485 def for_json result = { "name" => name, "chef_environment" => chef_environment, "json_class" => self.class.name, "automatic" => attributes.automatic, "normal" => attributes.normal, "chef_type" => "node", "default" => attributes.combined_default, "override" => attributes.combined_override, #Render correctly for run_list items so malformed json does not result "run_list" => @primary_runlist.run_list.map { |item| item.to_s }, } # Chef Server rejects node JSON with extra keys; prior to 12.3, # "policy_name" and "policy_group" are unknown; after 12.3 they are # optional, therefore only including them in the JSON if present # maximizes compatibility for most people. unless policy_group.nil? && policy_name.nil? result["policy_name"] = policy_name result["policy_group"] = policy_group end result end
used by include_recipe to add recipes to the expanded #run_list to be saved back to the node and be searchable
# File lib/chef/node.rb, line 284 def loaded_recipe(cookbook, recipe) fully_qualified_recipe = "#{cookbook}::#{recipe}" automatic_attrs[:recipes] << fully_qualified_recipe unless Array(self[:recipes]).include?(fully_qualified_recipe) end
Only works for attribute fetches, setting is no longer supported XXX: this should be deprecated
# File lib/chef/node.rb, line 262 def method_missing(method, *args, &block) attributes.public_send(method, *args, &block) end
Set the name of this Node, or return the current name.
# File lib/chef/node.rb, line 115 def name(arg = nil) if !arg.nil? validate( { :name => arg }, { :name => { :kind_of => String, :cannot_be => :blank, :regex => /^[\-[:alnum:]_:.]+$/ }, }) @name = arg else @name end end
Used by DSL
# File lib/chef/node.rb, line 98 def node self end
Set a normal attribute of this node, but auto-vivify any Mashes that might be missing
# File lib/chef/node.rb, line 201 def normal attributes.normal end
# File lib/chef/node.rb, line 229 def normal_attrs=(new_values) attributes.normal = new_values end
Set an override attribute of this node, but auto-vivify any Mashes that might be missing
# File lib/chef/node.rb, line 213 def override attributes.override end
# File lib/chef/node.rb, line 221 def override_attrs=(new_values) attributes.override = new_values end
# File lib/chef/node.rb, line 300 def override_runlist(*args) args.length > 0 ? @override_runlist.reset!(args) : @override_runlist end
The `policy_group` for this node. Setting this to a non-nil value will enable policyfile mode when `chef-client` is run. If set in the config file or in node json, running `chef-client` will update this value.
@see Chef::PolicyBuilder::Dynamic @see Chef::PolicyBuilder::Policyfile
@param arg [String] the new #policy_group value @return [String] the current #policy_group, or the one you just set
# File lib/chef/node.rb, line 174 def policy_group(arg = NULL_ARG) return @policy_group if arg.equal?(NULL_ARG) validate({ policy_group: arg }, { policy_group: { kind_of: [ String, NilClass ], regex: /^[\-:.[:alnum:]_]+$/ } }) @policy_group = arg end
A “non-DSL-style” setter for `policy_group`
@see policy_group
# File lib/chef/node.rb, line 183 def policy_group=(policy_group) policy_group(policy_group) end
The `policy_name` for this node. Setting this to a non-nil value will enable policyfile mode when `chef-client` is run. If set in the config file or in node json, running `chef-client` will update this value.
@see Chef::PolicyBuilder::Dynamic @see Chef::PolicyBuilder::Policyfile
@param arg [String] the new #policy_name value @return [String] the current #policy_name, or the one you just set
# File lib/chef/node.rb, line 152 def policy_name(arg = NULL_ARG) return @policy_name if arg.equal?(NULL_ARG) validate({ policy_name: arg }, { policy_name: { kind_of: [ String, NilClass ], regex: /^[\-:.[:alnum:]_]+$/ } }) @policy_name = arg end
A “non-DSL-style” setter for `policy_name`
@see policy_name
# File lib/chef/node.rb, line 161 def policy_name=(policy_name) policy_name(policy_name) end
Returns true if this Node expects a given recipe, false if not.
First, the run list is consulted to see whether the recipe is explicitly included. If it's not there, it looks in `node`, which is populated when the #run_list is expanded
NOTE: It's used by cookbook authors
# File lib/chef/node.rb, line 278 def recipe?(recipe_name) run_list.include?(recipe_name) || Array(self[:recipes]).include?(recipe_name) end
Clear defaults and overrides, so that any deleted attributes between runs are still gone.
# File lib/chef/node.rb, line 404 def reset_defaults_and_overrides default.clear override.clear end
Fix respond_to + method so that it works with #method_missing delegation
# File lib/chef/node.rb, line 267 def respond_to_missing?(method, include_private = false) attributes.respond_to?(method, false) end
Returns true if this Node expects a given role, false if not.
# File lib/chef/node.rb, line 291 def role?(role_name) run_list.include?("role[#{role_name}]") end
Returns an Array of roles and recipes, in the order they will be applied. If you call it with arguments, they will become the new list of roles and recipes.
# File lib/chef/node.rb, line 310 def run_list(*args) rl = select_run_list args.length > 0 ? rl.reset!(args) : rl end
# File lib/chef/node.rb, line 315 def run_list=(list) rl = select_run_list rl = list end
Returns true if this Node expects a given role, false if not.
# File lib/chef/node.rb, line 321 def run_list?(item) run_list.detect { |r| r == item } ? true : false end
Save this node via the REST API
# File lib/chef/node.rb, line 599 def save # Try PUT. If the node doesn't yet exist, PUT will return 404, # so then POST to create. begin if Chef::Config[:why_run] logger.warn("In why-run mode, so NOT performing node save.") else chef_server_rest.put("nodes/#{name}", data_for_save) end rescue Net::HTTPServerException => e if e.response.code == "404" chef_server_rest.post("nodes", data_for_save) else raise end end self end
# File lib/chef/node.rb, line 304 def select_run_list @override_runlist.empty? ? @primary_runlist : @override_runlist end
after the #run_context has been set on the node, go through the cookbook_collection and setup the node attribute so that it is published in the node object
# File lib/chef/node.rb, line 90 def set_cookbook_attribute return unless run_context.cookbook_collection run_context.cookbook_collection.each do |cookbook_name, cookbook| automatic_attrs[:cookbooks][cookbook_name][:version] = cookbook.version end end
# File lib/chef/node.rb, line 363 def tag(*args) args.each do |tag| tags.push(tag.to_s) unless tags.include? tag.to_s end tags end
Transform the node to a Hash
# File lib/chef/node.rb, line 454 def to_hash index_hash = Hash.new index_hash["chef_type"] = "node" index_hash["name"] = name index_hash["chef_environment"] = chef_environment attribute.each do |key, value| index_hash[key] = value end index_hash["recipe"] = run_list.recipe_names if run_list.recipe_names.length > 0 index_hash["role"] = run_list.role_names if run_list.role_names.length > 0 index_hash["run_list"] = run_list.run_list_items index_hash end
Serialize this object as a hash
# File lib/chef/node.rb, line 481 def to_json(*a) Chef::JSONCompat.to_json(for_json, *a) end
# File lib/chef/node.rb, line 633 def to_s "node[#{name}]" end
# File lib/chef/node.rb, line 509 def update_from!(o) run_list.reset!(o.run_list) self.automatic_attrs = o.automatic_attrs self.normal_attrs = o.normal_attrs self.override_attrs = o.override_attrs self.default_attrs = o.default_attrs chef_environment(o.chef_environment) self end
Private Instance Methods
# File lib/chef/node.rb, line 667 def data_for_save data = for_json %w{automatic default normal override}.each do |level| whitelist_config_option = "#{level}_attribute_whitelist".to_sym whitelist = Chef::Config[whitelist_config_option] unless whitelist.nil? # nil => save everything logger.info("Whitelisting #{level} node attributes for save.") data[level] = Chef::Whitelist.filter(data[level], whitelist) end blacklist_config_option = "#{level}_attribute_blacklist".to_sym blacklist = Chef::Config[blacklist_config_option] unless blacklist.nil? # nil => remove nothing logger.info("Blacklisting #{level} node attributes for save") data[level] = Chef::Blacklist.filter(data[level], blacklist) end end data end
# File lib/chef/node.rb, line 660 def data_for_save_without_policyfile_attrs data_for_save.tap do |trimmed_data| trimmed_data.delete("policy_name") trimmed_data.delete("policy_group") end end
# File lib/chef/node.rb, line 651 def save_without_policyfile_attrs trimmed_data = data_for_save_without_policyfile_attrs chef_server_rest.put("nodes/#{name}", trimmed_data) rescue Net::HTTPServerException => e raise e unless e.response.code == "404" chef_server_rest.post("nodes", trimmed_data) end