Class: Chef::Provider::Package::Yum::YumCache

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

Overview

Cache for our installed and available packages, pulled in from yum-dump.py

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Mixin::ShellOut

#a_to_s, #clean_array, #shell_out, #shell_out!, #shell_out_compact, #shell_out_compact!, #shell_out_compact_timeout, #shell_out_compact_timeout!, #shell_out_with_systems_locale, #shell_out_with_systems_locale!

Methods included from Mixin::PathSanity

#enforce_path_sanity, #sanitized_path

Methods included from Mixin::Which

#where, #which

Constructor Details

#initializeYumCache

Returns a new instance of YumCache


38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/chef/provider/package/yum/yum_cache.rb', line 38

def initialize
  @rpmdb = RPMDb.new

  # Next time @rpmdb is accessed:
  #  :all       - Trigger a run of "yum-dump.py --options --installed-provides", updates
  #               yum's cache and parses options from /etc/yum.conf. Pulls in Provides
  #               dependency data for installed packages only - this data is slow to
  #               gather.
  #  :provides  - Same as :all but pulls in Provides data for available packages as well.
  #               Used as a last resort when we can't find a Provides match.
  #  :installed - Trigger a run of "yum-dump.py --installed", only reads the local rpm
  #               db. Used between client runs for a quick refresh.
  #  :none      - Do nothing, a call to one of the reload methods is required.
  @next_refresh = :all

  @allow_multi_install = []

  @extra_repo_control = nil

  # these are for subsequent runs if we are on an interval
  Chef::Client.when_run_starts do
    YumCache.instance.reload
  end
end

Instance Attribute Details

#extra_repo_controlObject (readonly)

Returns the value of attribute extra_repo_control


63
64
65
# File 'lib/chef/provider/package/yum/yum_cache.rb', line 63

def extra_repo_control
  @extra_repo_control
end

#yum_binaryObject

Returns the value of attribute yum_binary


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

def yum_binary
  @yum_binary
end

Instance Method Details

#allow_multi_installObject

Return an array of packages allowed to be installed multiple times, such as the kernel


287
288
289
290
# File 'lib/chef/provider/package/yum/yum_cache.rb', line 287

def allow_multi_install
  refresh
  @allow_multi_install
end

#available_version(package_name, arch = nil) ⇒ Object Also known as: candidate_version

Return the latest available version for a package.arch


276
277
278
# File 'lib/chef/provider/package/yum/yum_cache.rb', line 276

def available_version(package_name, arch = nil)
  version(package_name, arch, true, false)
end

#disable_extra_repo_controlObject


300
301
302
303
304
305
306
# File 'lib/chef/provider/package/yum/yum_cache.rb', line 300

def disable_extra_repo_control
  # Only force reload when set
  if @extra_repo_control
    @extra_repo_control = nil
    reload
  end
end

#enable_extra_repo_control(arg) ⇒ Object


292
293
294
295
296
297
298
# File 'lib/chef/provider/package/yum/yum_cache.rb', line 292

def enable_extra_repo_control(arg)
  # Don't touch cache if it's the same repos as the last load
  unless @extra_repo_control == arg
    @extra_repo_control = arg
    reload
  end
end

#extract_interpreter(file) ⇒ Object


184
185
186
# File 'lib/chef/provider/package/yum/yum_cache.rb', line 184

def extract_interpreter(file)
  ::File.open(file, "r", &:readline)[2..-1].strip
end

#installed_version(package_name, arch = nil) ⇒ Object

Return the currently installed version for a package.arch


282
283
284
# File 'lib/chef/provider/package/yum/yum_cache.rb', line 282

def installed_version(package_name, arch = nil)
  version(package_name, arch, false, true)
end

#package_available?(package_name) ⇒ Boolean

Check for package by name or name+arch

Returns:

  • (Boolean)

230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/chef/provider/package/yum/yum_cache.rb', line 230

def package_available?(package_name)
  refresh

  if @rpmdb.lookup(package_name)
    return true
  else
    if package_name =~ /^(.*)\.(.*)$/
      pkg_name = $1
      pkg_arch = $2

      if matches = @rpmdb.lookup(pkg_name)
        matches.each do |m|
          return true if m.arch == pkg_arch
        end
      end
    end
  end

  false
end

#package_repository(package_name, desired_version, arch = nil) ⇒ Object

Return the source repository for a package-version.arch


267
268
269
270
271
272
273
# File 'lib/chef/provider/package/yum/yum_cache.rb', line 267

def package_repository(package_name, desired_version, arch = nil)
  package(package_name, arch, true, false) do |pkg|
    return pkg.repoid if desired_version == pkg.version.to_s
  end

  nil
end

#packages_from_require(rpmdep) ⇒ Object

Returns a array of packages satisfying an RPMDependency


252
253
254
255
# File 'lib/chef/provider/package/yum/yum_cache.rb', line 252

def packages_from_require(rpmdep)
  refresh
  @rpmdb.whatprovides(rpmdep)
end

#python_binObject


170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/chef/provider/package/yum/yum_cache.rb', line 170

def python_bin
  yum_executable = which(yum_binary)
  if yum_executable && shabang?(yum_executable)
    shabang_or_fallback(extract_interpreter(yum_executable))
  else
    Chef::Log.warn("Yum executable not found or doesn't start with #!. Using default python.")
    "/usr/bin/python"
  end
