Skip to content

Instantly share code, notes, and snippets.

@vinyar
Last active September 5, 2015 21:31
Show Gist options
  • Save vinyar/8e14c9ab738bcd4bae17 to your computer and use it in GitHub Desktop.
Save vinyar/8e14c9ab738bcd4bae17 to your computer and use it in GitHub Desktop.
cookbook reverse dependency lookup
Its a cookbook class that we use to abstract cookbooks and their
dependencies. The key method to look at is dependency_hash which creates
a hash based on the dependencies of the cookbook.
We do not explicitly do a reverse dependency lookup but we can use this
hash to infer a change in dependencies from a change in any cookbook in
an environment. So the flow is:
1. I change cookbook A and it gets sent to the chef server and indexed
in berks.
2. We do a berks update on the environment cookbook.
3. We invoke the kitchen builds for each cookbook in the environment
4. Each of these cookbooks checks to see if their dependency hash as
calculated by all the current berks changes from #2 above is now
different from the one we store in our LKG_{environment name}
environment that stores the hash of the cookbook as it was ehwn it last
passed its kitchen test.
Hope this makes sense.
require 'berkshelf'
require 'digest/sha1'
require 'json'
module Promote
class Cookbook
def initialize(cookbook_name, config)
@name = cookbook_name
@config = config
@metadata_path = File.join(path, 'metadata.rb')
@berks_path = File.join(path, "Berksfile")
end
def dependencies
@dependencies ||= get_synced_dependencies_from_lockfile
end
def metadata_dependencies
metadata.dependencies
end
def version(version=nil)
metadata.version
end
def version=(version)
version_line = raw_metadata[/^\s*version\s.*$/]
current_version = version_line[/('|").*("|')/].gsub(/('|")/,"")
if current_version != version.to_s
new_version_line = version_line.gsub(current_version, version.to_s)
new_content = raw_metadata.gsub(version_line, new_version_line)
save_metadata(new_content)
end
version
end
def stamp_commit(commit)
commit_line = "#sha1 '#{commit}'"
new_content = raw_metadata.gsub(/#sha1.*$/, commit_line)
if new_content[/#sha1.*$/].nil?
new_content += "\n#{commit_line}"
end
save_metadata(new_content)
end
def raw_metadata
@raw_metadata ||= File.read(@metadata_path)
end
def metadata
@metadata ||=get_metadata_from_file
end
def path
File.join(@config.cookbook_directory, @name)
end
def sync_berksfile(update=false)
return if berksfile.nil?
update = false unless File.exist?("#{@berks_path}.lock")
update ? berksfile_update : berksfile.install
end
def dependencies_changed_after_update?
old_deps = dependencies
sync_berksfile(true)
new_deps = get_dependencies_from_lockfile
return true unless old_deps.count == new_deps.count
old_deps.each do |k,v|
if k != @name
return true unless new_deps[k] == v
end
end
return false
end
def dependency_hash(environment_cookbook_name)
sync_latest_app_cookbooks(environment_cookbook_name)
hash_src = ''
dependencies.each do | k,v |
hash_src << "#{k}::#{v}::"
end
Digest::SHA1.hexdigest(hash_src)
end
def sync_latest_app_cookbooks(environment_cookbook_name)
result = {}
latest_server_cookbooks = Utils.chef_server_cookbooks(@config, 1)
env_cookbook = Cookbook.new(environment_cookbook_name, @config)
env_cookbook.metadata_dependencies.each do |key|
cb_key = key[0]
next if cb_key == @name || !latest_server_cookbooks.has_key?(cb_key)
latest_version = latest_server_cookbooks[cb_key]['versions'][0]['version']
if dependencies.keys.include?(cb_key) && latest_version > dependencies[cb_key].to_s
berksfile_update(cb_key)
result[cb_key] = latest_version
end
end
result
end
private
def berksfile
@berksfile ||= get_berksfile
end
def save_metadata(content)
File.open(@metadata_path, 'w') do |out|
out << content
end
@metadata=nil
@raw_metadata = nil
end
def get_metadata_from_file
metadata = Chef::Cookbook::Metadata.new
metadata.from_file(@metadata_path)
metadata
end
def get_synced_dependencies_from_lockfile
berksfile.install
get_dependencies_from_lockfile
end
def get_dependencies_from_lockfile
berks_deps = berksfile.list
deps = {}
berks_deps.each {|dep| deps[dep.name] = dep.locked_version}
deps
end
def get_berksfile
return unless File.exist?(@berks_path)
Berkshelf.set_format :null
Berkshelf::Berksfile.from_file(@berks_path)
end
def berksfile_update(cookbooks = nil)
cookbooks.nil? ? berksfile.update : berksfile.update(cookbooks)
@dependencies = nil
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment