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/immutable_collections.rb,
lib/chef/node/attribute_collections.rb

Defined Under Namespace

Modules: Immutablize Classes: AttrArray, Attribute, ImmutableArray, ImmutableMash, MultiMash, 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.



65
66
67
68
69
70
71
72
73
74
75
# File 'lib/chef/node.rb', line 65

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



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

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

Instance Attribute Details

#override_runlist(*args) ⇒ Object

Returns the value of attribute override_runlist



48
49
50
# File 'lib/chef/node.rb', line 48

def override_runlist
  @override_runlist
end

#recipe_listObject

Returns the value of attribute recipe_list



48
49
50
# File 'lib/chef/node.rb', line 48

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.



56
57
58
# File 'lib/chef/node.rb', line 56

def run_context
  @run_context
end

#run_stateObject

Returns the value of attribute run_state



48
49
50
# File 'lib/chef/node.rb', line 48

def run_state
  @run_state
end

Class Method Details

.build(node_name) ⇒ Object



487
488
489
490
491
492
# File 'lib/chef/node.rb', line 487

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

.find_or_create(node_name) ⇒ Object



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

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



437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
# File 'lib/chef/node.rb', line 437

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



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

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



457
458
459
460
461
462
463
464
465
# File 'lib/chef/node.rb', line 457

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



495
496
497
# File 'lib/chef/node.rb', line 495

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

Instance Method Details

#<=>(other_node) ⇒ Object



531
532
533
# File 'lib/chef/node.rb', line 531

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.



123
124
125
# File 'lib/chef/node.rb', line 123

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.



364
365
366
367
368
369
370
371
372
373
374
375
376
# File 'lib/chef/node.rb', line 364

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



210
211
212
# File 'lib/chef/node.rb', line 210

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

#attributesObject Also known as: attribute, construct_attributes



115
116
117
# File 'lib/chef/node.rb', line 115

def attributes
  @attributes
end

#automatic_attrsObject



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

def automatic_attrs
  attributes.top_level_breadcrumb = nil
  attributes.set_unless_value_present = false
  attributes.automatic
end

#automatic_attrs=(new_values) ⇒ Object



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

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

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



101
102
103
104
105
106
107
# File 'lib/chef/node.rb', line 101

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

#chef_environment=(environment) ⇒ Object



109
110
111
# File 'lib/chef/node.rb', line 109

def chef_environment=(environment)
  chef_environment(environment)
end

#chef_server_restObject



82
83
84
# File 'lib/chef/node.rb', line 82

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



295
296
297
298
299
300
# File 'lib/chef/node.rb', line 295

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.



282
283
284
285
286
287
288
289
290
291
292
# File 'lib/chef/node.rb', line 282

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



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

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



522
523
524
525
# File 'lib/chef/node.rb', line 522

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

#defaultObject Also known as: default_attrs

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



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

def default
  attributes.top_level_breadcrumb = nil
  attributes.set_unless_value_present = false
  attributes.default
end

#default_attrs=(new_values) ⇒ Object



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

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
161
# File 'lib/chef/node.rb', line 157

def default_unless
  attributes.top_level_breadcrumb = nil
  attributes.set_unless_value_present = true
  attributes.default
end

#destroyObject

Remove this node via the REST API



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

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

#display_hashObject



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

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

#each(&block) ⇒ Object

Yield each key of the top level to the block.



215
216
217
# File 'lib/chef/node.rb', line 215

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

#each_attribute(&block) ⇒ Object

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



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

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?



348
349
350
351
352
353
354
355
356
357
358
359
360
# File 'lib/chef/node.rb', line 348

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



410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
# File 'lib/chef/node.rb', line 410

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



242
243
244
245
# File 'lib/chef/node.rb', line 242

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.



87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/chef/node.rb', line 87

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



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

def node
  self
end

#normalObject Also known as: set, normal_attrs

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



129
130
131
132
133
# File 'lib/chef/node.rb', line 129

def normal
  attributes.top_level_breadcrumb = nil
  attributes.set_unless_value_present = false
  attributes.normal
end

#normal_attrs=(new_values) ⇒ Object



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

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



139
140
141
142
143
# File 'lib/chef/node.rb', line 139

def normal_unless
  attributes.top_level_breadcrumb = nil
  attributes.set_unless_value_present = true
  attributes.normal
end

#overrideObject Also known as: override_attrs

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



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

def override
  attributes.top_level_breadcrumb = nil
  attributes.set_unless_value_present = false
  attributes.override
end

#override_attrs=(new_values) ⇒ Object



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

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



173
174
175
176
177
# File 'lib/chef/node.rb', line 173

def override_unless
  attributes.top_level_breadcrumb = nil
  attributes.set_unless_value_present = true
  attributes.override
end

#primary_runlistObject



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

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`, which is populated when the run_list is expanded

NOTE: It's used by cookbook authors



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

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.



331
332
333
334
# File 'lib/chef/node.rb', line 331

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.



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

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.



266
267
268
269
# File 'lib/chef/node.rb', line 266

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

#run_list=(list) ⇒ Object



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

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.



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

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

#saveObject

Save this node via the REST API



505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
# File 'lib/chef/node.rb', line 505

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}", data_for_save)
    end
  rescue Net::HTTPServerException => e
    raise e unless e.response.code == "404"
    chef_server_rest.post_rest("nodes", data_for_save)
  end
  self
end

#select_run_listObject



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

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

#tag(*tags) ⇒ Object



308
309
310
311
312
313
314
# File 'lib/chef/node.rb', line 308

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



303
304
305
306
# File 'lib/chef/node.rb', line 303

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

#to_hashObject

Transform the node to a Hash



379
380
381
382
383
384
385
386
387
388
389
390
391
# File 'lib/chef/node.rb', line 379

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

#to_json(*a) ⇒ Object

Serialize this object as a hash



406
407
408
# File 'lib/chef/node.rb', line 406

def to_json(*a)
  Chef::JSONCompat.to_json(for_json, *a)
end

#to_sObject



527
528
529
# File 'lib/chef/node.rb', line 527

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

#update_from!(o) ⇒ Object



426
427
428
429
430
431
432
433
434
# File 'lib/chef/node.rb', line 426

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