Vendor the external cookbooks
Knife-Zero doesn't include Berkshelf support, so vendoring everything in the repo is convenient again
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
class Chef
|
||||
NOT_PASSED = Object.new if !defined?(NOT_PASSED)
|
||||
# Earlier versions of Chef didn't have this message
|
||||
module ChefCompatDeprecation
|
||||
def log_deprecation(message, location=nil)
|
||||
if !location
|
||||
# Pick the first caller that is *not* part of the Chef or ChefCompat gem,
|
||||
# that's the thing the user wrote.
|
||||
chef_compat_gem_path = File.expand_path("../../..", __FILE__)
|
||||
chef_gem_path = File.expand_path("../..",::Chef::Resource.instance_method(:initialize).source_location[0])
|
||||
caller(0..10).each do |c|
|
||||
if !c.start_with?(chef_gem_path) && !c.start_with?(chef_compat_gem_path)
|
||||
location = c
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
super
|
||||
# Bleagh. `super_method` doesn't exist on older rubies and I haven't
|
||||
# figured out a way to check for its existence otherwise.
|
||||
rescue NoMethodError
|
||||
Chef::Log.warn(message)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class<<self
|
||||
prepend ChefCompatDeprecation
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,10 @@
|
||||
require 'chef/exceptions'
|
||||
|
||||
class Chef
|
||||
class Exceptions
|
||||
# Used in Resource::ActionClass#load_current_resource to denote that
|
||||
# the resource doesn't actually exist (for example, the file does not exist)
|
||||
class CurrentValueDoesNotExist < RuntimeError; end unless defined?(CurrentValueDoesNotExist)
|
||||
class CannotValidateStaticallyError < RuntimeError; end unless defined?(CannotValidateStaticallyError)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,30 @@
|
||||
require 'chef/log'
|
||||
|
||||
# for now we have to patch this in everything
|
||||
class Chef
|
||||
class Log
|
||||
def self.caller_location
|
||||
# Pick the first caller that is *not* part of the Chef gem, that's the
|
||||
# thing the user wrote.
|
||||
compat_resource_path = File.expand_path("../../../../..", __FILE__)
|
||||
chef_gem_path = Gem.loaded_specs['chef'].full_gem_path
|
||||
caller(0..20).find { |c| !c.start_with?(compat_resource_path) && !c.start_with?(chef_gem_path) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if Gem::Requirement.new('< 12.13.37').satisfied_by?(Gem::Version.new(Chef::VERSION))
|
||||
|
||||
# FIXME: why does this not match the implementation in Chef itself?
|
||||
class Chef
|
||||
class Log
|
||||
module ChefCompatDeprecation
|
||||
def deprecation(message, location=nil)
|
||||
Chef.log_deprecation(message, location)
|
||||
end
|
||||
end
|
||||
extend ChefCompatDeprecation
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,17 @@
|
||||
if Chef::VERSION.to_f >= 12.5 && Chef::VERSION.to_f <= 12.8
|
||||
require 'chef/mixin/params_validate'
|
||||
class Chef
|
||||
module Mixin
|
||||
module ParamsValidate
|
||||
class SetOrReturnProperty < Chef::Property
|
||||
# 12.9 introduced a new optional parameter to `get()` to avoid a nil-set warning.
|
||||
# When their method gets called with 2 args, we need to ignore and call with 1.
|
||||
alias_method :_original_get2, :get
|
||||
def get(resource, *args)
|
||||
_original_get2(resource)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
class Chef
|
||||
class Node
|
||||
unless method_defined?(:set_cookbook_attribute)
|
||||
def set_cookbook_attribute
|
||||
# this implementation deliberately left blank - we don't need to do anything we just need to not fail
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,15 @@
|
||||
# 12.9 introduced a new optional parameter to `get()` to avoid a nil-set warning.
|
||||
# We need to mimick it here.
|
||||
if Chef::VERSION.to_f >= 12.5 && Chef::VERSION.to_f <= 12.8
|
||||
require 'chef/property'
|
||||
class Chef
|
||||
class Property
|
||||
# 12.9 introduced a new optional parameter to `get()` to avoid a nil-set warning.
|
||||
# When their method gets called with 2 args, we need to ignore and call with 1.
|
||||
alias_method :_original_get, :get
|
||||
def get(resource, *args)
|
||||
_original_get(resource)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,65 @@
|
||||
require 'chef/provider'
|
||||
require 'chef/provider/lwrp_base'
|
||||
|
||||
class Chef::Provider
|
||||
if !defined?(InlineResources)
|
||||
InlineResources = Chef::Provider::LWRPBase::InlineResources
|
||||
end
|
||||
module InlineResources
|
||||
require 'chef/dsl/recipe'
|
||||
require 'chef/dsl/platform_introspection'
|
||||
require 'chef/dsl/data_query'
|
||||
require 'chef/dsl/include_recipe'
|
||||
include Chef::DSL::Recipe
|
||||
include Chef::DSL::PlatformIntrospection
|
||||
include Chef::DSL::DataQuery
|
||||
include Chef::DSL::IncludeRecipe
|
||||
|
||||
unless Chef::Provider::InlineResources::ClassMethods.instance_method(:action).source_location[0] =~ /chefspec/
|
||||
# Don't override action if chefspec is doing its thing
|
||||
module ::ChefCompat
|
||||
module Monkeypatches
|
||||
module InlineResources
|
||||
module ClassMethods
|
||||
def action(name, &block)
|
||||
super(name) { send("compile_action_#{name}") }
|
||||
# We put the action in its own method so that super() works.
|
||||
define_method("compile_action_#{name}", &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
module ClassMethods
|
||||
prepend ChefCompat::Monkeypatches::InlineResources::ClassMethods
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class Chef
|
||||
class Provider
|
||||
class LWRPBase < Provider
|
||||
if defined?(InlineResources)
|
||||
module InlineResources
|
||||
# since we upgrade the Chef::Runner and Chef::RunContext globally to >= 12.14 style classes, we need to also
|
||||
# fix the use_inline_resources LWRPBase wrapper that creates a sub-resource collection with the ugpraded code
|
||||
# from the Chef::Provider subclasses that do similar things in post-12.5 chef.
|
||||
def recipe_eval_with_update_check(&block)
|
||||
old_run_context = run_context
|
||||
@run_context = run_context.create_child
|
||||
return_value = instance_eval(&block)
|
||||
Chef::Runner.new(run_context).converge
|
||||
return_value
|
||||
ensure
|
||||
if run_context.resource_collection.any? { |r| r.updated? }
|
||||
new_resource.updated_by_last_action(true)
|
||||
end
|
||||
@run_context = old_run_context
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,118 @@
|
||||
#
|
||||
# NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
|
||||
#
|
||||
# THIS IS A FILE AUTOGENERATED BY 'rake update' DO NOT EDIT!!!!
|
||||
#
|
||||
# NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
|
||||
#
|
||||
|
||||
if Gem::Requirement.new('< 12.16.42').satisfied_by?(Gem::Version.new(Chef::VERSION))
|
||||
#--
|
||||
# Author:: Adam Jacob (<adam@chef.io>)
|
||||
# Author:: Christopher Walters (<cw@chef.io>)
|
||||
# Copyright:: Copyright 2008-2016, Chef Software 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/dsl/recipe"
|
||||
require "chef/mixin/from_file"
|
||||
require "chef/mixin/deprecation"
|
||||
|
||||
class Chef
|
||||
# == Chef::Recipe
|
||||
# A Recipe object is the context in which Chef recipes are evaluated.
|
||||
class Recipe
|
||||
attr_accessor :cookbook_name, :recipe_name, :recipe, :params, :run_context
|
||||
|
||||
include Chef::DSL::Recipe
|
||||
|
||||
include Chef::Mixin::FromFile
|
||||
include Chef::Mixin::Deprecation
|
||||
|
||||
# Parses a potentially fully-qualified recipe name into its
|
||||
# cookbook name and recipe short name.
|
||||
#
|
||||
# For example:
|
||||
# "aws::elastic_ip" returns [:aws, "elastic_ip"]
|
||||
# "aws" returns [:aws, "default"]
|
||||
# "::elastic_ip" returns [ current_cookbook, "elastic_ip" ]
|
||||
#--
|
||||
# TODO: Duplicates functionality of RunListItem
|
||||
def self.parse_recipe_name(recipe_name, current_cookbook: nil)
|
||||
case recipe_name
|
||||
when /(.+?)::(.+)/
|
||||
[ $1.to_sym, $2 ]
|
||||
when /^::(.+)/
|
||||
raise "current_cookbook is nil, cannot resolve #{recipe_name}" if current_cookbook.nil?
|
||||
[ current_cookbook.to_sym, $1 ]
|
||||
else
|
||||
[ recipe_name.to_sym, "default" ]
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(cookbook_name, recipe_name, run_context)
|
||||
@cookbook_name = cookbook_name
|
||||
@recipe_name = recipe_name
|
||||
@run_context = run_context
|
||||
# TODO: 5/19/2010 cw/tim: determine whether this can be removed
|
||||
@params = Hash.new
|
||||
end
|
||||
|
||||
# Used in DSL mixins
|
||||
def node
|
||||
run_context.node
|
||||
end
|
||||
|
||||
# Used by the DSL to look up resources when executing in the context of a
|
||||
# recipe.
|
||||
def resources(*args)
|
||||
run_context.resource_collection.find(*args)
|
||||
end
|
||||
|
||||
# This was moved to Chef::Node#tag, redirecting here for compatibility
|
||||
def tag(*tags)
|
||||
run_context.node.tag(*tags)
|
||||
end
|
||||
|
||||
# Returns true if the node is tagged with *all* of the supplied +tags+.
|
||||
#
|
||||
# === Parameters
|
||||
# tags<Array>:: A list of tags
|
||||
#
|
||||
# === Returns
|
||||
# true<TrueClass>:: If all the parameters are present
|
||||
# false<FalseClass>:: If any of the parameters are missing
|
||||
def tagged?(*tags)
|
||||
tags.each do |tag|
|
||||
return false unless run_context.node.tags.include?(tag)
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
# Removes the list of tags from the node.
|
||||
#
|
||||
# === Parameters
|
||||
# tags<Array>:: A list of tags
|
||||
#
|
||||
# === Returns
|
||||
# tags<Array>:: The current list of run_context.node.tags
|
||||
def untag(*tags)
|
||||
tags.each do |tag|
|
||||
run_context.node.tags.delete(tag)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
require 'chef/recipe'
|
||||
require 'chef_compat/recipe'
|
||||
|
||||
class Chef::Recipe
|
||||
# If the cookbook depends on compat_resource, create a ChefCompat::Recipe object
|
||||
# instead of Chef::Recipe, for the extra goodies.
|
||||
def self.new(cookbook_name, recipe_name, run_context)
|
||||
if run_context &&
|
||||
cookbook_name &&
|
||||
recipe_name &&
|
||||
run_context.cookbook_collection &&
|
||||
run_context.cookbook_collection[cookbook_name] &&
|
||||
run_context.cookbook_collection[cookbook_name].metadata.dependencies.has_key?('compat_resource') &&
|
||||
self != ::ChefCompat::Recipe
|
||||
::ChefCompat::Recipe.new(cookbook_name, recipe_name, run_context)
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,156 @@
|
||||
# this is NOT an AUTOGENERATED file
|
||||
|
||||
require 'chef/resource'
|
||||
|
||||
class Chef
|
||||
class Resource
|
||||
|
||||
class UnresolvedSubscribes < self
|
||||
# The full key ise given as the name in {Resource#subscribes}
|
||||
alias_method :to_s, :name
|
||||
alias_method :declared_key, :name
|
||||
end
|
||||
|
||||
#
|
||||
# Force a delayed notification into this resource's run_context.
|
||||
#
|
||||
# This should most likely be paired with action :nothing
|
||||
#
|
||||
# @param arg [Array[Symbol], Symbol] A list of actions (e.g. `:create`)
|
||||
#
|
||||
def delayed_action(arg)
|
||||
arg = Array(arg).map(&:to_sym)
|
||||
arg.map do |action|
|
||||
validate(
|
||||
{ action: action },
|
||||
{ action: { kind_of: Symbol, equal_to: allowed_actions } }
|
||||
)
|
||||
# the resource effectively sends a delayed notification to itself
|
||||
run_context.add_delayed_action(Notification.new(self, action, self))
|
||||
end
|
||||
end
|
||||
|
||||
def subscribes(action, resources, timing = :delayed)
|
||||
resources = [resources].flatten
|
||||
resources.each do |resource|
|
||||
if resource.is_a?(String)
|
||||
resource = UnresolvedSubscribes.new(resource, run_context)
|
||||
end
|
||||
if resource.run_context.nil?
|
||||
resource.run_context = run_context
|
||||
end
|
||||
resource.notifies(action, self, timing)
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def notifies(action, resource_spec, timing = :delayed)
|
||||
# when using old-style resources(:template => "/foo.txt") style, you
|
||||
# could end up with multiple resources.
|
||||
validate_resource_spec!(resource_spec)
|
||||
|
||||
resources = [ resource_spec ].flatten
|
||||
resources.each do |resource|
|
||||
|
||||
case timing.to_s
|
||||
when "delayed"
|
||||
notifies_delayed(action, resource)
|
||||
when "immediate", "immediately"
|
||||
notifies_immediately(action, resource)
|
||||
when "before"
|
||||
notifies_before(action, resource)
|
||||
else
|
||||
raise ArgumentError, "invalid timing: #{timing} for notifies(#{action}, #{resources.inspect}, #{timing}) resource #{self} "\
|
||||
"Valid timings are: :delayed, :immediate, :immediately, :before"
|
||||
end
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
#
|
||||
# Iterates over all immediate and delayed notifications, calling
|
||||
# resolve_resource_reference on each in turn, causing them to
|
||||
# resolve lazy/forward references.
|
||||
def resolve_notification_references
|
||||
run_context.before_notifications(self).each { |n|
|
||||
n.resolve_resource_reference(run_context.resource_collection)
|
||||
}
|
||||
run_context.immediate_notifications(self).each { |n|
|
||||
n.resolve_resource_reference(run_context.resource_collection)
|
||||
}
|
||||
run_context.delayed_notifications(self).each {|n|
|
||||
n.resolve_resource_reference(run_context.resource_collection)
|
||||
}
|
||||
end
|
||||
|
||||
# Helper for #notifies
|
||||
def notifies_before(action, resource_spec)
|
||||
run_context.notifies_before(Notification.new(resource_spec, action, self))
|
||||
end
|
||||
|
||||
# Helper for #notifies
|
||||
def notifies_immediately(action, resource_spec)
|
||||
run_context.notifies_immediately(Notification.new(resource_spec, action, self))
|
||||
end
|
||||
|
||||
# Helper for #notifies
|
||||
def notifies_delayed(action, resource_spec)
|
||||
run_context.notifies_delayed(Notification.new(resource_spec, action, self))
|
||||
end
|
||||
|
||||
#
|
||||
# Get the current actual value of this resource.
|
||||
#
|
||||
# This does not cache--a new value will be returned each time.
|
||||
#
|
||||
# @return A new copy of the resource, with values filled in from the actual
|
||||
# current value.
|
||||
#
|
||||
def current_value
|
||||
provider = provider_for_action(Array(action).first)
|
||||
if provider.whyrun_mode? && !provider.whyrun_supported?
|
||||
raise "Cannot retrieve #{self.class.current_resource} in why-run mode: #{provider} does not support why-run"
|
||||
end
|
||||
provider.load_current_resource
|
||||
provider.current_resource
|
||||
end
|
||||
|
||||
# These methods are necessary for new resources to initialize old ones properly
|
||||
attr_reader :resource_initializing
|
||||
def resource_initializing=(value)
|
||||
if value
|
||||
@resource_initializing = value
|
||||
else
|
||||
remove_instance_variable(:@resource_initializing)
|
||||
end
|
||||
end
|
||||
|
||||
if !respond_to?(:resource_name)
|
||||
def self.resource_name(name=Chef::NOT_PASSED)
|
||||
# Setter
|
||||
if name != Chef::NOT_PASSED
|
||||
# remove_canonical_dsl
|
||||
|
||||
# Set the resource_name and call provides
|
||||
if name
|
||||
name = name.to_sym
|
||||
# If our class is not already providing this name, provide it.
|
||||
# Commented out: use of resource_name and provides will need to be
|
||||
# mutually exclusive in this world, generally.
|
||||
# if !Chef::ResourceResolver.includes_handler?(name, self)
|
||||
provides name#, canonical: true
|
||||
# end
|
||||
@resource_name = name
|
||||
else
|
||||
@resource_name = nil
|
||||
end
|
||||
end
|
||||
@resource_name
|
||||
end
|
||||
def self.resource_name=(name)
|
||||
resource_name(name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,60 @@
|
||||
require 'chef_compat/resource/lwrp_base'
|
||||
require 'chef/resource/lwrp_base'
|
||||
|
||||
module ChefCompat
|
||||
module Monkeypatches
|
||||
#
|
||||
# NOTE: LOTS OF METAPROGRAMMING HERE. NOT FOR FAINT OF HEART.
|
||||
#
|
||||
|
||||
# Add an empty module to Class so we can temporarily override it in build_from_file
|
||||
module Class
|
||||
end
|
||||
class<<::Class
|
||||
prepend(ChefCompat::Monkeypatches::Class)
|
||||
end
|
||||
|
||||
module Chef
|
||||
module Resource
|
||||
module LWRPBase
|
||||
def build_from_file(cookbook_name, filename, run_context)
|
||||
# If the cookbook this LWRP is from depends on compat_resource, fix its LWRPs up real good
|
||||
if run_context.cookbook_collection[cookbook_name].metadata.dependencies.has_key?('compat_resource')
|
||||
# All cookbooks do Class.new(Chef::Resource::LWRPBase). Change Class.new
|
||||
# temporarily to translate Chef::Resource::LWRPBase to ChefCompat::Resource
|
||||
ChefCompat::Monkeypatches::Class.module_eval do
|
||||
def new(*args, &block)
|
||||
# Trick it! Use ChefCompat::Resource instead of Chef::Resource::LWRPBase
|
||||
if args == [ ::Chef::Resource::LWRPBase ]
|
||||
ChefCompat::Monkeypatches::Class.module_eval do
|
||||
remove_method(:new) if method_defined?(:new)
|
||||
end
|
||||
args = [ ChefCompat::Resource::LWRPBase ]
|
||||
end
|
||||
super(*args, &block)
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
|
||||
# Call the actual build_from_file
|
||||
super
|
||||
|
||||
ensure
|
||||
class<<ChefCompat::Monkeypatches::Class
|
||||
remove_method(:new) if method_defined?(:new)
|
||||
end
|
||||
end
|
||||
else
|
||||
# Call the actual build_from_file
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
class <<::Chef::Resource::LWRPBase
|
||||
prepend(LWRPBase)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,167 @@
|
||||
#
|
||||
# Author:: Lamont Granquist (<lamont@chef.io>)
|
||||
# Copyright:: Copyright 2015-2016, Chef Software, 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.
|
||||
#
|
||||
|
||||
# XXX: we now have two copies of this file in the compat_resource cookbook. I'm uncertain if this is a
|
||||
# bug or a feature, and I suspect it is actually a feature. The point of this file is that for all
|
||||
# resources and cookbooks the global Chef::ResourceBuilder class must be upgraded to at least the
|
||||
# 12.10.24 version. The point of the other copy is that for compat_resource cookbooks all their
|
||||
# resources should be using the lastest version that has been sync'd. So these two files should
|
||||
# diverge as times goes on. I believe that is the correct behavior and that we want to have both
|
||||
# files in this cookbook.
|
||||
|
||||
# NOTE: this was extracted from the Recipe DSL mixin, relevant specs are in spec/unit/recipe_spec.rb
|
||||
|
||||
if Gem::Requirement.new("< 12.10.24").satisfied_by?(Gem::Version.new(Chef::VERSION))
|
||||
begin
|
||||
require 'chef/resource_builder'
|
||||
# we use the LoadError this creates on early 12.x to not monkeypatch chef client versions that don't have Chef::ResourceBuilder
|
||||
# (it is lazily included and doesn't appear until compile time so we can't resolve the symbol during library loading)
|
||||
|
||||
class Chef
|
||||
class ResourceBuilder
|
||||
attr_reader :type
|
||||
attr_reader :name
|
||||
attr_reader :created_at
|
||||
attr_reader :params
|
||||
attr_reader :run_context
|
||||
attr_reader :cookbook_name
|
||||
attr_reader :recipe_name
|
||||
attr_reader :enclosing_provider
|
||||
attr_reader :resource
|
||||
|
||||
# FIXME (ruby-2.1 syntax): most of these are mandatory
|
||||
def initialize(type:nil, name:nil, created_at: nil, params: nil, run_context: nil, cookbook_name: nil, recipe_name: nil, enclosing_provider: nil)
|
||||
@type = type
|
||||
@name = name
|
||||
@created_at = created_at
|
||||
@params = params
|
||||
@run_context = run_context
|
||||
@cookbook_name = cookbook_name
|
||||
@recipe_name = recipe_name
|
||||
@enclosing_provider = enclosing_provider
|
||||
end
|
||||
|
||||
def build(&block)
|
||||
raise ArgumentError, "You must supply a name when declaring a #{type} resource" if name.nil?
|
||||
|
||||
@resource = resource_class.new(name, run_context)
|
||||
if resource.resource_name.nil?
|
||||
raise Chef::Exceptions::InvalidResourceSpecification, "#{resource}.resource_name is `nil`! Did you forget to put `provides :blah` or `resource_name :blah` in your resource class?"
|
||||
end
|
||||
resource.source_line = created_at
|
||||
resource.declared_type = type
|
||||
|
||||
# If we have a resource like this one, we want to steal its state
|
||||
# This behavior is very counter-intuitive and should be removed.
|
||||
# See CHEF-3694, https://tickets.opscode.com/browse/CHEF-3694
|
||||
# Moved to this location to resolve CHEF-5052, https://tickets.opscode.com/browse/CHEF-5052
|
||||
if prior_resource
|
||||
resource.load_from(prior_resource)
|
||||
end
|
||||
|
||||
resource.cookbook_name = cookbook_name
|
||||
resource.recipe_name = recipe_name
|
||||
# Determine whether this resource is being created in the context of an enclosing Provider
|
||||
resource.enclosing_provider = enclosing_provider
|
||||
|
||||
# XXX: this is required for definition params inside of the scope of a
|
||||
# subresource to work correctly.
|
||||
resource.params = params
|
||||
|
||||
# Evaluate resource attribute DSL
|
||||
if block_given?
|
||||
resource.resource_initializing = true
|
||||
begin
|
||||
resource.instance_eval(&block)
|
||||
ensure
|
||||
resource.resource_initializing = false
|
||||
end
|
||||
end
|
||||
|
||||
# emit a cloned resource warning if it is warranted
|
||||
if prior_resource
|
||||
if is_trivial_resource?(prior_resource) && identicalish_resources?(prior_resource, resource)
|
||||
emit_harmless_cloning_debug
|
||||
else
|
||||
emit_cloned_resource_warning
|
||||
end
|
||||
end
|
||||
|
||||
# Run optional resource hook
|
||||
resource.after_created
|
||||
|
||||
resource
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def resource_class
|
||||
# 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(type, run_context.node)
|
||||
end
|
||||
|
||||
def is_trivial_resource?(resource)
|
||||
identicalish_resources?(resource_class.new(name, run_context), resource)
|
||||
end
|
||||
|
||||
# this is an equality test specific to checking for 3694 cloning warnings
|
||||
def identicalish_resources?(first, second)
|
||||
skipped_ivars = [ :@source_line, :@cookbook_name, :@recipe_name, :@params, :@elapsed_time, :@declared_type ]
|
||||
checked_ivars = ( first.instance_variables | second.instance_variables ) - skipped_ivars
|
||||
non_matching_ivars = checked_ivars.reject do |iv|
|
||||
if iv == :@action && ( [first.instance_variable_get(iv)].flatten == [:nothing] || [second.instance_variable_get(iv)].flatten == [:nothing] )
|
||||
# :nothing action on either side of the comparison always matches
|
||||
true
|
||||
else
|
||||
first.instance_variable_get(iv) == second.instance_variable_get(iv)
|
||||
end
|
||||
end
|
||||
Chef::Log.debug("ivars which did not match with the prior resource: #{non_matching_ivars}")
|
||||
non_matching_ivars.empty?
|
||||
end
|
||||
|
||||
def emit_cloned_resource_warning
|
||||
Chef::Log.warn("Cloning resource attributes for #{resource} from prior resource (CHEF-3694)")
|
||||
Chef::Log.warn("Previous #{prior_resource}: #{prior_resource.source_line}") if prior_resource.source_line
|
||||
Chef::Log.warn("Current #{resource}: #{resource.source_line}") if resource.source_line
|
||||
end
|
||||
|
||||
def emit_harmless_cloning_debug
|
||||
Chef::Log.debug("Harmless resource cloning from #{prior_resource}:#{prior_resource.source_line} to #{resource}:#{resource.source_line}")
|
||||
end
|
||||
|
||||
def prior_resource
|
||||
@prior_resource ||=
|
||||
begin
|
||||
key = "#{type}[#{name}]"
|
||||
run_context.resource_collection.lookup_local(key)
|
||||
rescue Chef::Exceptions::ResourceNotFound
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
rescue LoadError
|
||||
# cool we're just on early chef 12.x, nothing to do -- we don't have to worry because there's also not parent_run_context pointer, so we don't have to
|
||||
# use lookup_local to avoid resource cloning shit out of the parent run_context. the resource collection's lookup() method will always use lookup_local
|
||||
# over lookup_recursive.
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,103 @@
|
||||
#
|
||||
# Author:: Adam Jacob (<adam@chef.io>)
|
||||
# Author:: Christopher Walters (<cw@chef.io>)
|
||||
# Copyright:: Copyright 2008-2016, Chef Software 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_collection/resource_set"
|
||||
require "chef/resource_collection/resource_list"
|
||||
require "chef/resource_collection"
|
||||
require "chef/exceptions"
|
||||
|
||||
module ChefCompat
|
||||
module Monkeypatches
|
||||
module Chef
|
||||
module ResourceCollection
|
||||
module RecursiveNotificationLookup
|
||||
#
|
||||
# Copied verbatim from Chef 12.10.24
|
||||
#
|
||||
attr_accessor :run_context
|
||||
|
||||
def initialize(run_context = nil)
|
||||
super()
|
||||
@run_context = run_context
|
||||
end
|
||||
|
||||
def lookup_local(key)
|
||||
resource_set.lookup(key)
|
||||
end
|
||||
|
||||
def find_local(*args)
|
||||
resource_set.find(*args)
|
||||
end
|
||||
|
||||
def lookup(key)
|
||||
if run_context.nil?
|
||||
lookup_local(key)
|
||||
else
|
||||
lookup_recursive(run_context, key)
|
||||
end
|
||||
end
|
||||
|
||||
def find(*args)
|
||||
if run_context.nil?
|
||||
find_local(*args)
|
||||
else
|
||||
find_recursive(run_context, *args)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def lookup_recursive(rc, key)
|
||||
rc.resource_collection.send(:resource_set).lookup(key)
|
||||
rescue ::Chef::Exceptions::ResourceNotFound
|
||||
raise if !rc.respond_to?(:parent_run_context) || rc.parent_run_context.nil?
|
||||
lookup_recursive(rc.parent_run_context, key)
|
||||
end
|
||||
|
||||
def find_recursive(rc, *args)
|
||||
rc.resource_collection.send(:resource_set).find(*args)
|
||||
rescue ::Chef::Exceptions::ResourceNotFound
|
||||
raise if !rc.respond_to?(:parent_run_context) || rc.parent_run_context.nil?
|
||||
find_recursive(rc.parent_run_context, *args)
|
||||
end
|
||||
end
|
||||
|
||||
module DeleteResources
|
||||
#
|
||||
# Copied verbatim from Chef 12.10.24
|
||||
#
|
||||
def delete(key)
|
||||
resource_list.delete(key)
|
||||
resource_set.delete(key)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class Chef::ResourceCollection
|
||||
unless method_defined?(:lookup_local)
|
||||
prepend ChefCompat::Monkeypatches::Chef::ResourceCollection::RecursiveNotificationLookup
|
||||
end
|
||||
unless method_defined?(:delete)
|
||||
prepend ChefCompat::Monkeypatches::Chef::ResourceCollection::DeleteResources
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,49 @@
|
||||
#
|
||||
# Author:: Tyler Ball (<tball@chef.io>)
|
||||
# Copyright:: Copyright 2014-2016, Chef Software, 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_collection/resource_list"
|
||||
require "chef/exceptions"
|
||||
|
||||
module ChefCompat
|
||||
module Monkeypatches
|
||||
module Chef
|
||||
module ResourceCollection
|
||||
module ResourceList
|
||||
module DeleteResource
|
||||
# Copied verbatim from Chef 12.10.4
|
||||
def delete(key)
|
||||
raise ArgumentError, "Must pass a Chef::Resource or String to delete" unless key.is_a?(String) || key.is_a?(Chef::Resource)
|
||||
key = key.to_s
|
||||
ret = @resources.reject! { |r| r.to_s == key }
|
||||
if ret.nil?
|
||||
raise ::Chef::Exceptions::ResourceNotFound, "Cannot find a resource matching #{key} (did you define it first?)"
|
||||
end
|
||||
ret
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Chef::ResourceCollection::ResourceList
|
||||
unless method_defined?(:delete)
|
||||
prepend ChefCompat::Monkeypatches::Chef::ResourceCollection::ResourceList::DeleteResource
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,49 @@
|
||||
#
|
||||
# Author:: Tyler Ball (<tball@chef.io>)
|
||||
# Copyright:: Copyright 2014-2016, Chef Software, 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_collection/resource_list"
|
||||
require "chef/exceptions"
|
||||
|
||||
module ChefCompat
|
||||
module Monkeypatches
|
||||
module Chef
|
||||
module ResourceCollection
|
||||
module ResourceSet
|
||||
module DeleteResource
|
||||
def delete(key)
|
||||
raise ArgumentError, "Must pass a Chef::Resource or String to delete" unless key.is_a?(String) || key.is_a?(Chef::Resource)
|
||||
key = key.to_s
|
||||
res = @resources_by_key.delete(key)
|
||||
|
||||
if res == @resources_by_key.default
|
||||
raise Chef::Exceptions::ResourceNotFound, "Cannot find a resource matching #{key} (did you define it first?)"
|
||||
end
|
||||
res
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Chef::ResourceCollection::ResourceSet
|
||||
unless method_defined?(:delete)
|
||||
prepend ChefCompat::Monkeypatches::Chef::ResourceCollection::ResourceSet::DeleteResource
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,691 @@
|
||||
#
|
||||
# NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
|
||||
#
|
||||
# THIS IS A FILE AUTOGENERATED BY 'rake update' DO NOT EDIT!!!!
|
||||
#
|
||||
# NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
|
||||
#
|
||||
|
||||
if Gem::Requirement.new('< 12.16.42').satisfied_by?(Gem::Version.new(Chef::VERSION))
|
||||
#
|
||||
# Author:: Adam Jacob (<adam@chef.io>)
|
||||
# Author:: Christopher Walters (<cw@chef.io>)
|
||||
# Author:: Tim Hinderliter (<tim@chef.io>)
|
||||
# Copyright:: Copyright 2008-2016, Chef Software 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_collection"
|
||||
require "chef/cookbook_version"
|
||||
require "chef/node"
|
||||
require "chef/role"
|
||||
require "chef/log"
|
||||
require "chef/recipe"
|
||||
require "chef/run_context/cookbook_compiler"
|
||||
require "chef/event_dispatch/events_output_stream"
|
||||
require "forwardable"
|
||||
|
||||
class Chef
|
||||
|
||||
# == Chef::RunContext
|
||||
# Value object that loads and tracks the context of a Chef run
|
||||
class RunContext
|
||||
#
|
||||
# Global state
|
||||
#
|
||||
|
||||
#
|
||||
# The node for this run
|
||||
#
|
||||
# @return [Chef::Node]
|
||||
#
|
||||
attr_reader :node
|
||||
|
||||
#
|
||||
# The set of cookbooks involved in this run
|
||||
#
|
||||
# @return [Chef::CookbookCollection]
|
||||
#
|
||||
attr_reader :cookbook_collection
|
||||
|
||||
#
|
||||
# Resource Definitions for this run. Populated when the files in
|
||||
# +definitions/+ are evaluated (this is triggered by #load).
|
||||
#
|
||||
# @return [Array[Chef::ResourceDefinition]]
|
||||
#
|
||||
attr_reader :definitions
|
||||
|
||||
#
|
||||
# Event dispatcher for this run.
|
||||
#
|
||||
# @return [Chef::EventDispatch::Dispatcher]
|
||||
#
|
||||
attr_reader :events
|
||||
|
||||
#
|
||||
# Hash of factoids for a reboot request.
|
||||
#
|
||||
# @return [Hash]
|
||||
#
|
||||
attr_accessor :reboot_info
|
||||
|
||||
#
|
||||
# Scoped state
|
||||
#
|
||||
|
||||
#
|
||||
# The parent run context.
|
||||
#
|
||||
# @return [Chef::RunContext] The parent run context, or `nil` if this is the
|
||||
# root context.
|
||||
#
|
||||
attr_reader :parent_run_context
|
||||
|
||||
#
|
||||
# The root run context.
|
||||
#
|
||||
# @return [Chef::RunContext] The root run context.
|
||||
#
|
||||
def root_run_context
|
||||
rc = self
|
||||
rc = rc.parent_run_context until rc.parent_run_context.nil?
|
||||
rc
|
||||
end
|
||||
|
||||
#
|
||||
# The collection of resources intended to be converged (and able to be
|
||||
# notified).
|
||||
#
|
||||
# @return [Chef::ResourceCollection]
|
||||
#
|
||||
# @see CookbookCompiler
|
||||
#
|
||||
attr_reader :resource_collection
|
||||
|
||||
#
|
||||
# The list of control groups to execute during the audit phase
|
||||
#
|
||||
attr_reader :audits
|
||||
|
||||
#
|
||||
# Notification handling
|
||||
#
|
||||
|
||||
#
|
||||
# A Hash containing the before notifications triggered by resources
|
||||
# during the converge phase of the chef run.
|
||||
#
|
||||
# @return [Hash[String, Array[Chef::Resource::Notification]]] A hash from
|
||||
# <notifying resource name> => <list of notifications it sent>
|
||||
#
|
||||
attr_reader :before_notification_collection
|
||||
|
||||
#
|
||||
# A Hash containing the immediate notifications triggered by resources
|
||||
# during the converge phase of the chef run.
|
||||
#
|
||||
# @return [Hash[String, Array[Chef::Resource::Notification]]] A hash from
|
||||
# <notifying resource name> => <list of notifications it sent>
|
||||
#
|
||||
attr_reader :immediate_notification_collection
|
||||
|
||||
#
|
||||
# A Hash containing the delayed (end of run) notifications triggered by
|
||||
# resources during the converge phase of the chef run.
|
||||
#
|
||||
# @return [Hash[String, Array[Chef::Resource::Notification]]] A hash from
|
||||
# <notifying resource name> => <list of notifications it sent>
|
||||
#
|
||||
attr_reader :delayed_notification_collection
|
||||
|
||||
#
|
||||
# An Array containing the delayed (end of run) notifications triggered by
|
||||
# resources during the converge phase of the chef run.
|
||||
#
|
||||
# @return [Array[Chef::Resource::Notification]] An array of notification objects
|
||||
#
|
||||
attr_reader :delayed_actions
|
||||
|
||||
# Creates a new Chef::RunContext object and populates its fields. This object gets
|
||||
# used by the Chef Server to generate a fully compiled recipe list for a node.
|
||||
#
|
||||
# @param node [Chef::Node] The node to run against.
|
||||
# @param cookbook_collection [Chef::CookbookCollection] The cookbooks
|
||||
# involved in this run.
|
||||
# @param events [EventDispatch::Dispatcher] The event dispatcher for this
|
||||
# run.
|
||||
#
|
||||
def initialize(node, cookbook_collection, events)
|
||||
@node = node
|
||||
@cookbook_collection = cookbook_collection
|
||||
@events = events
|
||||
|
||||
node.run_context = self
|
||||
node.set_cookbook_attribute
|
||||
|
||||
@definitions = Hash.new
|
||||
@loaded_recipes_hash = {}
|
||||
@loaded_attributes_hash = {}
|
||||
@reboot_info = {}
|
||||
@cookbook_compiler = nil
|
||||
@delayed_actions = []
|
||||
|
||||
initialize_child_state
|
||||
end
|
||||
|
||||
#
|
||||
# Triggers the compile phase of the chef run.
|
||||
#
|
||||
# @param run_list_expansion [Chef::RunList::RunListExpansion] The run list.
|
||||
# @see Chef::RunContext::CookbookCompiler
|
||||
#
|
||||
def load(run_list_expansion)
|
||||
@cookbook_compiler = CookbookCompiler.new(self, run_list_expansion, events)
|
||||
cookbook_compiler.compile
|
||||
end
|
||||
|
||||
#
|
||||
# Initialize state that applies to both Chef::RunContext and Chef::ChildRunContext
|
||||
#
|
||||
def initialize_child_state
|
||||
@audits = {}
|
||||
@resource_collection = Chef::ResourceCollection.new(self)
|
||||
@before_notification_collection = Hash.new { |h, k| h[k] = [] }
|
||||
@immediate_notification_collection = Hash.new { |h, k| h[k] = [] }
|
||||
@delayed_notification_collection = Hash.new { |h, k| h[k] = [] }
|
||||
@delayed_actions = []
|
||||
end
|
||||
|
||||
#
|
||||
# Adds an before notification to the +before_notification_collection+.
|
||||
#
|
||||
# @param [Chef::Resource::Notification] The notification to add.
|
||||
#
|
||||
def notifies_before(notification)
|
||||
# Note for the future, notification.notifying_resource may be an instance
|
||||
# of Chef::Resource::UnresolvedSubscribes when calling {Resource#subscribes}
|
||||
# with a string value.
|
||||
before_notification_collection[notification.notifying_resource.declared_key] << notification
|
||||
end
|
||||
|
||||
#
|
||||
# Adds an immediate notification to the +immediate_notification_collection+.
|
||||
#
|
||||
# @param [Chef::Resource::Notification] The notification to add.
|
||||
#
|
||||
def notifies_immediately(notification)
|
||||
# Note for the future, notification.notifying_resource may be an instance
|
||||
# of Chef::Resource::UnresolvedSubscribes when calling {Resource#subscribes}
|
||||
# with a string value.
|
||||
immediate_notification_collection[notification.notifying_resource.declared_key] << notification
|
||||
end
|
||||
|
||||
#
|
||||
# Adds a delayed notification to the +delayed_notification_collection+.
|
||||
#
|
||||
# @param [Chef::Resource::Notification] The notification to add.
|
||||
#
|
||||
def notifies_delayed(notification)
|
||||
# Note for the future, notification.notifying_resource may be an instance
|
||||
# of Chef::Resource::UnresolvedSubscribes when calling {Resource#subscribes}
|
||||
# with a string value.
|
||||
delayed_notification_collection[notification.notifying_resource.declared_key] << notification
|
||||
end
|
||||
|
||||
#
|
||||
# Adds a delayed action to the +delayed_actions+.
|
||||
#
|
||||
def add_delayed_action(notification)
|
||||
if delayed_actions.any? { |existing_notification| existing_notification.duplicates?(notification) }
|
||||
Chef::Log.info( "#{notification.notifying_resource} not queuing delayed action #{notification.action} on #{notification.resource}"\
|
||||
" (delayed), as it's already been queued")
|
||||
else
|
||||
delayed_actions << notification
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Get the list of before notifications sent by the given resource.
|
||||
#
|
||||
# @return [Array[Notification]]
|
||||
#
|
||||
def before_notifications(resource)
|
||||
return before_notification_collection[resource.declared_key]
|
||||
end
|
||||
|
||||
#
|
||||
# Get the list of immediate notifications sent by the given resource.
|
||||
#
|
||||
# @return [Array[Notification]]
|
||||
#
|
||||
def immediate_notifications(resource)
|
||||
return immediate_notification_collection[resource.declared_key]
|
||||
end
|
||||
|
||||
#
|
||||
# Get the list of delayed (end of run) notifications sent by the given
|
||||
# resource.
|
||||
#
|
||||
# @return [Array[Notification]]
|
||||
#
|
||||
def delayed_notifications(resource)
|
||||
return delayed_notification_collection[resource.declared_key]
|
||||
end
|
||||
|
||||
#
|
||||
# Cookbook and recipe loading
|
||||
#
|
||||
|
||||
#
|
||||
# Evaluates the recipes +recipe_names+. Used by DSL::IncludeRecipe
|
||||
#
|
||||
# @param recipe_names [Array[String]] The list of recipe names (e.g.
|
||||
# 'my_cookbook' or 'my_cookbook::my_resource').
|
||||
# @param current_cookbook The cookbook we are currently running in.
|
||||
#
|
||||
# @see DSL::IncludeRecipe#include_recipe
|
||||
#
|
||||
def include_recipe(*recipe_names, current_cookbook: nil)
|
||||
result_recipes = Array.new
|
||||
recipe_names.flatten.each do |recipe_name|
|
||||
if result = load_recipe(recipe_name, current_cookbook: current_cookbook)
|
||||
result_recipes << result
|
||||
end
|
||||
end
|
||||
result_recipes
|
||||
end
|
||||
|
||||
#
|
||||
# Evaluates the recipe +recipe_name+. Used by DSL::IncludeRecipe
|
||||
#
|
||||
# TODO I am sort of confused why we have both this and include_recipe ...
|
||||
# I don't see anything different beyond accepting and returning an
|
||||
# array of recipes.
|
||||
#
|
||||
# @param recipe_names [Array[String]] The recipe name (e.g 'my_cookbook' or
|
||||
# 'my_cookbook::my_resource').
|
||||
# @param current_cookbook The cookbook we are currently running in.
|
||||
#
|
||||
# @return A truthy value if the load occurred; `false` if already loaded.
|
||||
#
|
||||
# @see DSL::IncludeRecipe#load_recipe
|
||||
#
|
||||
def load_recipe(recipe_name, current_cookbook: nil)
|
||||
Chef::Log.debug("Loading recipe #{recipe_name} via include_recipe")
|
||||
|
||||
cookbook_name, recipe_short_name = Chef::Recipe.parse_recipe_name(recipe_name, current_cookbook: current_cookbook)
|
||||
|
||||
if unreachable_cookbook?(cookbook_name) # CHEF-4367
|
||||
Chef::Log.warn(<<-ERROR_MESSAGE)
|
||||
MissingCookbookDependency:
|
||||
Recipe `#{recipe_name}` is not in the run_list, and cookbook '#{cookbook_name}'
|
||||
is not a dependency of any cookbook in the run_list. To load this recipe,
|
||||
first add a dependency on cookbook '#{cookbook_name}' in the cookbook you're
|
||||
including it from in that cookbook's metadata.
|
||||
ERROR_MESSAGE
|
||||
end
|
||||
|
||||
if loaded_fully_qualified_recipe?(cookbook_name, recipe_short_name)
|
||||
Chef::Log.debug("I am not loading #{recipe_name}, because I have already seen it.")
|
||||
false
|
||||
else
|
||||
loaded_recipe(cookbook_name, recipe_short_name)
|
||||
node.loaded_recipe(cookbook_name, recipe_short_name)
|
||||
cookbook = cookbook_collection[cookbook_name]
|
||||
cookbook.load_recipe(recipe_short_name, self)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Load the given recipe from a filename.
|
||||
#
|
||||
# @param recipe_file [String] The recipe filename.
|
||||
#
|
||||
# @return [Chef::Recipe] The loaded recipe.
|
||||
#
|
||||
# @raise [Chef::Exceptions::RecipeNotFound] If the file does not exist.
|
||||
#
|
||||
def load_recipe_file(recipe_file)
|
||||
if !File.exist?(recipe_file)
|
||||
raise Chef::Exceptions::RecipeNotFound, "could not find recipe file #{recipe_file}"
|
||||
end
|
||||
|
||||
Chef::Log.debug("Loading recipe file #{recipe_file}")
|
||||
recipe = Chef::Recipe.new("@recipe_files", recipe_file, self)
|
||||
recipe.from_file(recipe_file)
|
||||
recipe
|
||||
end
|
||||
|
||||
#
|
||||
# Look up an attribute filename.
|
||||
#
|
||||
# @param cookbook_name [String] The cookbook name of the attribute file.
|
||||
# @param attr_file_name [String] The attribute file's name (not path).
|
||||
#
|
||||
# @return [String] The filename.
|
||||
#
|
||||
# @see DSL::IncludeAttribute#include_attribute
|
||||
#
|
||||
# @raise [Chef::Exceptions::CookbookNotFound] If the cookbook could not be found.
|
||||
# @raise [Chef::Exceptions::AttributeNotFound] If the attribute file could not be found.
|
||||
#
|
||||
def resolve_attribute(cookbook_name, attr_file_name)
|
||||
cookbook = cookbook_collection[cookbook_name]
|
||||
raise Chef::Exceptions::CookbookNotFound, "could not find cookbook #{cookbook_name} while loading attribute #{name}" unless cookbook
|
||||
|
||||
attribute_filename = cookbook.attribute_filenames_by_short_filename[attr_file_name]
|
||||
raise Chef::Exceptions::AttributeNotFound, "could not find filename for attribute #{attr_file_name} in cookbook #{cookbook_name}" unless attribute_filename
|
||||
|
||||
attribute_filename
|
||||
end
|
||||
|
||||
#
|
||||
# A list of all recipes that have been loaded.
|
||||
#
|
||||
# This is stored internally as a Hash, so ordering is predictable.
|
||||
#
|
||||
# TODO is the above statement true in a 1.9+ ruby world? Is it relevant?
|
||||
#
|
||||
# @return [Array[String]] A list of recipes in fully qualified form, e.g.
|
||||
# the recipe "nginx" will be given as "nginx::default".
|
||||
#
|
||||
# @see #loaded_recipe? To determine if a particular recipe has been loaded.
|
||||
#
|
||||
def loaded_recipes
|
||||
loaded_recipes_hash.keys
|
||||
end
|
||||
|
||||
#
|
||||
# A list of all attributes files that have been loaded.
|
||||
#
|
||||
# Stored internally using a Hash, so order is predictable.
|
||||
#
|
||||
# TODO is the above statement true in a 1.9+ ruby world? Is it relevant?
|
||||
#
|
||||
# @return [Array[String]] A list of attribute file names in fully qualified
|
||||
# form, e.g. the "nginx" will be given as "nginx::default".
|
||||
#
|
||||
def loaded_attributes
|
||||
loaded_attributes_hash.keys
|
||||
end
|
||||
|
||||
#
|
||||
# Find out if a given recipe has been loaded.
|
||||
#
|
||||
# @param cookbook [String] Cookbook name.
|
||||
# @param recipe [String] Recipe name.
|
||||
#
|
||||
# @return [Boolean] `true` if the recipe has been loaded, `false` otherwise.
|
||||
#
|
||||
def loaded_fully_qualified_recipe?(cookbook, recipe)
|
||||
loaded_recipes_hash.has_key?("#{cookbook}::#{recipe}")
|
||||
end
|
||||
|
||||
#
|
||||
# Find out if a given recipe has been loaded.
|
||||
#
|
||||
# @param recipe [String] Recipe name. "nginx" and "nginx::default" yield
|
||||
# the same results.
|
||||
#
|
||||
# @return [Boolean] `true` if the recipe has been loaded, `false` otherwise.
|
||||
#
|
||||
def loaded_recipe?(recipe)
|
||||
cookbook, recipe_name = Chef::Recipe.parse_recipe_name(recipe)
|
||||
loaded_fully_qualified_recipe?(cookbook, recipe_name)
|
||||
end
|
||||
|
||||
#
|
||||
# Mark a given recipe as having been loaded.
|
||||
#
|
||||
# @param cookbook [String] Cookbook name.
|
||||
# @param recipe [String] Recipe name.
|
||||
#
|
||||
def loaded_recipe(cookbook, recipe)
|
||||
loaded_recipes_hash["#{cookbook}::#{recipe}"] = true
|
||||
end
|
||||
|
||||
#
|
||||
# Find out if a given attribute file has been loaded.
|
||||
#
|
||||
# @param cookbook [String] Cookbook name.
|
||||
# @param attribute_file [String] Attribute file name.
|
||||
#
|
||||
# @return [Boolean] `true` if the recipe has been loaded, `false` otherwise.
|
||||
#
|
||||
def loaded_fully_qualified_attribute?(cookbook, attribute_file)
|
||||
loaded_attributes_hash.has_key?("#{cookbook}::#{attribute_file}")
|
||||
end
|
||||
|
||||
#
|
||||
# Mark a given attribute file as having been loaded.
|
||||
#
|
||||
# @param cookbook [String] Cookbook name.
|
||||
# @param attribute_file [String] Attribute file name.
|
||||
#
|
||||
def loaded_attribute(cookbook, attribute_file)
|
||||
loaded_attributes_hash["#{cookbook}::#{attribute_file}"] = true
|
||||
end
|
||||
|
||||
##
|
||||
# Cookbook File Introspection
|
||||
|
||||
#
|
||||
# Find out if the cookbook has the given template.
|
||||
#
|
||||
# @param cookbook [String] Cookbook name.
|
||||
# @param template_name [String] Template name.
|
||||
#
|
||||
# @return [Boolean] `true` if the template is in the cookbook, `false`
|
||||
# otherwise.
|
||||
# @see Chef::CookbookVersion#has_template_for_node?
|
||||
#
|
||||
def has_template_in_cookbook?(cookbook, template_name)
|
||||
cookbook = cookbook_collection[cookbook]
|
||||
cookbook.has_template_for_node?(node, template_name)
|
||||
end
|
||||
|
||||
#
|
||||
# Find out if the cookbook has the given file.
|
||||
#
|
||||
# @param cookbook [String] Cookbook name.
|
||||
# @param cb_file_name [String] File name.
|
||||
#
|
||||
# @return [Boolean] `true` if the file is in the cookbook, `false`
|
||||
# otherwise.
|
||||
# @see Chef::CookbookVersion#has_cookbook_file_for_node?
|
||||
#
|
||||
def has_cookbook_file_in_cookbook?(cookbook, cb_file_name)
|
||||
cookbook = cookbook_collection[cookbook]
|
||||
cookbook.has_cookbook_file_for_node?(node, cb_file_name)
|
||||
end
|
||||
|
||||
#
|
||||
# Find out whether the given cookbook is in the cookbook dependency graph.
|
||||
#
|
||||
# @param cookbook_name [String] Cookbook name.
|
||||
#
|
||||
# @return [Boolean] `true` if the cookbook is reachable, `false` otherwise.
|
||||
#
|
||||
# @see Chef::CookbookCompiler#unreachable_cookbook?
|
||||
def unreachable_cookbook?(cookbook_name)
|
||||
cookbook_compiler.unreachable_cookbook?(cookbook_name)
|
||||
end
|
||||
|
||||
#
|
||||
# Open a stream object that can be printed into and will dispatch to events
|
||||
#
|
||||
# @param name [String] The name of the stream.
|
||||
# @param options [Hash] Other options for the stream.
|
||||
#
|
||||
# @return [EventDispatch::EventsOutputStream] The created stream.
|
||||
#
|
||||
# @yield If a block is passed, it will be run and the stream will be closed
|
||||
# afterwards.
|
||||
# @yieldparam stream [EventDispatch::EventsOutputStream] The created stream.
|
||||
#
|
||||
def open_stream(name: nil, **options)
|
||||
stream = EventDispatch::EventsOutputStream.new(events, name: name, **options)
|
||||
if block_given?
|
||||
begin
|
||||
yield stream
|
||||
ensure
|
||||
stream.close
|
||||
end
|
||||
else
|
||||
stream
|
||||
end
|
||||
end
|
||||
|
||||
# there are options for how to handle multiple calls to these functions:
|
||||
# 1. first call always wins (never change reboot_info once set).
|
||||
# 2. last call always wins (happily change reboot_info whenever).
|
||||
# 3. raise an exception on the first conflict.
|
||||
# 4. disable reboot after this run if anyone ever calls :cancel.
|
||||
# 5. raise an exception on any second call.
|
||||
# 6. ?
|
||||
def request_reboot(reboot_info)
|
||||
Chef::Log.info "Changing reboot status from #{self.reboot_info.inspect} to #{reboot_info.inspect}"
|
||||
@reboot_info = reboot_info
|
||||
end
|
||||
|
||||
def cancel_reboot
|
||||
Chef::Log.info "Changing reboot status from #{reboot_info.inspect} to {}"
|
||||
@reboot_info = {}
|
||||
end
|
||||
|
||||
def reboot_requested?
|
||||
reboot_info.size > 0
|
||||
end
|
||||
|
||||
#
|
||||
# Create a child RunContext.
|
||||
#
|
||||
def create_child
|
||||
ChildRunContext.new(self)
|
||||
end
|
||||
|
||||
# @api private
|
||||
attr_writer :resource_collection
|
||||
|
||||
protected
|
||||
|
||||
attr_reader :cookbook_compiler
|
||||
attr_reader :loaded_attributes_hash
|
||||
attr_reader :loaded_recipes_hash
|
||||
|
||||
module Deprecated
|
||||
###
|
||||
# These need to be settable so deploy can run a resource_collection
|
||||
# independent of any cookbooks via +recipe_eval+
|
||||
def audits=(value)
|
||||
Chef.log_deprecation("Setting run_context.audits will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
|
||||
@audits = value
|
||||
end
|
||||
|
||||
def immediate_notification_collection=(value)
|
||||
Chef.log_deprecation("Setting run_context.immediate_notification_collection will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
|
||||
@immediate_notification_collection = value
|
||||
end
|
||||
|
||||
def delayed_notification_collection=(value)
|
||||
Chef.log_deprecation("Setting run_context.delayed_notification_collection will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
|
||||
@delayed_notification_collection = value
|
||||
end
|
||||
end
|
||||
prepend Deprecated
|
||||
|
||||
#
|
||||
# A child run context. Delegates all root context calls to its parent.
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
class ChildRunContext < RunContext
|
||||
extend Forwardable
|
||||
def_delegators :parent_run_context, *%w{
|
||||
cancel_reboot
|
||||
config
|
||||
cookbook_collection
|
||||
cookbook_compiler
|
||||
definitions
|
||||
events
|
||||
has_cookbook_file_in_cookbook?
|
||||
has_template_in_cookbook?
|
||||
load
|
||||
loaded_attribute
|
||||
loaded_attributes
|
||||
loaded_attributes_hash
|
||||
loaded_fully_qualified_attribute?
|
||||
loaded_fully_qualified_recipe?
|
||||
loaded_recipe
|
||||
loaded_recipe?
|
||||
loaded_recipes
|
||||
loaded_recipes_hash
|
||||
node
|
||||
open_stream
|
||||
reboot_info
|
||||
reboot_info=
|
||||
reboot_requested?
|
||||
request_reboot
|
||||
resolve_attribute
|
||||
unreachable_cookbook?
|
||||
}
|
||||
|
||||
def initialize(parent_run_context)
|
||||
@parent_run_context = parent_run_context
|
||||
|
||||
# We don't call super, because we don't bother initializing stuff we're
|
||||
# going to delegate to the parent anyway. Just initialize things that
|
||||
# every instance needs.
|
||||
initialize_child_state
|
||||
end
|
||||
|
||||
CHILD_STATE = %w{
|
||||
audits
|
||||
audits=
|
||||
create_child
|
||||
add_delayed_action
|
||||
delayed_actions
|
||||
delayed_notification_collection
|
||||
delayed_notification_collection=
|
||||
delayed_notifications
|
||||
immediate_notification_collection
|
||||
immediate_notification_collection=
|
||||
immediate_notifications
|
||||
before_notification_collection
|
||||
before_notifications
|
||||
include_recipe
|
||||
initialize_child_state
|
||||
load_recipe
|
||||
load_recipe_file
|
||||
notifies_before
|
||||
notifies_immediately
|
||||
notifies_delayed
|
||||
parent_run_context
|
||||
root_run_context
|
||||
resource_collection
|
||||
resource_collection=
|
||||
}.map { |x| x.to_sym }
|
||||
|
||||
# Verify that we didn't miss any methods
|
||||
unless @__skip_method_checking # hook specifically for compat_resource
|
||||
missing_methods = superclass.instance_methods(false) - instance_methods(false) - CHILD_STATE
|
||||
if !missing_methods.empty?
|
||||
raise "ERROR: not all methods of RunContext accounted for in ChildRunContext! All methods must be marked as child methods with CHILD_STATE or delegated to the parent_run_context. Missing #{missing_methods.join(", ")}."
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,153 @@
|
||||
#
|
||||
# NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
|
||||
#
|
||||
# THIS IS A FILE AUTOGENERATED BY 'rake update' DO NOT EDIT!!!!
|
||||
#
|
||||
# NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
|
||||
#
|
||||
|
||||
if Gem::Requirement.new('< 12.16.42').satisfied_by?(Gem::Version.new(Chef::VERSION))
|
||||
#--
|
||||
# Author:: Adam Jacob (<adam@chef.io>)
|
||||
# Author:: Christopher Walters (<cw@chef.io>)
|
||||
# Author:: Tim Hinderliter (<tim@chef.io>)
|
||||
# Copyright:: Copyright 2008-2016, Chef Software 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/exceptions"
|
||||
require "chef/mixin/params_validate"
|
||||
require "chef/node"
|
||||
require "chef/resource_collection"
|
||||
|
||||
class Chef
|
||||
# == Chef::Runner
|
||||
# This class is responsible for executing the steps in a Chef run.
|
||||
class Runner
|
||||
|
||||
attr_reader :run_context
|
||||
|
||||
include Chef::Mixin::ParamsValidate
|
||||
|
||||
def initialize(run_context)
|
||||
@run_context = run_context
|
||||
end
|
||||
|
||||
def delayed_actions
|
||||
@run_context.delayed_actions
|
||||
end
|
||||
|
||||
def events
|
||||
@run_context.events
|
||||
end
|
||||
|
||||
# Determine the appropriate provider for the given resource, then
|
||||
# execute it.
|
||||
def run_action(resource, action, notification_type = nil, notifying_resource = nil)
|
||||
# If there are any before notifications, why-run the resource
|
||||
# and notify anyone who needs notifying
|
||||
before_notifications = run_context.before_notifications(resource) || []
|
||||
unless before_notifications.empty?
|
||||
forced_why_run do
|
||||
Chef::Log.info("#{resource} running why-run #{action} action to support before action")
|
||||
resource.run_action(action, notification_type, notifying_resource)
|
||||
end
|
||||
|
||||
if resource.updated_by_last_action?
|
||||
before_notifications.each do |notification|
|
||||
Chef::Log.info("#{resource} sending #{notification.action} action to #{notification.resource} (before)")
|
||||
run_action(notification.resource, notification.action, :before, resource)
|
||||
end
|
||||
resource.updated_by_last_action(false)
|
||||
end
|
||||
end
|
||||
|
||||
# Actually run the action for realsies
|
||||
resource.run_action(action, notification_type, notifying_resource)
|
||||
|
||||
# Execute any immediate and queue up any delayed notifications
|
||||
# associated with the resource, but only if it was updated *this time*
|
||||
# we ran an action on it.
|
||||
if resource.updated_by_last_action?
|
||||
run_context.immediate_notifications(resource).each do |notification|
|
||||
Chef::Log.info("#{resource} sending #{notification.action} action to #{notification.resource} (immediate)")
|
||||
run_action(notification.resource, notification.action, :immediate, resource)
|
||||
end
|
||||
|
||||
run_context.delayed_notifications(resource).each do |notification|
|
||||
# send the notification to the run_context of the receiving resource
|
||||
notification.resource.run_context.add_delayed_action(notification)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Iterates over the +resource_collection+ in the +run_context+ calling
|
||||
# +run_action+ for each resource in turn.
|
||||
def converge
|
||||
# Resolve all lazy/forward references in notifications
|
||||
run_context.resource_collection.each do |resource|
|
||||
resource.resolve_notification_references
|
||||
end
|
||||
|
||||
# Execute each resource.
|
||||
run_context.resource_collection.execute_each_resource do |resource|
|
||||
Array(resource.action).each { |action| run_action(resource, action) }
|
||||
end
|
||||
|
||||
rescue Exception => e
|
||||
Chef::Log.info "Running queued delayed notifications before re-raising exception"
|
||||
run_delayed_notifications(e)
|
||||
else
|
||||
run_delayed_notifications(nil)
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Run all our :delayed actions
|
||||
def run_delayed_notifications(error = nil)
|
||||
collected_failures = Exceptions::MultipleFailures.new
|
||||
collected_failures.client_run_failure(error) unless error.nil?
|
||||
delayed_actions.each do |notification|
|
||||
result = run_delayed_notification(notification)
|
||||
if result.kind_of?(Exception)
|
||||
collected_failures.notification_failure(result)
|
||||
end
|
||||
end
|
||||
collected_failures.raise!
|
||||
end
|
||||
|
||||
def run_delayed_notification(notification)
|
||||
Chef::Log.info( "#{notification.notifying_resource} sending #{notification.action}"\
|
||||
" action to #{notification.resource} (delayed)")
|
||||
# Struct of resource/action to call
|
||||
run_action(notification.resource, notification.action, :delayed)
|
||||
true
|
||||
rescue Exception => e
|
||||
e
|
||||
end
|
||||
|
||||
# helper to run a block of code with why_run forced to true and then restore it correctly
|
||||
def forced_why_run
|
||||
saved = Chef::Config[:why_run]
|
||||
Chef::Config[:why_run] = true
|
||||
yield
|
||||
ensure
|
||||
Chef::Config[:why_run] = saved
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user