diff options
Diffstat (limited to 'lib/chef/dsl')
-rw-r--r-- | lib/chef/dsl/data_query.rb | 66 | ||||
-rw-r--r-- | lib/chef/dsl/include_attribute.rb | 60 | ||||
-rw-r--r-- | lib/chef/dsl/include_recipe.rb | 42 | ||||
-rw-r--r-- | lib/chef/dsl/platform_introspection.rb | 215 | ||||
-rw-r--r-- | lib/chef/dsl/recipe.rb | 84 |
5 files changed, 467 insertions, 0 deletions
diff --git a/lib/chef/dsl/data_query.rb b/lib/chef/dsl/data_query.rb new file mode 100644 index 0000000000..ef5b490020 --- /dev/null +++ b/lib/chef/dsl/data_query.rb @@ -0,0 +1,66 @@ +# +# Author:: Adam Jacob (<adam@opscode.com>) +# Copyright:: Copyright (c) 2008 Opscode, Inc. +# 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 'chef/search/query' +require 'chef/data_bag' +require 'chef/data_bag_item' +require 'chef/encrypted_data_bag_item' + +class Chef + module DSL + + # ==Chef::DSL::DataQuery + # Provides DSL for querying data from the chef-server via search or data + # bag. + module DataQuery + + def search(*args, &block) + # If you pass a block, or have at least the start argument, do raw result parsing + # + # Otherwise, do the iteration for the end user + if Kernel.block_given? || args.length >= 4 + Chef::Search::Query.new.search(*args, &block) + else + results = Array.new + Chef::Search::Query.new.search(*args) do |o| + results << o + end + results + end + end + + def data_bag(bag) + DataBag.validate_name!(bag.to_s) + rbag = DataBag.load(bag) + rbag.keys + rescue Exception + Log.error("Failed to list data bag items in data bag: #{bag.inspect}") + raise + end + + def data_bag_item(bag, item) + DataBag.validate_name!(bag.to_s) + DataBagItem.validate_id!(item) + DataBagItem.load(bag, item) + rescue Exception + Log.error("Failed to load data bag item: #{bag.inspect} #{item.inspect}") + raise + end + end + end +end diff --git a/lib/chef/dsl/include_attribute.rb b/lib/chef/dsl/include_attribute.rb new file mode 100644 index 0000000000..d8342af6a7 --- /dev/null +++ b/lib/chef/dsl/include_attribute.rb @@ -0,0 +1,60 @@ +# +# Author:: Adam Jacob (<adam@opscode.com>) +# Copyright:: Copyright (c) 2008, 2009 Opscode, Inc. +# 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 'chef/log' + +class Chef + module DSL + module IncludeAttribute + + # Loads the attribute file specified by the short name of the + # file, e.g., loads specified cookbook's + # "attributes/mailservers.rb" + # if passed + # "mailservers" + def include_attribute(*attr_file_specs) + attr_file_specs.flatten.each do |attr_file_spec| + cookbook_name, attr_file = parse_attribute_file_spec(attr_file_spec) + if run_context.loaded_fully_qualified_attribute?(cookbook_name, attr_file) + Chef::Log.debug("I am not loading attribute file #{cookbook_name}::#{attr_file}, because I have already seen it.") + else + Chef::Log.debug("Loading Attribute #{cookbook_name}::#{attr_file}") + run_context.loaded_attribute(cookbook_name, attr_file) + attr_file_path = run_context.resolve_attribute(cookbook_name, attr_file) + node.from_file(attr_file_path) + end + end + true + end + + # Takes a attribute file specification, like "apache2" or "mysql::server" + # and converts it to a 2 element array of [cookbook_name, attribute_file_name] + def parse_attribute_file_spec(file_spec) + if match = file_spec.match(/(.+?)::(.+)/) + [match[1], match[2]] + else + [file_spec, "default"] + end + end + + end + end +end + + + diff --git a/lib/chef/dsl/include_recipe.rb b/lib/chef/dsl/include_recipe.rb new file mode 100644 index 0000000000..8cbee7a733 --- /dev/null +++ b/lib/chef/dsl/include_recipe.rb @@ -0,0 +1,42 @@ +# +# Author:: Adam Jacob (<adam@opscode.com>) +# Copyright:: Copyright (c) 2008, 2009 Opscode, Inc. +# 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 'chef/log' + +class Chef + module DSL + module IncludeRecipe + + def include_recipe(*recipe_names) + run_context.include_recipe(*recipe_names) + end + + def load_recipe(recipe_name) + run_context.load_recipe(recipe_name) + end + + def require_recipe(*args) + Chef::Log.warn("require_recipe is deprecated and will be removed in a future release, please use include_recipe") + include_recipe(*args) + end + + end + end +end + + diff --git a/lib/chef/dsl/platform_introspection.rb b/lib/chef/dsl/platform_introspection.rb new file mode 100644 index 0000000000..211def2797 --- /dev/null +++ b/lib/chef/dsl/platform_introspection.rb @@ -0,0 +1,215 @@ +# +# Author:: Adam Jacob (<adam@opscode.com>) +# Copyright:: Copyright (c) 2008 Opscode, Inc. +# 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. +# + +class Chef + module DSL + + # == Chef::DSL::PlatformIntrospection + # Provides the DSL for platform-dependent switch logic, such as + # #value_for_platform. + module PlatformIntrospection + + # Implementation class for determining platform dependent values + class PlatformDependentValue + + # Create a platform dependent value object. + # === Arguments + # platform_hash (Hash) a hash of the same structure as Chef::Platform, + # like this: + # { + # :debian => {:default => 'the value for all debian'} + # [:centos, :redhat, :fedora] => {:default => "value for all EL variants"} + # :ubuntu => { :default => "default for ubuntu", '10.04' => "value for 10.04 only"}, + # :default => "the default when nothing else matches" + # } + # * platforms can be specified as Symbols or Strings + # * multiple platforms can be grouped by using an Array as the key + # * values for platforms need to be Hashes of the form: + # {platform_version => value_for_that_version} + # * the exception to the above is the default value, which is given as + # :default => default_value + def initialize(platform_hash) + @values = {} + platform_hash.each { |platforms, value| set(platforms, value)} + end + + def value_for_node(node) + platform, version = node[:platform].to_s, node[:platform_version].to_s + if @values.key?(platform) && @values[platform].key?(version) + @values[platform][version] + elsif @values.key?(platform) && @values[platform].key?("default") + @values[platform]["default"] + elsif @values.key?("default") + @values["default"] + else + nil + end + end + + private + + def set(platforms, value) + if platforms.to_s == 'default' + @values["default"] = value + else + assert_valid_platform_values!(platforms, value) + Array(platforms).each { |platform| @values[platform.to_s] = normalize_keys(value)} + value + end + end + + def normalize_keys(hash) + hash.inject({}) do |h, key_value| + keys, value = *key_value + Array(keys).each do |key| + h[key.to_s] = value + end + h + end + end + + def assert_valid_platform_values!(platforms, value) + unless value.kind_of?(Hash) + msg = "platform dependent values must be specified in the format :platform => {:version => value} " + msg << "you gave a value #{value.inspect} for platform(s) #{platforms}" + raise ArgumentError, msg + end + end + end + + + + # Given a hash similar to the one we use for Platforms, select a value from the hash. Supports + # per platform defaults, along with a single base default. Arrays may be passed as hash keys and + # will be expanded. + # + # === Parameters + # platform_hash:: A platform-style hash. + # + # === Returns + # value:: Whatever the most specific value of the hash is. + def value_for_platform(platform_hash) + PlatformDependentValue.new(platform_hash).value_for_node(node) + end + + # Given a list of platforms, returns true if the current recipe is being run on a node with + # that platform, false otherwise. + # + # === Parameters + # args:: A list of platforms. Each platform can be in string or symbol format. + # + # === Returns + # true:: If the current platform is in the list + # false:: If the current platform is not in the list + def platform?(*args) + has_platform = false + + args.flatten.each do |platform| + has_platform = true if platform.to_s == node[:platform] + end + + has_platform + end + + + + # Implementation class for determining platform family dependent values + class PlatformFamilyDependentValue + + # Create a platform family dependent value object. + # === Arguments + # platform_family_hash (Hash) a map of platform families to values. + # like this: + # { + # :rhel => "value for all EL variants" + # :fedora => "value for fedora variants fedora and amazon" , + # [:fedora, :rhel] => "value for all known redhat variants" + # :debian => "value for debian variants including debian, ubuntu, mint" , + # :default => "the default when nothing else matches" + # } + # * platform families can be specified as Symbols or Strings + # * multiple platform families can be grouped by using an Array as the key + # * values for platform families can be any object, with no restrictions. Some examples: + # - [:stop, :start] + # - "mysql-devel" + # - { :key => "value" } + def initialize(platform_family_hash) + @values = {} + @values["default"] = nil + platform_family_hash.each { |platform_families, value| set(platform_families, value)} + end + + def value_for_node(node) + if node.key?(:platform_family) + platform_family = node[:platform_family].to_s + if @values.key?(platform_family) + @values[platform_family] + else + @values["default"] + end + else + @values["default"] + end + end + + private + + def set(platform_family, value) + if platform_family.to_s == 'default' + @values["default"] = value + else + Array(platform_family).each { |family| @values[family.to_s] = value } + value + end + end + end + + + # Given a hash mapping platform families to values, select a value from the hash. Supports a single + # base default if platform family is not in the map. Arrays may be passed as hash keys and will be + # expanded + # + # === Parameters + # platform_family_hash:: A hash in the form { platform_family_name => value } + # + # === Returns + # value:: Whatever the most specific value of the hash is. + def value_for_platform_family(platform_family_hash) + PlatformFamilyDependentValue.new(platform_family_hash).value_for_node(node) + end + + # Given a list of platform families, returns true if the current recipe is being run on a + # node within that platform family, false otherwise. + # + # === Parameters + # args:: A list of platform families. Each platform family can be in string or symbol format. + # + # === Returns + # true:: if the current node platform family is in the list. + # false:: if the current node platform family is not in the list. + def platform_family?(*args) + has_pf = false + args.flatten.each do |platform_family| + has_pf = true if platform_family.to_s == node[:platform_family] + end + has_pf + end + + end + end +end diff --git a/lib/chef/dsl/recipe.rb b/lib/chef/dsl/recipe.rb new file mode 100644 index 0000000000..1bfe8da0e0 --- /dev/null +++ b/lib/chef/dsl/recipe.rb @@ -0,0 +1,84 @@ +#-- +# Author:: Adam Jacob (<adam@opscode.com>) +# Author:: Christopher Walters (<cw@opscode.com>) +# Copyright:: Copyright (c) 2008, 2009 Opscode, Inc. +# 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 'chef/resource' +require 'chef/resource_platform_map' +require 'chef/mixin/convert_to_class_name' +require 'chef/mixin/language' + +class Chef + module DSL + + # == Chef::DSL::Recipe + # Provides the primary recipe DSL functionality for defining Chef resource + # objects via method calls. + module Recipe + + include Chef::Mixin::ConvertToClassName + + def method_missing(method_symbol, *args, &block) + # If we have a definition that matches, we want to use that instead. This should + # let you do some really crazy over-riding of "native" types, if you really want + # to. + if run_context.definitions.has_key?(method_symbol) + # This dupes the high level object, but we still need to dup the params + new_def = run_context.definitions[method_symbol].dup + new_def.params = new_def.params.dup + new_def.node = run_context.node + # This sets up the parameter overrides + new_def.instance_eval(&block) if block + new_recipe = Chef::Recipe.new(cookbook_name, @recipe_name, run_context) + new_recipe.params = new_def.params + new_recipe.params[:name] = args[0] + new_recipe.instance_eval(&new_def.recipe) + else + # Otherwise, we're rocking the regular resource call route. + + # Checks the new platform => short_name => resource mapping initially + # then fall back to the older approach (Chef::Resource.const_get) for + # backward compatibility + resource_class = Chef::Resource.resource_for_node(method_symbol, run_context.node) + + super unless resource_class + raise ArgumentError, "You must supply a name when declaring a #{method_symbol} resource" unless args.size > 0 + + # If we have a resource like this one, we want to steal its state + args << run_context + resource = resource_class.new(*args) + resource.load_prior_resource + resource.cookbook_name = cookbook_name + resource.recipe_name = @recipe_name + resource.params = @params + resource.source_line = caller[0] + # Determine whether this resource is being created in the context of an enclosing Provider + resource.enclosing_provider = self.is_a?(Chef::Provider) ? self : nil + # Evaluate resource attribute DSL + resource.instance_eval(&block) if block + + # Run optional resource hook + resource.after_created + + run_context.resource_collection.insert(resource) + resource + end + end + + end + end +end |