Skip to content

Instantly share code, notes, and snippets.

@sbotman
Created February 5, 2016 20:12
Show Gist options
  • Save sbotman/9289a0223f82831c8f59 to your computer and use it in GitHub Desktop.
Save sbotman/9289a0223f82831c8f59 to your computer and use it in GitHub Desktop.
# file: /opt/opscode/embedded/service/opscode-erchef/lib/chef_objects-12.2.0/priv/depselector_rb/depselector.rb
# ensure that the Gemfile is in the cwd
Dir.chdir(File.dirname(__FILE__))
require 'rubygems'
require 'bundler/setup'
require 'dep_selector'
require 'erlectricity'
require 'json'
require 'pp'
def log(comment)
file = "/var/tmp/ruby_sander"
File.open(file, 'a') { |file| file.write(Time.now.to_s + ": " + comment + "\n") }
end
def run_list_func(comment)
file = "/var/tmp/run_list"
File.open(file, 'w') { |file| file.write(comment) }
end
def data_func(comment)
file = "/var/tmp/data.json"
File.open(file, 'w') { |file| file.write(comment) }
end
def all_versions_func(comment)
file = "/var/tmp/all_versions"
File.open(file, 'w') { |file| file.write(comment) }
end
def translate_constraint(constraint)
case constraint
when Symbol
case constraint
when :gt then ">"
when :gte then ">="
when :lt then "<"
when :lte then "<="
when :eq then "="
when :pes then "~>"
else constraint.to_s
end
when NilClass
"="
else
constraint
end
end
def constraint_to_str(constraint, constraint_version)
return nil unless constraint_version
"#{translate_constraint(constraint)} #{constraint_version}"
end
receive do |m|
log("step0")
m.when([:get_pid]) do
m.send!(Process.pid)
m.receive_loop
end
m.when([:solve, Erl.hash]) do |data|
data_func(data.to_json)
begin
# create dependency graph from cookbooks
log("step1")
graph = DepSelector::DependencyGraph.new
env_constraints = data[:environment_constraints].inject({}) do |acc, env_constraint|
name, version, constraint = env_constraint
acc[name] = DepSelector::VersionConstraint.new(constraint_to_str(constraint, version))
acc
end
all_versions = []
data[:all_versions].each do | vsn|
name, version_constraints = vsn
version_constraints.each do |version_constraint| # todo: constraints become an array in ruby
# due to the erlectricity conversion from
# tuples
version, constraints = version_constraint
# filter versions based on environment constraints
env_constraint = env_constraints[name]
if (!env_constraint || env_constraint.include?(DepSelector::Version.new(version)))
package_version = graph.package(name).add_version(DepSelector::Version.new(version))
constraints.each do |package_constraint|
constraint_name, constraint_version, constraint = package_constraint
version_constraint = DepSelector::VersionConstraint.new(constraint_to_str(constraint, constraint_version))
dependency = DepSelector::Dependency.new(graph.package(constraint_name), version_constraint)
package_version.dependencies << dependency
end
end
end
# regardless of filter, add package reference to all_packages
all_versions << graph.package(name)
end
run_list = data[:run_list].map do |run_list_item|
item_name, item_constraint_version, item_constraint = run_list_item
version_constraint = DepSelector::VersionConstraint.new(constraint_to_str(item_constraint,
item_constraint_version))
DepSelector::SolutionConstraint.new(graph.package(item_name), version_constraint)
end
log("step2")
timeout_ms = data[:timeout_ms]
selector = DepSelector::Selector.new(graph, (timeout_ms / 1000.0))
log("step3")
answer = begin
log("step4")
#log("runlist1" + run_list.class.to_s)
run_list.each do |object|
#log("runlist item" + object.class.to_s + ":" + object.package.class.to_s + ":" + object.contraint.class.to_s)
log("runlist item" + object.class.to_s + ":" + object.package.name.to_s)
object.package.versions.each do |version|
log("gimme version: " + version.to_s)
end
end
all_versions.each do |object|
log("runlist item version" + object.name.to_s)
end
#log("runlist2" + all_versions.o_s)
solution = selector.find_solution(run_list, all_versions)
log("step4.5")
packages = Erl::List.new
log("step5")
solution.each do |package, v|
log ("step5.5")
packages << [package, [v.major, v.minor, v.patch]]
end
[:ok, packages]
log("step6")
rescue DepSelector::Exceptions::InvalidSolutionConstraints => e
non_existent_cookbooks = e.non_existent_packages.inject(Erl::List.new) do |list, constraint|
list << constraint.package.name
end
constrained_to_no_versions = e.constrained_to_no_versions.inject(Erl::List.new) do |list, constraint|
list << constraint.to_s
end
error_detail = Erl::List.new([[:non_existent_cookbooks, non_existent_cookbooks],
[:constraints_not_met, constrained_to_no_versions]])
[:error, :invalid_constraints, error_detail]
rescue DepSelector::Exceptions::NoSolutionExists => e
most_constrained_cookbooks = e.disabled_most_constrained_packages.inject(Erl::List.new) do |list, package|
# WTF: this is the reported error format but I can't find this anywhere in the ruby code
list << "#{package.name} = #{package.versions.first.to_s}"
end
non_existent_cookbooks = e.disabled_non_existent_packages.inject(Erl::List.new) do |list, package|
list << package.name
end
error_detail = Erl::List.new([[:message, e.message],
[:unsatisfiable_run_list_item, e.unsatisfiable_solution_constraint.to_s],
[:non_existent_cookbooks, non_existent_cookbooks],
[:most_constrained_cookbooks, most_constrained_cookbooks]])
[:error, :no_solution, error_detail]
rescue DepSelector::Exceptions::TimeBoundExceeded,
DepSelector::Exceptions::TimeBoundExceededNoSolution => e
# While dep_selector differentiates between the two solutions, the opscode-chef
# API returns the same error regardless of the timeout type. We'll swallow the
# difference here and return a unified timeout to erchef
[:error, :resolution_timeout]
end
m.send!(answer)
m.receive_loop
rescue => e
answer = [:error, :exception, e.message, Erl::List.new(e.backtrace)]
m.send!(answer)
m.receive_loop
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment