Skip to content

Commit

Permalink
Add versionlock
Browse files Browse the repository at this point in the history
Adding versionlock class. This is a port of the versionlock class from
the yum module. The purpose of this is to prevent zypper from updating
a specified package under a normal update run.
  • Loading branch information
msurato committed Mar 17, 2020
1 parent 58c0d29 commit 4a7fd2c
Show file tree
Hide file tree
Showing 8 changed files with 256 additions and 1 deletion.
4 changes: 4 additions & 0 deletions .fixtures.yml
@@ -0,0 +1,4 @@
fixtures:
repositories:
stdlib: 'https://github.com/puppetlabs/puppetlabs-stdlib.git'
concat: 'https://github.com/puppetlabs/puppetlabs-concat.git'
17 changes: 17 additions & 0 deletions README.md
Expand Up @@ -26,5 +26,22 @@ zypprepo { 'openSUSE_12.1':
}
```

### Lock a package with the *versionlock* plugin

Locks explicitly specified packages from updates. Package name must be precisely specified in format *`NAME-VERSION-RELEASE.ARCH`*. Wild card in package name is allowed provided it does not span a field seperator.

**PLEASE NOTE: Once you define a lock in code, all locks must be defined in code.**

```puppet
zypprepo::versionlock { 'bash-4.1.2-9.sles12.*': }
```

Use the following command to retrieve a properly-formated string:

```sh
PACKAGE_NAME='bash'
rpm -q "$PACKAGE_NAME" --qf '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n'
```

This Puppet 'type' is a port of the 'yumrepo' type from 2.7 code base
and is licensed under the Apache-2.0.
26 changes: 26 additions & 0 deletions manifests/plugin/versionlock.pp
@@ -0,0 +1,26 @@
# Class: zypprepo::plugin::versionlock
#
# @summary This class sets the structure for the lock file
#
# @param path
# Absolute path to the Zypper locks file. Defaults /etc/zypp/locks.
#
# @example Sample usage:
# include zypprepo::plugin::versionlock
#
class zypprepo::plugin::versionlock (
Stdlib::Absolutepath $path = '/etc/zypp/locks',
) {

concat { $path:
mode => '0644',
owner => 'root',
group => 'root',
}

concat::fragment { 'versionlock_header':
target => $path,
content => '# File managed by puppet\n',
order => '01',
}
}
30 changes: 30 additions & 0 deletions manifests/versionlock.pp
@@ -0,0 +1,30 @@
# @summary Locks package from updates.
#
# @example Sample usage
# zypprepo::versionlock { 'bash-4.1.2-9.sles12.*': }
#
# @param ensure
# Specifies if versionlock should be `present` or `absent`.
#
# @note The resource title must use the format
# "%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}". This can be retrieved via
# the command `rpm -q --qf '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}'.
# Wildcards may be used within token slots, but must not cover seperators,
# e.g., 'b*sh-4.1.2-9.*' covers Bash version 4.1.2, revision 9 on all
# architectures.
#
define zypprepo::versionlock {
require zypprepo::plugin::versionlock

assert_type(Zypprepo::VersionlockString, $name) |$_expected, $actual | {
fail("Package name must be formatted as %{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}, not \'${actual}\'. See Zypprepo::Versionlock documentation for details.")
}

concat::fragment { "zypprepo-versionlock-${name}":
content => "\ntype: package\n\
match_type: glob\n\
case_sensitive: on\n\
solvable_name: ${name}\n",
target => $zypprepo::plugin::versionlock::path,
}
}
11 changes: 10 additions & 1 deletion metadata.json
Expand Up @@ -8,7 +8,16 @@
"description": "zypprepo is a client-side description or type of a zypper repository for OpenSUSE/SuSE systems.",
"project_page": "https://github.com/voxpupuli/puppet-zypprepo",
"issues_url": "https://github.com/voxpupuli/puppet-zypprepo/issues",
"dependencies": [],
"dependencies": [
{
"name": "puppetlabs/stdlib",
"version_requirement": ">= 4.18.0 < 7.0.0"
},
{
"name": "puppetlabs/concat",
"version_requirement": ">= 1.2.5 < 7.0.0"
}
],
"requirements": [
{
"name": "puppet",
Expand Down
47 changes: 47 additions & 0 deletions spec/classes/plugin/versionlock_spec.rb
@@ -0,0 +1,47 @@
require 'spec_helper'

describe 'zypprepo::plugin::versionlock' do
on_supported_os.each do |os, os_facts|
context "on #{os}" do
let(:facts) { os_facts }

context 'without any parameters' do
it {
is_expected.to contain_concat('/etc/zypp/locks').with(
mode: '0644',
owner: 'root',
group: 'root'
)
}

it {
is_expected.to contain_concat__fragment('versionlock_header').with(
target: '/etc/zypp/locks',
content: '# File managed by puppet\n',
order: '01'
)
}
end

context 'with path=/test/path' do
let(:params) { { 'path' => '/test/path' } }

it {
is_expected.to contain_concat('/test/path').with(
mode: '0644',
owner: 'root',
group: 'root'
)
}

it {
is_expected.to contain_concat__fragment('versionlock_header').with(
target: '/test/path',
content: '# File managed by puppet\n',
order: '01'
)
}
end
end
end
end
82 changes: 82 additions & 0 deletions spec/defines/versionlock_spec.rb
@@ -0,0 +1,82 @@
require 'spec_helper'

shared_examples 'a well-defined versionlock' do
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_class('zypprepo::plugin::versionlock') }
end

describe 'zypprepo::versionlock' do
on_supported_os.each do |os, os_facts|
context "on #{os}" do
let(:facts) { os_facts }

context 'with a simple, well-formed title' do
let(:title) { 'bash-4.4-9.10.1.x86_64' }

it_behaves_like 'a well-defined versionlock'
it 'contains a well-formed Concat::Fragment' do
is_expected.to contain_concat__fragment("zypprepo-versionlock-#{title}").with_content(
"\ntype: package\n" \
"match_type: glob\n" \
"case_sensitive: on\n" \
"solvable_name: #{title}\n"
)
end
end

context 'with a trailing wildcard title' do
let(:title) { 'bash-4.4-9.10.1.*' }

it_behaves_like 'a well-defined versionlock'
it 'contains a well-formed Concat::Fragment' do
is_expected.to contain_concat__fragment("zypprepo-versionlock-#{title}").with_content(
"\ntype: package\n" \
"match_type: glob\n" \
"case_sensitive: on\n" \
"solvable_name: #{title}\n"
)
end
end

context 'with a complex wildcard title' do
let(:title) { 'bash-4.*-*.1' }

it_behaves_like 'a well-defined versionlock'
it 'contains a well-formed Concat::Fragment' do
is_expected.to contain_concat__fragment("zypprepo-versionlock-#{title}").with_content(
"\ntype: package\n" \
"match_type: glob\n" \
"case_sensitive: on\n" \
"solvable_name: #{title}\n"
)
end
end

context 'with a release containing dots' do
let(:title) { 'java-1.7.0-openjdk-1.7.0.121-2.6.8.0.3.x86_64' }

it_behaves_like 'a well-defined versionlock'
it 'contains a well-formed Concat::Fragment' do
is_expected.to contain_concat__fragment("zypprepo-versionlock-#{title}").with_content(
"\ntype: package\n" \
"match_type: glob\n" \
"case_sensitive: on\n" \
"solvable_name: #{title}\n"
)
end
end

context 'with an invalid title' do
let(:title) { 'bash-4.4.1' }

it { is_expected.to raise_error(Puppet::PreformattedError, %r(%\{NAME\}-%\{VERSION\}-%\{RELEASE\}\.%\{ARCH\})) }
end

context 'with an invalid wildcard pattern' do
let(:title) { 'bash-4.4.9*' }

it { is_expected.to raise_error(Puppet::PreformattedError, %r(%\{NAME\}-%\{VERSION\}-%\{RELEASE\}\.%\{ARCH\})) }
end
end
end
end
40 changes: 40 additions & 0 deletions types/versionlockstring.pp
@@ -0,0 +1,40 @@
# This type matches strings appropriate for use with zypprepo-versionlock.
# Its basic format, using the `rpm(8)` query string format, is
# `%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}`. As a Regex, it
# breaks down into five distinct parts, plus the seperators.
#
# # NAME: Any valid package name (see https://github.com/rpm-software-management/rpm/blob/master/doc/manual/spec)
# type Zypprepo::PackageName = Regexp[/[0-9a-zA-Z\._\+%\{\}\*-]+/]
#
# # VERSION: Any valid version string. The only limitation here, according to the RPM manual, is that it may not contain a dash (`-`).
# type Zypprepo::PackageVersion = Regexp[/[^-]+/]
#
# # RELEASE: Any valid release string. Only limitation is that it is not a dash (`-`)
# type Zypprepo::PackageRelease = Regexp[/[^-]+/]
#
# # ARCH: Matches a string such as `sles12.x86_64`. This is actuall two sub-expressions. See below.
# type Zypprepo::PackageArch = Regexp[/([0-9a-zZ-Z_\*]+)(?:\.(noarch|x86_64|i386|arm|ppc64|ppc64le|sparc64|ia64|alpha|ip|m68k|mips|mipsel|mk68k|mint|ppc|rs6000|s390|s390x|sh|sparc|xtensa|\*))?/]
#
# The `%{ARCH}` sub-expression is composed of two sub-expressions
# separated by a dot (`.`), where the second part is optional. The RPM
# specification calls the first field the `DistTag`, and the second the
# `BuildArch`.
#
# # DistTag: Any string consiting of only letters, numbers, or an underscore, e.g., `sles12` or `suse15`.
# type Zypprepo::PackageDistTag = Regexp[/[0-9a-zZ-Z_\*]+/]
#
# # BuildArch: Any string from the list at https://github.com/rpm-software-management/rpm/blob/master/rpmrc.in. Strings are roughly listed from most common to least common to improve performance.
# type Zypprepo::PackageBuildArch = Regexp[/noarch|x86_64|i386|arm|ppc64|ppc64le|sparc64|ia64|alpha|ip|m68k|mips|mipsel|mk68k|mint|ppc|rs6000|s390|s390x|sh|sparc|xtensa/]
#
# @note Each field may contain wildcard characters (`*`), but the
# wildcard characters may not span the fields, may not cover the
# seperators. This is an undocumented but tested limitation of
# yum-versionlock.
#
# @example A complete, well-formed string: `ash-4.1.2-9.sles12_2.x86_64'
# @example A well-formed string that has dropped the optional BuildArch sub-field: `bash-4.1.2-9.suse15`
# @example A well-formed string using wildcards: `*0:bash*-4.*-*.*`
# @example An invalid string (wildcard spans the VERSION and RELEASE fields): `0:bash-4.*-sles12.x86_64
# @example An invlaid string (wildcard spans the VERSION, RELEASE, and ARCH fields): `0:bash-*`
#
type Zypprepo::VersionlockString = Pattern[/^([0-9a-zA-Z\._\+%\{\}\*-]+)-([^-]+)-([^-]+)\.(([0-9a-zZ-Z_\*]+)(?:\.(noarch|x86_64|i386|arm|ppc64|ppc64le|sparc64|ia64|alpha|ip|m68k|mips|mipsel|mk68k|mint|ppc|rs6000|s390|s390x|sh|sparc|xtensa|\*))?)$/]

0 comments on commit 4a7fd2c

Please sign in to comment.