自定义包提供程序以解决名称空间冲突

Custom package provider to work around namespace conflicts

我有一套RHEL/CentOS系统,我使用的是NetBSD pkgsrc系统 将各种包安装到某个树中(例如 /opt/local)。我想要 使用 Puppet 管理它。我开始只是使用 pkgin 提供程序,但是 我很快运行陷入命名空间冲突。例如,yum 和 pkgin 供应商正在添加 Package[openssl],但他们发生了冲突。

为了解决这个问题,我一直在尝试在我的 pkgsrc 包上使用静态前缀, 然后在实际传递给pkgin命令进行处理时将其剥离。

首先,我写了一个定义来添加前缀并设置提供者。

define mypkgin::package($ensure='latest') {

    package { "mypkgin_${title}":
        ensure   => $ensure,
        provider => 'mypkgin',
    }

}

这将在命名空间中显示为 Mypkgin::Package[openssl],它确实 本身不与任何事物发生冲突。它的目标是添加一个名为 Package[mypkgin_openssl],不与 yum 的 Package[openssl] 冲突。

然后,我复制了pkgin provider的代码,修改了一下,提供了一个东西 命名为 mypkgin。我在这里有一个小方法可以从 名称,并在一些地方使用,以便 pkgin 命令接收 pkgsrc 包名称而不是前缀版本。

require "puppet/provider/package"

Puppet::Type.type(:package).provide :mypkgin, :parent => Puppet::Provider::Package do
  desc "Package management using pkgin/pkgsrc, my local edition."

  # Specify full path since it's not in Puppet's exec path
  commands :pkgin => "/opt/local/bin/pkgin"

  has_feature :installable, :uninstallable, :upgradeable, :versionable

  # Strip prefix off of package name
  def my_pkgname(package)
    package.gsub(/^mypkgin_/, '')
  end

  def self.parse_pkgin_line(package)

    # e.g.
    #   vim-7.2.446 =        Vim editor (vi clone) without GUI
    match, name, version, status = *package.match(/(\S+)-(\S+)(?: (=|>|<))?\s+.+$/)
    if match
      {
        :name     => name,
        :status   => status,
        :ensure   => version
      }
    end
  end

  def self.prefetch(packages)
    super
    # Without -f, no fresh pkg_summary files are downloaded
    pkgin("-yf", :update)
  end

  def self.instances
    pkgin(:list).split("\n").map do |package|
      new(parse_pkgin_line(package))
    end
  end

  def query
    packages = parse_pkgsearch_line

    if packages.empty?
      if @resource[:ensure] == :absent
        notice "declared as absent but unavailable #{@resource.file}:#{resource.line}"
        return false
      else
        @resource.fail "No candidate to be installed"
      end
    end

    packages.first.update( :ensure => :absent )
  end

  def parse_pkgsearch_line
    packages = pkgin(:search, my_pkgname(resource[:name])).split("\n")

    return [] if packages.length == 1

    packages.slice!(-4, 4)

    pkglist = packages.map{ |line| self.class.parse_pkgin_line(line) }
    pkglist.select{ |package| my_pkgname(resource[:name]) == package[:name] }
  end

  def install
    if String === @resource[:ensure]
      pkgin("-y", :install, "#{my_pkgname(resource[:name])}-#{resource[:ensure]}")
    else
      pkgin("-y", :install, my_pkgname(resource[:name]))
    end
  end

  def uninstall
    pkgin("-y", :remove, my_pkgname(resource[:name]))
  end

  def latest
    package = parse_pkgsearch_line.detect{ |package| package[:status] == '<' }
    return properties[:ensure] if not package
    return package[:ensure]
  end

  def update
    pkgin("-y", :install, my_pkgname(resource[:name]))
  end

end

每次我运行puppet agent --test,它都告诉我它已经添加了所有这些 包。很明显我有一些命名空间有点不对。它认为每个 运行 该软件包需要安装或升级,并且它会尝试这样做。 我假设在一个或多个地方,我应该添加或删除对 my_pkgname(),但我似乎无法弄清楚哪里出错了。

您的提供商通过预取确定系统上已经可用的包。看起来您没有替换或覆盖将导致该过程最终依赖于 instances() 方法的位。

查看该方法,它似乎将生成与 pkgin 提供程序完全相同的实例,一直到生成的包名称。这对你来说是个问题,因为你的方案涉及翻译包名称。仅仅将 Puppet 使用的名称转换为本地名称是不够的——您还必须将本地名称转换为您希望 Puppet 在提供者端创建实例时看到的名称。

看起来 self.parse_pkgin_line() 可以很容易地修改,以便在您的包名称前添加所需的前缀。这似乎是它应该做的正确事情,但我不确定它是否会产生任何不良副作用。可能还需要进行其他更改,例如 parse_pkgsearch_line(),但我不是很清楚。