Class: Chef::Node

Inherits:
Object show all
Extended by:
Forwardable
Includes:
DSL::IncludeAttribute, DSL::PlatformIntrospection, Mixin::FromFile, Mixin::ParamsValidate
Defined in:
lib/chef/node.rb,
lib/chef/node/attribute.rb,
lib/chef/node/attribute_collections.rb,
lib/chef/node/immutable_collections.rb

Defined Under Namespace

Modules: Immutablize Classes: AttrArray, Attribute, ImmutableArray, ImmutableMash, VividMash

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Mixin::ParamsValidate

#lazy, #set_or_return, #validate

Methods included from DSL::PlatformIntrospection

#platform?, #platform_family?, #value_for_platform, #value_for_platform_family

Methods included from DSL::IncludeAttribute

#include_attribute, #parse_attribute_file_spec

Methods included from Mixin::FromFile

#class_from_file, #from_file

Constructor Details

#initializeNode

Create a new Chef::Node object.



62
63
64
65
66
67
68
69
70
71
72
# File 'lib/chef/node.rb', line 62

def initialize
  @name = nil

  @chef_environment = '_default'
  @primary_runlist = Chef::RunList.new
  @override_runlist = Chef::RunList.new

  @attributes = Chef::Node::Attribute.new({}, {}, {}, {})

  @run_state = {}
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(symbol, *args) ⇒ Object

Only works for attribute fetches, setting is no longer supported



236
237
238
# File 'lib/chef/node.rb', line 236

def method_missing(symbol, *args)
  attributes.send(symbol, *args)
end

Instance Attribute Details

#override_runlist(*args) ⇒ Object

Returns the value of attribute override_runlist.



45
46
47
# File 'lib/chef/node.rb', line 45

def override_runlist
  @override_runlist
end

#recipe_listObject

Returns the value of attribute recipe_list.



45
46
47
# File 'lib/chef/node.rb', line 45

def recipe_list
  @recipe_list
end

#run_contextObject

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. – TODO: This is a pretty ugly way to solve that problem.



53
54
55
# File 'lib/chef/node.rb', line 53

def run_context
  @run_context
end

#run_stateObject

Returns the value of attribute run_state.



45
46
47
# File 'lib/chef/node.rb', line 45

def run_state
  @run_state
end

Class Method Details

.build(node_name) ⇒ Object



498
499
500
501
502
503
# File 'lib/chef/node.rb', line 498

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].chop.empty?
  node
end

.find_or_create(node_name) ⇒ Object



490
491
492
493
494
495
496
# File 'lib/chef/node.rb', line 490

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

.json_create(o) ⇒ Object

Create a Chef::Node from JSON



448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
# File 'lib/chef/node.rb', line 448

def self.json_create(o)
  node = new
  node.name(o["name"])
  node.chef_environment(o["chef_environment"])
  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"])
  else
    o["recipes"].each { |r| node.recipes << r }
  end
  node
end

.list(inflate = false) ⇒ Object



478
479
480
481
482
483
484
485
486
487
488
# File 'lib/chef/node.rb', line 478

def self.list(inflate=false)
  if inflate
    response = Hash.new
    Chef::Search::Query.new.search(:node) do |n|
      response[n.name] = n unless n.nil?
    end
    response
  else
    Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("nodes")
  end
end

.list_by_environment(environment, inflate = false) ⇒ Object



468
469
470
471
472
473
474
475
476
# File 'lib/chef/node.rb', line 468

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::REST.new(Chef::Config[:chef_server_url]).get_rest("environments/#{environment}/nodes")
  end
end

.load(name) ⇒ Object

Load a node by name



506
507
508
# File 'lib/chef/node.rb', line 506

def self.load(name)
  Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("nodes/#{name}")
end

Instance Method Details

#<=>(other_node) ⇒ Object



542
543
544
# File 'lib/chef/node.rb', line 542

def <=>(other_node)
  self.name <=> other_node.name
end

#[](attrib) ⇒ Object

