Class: Chef::Provider::Package::Yum::PythonHelper

Inherits:
Object
  • Object
show all
Includes:
Mixin::ShellOut, Mixin::Which, Singleton
Defined in:
lib/chef/provider/package/yum/python_helper.rb

Constant Summary collapse

YUM_HELPER =
::File.expand_path(::File.join(::File.dirname(__FILE__), "yum_helper.py")).freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#inpipeObject

Returns the value of attribute inpipe.



36
37
38
# File 'lib/chef/provider/package/yum/python_helper.rb', line 36

def inpipe
  @inpipe
end

#outpipeObject

Returns the value of attribute outpipe.



37
38
39
# File 'lib/chef/provider/package/yum/python_helper.rb', line 37

def outpipe
  @outpipe
end

#stderrObject

Returns the value of attribute stderr.



35
36
37
# File 'lib/chef/provider/package/yum/python_helper.rb', line 35

def stderr
  @stderr
end

#stdinObject

Returns the value of attribute stdin.



33
34
35
# File 'lib/chef/provider/package/yum/python_helper.rb', line 33

def stdin
  @stdin
end

#stdoutObject

Returns the value of attribute stdout.



34
35
36
# File 'lib/chef/provider/package/yum/python_helper.rb', line 34

def stdout
  @stdout
end

#wait_thrObject

Returns the value of attribute wait_thr.



38
39
40
# File 'lib/chef/provider/package/yum/python_helper.rb', line 38

def wait_thr
  @wait_thr
end

Instance Method Details

#checkObject



80
81
82
# File 'lib/chef/provider/package/yum/python_helper.rb', line 80

def check
  start if stdin.nil?
end

#close_rpmdbObject



84
85
86
# File 'lib/chef/provider/package/yum/python_helper.rb', line 84

def close_rpmdb
  query("close_rpmdb", {})
end

#combine_args(provides, version, arch) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

In the default case for the yum provider we now do terrible things with ruby to concatenate all the properties together to form a single string to feed to the python which favors using returnPackages/searchProvides over the searchNevra API. That means that these two different ways of constructing the resource are now perfectly identical:

yum_package “zabbix-agent-4.0.15-1.fc31.x86_64”

yum_package “zabbix-agent” do

version "4.0.15-1.fc31"
arch "x86_64"

end

This function handles turning the second form into the first form.

In the case where the epoch is given in the version and we do not have any glob patterns that is handled by going the other way and calling deconstruct_args due to the yum libraries not supporting that calling pattern other than by searchNevra.

NOTE: This is an ugly hack and should NOT be considered an endorsement of this approach towards any kind of features or bugfixes in the DNF provider. I’m doing this because YUM is sunsetting at this point and its very difficult to fight with the libraries on the python side of things.



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/chef/provider/package/yum/python_helper.rb', line 180

def combine_args(provides, version, arch)
  provides = provides.to_s.strip
  version = if !version.nil? && !version.empty?
              version.to_s.strip
            end
  arch = if !arch.nil? && !arch.empty?
           arch.to_s.strip
         end
  if version =~ /^[><=]/
    if arch
      return { "provides" => "#{provides}.#{arch} #{version}" }
    else
      return { "provides" => "#{provides} #{version}" }
    end
  end
  maybe_arch = provides.rpartition(".").last
  if is_arch?(maybe_arch)
    arch = maybe_arch
    provides.delete_suffix!(".#{arch}")
  end
  provides = "#{provides}-#{version}" if version
  provides = "#{provides}.#{arch}" if arch
  # yum (on rhel7) can't handle an epoch in provides, but
  # deconstructing the args can't handle dealing with globs
  if provides =~ /-\d+:/ && provides !~ /[\*\?]/
    deconstruct_args(provides)
  else
    { "provides" => provides }
  end
end

#compare_versions(version1, version2) ⇒ Object



88
89
90
# File 'lib/chef/provider/package/yum/python_helper.rb', line 88

def compare_versions(version1, version2)
  query("versioncompare", { "versions" => [version1, version2] }).to_i
end

#deconstruct_args(provides) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

