forked from puppetlabs/puppet
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request puppetlabs#8976 from Animeshz/xbps
(maint) Add xbps used by voidlinux as a package provider
- Loading branch information
Showing
2 changed files
with
270 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
# frozen_string_literal: true | ||
|
||
require_relative "../../../puppet/provider/package" | ||
|
||
Puppet::Type.type(:package).provide :xbps, :parent => Puppet::Provider::Package do | ||
desc "Support for the Package Manager Utility (xbps) used in VoidLinux. | ||
This provider supports the `install_options` attribute, which allows command-line flags to be passed to xbps-install. | ||
These options should be specified as an array where each element is either a string or a hash." | ||
|
||
commands :xbps_install => "/usr/bin/xbps-install" | ||
commands :xbps_remove => "/usr/bin/xbps-remove" | ||
commands :xbps_query => "/usr/bin/xbps-query" | ||
commands :xbps_pkgdb => "/usr/bin/xbps-pkgdb" | ||
|
||
confine 'os.name' => :void | ||
defaultfor 'os.name' => :void | ||
has_feature :install_options, :uninstall_options, :upgradeable, :holdable, :virtual_packages | ||
|
||
def self.defaultto_allow_virtual | ||
false | ||
end | ||
|
||
# Fetch the list of packages that are currently installed on the system. | ||
def self.instances | ||
packages = [] | ||
execpipe([command(:xbps_query), "-l"]) do |pipe| | ||
# xbps-query -l output is 'ii package-name-version desc' | ||
regex = /^\S+\s(\S+)-(\S+)\s+\S+/ | ||
pipe.each_line do |line| | ||
match = regex.match(line.chomp) | ||
if match | ||
packages << new({ name: match.captures[0], ensure: match.captures[1], provider: name }) | ||
else | ||
warning(_("Failed to match line '%{line}'") % { line: line }) | ||
end | ||
end | ||
end | ||
|
||
packages | ||
rescue Puppet::ExecutionFailure | ||
fail(_("Error getting installed packages")) | ||
end | ||
|
||
# Install a package quietly (without confirmation or progress bar) using 'xbps-install'. | ||
def install | ||
resource_name = @resource[:name] | ||
resource_source = @resource[:source] | ||
|
||
cmd = %w[-S -y] | ||
cmd += install_options if @resource[:install_options] | ||
cmd << "--repository=#{resource_source}" if resource_source | ||
cmd << resource_name | ||
|
||
unhold if properties[:mark] == :hold | ||
begin | ||
xbps_install(*cmd) | ||
ensure | ||
hold if @resource[:mark] == :hold | ||
end | ||
end | ||
|
||
# Because Voidlinux is a rolling release based distro, installing a package | ||
# should always result in the newest release. | ||
def update | ||
install | ||
end | ||
|
||
# Removes a package from the system. | ||
def uninstall | ||
resource_name = @resource[:name] | ||
|
||
cmd = %w[-R -y] | ||
cmd += uninstall_options if @resource[:uninstall_options] | ||
cmd << resource_name | ||
|
||
xbps_remove(*cmd) | ||
end | ||
|
||
# The latest version of a given package | ||
def latest | ||
query&.[] :ensure | ||
end | ||
|
||
# Queries information for a package | ||
def query | ||
resource_name = @resource[:name] | ||
installed_packages = self.class.instances | ||
|
||
installed_packages.each do |pkg| | ||
return pkg.properties if @resource[:name].casecmp(pkg.name).zero? | ||
end | ||
|
||
return nil unless @resource.allow_virtual? | ||
|
||
# Search for virtual package | ||
output = xbps_query("-Rs", resource_name).chomp | ||
|
||
# xbps-query -Rs output is '[*] package-name-version description' | ||
regex = /^\[\*\]+\s(\S+)-(\S+)\s+\S+/ | ||
match = regex.match(output) | ||
|
||
return nil unless match | ||
|
||
{ name: match.captures[0], ensure: match.captures[1], provider: self.class.name } | ||
end | ||
|
||
# Puts a package on hold, so it doesn't update by itself on system update | ||
def hold | ||
xbps_pkgdb("-m", "hold", @resource[:name]) | ||
end | ||
|
||
# Puts a package out of hold | ||
def unhold | ||
xbps_pkgdb("-m", "unhold", @resource[:name]) | ||
end | ||
|
||
private | ||
|
||
def install_options | ||
join_options(@resource[:install_options]) | ||
end | ||
|
||
def uninstall_options | ||
join_options(@resource[:uninstall_options]) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
require "spec_helper" | ||
require "stringio" | ||
|
||
describe Puppet::Type.type(:package).provider(:xbps) do | ||
before do | ||
@resource = Puppet::Type.type(:package).new(name: "gcc", provider: "xbps") | ||
@provider = described_class.new(@resource) | ||
@resolver = Puppet::Util | ||
|
||
allow(described_class).to receive(:which).with("/usr/bin/xbps-install").and_return("/usr/bin/xbps-install") | ||
allow(described_class).to receive(:which).with("/usr/bin/xbps-remove").and_return("/usr/bin/xbps-remove") | ||
allow(described_class).to receive(:which).with("/usr/bin/xbps-query").and_return("/usr/bin/xbps-query") | ||
end | ||
|
||
it { is_expected.to be_installable } | ||
it { is_expected.to be_uninstallable } | ||
it { is_expected.to be_install_options } | ||
it { is_expected.to be_uninstall_options } | ||
it { is_expected.to be_upgradeable } | ||
it { is_expected.to be_holdable } | ||
it { is_expected.to be_virtual_packages } | ||
|
||
it "should be the default provider on 'os.name' => Void" do | ||
expect(Facter).to receive(:value).with('os.name').and_return("Void") | ||
expect(described_class.default?).to be_truthy | ||
end | ||
|
||
describe "when determining instances" do | ||
it "should return installed packages" do | ||
sample_installed_packages = %{ | ||
ii gcc-12.2.0_1 GNU Compiler Collection | ||
ii ruby-devel-3.1.3_1 Ruby programming language - development files | ||
} | ||
|
||
expect(described_class).to receive(:execpipe).with(["/usr/bin/xbps-query", "-l"]) | ||
.and_yield(StringIO.new(sample_installed_packages)) | ||
|
||
instances = described_class.instances | ||
expect(instances.length).to eq(2) | ||
|
||
expect(instances[0].properties).to eq({ | ||
:name => "gcc", | ||
:ensure => "12.2.0_1", | ||
:provider => :xbps, | ||
}) | ||
|
||
expect(instances[1].properties).to eq({ | ||
:name => "ruby-devel", | ||
:ensure => "3.1.3_1", | ||
:provider => :xbps, | ||
}) | ||
end | ||
|
||
it "should warn on invalid input" do | ||
expect(described_class).to receive(:execpipe).and_yield(StringIO.new("blah")) | ||
expect(described_class).to receive(:warning).with('Failed to match line \'blah\'') | ||
expect(described_class.instances).to eq([]) | ||
end | ||
end | ||
|
||
describe "when installing" do | ||
it "and install_options are given it should call xbps to install the package quietly with the passed options" do | ||
@resource[:install_options] = ["-x", { "--arg" => "value" }] | ||
args = ["-S", "-y", "-x", "--arg=value", @resource[:name]] | ||
expect(@provider).to receive(:xbps_install).with(*args).and_return("") | ||
expect(described_class).to receive(:execpipe).with(["/usr/bin/xbps-query", "-l"]) | ||
|
||
@provider.install | ||
end | ||
|
||
it "and source is given it should call xbps to install the package from the source as repository" do | ||
@resource[:source] = "/path/to/xbps/containing/directory" | ||
args = ["-S", "-y", "--repository=#{@resource[:source]}", @resource[:name]] | ||
expect(@provider).to receive(:xbps_install).at_least(:once).with(*args).and_return("") | ||
expect(described_class).to receive(:execpipe).with(["/usr/bin/xbps-query", "-l"]) | ||
|
||
@provider.install | ||
end | ||
end | ||
|
||
describe "when updating" do | ||
it "should call install" do | ||
expect(@provider).to receive(:install).and_return("ran install") | ||
expect(@provider.update).to eq("ran install") | ||
end | ||
end | ||
|
||
describe "when uninstalling" do | ||
it "should call xbps to remove the right package quietly" do | ||
args = ["-R", "-y", @resource[:name]] | ||
expect(@provider).to receive(:xbps_remove).with(*args).and_return("") | ||
@provider.uninstall | ||
end | ||
|
||
it "adds any uninstall_options" do | ||
@resource[:uninstall_options] = ["-x", { "--arg" => "value" }] | ||
args = ["-R", "-y", "-x", "--arg=value", @resource[:name]] | ||
expect(@provider).to receive(:xbps_remove).with(*args).and_return("") | ||
@provider.uninstall | ||
end | ||
end | ||
|
||
describe "when determining the latest version" do | ||
it "should return the latest version number of the package" do | ||
@resource[:name] = "ruby-devel" | ||
|
||
expect(described_class).to receive(:execpipe).with(["/usr/bin/xbps-query", "-l"]).and_yield(StringIO.new(%{ | ||
ii ruby-devel-3.1.3_1 Ruby programming language - development files | ||
})) | ||
|
||
expect(@provider.latest).to eq("3.1.3_1") | ||
end | ||
end | ||
|
||
describe "when querying" do | ||
it "should call self.instances and return nil if the package is missing" do | ||
expect(described_class).to receive(:instances) | ||
.and_return([]) | ||
|
||
expect(@provider.query).to be_nil | ||
end | ||
|
||
it "should get real-package in case allow_virtual is true" do | ||
@resource[:name] = "nodejs-runtime" | ||
@resource[:allow_virtual] = true | ||
|
||
expect(described_class).to receive(:execpipe).with(["/usr/bin/xbps-query", "-l"]) | ||
.and_yield(StringIO.new("")) | ||
|
||
args = ["-Rs", @resource[:name]] | ||
expect(@provider).to receive(:xbps_query).with(*args).and_return(%{ | ||
[*] nodejs-16.19.0_1 Evented I/O for V8 javascript | ||
[-] nodejs-lts-12.22.10_2 Evented I/O for V8 javascript' | ||
}) | ||
|
||
expect(@provider.query).to eq({ | ||
:name => "nodejs", | ||
:ensure => "16.19.0_1", | ||
:provider => :xbps, | ||
}) | ||
end | ||
end | ||
end |