Return an attribute of this node. Returns nil if the attribute is not found.



120
121
122
# File 'lib/chef/node.rb', line 120

def [](attrib)
  attributes[attrib]
end

#apply_expansion_attributes(expansion) ⇒ Object

Apply the default and overrides attributes from the expansion passed in, which came from roles.



375
376
377
378
379
380
381
382
383
384
385
386
387
# File 'lib/chef/node.rb', line 375

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

#attribute?(attrib) ⇒ Boolean

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?()



221
222
223
# File 'lib/chef/node.rb', line 221

def attribute?(attrib)
  attributes.attribute?(attrib)
end

#attributesObject Also known as: attribute, construct_attributes



112
113
114
# File 'lib/chef/node.rb', line 112

def attributes
  @attributes
end

#automatic_attrsObject



208
209
210
# File 'lib/chef/node.rb', line 208

def automatic_attrs
  attributes.automatic
end

#automatic_attrs=(new_values) ⇒ Object



212
213
214
# File 'lib/chef/node.rb', line 212

def automatic_attrs=(new_values)
  attributes.automatic = new_values
end

#chef_environment(arg = nil) ⇒ Object Also known as: environment



98
99
100
101
102
103
104
# File 'lib/chef/node.rb', line 98

def chef_environment(arg=nil)
  set_or_return(
    :chef_environment,
    arg,
    { :regex => /^[\-[:alnum:]_]+$/, :kind_of => String }
  )
end

#chef_environment=(environment) ⇒ Object



106
107
108
# File 'lib/chef/node.rb', line 106

def chef_environment=(environment)
  chef_environment(environment)
end

#chef_server_restObject



79
80
81
# File 'lib/chef/node.rb', line 79

def chef_server_rest
  Chef::REST.new(Chef::Config[:chef_server_url])
end

#consume_attributes(attrs) ⇒ Object

Consumes the combined run_list and other attributes in attrs



306
307
308
309
310
311
# File 'lib/chef/node.rb', line 306

def consume_attributes(attrs)
  normal_attrs_to_merge = consume_run_list(attrs)
  Chef::Log.debug("Applying attributes from json file")
  self.normal_attrs = Chef::Mixin::DeepMerge.merge(normal_attrs,normal_attrs_to_merge)
  self.tags # make sure they're defined
end

#consume_external_attrs(ohai_data, json_cli_attrs) ⇒ Object

Consume data from ohai and Attributes provided as JSON on the command line.



293
294
295
296
297
298
299
300
301
302
303
# File 'lib/chef/node.rb', line 293

def consume_external_attrs(ohai_data, json_cli_attrs)
  Chef::Log.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)
  Chef::Log.debug("Platform is #{platform} version #{version}")
  self.automatic[:platform] = platform
  self.automatic[:platform_version] = version
end

#consume_run_list(attrs) ⇒ Object

Extracts the run list from attrs and applies it. Returns the remaining attributes



328
329
330
331
332
333
334
335
336
337
338
# File 'lib/chef/node.rb', line 328

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
    Chef::Log.info("Setting the run_list to #{new_run_list.inspect} from CLI options")
    run_list(new_run_list)
  end
  attrs
end

#createObject

Create the node via the REST API



533
534
535
536
# File 'lib/chef/node.rb', line 533

def create
  chef_server_rest.post_rest("nodes", self)
  self
end

#defaultObject

Set a default of this node, but auto-vivify any Mashes that might be missing



143
144
145
146
# File 'lib/chef/node.rb', line 143

def default
  attributes.set_unless_value_present = false
  attributes.default
end

#default!Object

Set a force default attribute. Intermediate mashes will be created by auto-vivify if necessary.



150
151
152
153
# File 'lib/chef/node.rb', line 150

def default!
  attributes.set_unless_value_present = false
  attributes.default!
end

#default_attrsObject



192
193
194
# File 'lib/chef/node.rb', line 192

def default_attrs
  attributes.default
end