We have a provides line with an epoch in it and yum cannot parse that, so we need to deconstruct the args. This doesn’t support splats which is why we only do it for this particularly narrow use case.

name-epoch:version name-epoch:version.arch name-epoch:version-release name-epoch:version-release.arch



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/chef/provider/package/yum/python_helper.rb', line 134

def deconstruct_args(provides)
  raise "provides must have an epoch in the version to deconstruct" unless provides =~ /^(\S+)-(\d+):(\S+)/

  name = $1
  epoch = $2
  other = $3
  ret = { "provides" => name, "epoch" => epoch }
  maybe_arch = other.rpartition(".").last
  arch = if is_arch?(maybe_arch)
           other.delete_suffix!(".#{maybe_arch}")
           maybe_arch
         end
  ret.merge!({ "arch" => arch }) if arch
  (version, _, release) = other.rpartition("-")
  if version.empty?
    ret.merge!({ "version" => release }) # yeah, rpartition is just weird
  else
    ret.merge!({ "version" => version, "release" => release })
  end
end

#install_only_packages(name) ⇒ Object



92
93
94
95
96
97
98
99
# File 'lib/chef/provider/package/yum/python_helper.rb', line 92

def install_only_packages(name)
  query_output = query("installonlypkgs", { "package" => name })
  if query_output == "False"
    false
  elsif query_output == "True"
    true
  end
end

#is_arch?(arch) ⇒ Boolean

Returns:

  • (Boolean)


118
119
120
121
122
# File 'lib/chef/provider/package/yum/python_helper.rb', line 118

def is_arch?(arch)
  # cspell:disable-next
  arches = %w{aarch64 alpha alphaev4 alphaev45 alphaev5 alphaev56 alphaev6 alphaev67 alphaev68 alphaev7 alphapca56 armv5tejl armv5tel armv5tl armv6l armv7l armv8l armv6hl armv7hl armv7hnl armv8hl i386 athlon geode i386 i486 i586 i686 ia64 mips mipsel mips64 mips64el noarch ppc ppc64 ppc64iseries ppc64p7 ppc64pseries ppc64le riscv32 riscv64 riscv128 s390 s390x sh3 sh4 sh4a sparc sparc64 sparc64v sparcv8 sparcv9 sparcv9v x86_64 amd64 ia32e}
  arches.include?(arch)
end

#options_params(options) ⇒ Object



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/chef/provider/package/yum/python_helper.rb', line 101

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: {}) ⇒ Object

NB: “options” here is the yum_package options hash and is deliberately not **opts



213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/chef/provider/package/yum/python_helper.rb', line 213

def package_query(action, provides, version: nil, arch: nil, options: {})
  parameters = combine_args(provides, version, arch)
  repo_opts = options_params(options || {})
  parameters.merge!(repo_opts)
  # XXX: for now we close the rpmdb before and after every query with an enablerepo/disablerepo to clean the helpers internal state
  close_rpmdb unless repo_opts.empty?
  query_output = query(action, parameters)
  version = parse_response(query_output.lines.last)
  Chef::Log.trace "parsed #{version} from python helper"
  close_rpmdb unless repo_opts.empty?
  version
end

#reapObject



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/chef/provider/package/yum/python_helper.rb', line 61

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?
    @stdin = @stdout = @stderr = @inpipe = @outpipe = @wait_thr = nil
  end
end

#restartObject



226
227
228
229
# File 'lib/chef/provider/package/yum/python_helper.rb', line 226

def restart
  reap
  start
end

#startObject



53
54
55
56
57
58
59
# File 'lib/chef/provider/package/yum/python_helper.rb', line 53

def start
  @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_commandObject



42
43
44
45
46
47
48
49
50
51
# File 'lib/chef/provider/package/yum/python_helper.rb', line 42

def yum_command
  @yum_command ||= begin
    cmd = which("platform-python", "python", "python2", "python2.7", extra_path: "/usr/libexec") do |f|
      shell_out("#{f} -c 'import yum'").exitstatus == 0
    end
    raise Chef::Exceptions::Package, "cannot find yum libraries, you may need to use dnf_package" unless cmd

    "#{cmd} #{YUM_HELPER}"
  end
end