定制包提供程序以解决命名空间冲突

时间:2016-03-19 15:15:18

标签: ruby puppet

我有一套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提供程序代码并对其进行了修改以提供一个东西 名为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(),但我似乎无法弄清楚我哪里出错了。

1 个答案:

答案 0 :(得分:1)

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

看一下这个方法,看起来它会像pkgin提供者那样生成实例,直到生成的包名。这对您来说是个问题,因为您的方案涉及翻译包名称。仅仅将Puppet使用的名称翻译为本地名称是不够的 - 您还必须将原生名称翻译为您希望Puppet在提供者端创建实例时看到的名称。

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