#default_attrs=(new_values) ⇒ Object



196
197
198
# File 'lib/chef/node.rb', line 196

def default_attrs=(new_values)
  attributes.default = new_values
end

#default_unlessObject

Set a default attribute of this node, auto-vivifying any mashes that are missing, but if the final value already exists, don’t set it



157
158
159
160
# File 'lib/chef/node.rb', line 157

def default_unless
  attributes.set_unless_value_present = true
  attributes.default
end

#destroyObject

Remove this node via the REST API



511
512
513
# File 'lib/chef/node.rb', line 511

def destroy
  chef_server_rest.delete_rest("nodes/#{name}")
end

#display_hashObject



404
405
406
407
408
409
410
411
412
413
414
# File 'lib/chef/node.rb', line 404

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
  display
end

#each(&block) ⇒ Object

Yield each key of the top level to the block.



226
227
228
# File 'lib/chef/node.rb', line 226

def each(&block)
  attributes.each(&block)
end

#each_attribute(&block) ⇒ Object

Iterates over each attribute, passing the attribute and value to the block.



231
232
233
# File 'lib/chef/node.rb', line 231

def each_attribute(&block)
  attributes.each_attribute(&block)
end

#expand!(data_source = 'server') ⇒ Object

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.

– TODO: timh/cw, 5-14-2010: Should this method exist? Should we instead modify default_attrs and override_attrs whenever our run_list is mutated? Or perhaps do something smarter like on-demand generation of default_attrs and override_attrs, invalidated only when run_list is mutated?



359
360
361
362
363
364
365
366
367
368
369
370
371
# File 'lib/chef/node.rb', line 359

def expand!(data_source = 'server')
  expansion = run_list.expand(chef_environment, data_source)
  raise Chef::Exceptions::MissingRole, expansion if expansion.errors?

  self.tags # make sure they're defined

  automatic_attrs[:recipes] = expansion.recipes
  automatic_attrs[:roles] = expansion.roles

  apply_expansion_attributes(expansion)

  expansion
end

#for_jsonObject



421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
# File 'lib/chef/node.rb', line 421

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 }
  }
  result
end

#loaded_recipe(cookbook, recipe) ⇒ Object

used by include_recipe to add recipes to the expanded run_list to be saved back to the node and be searchable



253
254
255
256
# File 'lib/chef/node.rb', line 253

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

#name(arg = nil) ⇒ Object

Set the name of this Node, or return the current name.



84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/chef/node.rb', line 84

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

#nodeObject

Used by DSL



75
76
77
# File 'lib/chef/node.rb', line 75

def node
  self
end

#normalObject Also known as: set

Set a normal attribute of this node, but auto-vivify any Mashes that might be missing



126
127
128
129
# File 'lib/chef/node.rb', line 126

def normal
  attributes.set_unless_value_present = false
  attributes.normal
end

#normal_attrsObject



200
201
202
# File 'lib/chef/node.rb', line 200

def normal_attrs
  attributes.normal
end

#normal_attrs=(new_values) ⇒ Object



204
205
206
# File 'lib/chef/node.rb', line 204

def normal_attrs=(new_values)
  attributes.normal = new_values
end

#normal_unlessObject Also known as: set_unless

Set a normal attribute of this node, auto-vivifying any mashes that are missing, but if the final value already exists, don’t set it



135
136
137
138
# File 'lib/chef/node.rb', line 135

def normal_unless
  attributes.set_unless_value_present = true
  attributes.normal
end

#overrideObject

Set an override attribute of this node, but auto-vivify any Mashes that might be missing



164
165
166
167
# File 'lib/chef/node.rb', line 164

def override
  attributes.set_unless_value_present = false
  attributes.override
end

#override!Object

Set a force override attribute. Intermediate mashes will be created by auto-vivify if needed.



171
172
173
174
# File 'lib/chef/node.rb', line 171

def override!
  attributes.set_unless_value_present = false
  attributes.override!
end

#override_attrsObject



