diff options
author | Ezra Zygmuntowicz <ez@engineyard.com> | 2008-10-08 14:19:52 -0700 |
---|---|---|
committer | Ezra Zygmuntowicz <ez@engineyard.com> | 2008-10-08 14:19:52 -0700 |
commit | c5d33c1298834ce40b8fbf344f281045771b5371 (patch) | |
tree | 1f0d4c7eab1eb379b544282a7ce48052acf719a5 /chef/lib/chef/node.rb | |
parent | 3d14601aea23dee3899d097324875274da419d84 (diff) | |
download | chef-c5d33c1298834ce40b8fbf344f281045771b5371.tar.gz |
big refactor of the repo layout. move to a chef gem and a chef-server gem all with proper deps
Diffstat (limited to 'chef/lib/chef/node.rb')
-rw-r--r-- | chef/lib/chef/node.rb | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/chef/lib/chef/node.rb b/chef/lib/chef/node.rb new file mode 100644 index 0000000000..e1c8ef2d9a --- /dev/null +++ b/chef/lib/chef/node.rb @@ -0,0 +1,260 @@ +# +# Author:: Adam Jacob (<adam@hjksolutions.com>) +# Copyright:: Copyright (c) 2008 HJK Solutions, LLC +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.join(File.dirname(__FILE__), "mixin", "check_helper") +require File.join(File.dirname(__FILE__), "mixin", "params_validate") +require File.join(File.dirname(__FILE__), "mixin", "from_file") + +require 'extlib' +require 'rubygems' +require 'json' + +class Chef + class Node + + attr_accessor :attribute, :recipe_list, :couchdb_rev + + include Chef::Mixin::CheckHelper + include Chef::Mixin::FromFile + include Chef::Mixin::ParamsValidate + + DESIGN_DOCUMENT = { + "version" => 3, + "language" => "javascript", + "views" => { + "all" => { + "map" => <<-EOJS + function(doc) { + if (doc.chef_type == "node") { + emit(doc.name, doc); + } + } + EOJS + }, + "all_id" => { + "map" => <<-EOJS + function(doc) { + if (doc.chef_type == "node") { + emit(doc.name, doc.name); + } + } + EOJS + }, + }, + } + + # Create a new Chef::Node object. + def initialize() + @name = nil + @attribute = Mash.new + @recipe_list = Array.new + @couchdb_rev = nil + @couchdb = Chef::CouchDB.new + end + + # Find a recipe for this Chef::Node by fqdn. Will search first for + # Chef::Config["node_path"]/fqdn.rb, then hostname.rb, then default.rb. + # + # Returns a new Chef::Node object. + # + # Raises an ArgumentError if it cannot find the node. + def find_file(fqdn) + node_file = nil + host_parts = fqdn.split(".") + hostname = host_parts[0] + + if File.exists?(File.join(Chef::Config[:node_path], "#{fqdn}.rb")) + node_file = File.join(Chef::Config[:node_path], "#{fqdn}.rb") + elsif File.exists?(File.join(Chef::Config[:node_path], "#{hostname}.rb")) + node_file = File.join(Chef::Config[:node_path], "#{hostname}.rb") + elsif File.exists?(File.join(Chef::Config[:node_path], "default.rb")) + node_file = File.join(Chef::Config[:node_path], "default.rb") + end + unless node_file + raise ArgumentError, "Cannot find a node matching #{fqdn}, not even with default.rb!" + end + self.from_file(node_file) + end + + # Set the name of this Node, or return the current name. + def name(arg=nil) + if arg != nil + validate( + { :name => arg }, + { + :name => { + :kind_of => String + } + } + ) + @name = arg + else + @name + end + end + + # Return an attribute of this node. Returns nil if the attribute is not found. + def [](attrib) + if @attribute.has_key?(attrib) + @attribute[attrib] + elsif @attribute.has_key?(attrib.to_s) + @attribute[attrib.to_s] + else + nil + end + end + + # Set an attribute of this node + def []=(attrib, value) + @attribute[attrib] = value + end + + # Yield each key to the block + def each(&block) + @attribute.each_key do |k| + yield(k) + end + end + + # Iterates over each attribute, passing the attribute and value to the block. + def each_attribute(&block) + @attribute.each do |k,v| + yield(k, v) + end + end + + # Return true if this Node has a given attribute, false if not. Takes either a symbol or + # a string. + def attribute?(attrib) + result = false + result = @attribute.has_key?(attrib) + return result if result + return @attribute.has_key?(attrib.to_sym) + end + + # Returns true if this Node expects a given recipe, false if not. + def recipe?(recipe_name) + @recipe_list.detect { |r| r == recipe_name } ? true : false + end + + # Returns an Array of recipes. If you call it with arguments, they will become the new + # list of recipes. + def recipes(*args) + if args.length > 0 + @recipe_list = args.flatten + else + @recipe_list + end + end + + # Set an attribute based on the missing method. If you pass an argument, we'll use that + # to set the attribute values. Otherwise, we'll wind up just returning the attributes + # value. + def method_missing(symbol, *args) + if args.length != 0 + @attribute[symbol] = args.length == 1 ? args[0] : args + else + if @attribute.has_key?(symbol) + @attribute[symbol] + else + raise ArgumentError, "Attribute #{symbol.to_s} is not defined!" + end + end + end + + def to_index + index_hash = { + :index_name => "node", + :id => "node_#{@name}", + :name => @name, + } + @attribute.each do |key, value| + index_hash[key] = value + end + index_hash[:recipe] = @recipe_list if @recipe_list.length > 0 + index_hash + end + + # Serialize this object as a hash + def to_json(*a) + result = { + "name" => @name, + 'json_class' => self.class.name, + "attributes" => @attribute, + "chef_type" => "node", + "recipes" => @recipe_list, + } + result["_rev"] = @couchdb_rev if @couchdb_rev + result.to_json(*a) + end + + # Create a Chef::Node from JSON + def self.json_create(o) + node = new + node.name(o["name"]) + o["attributes"].each do |k,v| + node[k] = v + end + o["recipes"].each do |r| + node.recipes << r + end + node.couchdb_rev = o["_rev"] if o.has_key?("_rev") + node + end + + # List all the Chef::Node objects in the CouchDB. If inflate is set to true, you will get + # the full list of all Nodes, fully inflated. + def self.list(inflate=false) + rs = Chef::CouchDB.new.list("nodes", inflate) + if inflate + rs["rows"].collect { |r| r["value"] } + else + rs["rows"].collect { |r| r["key"] } + end + end + + # Load a node by name from CouchDB + def self.load(name) + Chef::CouchDB.new.load("node", name) + end + + # Remove this node from the CouchDB + def destroy + Chef::Queue.send_msg(:queue, :remove, self) + @couchdb.delete("node", @name, @couchdb_rev) + end + + # Save this node to the CouchDB + def save + Chef::Queue.send_msg(:queue, :index, self) + results = @couchdb.store("node", @name, self) + @couchdb_rev = results["rev"] + end + + # Set up our CouchDB design document + def self.create_design_document + Chef::CouchDB.new.create_design_document("nodes", DESIGN_DOCUMENT) + end + + # As a string + def to_s + "node[#{@name}]" + end + + end +end |