# File lib/chef/node.rb, line 299 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 606 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 598 def self.find_or_create(node_name) load(node_name) rescue Net::HTTPClientException => e raise unless e.response.code == "404" node = build(node_name) node.create end
# File lib/chef/node.rb, line 544 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.key?("policy_name") node.policy_group = o["policy_group"] if o.key?("policy_group") unless node.policy_group.nil? node.chef_environment(o["policy_group"]) else node.chef_environment(o["chef_environment"]) end if o.key?("attributes") node.normal_attrs = o["attributes"] end node.automatic_attrs = Mash.new(o["automatic"]) if o.key?("automatic") node.normal_attrs = Mash.new(o["normal"]) if o.key?("normal") node.default_attrs = Mash.new(o["default"]) if o.key?("default") node.override_attrs = Mash.new(o["override"]) if o.key?("override") if o.key?("run_list") node.run_list.reset!(o["run_list"]) elsif o.key?("recipes") o["recipes"].each { |r| node.recipes << r } end node end
# File lib/chef/node.rb, line 585 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 575 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 614 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 72 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 670 def <=>(other) name <=> other.name end
# File lib/chef/node.rb, line 662 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 196 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 467 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 247 def attribute?(attrib) attributes.attribute?(attrib) end
# File lib/chef/node.rb, line 188 def attributes @attributes end
# File lib/chef/node.rb, line 234 def automatic_attrs attributes.automatic end
# File lib/chef/node.rb, line 238 def automatic_attrs=(new_values) attributes.automatic = new_values end
# File lib/chef/node.rb, line 130 def chef_environment(arg = nil) set_or_return( :chef_environment, arg, { regex: /^[\-[:alnum:]_]+$/, kind_of: String } ) end
# File lib/chef/node.rb, line 138 def chef_environment=(environment) chef_environment(environment) end
# File lib/chef/node.rb, line 103 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 376 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 422 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 354 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] || ( Chef::Config[:chef_guid] = node_uuid ) automatic[:name] = name automatic[:chef_environment] = chef_environment end
# File lib/chef/node.rb, line 371 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 400 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 644 def create chef_server_rest.post("nodes", data_for_save) self rescue Net::HTTPClientException => 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 208 def default attributes.default end
# File lib/chef/node.rb, line 226 def default_attrs=(new_values) attributes.default = new_values end
Remove this node via the REST API
# File lib/chef/node.rb, line 619 def destroy chef_server_rest.delete("nodes/#{name}") end
# File lib/chef/node.rb, line 493 def display_hash display = {} display["name"] = name display["chef_environment"] = chef_environment display["automatic"] = attributes.automatic.to_hash display["normal"] = attributes.normal.to_hash display["default"] = attributes.combined_default.to_hash display["override"] = attributes.combined_override.to_hash 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 252 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 257 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 449 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 510 def for_json result = { "name" => name, "chef_environment" => chef_environment, "json_class" => self.class.name, "automatic" => attributes.automatic.to_hash, "normal" => attributes.normal.to_hash, "chef_type" => "node", "default" => attributes.combined_default.to_hash, "override" => attributes.combined_override.to_hash, # 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 285 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 263 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 116 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 99 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 202 def normal attributes.normal end
# File lib/chef/node.rb, line 230 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 214 def override attributes.override end
# File lib/chef/node.rb, line 222 def override_attrs=(new_values) attributes.override = new_values end
Accessor for override_runlist
(this cannot set an empty override run list)
@params args [Array] override run list to set @return [Chef::RunList] the override run list
# File lib/chef/node.rb, line 317 def override_runlist(*args) return @override_runlist if args.length == 0 @override_runlist_set = true @override_runlist.reset!(args) end
Setter for override_runlist
which allows setting an empty override run list and marking it to be used
@params array [Array] override run list to set @return [Chef::RunList] the override run list
# File lib/chef/node.rb, line 327 def override_runlist=(array) @override_runlist_set = true @override_runlist.reset!(array) end
This boolean can be useful to determine if an override_runlist
is set, it can be true even if the override_runlist
is empty.
(Mutators can set the override_runlist
so any non-empty override_runlist
is considered set)
@return [Boolean] if the override run list has been set
# File lib/chef/node.rb, line 309 def override_runlist_set? !!@override_runlist_set || !override_runlist.empty? 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 175 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 184 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 153 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 162 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 279 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 432 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 268 def respond_to_missing?(method, include_private = false) attributes.respond_to?(method, false) 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 338 def run_list(*args) rl = select_run_list args.length > 0 ? rl.reset!(args) : rl end
# File lib/chef/node.rb, line 343 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 349 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 624 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::HTTPClientException => 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 332 def select_run_list override_runlist_set? ? @override_runlist : @primary_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 91 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 391 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 482 def to_hash index_hash = attributes.to_hash index_hash["chef_type"] = "node" index_hash["name"] = name index_hash["chef_environment"] = chef_environment 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 506 def to_json(*a) Chef::JSONCompat.to_json(for_json, *a) end
# File lib/chef/node.rb, line 658 def to_s "node[#{name}]" end
# File lib/chef/node.rb, line 534 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 692 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 685 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
Returns a UUID that uniquely identifies this node for reporting reasons.
The node is read in from disk if it exists, or it's generated if it does does not exist.
@return [String] UUID for the node
# File lib/chef/node.rb, line 719 def node_uuid path = File.expand_path(Chef::Config[:chef_guid_path]) dir = File.dirname(path) unless File.exists?(path) FileUtils.mkdir_p(dir) File.write(path, SecureRandom.uuid) end File.open(path).first.chomp end
# File lib/chef/node.rb, line 676 def save_without_policyfile_attrs trimmed_data = data_for_save_without_policyfile_attrs chef_server_rest.put("nodes/#{name}", trimmed_data) rescue Net::HTTPClientException => e raise e unless e.response.code == "404" chef_server_rest.post("nodes", trimmed_data) end