Skip to content

Commit 51f94a3

Browse files
author
Sumit Jamgade
committed
Added ability to include recipes smartly
Roles can get smart by making recipe inform the role about its dependencies across the proposal. So a recipe will be included by the role only if the attributes registered by the recipe are actually changed in current chef-client run. a recipe can register its dependencies as ``` mydependson = { "horizon::server" => [ ['glance','api','bind_port'] ] } BarclampLibrary::Barclamp::DependsOn.add(mydependson) ``` So here the "horizon::server" is informing about its dependencies. This is accomplished by comparing the value of the current attributes of node against the one already stored in the databag after the previous successful chef-client run. BarclampLibrary::Barclamp::DependsOn.add: takes in a map: recipe_name => [ [barclampname,drill,down,till,value], [otherbarclampname,onlyhere], ] This map is flushed and recreated only everyrun, however like resources this could also be cached. this behaviour can be altered by adding flag to config and using it 'include_recipe_smartly', however that is an extension to this behavior and can be addressed in subsequent commits Use the role(proposal) that was committed to compare against databag This object enables us to do real comparison on proposal level. Thus every barclamp can have proposal level dependency hound fixes and refactor
1 parent 43b9e38 commit 51f94a3

File tree

2 files changed

+100
-21
lines changed

2 files changed

+100
-21
lines changed

chef/cookbooks/barclamp/libraries/barclamp_library.rb

Lines changed: 78 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#
1515

1616
require_relative "conduit_resolver.rb"
17+
require "chef/role"
1718

1819
module BarclampLibrary
1920
class Barclamp
@@ -417,21 +418,93 @@ def self.size_to_bytes(s)
417418
end
418419
end
419420

421+
class DependsOn
422+
class << self
423+
def add(dependency)
424+
@recipe_depedencies ||= Mash.new
425+
@recipe_depedencies.merge!(dependency)
426+
end
427+
428+
def get(recipe)
429+
@recipe_depedencies.fetch(recipe, [])
430+
end
431+
end
432+
end
433+
420434
class Config
421435
class << self
422436
attr_accessor :node
423437

424-
def load(group, barclamp, instance = nil)
425-
# If no instance is specified, see if this node uses an instance of
426-
# this barclamp and use it
438+
def last_two_configs(group, barclamp, instance)
439+
prev_cfg = load(group, barclamp, instance)
440+
instance = infer_instance_from_cached_databag(group, barclamp, instance)
441+
cur_cfg = Chef::Role.load "#{barclamp}-config-#{instance}"
442+
return prev_cfg, cur_cfg.default_attributes[barclamp]
443+
end
444+
445+
def loadattr(collection, attrlist)
446+
begin
447+
attrlist.each do |item|
448+
unless collection.key?(item)
449+
Chef::Log.info("[smart] #{item} is missing from collection")
450+
return nil
451+
end
452+
collection = collection[item]
453+
end
454+
rescue NoMethodError
455+
Chef::Log.info("[smart] collection did not respond to []/key? while looking for #{item}")
456+
return nil
457+
end
458+
collection
459+
end
460+
461+
def guess_instance(barclamp, instance)
427462
if instance.nil? && @node[barclamp] && @node[barclamp][:config]
428463
instance = @node[barclamp][:config][:environment]
429464
end
430465

431-
# Accept environments passed as instances
432466
if instance =~ /^#{barclamp}-config-(.*)/
433467
instance = $1
434468
end
469+
instance
470+
end
471+
472+
def infer_instance_from_cached_databag(group, barclamp, instance)
473+
if instance.nil?
474+
# try the "default" instance, and fallback on any existing instance
475+
instance = "default"
476+
unless @cache["groups"][group].fetch(instance, {}).key?(barclamp)
477+
# sort to guarantee a consistent order
478+
@cache["groups"][group].keys.sort.each do |key|
479+
# ignore the id attribute from the data bag item, which is not
480+
# an instance
481+
next if key == "id"
482+
if @cache["groups"][group][key].key?(barclamp)
483+
instance = key
484+
break
485+
end
486+
end
487+
end
488+
end
489+
instance
490+
end
491+
492+
def changes_to_apply?(depedency, group = "openstack", instance = nil)
493+
barclamp = depedency.shift
494+
instance = guess_instance(barclamp, instance)
495+
prev_cfg, curr_cfg = last_two_configs(group, barclamp, instance)
496+
old = loadattr(prev_cfg, depedency)
497+
new = loadattr(curr_cfg, depedency)
498+
# Chef::Log.info("[smart] loadattr prev #{depedency}, #{prev_cfg}")
499+
# Chef::Log.info("[smart] loadattr curr #{depedency}, #{curr_cfg.inspect}")
500+
# Chef::Log.info("[smart] comparision #{old}, #{new}")
501+
old != new
502+
end
503+
504+
def load(group, barclamp, instance = nil)
505+
# If no instance is specified, see if this node uses an instance of
506+
# this barclamp and use it
507+
instance = guess_instance(barclamp, instance)
435508

436509
# Cache the config we load from data bag items.
437510
# This cache needs to be invalidated for each chef-client run from
@@ -453,23 +526,7 @@ def load(group, barclamp, instance = nil)
453526
{}
454527
end
455528

456-
if instance.nil?
457-
# try the "default" instance, and fallback on any existing instance
458-
instance = "default"
459-
unless @cache["groups"][group].fetch(instance, {}).key?(barclamp)
460-
# sort to guarantee a consistent order
461-
@cache["groups"][group].keys.sort.each do |key|
462-
# ignore the id attribute from the data bag item, which is not
463-
# an instance
464-
next if key == "id"
465-
if @cache["groups"][group][key].key?(barclamp)
466-
instance = key
467-
break
468-
end
469-
end
470-
end
471-
end
472-
529+
instance = infer_instance_from_cached_databag(group, barclamp, instance)
473530
@cache["groups"][group].fetch(instance, {}).fetch(barclamp, {})
474531
end
475532
end
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
require "chef/mixin/language_include_recipe"
2+
3+
class Chef
4+
module Mixin
5+
module LanguageIncludeRecipe
6+
def include_recipe_smartly(*list_of_recipes)
7+
list_of_recipes.each do |recipe|
8+
included = false
9+
BarclampLibrary::Barclamp::DependsOn.get(recipe).each do |dependency|
10+
next unless BarclampLibrary::Barclamp::Config.changes_to_apply?(dependency)
11+
Chef::Log.info("[smart] including recipe: #{recipe}")
12+
Chef::Log.debug("[smart] due to change in: #{dependency}")
13+
include_recipe recipe
14+
included = true
15+
break
16+
end # each
17+
Chef::Log.info("[smart] recipe excluded: #{recipe}") unless included
18+
end # each
19+
end # def include_recipe_smartly
20+
end
21+
end
22+
end

0 commit comments

Comments
 (0)