184
185
186
# File 'lib/chef/node.rb', line 184

def override_attrs
 attributes.override
end

#override_attrs=(new_values) ⇒ Object



188
189
190
# File 'lib/chef/node.rb', line 188

def override_attrs=(new_values)
  attributes.override = new_values
end

#override_unlessObject

Set an override attribute of this node, auto-vivifying any mashes that are missing, but if the final value already exists, don’t set it



178
179
180
181
# File 'lib/chef/node.rb', line 178

def override_unless
  attributes.set_unless_value_present = true
  attributes.override
end

#primary_runlistObject



263
264
265
# File 'lib/chef/node.rb', line 263

def primary_runlist
  @primary_runlist
end

#recipe?(recipe_name) ⇒ Boolean

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[:recipes], which is populated when the run_list is expanded

NOTE: It’s used by cookbook authors



247
248
249
# File 'lib/chef/node.rb', line 247

def recipe?(recipe_name)
  run_list.include?(recipe_name) || Array(self[:recipes]).include?(recipe_name)
end

#reset_defaults_and_overridesObject

Clear defaults and overrides, so that any deleted attributes between runs are still gone.



342
343
344
345
# File 'lib/chef/node.rb', line 342

def reset_defaults_and_overrides
  self.default.clear
  self.override.clear
end

#role?(role_name) ⇒ Boolean

Returns true if this Node expects a given role, false if not.



259
260
261
# File 'lib/chef/node.rb', line 259

def role?(role_name)
  run_list.include?("role[#{role_name}]")
end

#run_list(*args) ⇒ Object

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.



277
278
279
280
# File 'lib/chef/node.rb', line 277

def run_list(*args)
  rl = select_run_list
  args.length > 0 ? rl.reset!(args) : rl
end

#run_list=(list) ⇒ Object



282
283
284
285
# File 'lib/chef/node.rb', line 282

def run_list=(list)
  rl = select_run_list
  rl = list
end

#run_list?(item) ⇒ Boolean

Returns true if this Node expects a given role, false if not.



288
289
290
# File 'lib/chef/node.rb', line 288

def run_list?(item)
  run_list.detect { |r| r == item } ? true : false
end

#saveObject

Save this node via the REST API



516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
# File 'lib/chef/node.rb', line 516

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]
      Chef::Log.warn("In whyrun mode, so NOT performing node save.")
    else
      chef_server_rest.put_rest("nodes/#{name}", self)
    end
  rescue Net::HTTPServerException => e
    raise e unless e.response.code == "404"
    chef_server_rest.post_rest("nodes", self)
  end
  self
end

#select_run_listObject



271
272
273
# File 'lib/chef/node.rb', line 271

def select_run_list
  @override_runlist.empty? ? @primary_runlist : @override_runlist
end

#tag(*tags) ⇒ Object



319
320
321
322
323
324
325
# File 'lib/chef/node.rb', line 319

def tag(*tags)
  tags.each do |tag|
    self.normal[:tags].push(tag.to_s) unless self[:tags].include? tag.to_s
  end

  self[:tags]
end

#tagsObject

Lazy initializer for tags attribute



314
315
316
317
# File 'lib/chef/node.rb', line 314

def tags
  normal[:tags] = [] unless attribute?(:tags)
  normal[:tags]
end

#to_hashObject

Transform the node to a Hash



390
391
392
393
394
395
396
397
398
399
400
401
402
# File 'lib/chef/node.rb', line 390

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 if run_list.run_list.length > 0
  index_hash
end

#to_json(*a) ⇒ Object

Serialize this object as a hash



417
418
419
# File 'lib/chef/node.rb', line 417

def to_json(*a)
  for_json.to_json(*a)
end

#to_sObject



538
539
540
# File 'lib/chef/node.rb', line 538

def to_s
  "node[#{name}]"
end

#update_from!(o) ⇒ Object



437
438
439
440
441
442
443
444
445
# File 'lib/chef/node.rb', line 437

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