rescue StandardError => e
  Chef::Log.warn("An error occurred attempting to determine correct python executable. Using default.")
  Chef::Log.debug(e)
  "/usr/bin/python"
end

#refreshObject


72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/chef/provider/package/yum/yum_cache.rb', line 72

def refresh
  case @next_refresh
  when :none
    return nil
  when :installed
    reset_installed
    # fast
    opts = " --installed"
  when :all
    reset
    # medium
    opts = " --options --installed-provides"
  when :provides
    reset
    # slow!
    opts = " --options --all-provides"
  else
    raise ArgumentError, "Unexpected value in next_refresh: #{@next_refresh}"
  end

  if @extra_repo_control
    opts << " #{@extra_repo_control}"
  end

  opts << " --yum-lock-timeout #{Chef::Config[:yum_lock_timeout]}"

  one_line = false
  error = nil

  status = nil

  begin
    status = shell_out!("#{python_bin} #{yum_dump_path}#{opts}", timeout: Chef::Config[:yum_timeout])
    status.stdout.each_line do |line|
      one_line = true

      line.chomp!
      if line =~ /\[option (.*)\] (.*)/
        if $1 == "installonlypkgs"
          @allow_multi_install = $2.split
        else
          raise Chef::Exceptions::Package, "Strange, unknown option line '#{line}' from yum-dump.py"
        end
        next
      end

      if line =~ /^(\S+) ([0-9]+) (\S+) (\S+) (\S+) \[(.*)\] ([i,a,r]) (\S+)$/
        name     = $1
        epoch    = $2
        version  = $3
        release  = $4
        arch     = $5
        provides = parse_provides($6)
        type     = $7
        repoid   = $8
      else
        Chef::Log.warn("Problem parsing line '#{line}' from yum-dump.py! " \
                       "Please check your yum configuration.")
        next
      end

      case type
      when "i"
        # if yum-dump was called with --installed this may not be true, but it's okay
        # since we don't touch the @available Set in reload_installed
        available = false
        installed = true
      when "a"
        available = true
        installed = false
      when "r"
        available = true
        installed = true
      end

      pkg = RPMDbPackage.new(name, epoch, version, release, arch, provides, installed, available, repoid)
      @rpmdb << pkg
    end

    error = status.stderr
  rescue Mixlib::ShellOut::CommandTimeout => e
    Chef::Log.error("#{yum_dump_path} exceeded timeout #{Chef::Config[:yum_timeout]}")
    raise(e)
  end

  if status.exitstatus != 0
    raise Chef::Exceptions::Package, "Yum failed - #{status.inspect} - returns: #{error}"
  else
    unless one_line
      Chef::Log.warn("Odd, no output from yum-dump.py. Please check " \
                     "your yum configuration.")
    end
  end

  # A reload method must be called before the cache is altered
  @next_refresh = :none
end

#reloadObject


206
207
208
# File 'lib/chef/provider/package/yum/yum_cache.rb', line 206

def reload
  @next_refresh = :all
end

#reload_installedObject


210
211
212
# File 'lib/chef/provider/package/yum/yum_cache.rb', line 210

def reload_installed
  @next_refresh = :installed
end

#reload_providesObject


214
215
216
# File 'lib/chef/provider/package/yum/yum_cache.rb', line 214

def reload_provides
  @next_refresh = :provides
end

#resetObject


218
219
220
# File 'lib/chef/provider/package/yum/yum_cache.rb', line 218

def reset
  @rpmdb.clear
end

#reset_installedObject


222
223
224
# File 'lib/chef/provider/package/yum/yum_cache.rb', line 222

def reset_installed
  @rpmdb.clear_installed
end

#shabang?(file) ⇒ Boolean

Returns:

  • (Boolean)

198
199
200
201
202
203
204
# File 'lib/chef/provider/package/yum/yum_cache.rb', line 198

def shabang?(file)
  ::File.open(file, "r") do |f|
    f.read(2) == "#!"
  end
rescue Errno::ENOENT
  false
end

#shabang_or_fallback(interpreter) ⇒ Object

dnf based systems have a yum shim that has /bin/bash as the interpreter. Don't use this.


189
190
191
192
193
194
195
196
# File 'lib/chef/provider/package/yum/yum_cache.rb', line 189

def shabang_or_fallback(interpreter)
  if interpreter == "/bin/bash"
    Chef::Log.warn("Yum executable interpreter is /bin/bash. Falling back to default python.")
    "/usr/bin/python"
  else
    interpreter
  end
end

#version_available?(package_name, desired_version, arch = nil) ⇒ Boolean

Check if a package-version.arch is available to install

Returns:

  • (Boolean)

258
259
260
261
262
263
264
# File 'lib/chef/provider/package/yum/yum_cache.rb', line 258

def version_available?(package_name, desired_version, arch = nil)
  version(package_name, arch, true, false) do |v|
    return true if desired_version == v
  end

  false
end

#yum_dump_pathObject

Cache management


68
69
70
# File 'lib/chef/provider/package/yum/yum_cache.rb', line 68

def yum_dump_path
  ::File.join(::File.dirname(__FILE__), "yum-dump.py")
end