Skip to content

Commit

Permalink
Added ability to include recipes smartly
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Sumit Jamgade committed Jan 22, 2019
1 parent 43b9e38 commit 319518b
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 21 deletions.
102 changes: 81 additions & 21 deletions chef/cookbooks/barclamp/libraries/barclamp_library.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#

require_relative "conduit_resolver.rb"
require "chef/role"

module BarclampLibrary
class Barclamp
Expand Down Expand Up @@ -417,21 +418,96 @@ def self.size_to_bytes(s)
end
end

class DependsOn
class << self
def add(dependency)
@recipe_depedencies ||= Mash.new
@recipe_depedencies.merge!(dependency)
end

def get(recipe)
@recipe_depedencies.fetch(recipe, [])
end
end
end

class Config
class << self
attr_accessor :node

def load(group, barclamp, instance = nil)
# If no instance is specified, see if this node uses an instance of
# this barclamp and use it
def last_two_configs(group, barclamp, instance)
prev_cfg = load(group, barclamp, instance)
instance = infer_instance(group, barclamp, instance)
cur_cfg = Chef::Role.load "#{barclamp}-config-#{instance}"
return prev_cfg, cur_cfg.default_attributes[barclamp]
end

def loadattr(hash, attrlist)
i = 0
return hash unless attrlist.length > 0
while hash.respond_to?(:[])
unless hash.has_key?(attrlist[i])
Chef::Log.debug("[smart] hash is missing #{attrlist[i]}")
return nil
end
hash = hash[attrlist[i]]
i += 1
if attrlist.length == i
# Chef::Log.debug("[smart] loadattr return #{hash}")
return hash
end
end
Chef::Log.debug("[smart] hash does not respond to []")
nil
end

def guess_instance(barclamp, instance)
if instance.nil? && @node[barclamp] && @node[barclamp][:config]
instance = @node[barclamp][:config][:environment]
end

# Accept environments passed as instances
if instance =~ /^#{barclamp}-config-(.*)/
instance = $1
end
instance
end

def infer_instance(group, barclamp, instance)
if instance.nil?
# try the "default" instance, and fallback on any existing instance
instance = "default"
unless @cache["groups"][group].fetch(instance, {}).key?(barclamp)
# sort to guarantee a consistent order
@cache["groups"][group].keys.sort.each do |key|
# ignore the id attribute from the data bag item, which is not
# an instance
next if key == "id"
if @cache["groups"][group][key].key?(barclamp)
instance = key
break
end
end
end
end
instance
end

def changes_to_apply?(depedency, group = "openstack", instance = nil)
barclamp = depedency.shift
instance = guess_instance(barclamp, instance)
prev_cfg, curr_cfg = last_two_configs(group, barclamp, instance)
old = loadattr(prev_cfg, depedency)
new = loadattr(curr_cfg, depedency)
# Chef::Log.debug("[smart] loadattr prev #{depedency}, #{prev_cfg}")
# Chef::Log.debug("[smart] loadattr curr #{depedency}, #{curr_cfg.inspect}")
# Chef::Log.debug("[smart] comparision #{old}, #{new}")
old != new
end

def load(group, barclamp, instance = nil)
# If no instance is specified, see if this node uses an instance of
# this barclamp and use it
instance = guess_instance(barclamp, instance)

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

if instance.nil?
# try the "default" instance, and fallback on any existing instance
instance = "default"
unless @cache["groups"][group].fetch(instance, {}).key?(barclamp)
# sort to guarantee a consistent order
@cache["groups"][group].keys.sort.each do |key|
# ignore the id attribute from the data bag item, which is not
# an instance
next if key == "id"
if @cache["groups"][group][key].key?(barclamp)
instance = key
break
end
end
end
end

instance = infer_instance(group, barclamp, instance)
@cache["groups"][group].fetch(instance, {}).fetch(barclamp, {})
end
end
Expand Down
22 changes: 22 additions & 0 deletions chef/cookbooks/barclamp/libraries/smartroles.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
require "chef/mixin/language_include_recipe"

class Chef
module Mixin
module LanguageIncludeRecipe
def include_recipe_smartly(*list_of_recipes)
list_of_recipes.each do |recipe|
included = false
BarclampLibrary::Barclamp::DependsOn.get(recipe).each do |dependency|
next unless BarclampLibrary::Barclamp::Config.changes_to_apply?(dependency)
Chef::Log.info("[smart] including recipe: #{recipe}")
Chef::Log.debug("[smart] due to change in: #{dependency}")
include_recipe recipe
included = true
break
end # each
Chef::Log.info("[smart] recipe excluded: #{recipe}") unless included
end # each
end # def include_recipe_smartly
end
end
end

0 comments on commit 319518b

Please sign in to comment.