class Chef::Provider::Package::Yum::PythonHelper

Constants

YUM_HELPER

Attributes

inpipe[RW]
outpipe[RW]
stderr[RW]
stdin[RW]
stdout[RW]
wait_thr[RW]

Public Instance Methods

check() click to toggle source
# File lib/chef/provider/package/yum/python_helper.rb, line 75
def check
  start if stdin.nil?
end
compare_versions(version1, version2) click to toggle source
# File lib/chef/provider/package/yum/python_helper.rb, line 79
def compare_versions(version1, version2)
  query("versioncompare", { "versions" => [version1, version2] }).to_i
end
install_only_packages(name) click to toggle source
# File lib/chef/provider/package/yum/python_helper.rb, line 83
def install_only_packages(name)
  query_output = query("installonlypkgs", { "package" => name })
  if query_output == "False"
    return false
  elsif query_output == "True"
    return true
  end
end
options_params(options) click to toggle source
# File lib/chef/provider/package/yum/python_helper.rb, line 92
def options_params(options)
  options.each_with_object({}) do |opt, h|
    if opt =~ /--enablerepo=(.+)/
      $1.split(",").each do |repo|
        h["repos"] ||= []
        h["repos"].push( { "enable" => repo } )
      end
    end
    if opt =~ /--disablerepo=(.+)/
      $1.split(",").each do |repo|
        h["repos"] ||= []
        h["repos"].push( { "disable" => repo } )
      end
    end
  end
end
package_query(action, provides, version: nil, arch: nil, options: {}) click to toggle source

@returns Array<Version> NB: “options” here is the yum_package options hash and is deliberately not **opts

# File lib/chef/provider/package/yum/python_helper.rb, line 111
def package_query(action, provides, version: nil, arch: nil, options: {})
  parameters = { "provides" => provides, "version" => version, "arch" => arch }
  repo_opts = options_params(options || {})
  parameters.merge!(repo_opts)
  query_output = query(action, parameters)
  version = parse_response(query_output.lines.last)
  Chef::Log.trace "parsed #{version} from python helper"
  # XXX: for now we restart after every query with an enablerepo/disablerepo to clean the helpers internal state
  restart unless repo_opts.empty?
  version
end
reap() click to toggle source
# File lib/chef/provider/package/yum/python_helper.rb, line 57
def reap
  unless wait_thr.nil?
    Process.kill("INT", wait_thr.pid) rescue nil
    begin
      Timeout.timeout(3) do
        wait_thr.value # this calls waitpid()
      end
    rescue Timeout::Error
      Process.kill("KILL", wait_thr.pid) rescue nil
    end
    stdin.close unless stdin.nil?
    stdout.close unless stdout.nil?
    stderr.close unless stderr.nil?
    inpipe.close unless inpipe.nil?
    outpipe.close unless outpipe.nil?
  end
end
restart() click to toggle source
# File lib/chef/provider/package/yum/python_helper.rb, line 123
def restart
  reap
  start
end
start() click to toggle source
# File lib/chef/provider/package/yum/python_helper.rb, line 48
def start
  ENV["PYTHONUNBUFFERED"] = "1"
  @inpipe, inpipe_write = IO.pipe
  outpipe_read, @outpipe = IO.pipe
  @stdin, @stdout, @stderr, @wait_thr = Open3.popen3("#{yum_command} #{outpipe_read.fileno} #{inpipe_write.fileno}", outpipe_read.fileno => outpipe_read, inpipe_write.fileno => inpipe_write, close_others: false)
  outpipe_read.close
  inpipe_write.close
end
yum_command() click to toggle source
# File lib/chef/provider/package/yum/python_helper.rb, line 42
def yum_command
  @yum_command ||= which("python", "python2", "python2.7") do |f|
    shell_out("#{f} -c 'import yum'").exitstatus == 0
  end + " #{YUM_HELPER}"
end

Private Instance Methods

add_version(hash, version) click to toggle source

i couldn't figure out how to decompose an evr on the python side, it seems reasonably painless to do it in ruby (generally massaging nevras in the ruby side is HIGHLY discouraged – this is an “every rule has an exception” exception – any additional functionality should probably trigger moving this regexp logic into python)

# File lib/chef/provider/package/yum/python_helper.rb, line 134
def add_version(hash, version)
  epoch = nil
  if version =~ /(\S+):(\S+)/
    epoch = $1
    version = $2
  end
  if version =~ /(\S+)-(\S+)/
    version = $1
    release = $2
  end
  hash["epoch"] = epoch unless epoch.nil?
  hash["release"] = release unless release.nil?
  hash["version"] = version
end
build_query(action, parameters) click to toggle source
# File lib/chef/provider/package/yum/python_helper.rb, line 160
def build_query(action, parameters)
  hash = { "action" => action }
  parameters.each do |param_name, param_value|
    hash[param_name] = param_value unless param_value.nil?
  end

  # Special handling for certain action / param combos
  if [:whatinstalled, :whatavailable].include?(action)
    add_version(hash, parameters["version"]) unless parameters["version"].nil?
  end

  FFI_Yajl::Encoder.encode(hash)
end
drain_fds() click to toggle source
# File lib/chef/provider/package/yum/python_helper.rb, line 179
def drain_fds
  output = ""
  fds, = IO.select([stderr, stdout, inpipe], nil, nil, 0)
  unless fds.nil?
    fds.each do |fd|
      output += fd.sysread(4096) rescue ""
    end
  end
  output
rescue => e
  output
end
parse_response(output) click to toggle source
# File lib/chef/provider/package/yum/python_helper.rb, line 174
def parse_response(output)
  array = output.split.map { |x| x == "nil" ? nil : x }
  array.each_slice(3).map { |x| Version.new(*x) }.first
end
query(action, parameters) click to toggle source
# File lib/chef/provider/package/yum/python_helper.rb, line 149
def query(action, parameters)
  with_helper do
    json = build_query(action, parameters)
    Chef::Log.trace "sending '#{json}' to python helper"
    outpipe.syswrite json + "\n"
    output = inpipe.sysread(4096).chomp
    Chef::Log.trace "got '#{output}' from python helper"
    return output
  end
end
with_helper() { || ... } click to toggle source
# File lib/chef/provider/package/yum/python_helper.rb, line 192
def with_helper
  max_retries ||= 5
  ret = nil
  Timeout.timeout(600) do
    check
    ret = yield
  end
  output = drain_fds
  unless output.empty?
    Chef::Log.trace "discarding output on stderr/stdout from python helper: #{output}"
  end
  ret
rescue EOFError, Errno::EPIPE, Timeout::Error, Errno::ESRCH => e
  output = drain_fds
  if ( max_retries -= 1 ) > 0
    unless output.empty?
      Chef::Log.trace "discarding output on stderr/stdout from python helper: #{output}"
    end
    restart
    retry
  else
    raise e if output.empty?
    raise "yum-helper.py had stderr/stdout output:\n\n#{output}"
  end
end