Merge "Simplifying the check of the latest package version validation"
This commit is contained in:
commit
1e8a20510d
@ -57,18 +57,17 @@ SUPPORTED_PKG_MGRS = (
|
||||
|
||||
|
||||
PackageDetails = collections.namedtuple('PackageDetails',
|
||||
['name', 'arch', 'version'])
|
||||
['name', 'version', 'release', 'arch'])
|
||||
|
||||
|
||||
def get_package_details(line):
|
||||
# Parses an output line from a package manager's
|
||||
# `list (available|installed)` command and returns
|
||||
# a named tuple
|
||||
parts = line.rstrip().split()
|
||||
name, arch = parts[0].split('.')
|
||||
# Version string, excluding release string and epoch
|
||||
version = parts[1].split('-')[0].split(':')[-1]
|
||||
return PackageDetails(name, arch, version)
|
||||
def get_package_details(output):
|
||||
if output:
|
||||
return PackageDetails(
|
||||
output.split('|')[0],
|
||||
output.split('|')[1],
|
||||
output.split('|')[2],
|
||||
output.split('|')[3],
|
||||
)
|
||||
|
||||
|
||||
def _command(command):
|
||||
@ -81,37 +80,6 @@ def _command(command):
|
||||
return process.communicate()
|
||||
|
||||
|
||||
def _get_installed_version_from_output(output, package):
|
||||
for line in output.split('\n'):
|
||||
if package in line:
|
||||
return get_package_details(line)
|
||||
|
||||
|
||||
def _get_latest_available_versions(output, installed):
|
||||
# Returns the latest available minor and major versions,
|
||||
# one for each.
|
||||
latest_minor = None
|
||||
latest_major = None
|
||||
# Get all packages with the same architecture
|
||||
packages = list([get_package_details(line) for line in output.split('\n')
|
||||
if '{i.name}.{i.arch}'.format(i=installed) in line])
|
||||
# Get all packages with the *same* major version
|
||||
minor = sorted((p for p in packages
|
||||
if p.version[0] == installed.version[0]))
|
||||
if len(minor) > 0:
|
||||
latest_minor = minor[-1].version
|
||||
# Get all packages with a *higher* available major version
|
||||
major = sorted((p for p in packages
|
||||
if p.version[0] > installed.version[0]))
|
||||
if len(major) > 0:
|
||||
latest_major = major[-1].version
|
||||
# If the output doesn't contain packages with the same major version
|
||||
# let's assume the currently installed version as latest minor one.
|
||||
if latest_minor is None:
|
||||
latest_minor = installed.version
|
||||
return latest_minor, latest_major
|
||||
|
||||
|
||||
def check_update(module, package, pkg_mgr):
|
||||
if pkg_mgr not in SUPPORTED_PKG_MGRS:
|
||||
module.fail_json(
|
||||
@ -119,23 +87,47 @@ def check_update(module, package, pkg_mgr):
|
||||
return
|
||||
|
||||
installed_stdout, installed_stderr = _command(
|
||||
[pkg_mgr, 'list', 'installed', package])
|
||||
['rpm', '-qa', '--qf',
|
||||
'%{NAME}|%{VERSION}|%{RELEASE}|%{ARCH}',
|
||||
package])
|
||||
|
||||
# Fail the module if for some reason we can't lookup the current package.
|
||||
if installed_stderr != '':
|
||||
module.fail_json(msg=installed_stderr)
|
||||
return
|
||||
installed = _get_installed_version_from_output(installed_stdout, package)
|
||||
elif not installed_stdout:
|
||||
module.fail_json(
|
||||
msg='"{}" is not an installed package.'.format(package))
|
||||
return
|
||||
|
||||
installed = get_package_details(installed_stdout)
|
||||
|
||||
pkg_mgr_option = 'available'
|
||||
if pkg_mgr == 'dnf':
|
||||
pkg_mgr_option = '--available'
|
||||
|
||||
available_stdout, available_stderr = _command(
|
||||
[pkg_mgr, 'list', 'available', installed.name])
|
||||
latest_minor_version, latest_major_version = \
|
||||
_get_latest_available_versions(available_stdout, installed)
|
||||
[pkg_mgr, '-q', 'list', pkg_mgr_option, installed.name])
|
||||
|
||||
module.exit_json(changed=False,
|
||||
name=installed.name,
|
||||
current_version=installed.version,
|
||||
latest_minor_version=latest_minor_version,
|
||||
latest_major_version=latest_major_version)
|
||||
if available_stdout:
|
||||
new_pkg_info = available_stdout.split('\n')[1].rstrip().split()[:2]
|
||||
new_ver, new_rel = new_pkg_info[1].split('-')
|
||||
|
||||
module.exit_json(
|
||||
changed=False,
|
||||
name=installed.name,
|
||||
current_version=installed.version,
|
||||
current_release=installed.release,
|
||||
new_version=new_ver,
|
||||
new_release=new_rel)
|
||||
else:
|
||||
module.exit_json(
|
||||
changed=False,
|
||||
name=installed.name,
|
||||
current_version=installed.version,
|
||||
current_release=installed.release,
|
||||
new_version=None,
|
||||
new_release=None)
|
||||
|
||||
|
||||
def main():
|
||||
|
@ -3,11 +3,11 @@
|
||||
gather_facts: yes
|
||||
vars:
|
||||
metadata:
|
||||
name: Check if latest minor version is installed
|
||||
name: Check if latest version of packages is installed
|
||||
description: >
|
||||
Makes sure python-tripleoclient is at its latest minor version
|
||||
Makes sure python-tripleoclient is at its latest version
|
||||
before starting an upgrade.
|
||||
groups:
|
||||
- pre-upgrade
|
||||
roles:
|
||||
- check-latest-minor-version
|
||||
- check-latest-packages-version
|
@ -1,16 +0,0 @@
|
||||
---
|
||||
- name: Get available updates for packages
|
||||
check_package_update:
|
||||
package: "{{ item }}"
|
||||
pkg_mgr: "{{ ansible_pkg_mgr }}"
|
||||
with_items: "{{ packages }}"
|
||||
register: updates
|
||||
|
||||
- name: Check if current version is latest minor
|
||||
with_items: "{{ updates.results }}"
|
||||
assert:
|
||||
that: "item.latest_minor_version == item.current_version"
|
||||
msg: >-
|
||||
"A newer version of the {{ item.name }} package is
|
||||
available: {{ item.latest_minor_version }} (currently
|
||||
{{ item.current_version }})."
|
@ -1,8 +0,0 @@
|
||||
---
|
||||
metadata:
|
||||
name: Check if latest minor version is installed
|
||||
description: >
|
||||
Makes sure python-tripleoclient is at its latest minor version
|
||||
before starting an upgrade.
|
||||
groups:
|
||||
- pre-upgrade
|
@ -0,0 +1,37 @@
|
||||
# Molecule managed
|
||||
# Copyright 2019 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
{% if item.registry is defined %}
|
||||
FROM {{ item.registry.url }}/{{ item.image }}
|
||||
{% else %}
|
||||
FROM {{ item.image }}
|
||||
{% endif %}
|
||||
|
||||
RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \
|
||||
elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash {{ item.pkg_extras | default('') }} && dnf clean all; \
|
||||
elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl python-setuptools bash {{ item.pkg_extras | default('') }} && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \
|
||||
elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml {{ item.pkg_extras | default('') }} && zypper clean -a; \
|
||||
elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates {{ item.pkg_extras | default('') }}; \
|
||||
elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates {{ item.pkg_extras | default('') }} && xbps-remove -O; fi
|
||||
|
||||
{% for pkg in item.easy_install | default([]) %}
|
||||
# install pip for centos where there is no python-pip rpm in default repos
|
||||
RUN easy_install {{ pkg }}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
CMD ["sh", "-c", "while true; do sleep 10000; done"]
|
@ -0,0 +1,47 @@
|
||||
---
|
||||
driver:
|
||||
name: docker
|
||||
|
||||
log: true
|
||||
|
||||
platforms:
|
||||
- name: centos7
|
||||
hostname: centos7
|
||||
image: centos:7
|
||||
pkg_extras: python-setuptools
|
||||
easy_install:
|
||||
- pip
|
||||
environment: &env
|
||||
http_proxy: "{{ lookup('env', 'http_proxy') }}"
|
||||
https_proxy: "{{ lookup('env', 'https_proxy') }}"
|
||||
|
||||
- name: fedora28
|
||||
hostname: fedora28
|
||||
image: fedora:28
|
||||
pkg_extras: python*-setuptools
|
||||
environment:
|
||||
<<: *env
|
||||
|
||||
provisioner:
|
||||
name: ansible
|
||||
log: true
|
||||
env:
|
||||
ANSIBLE_STDOUT_CALLBACK: yaml
|
||||
ANSIBLE_LIBRARY: "../../../../library"
|
||||
|
||||
scenario:
|
||||
test_sequence:
|
||||
- destroy
|
||||
- create
|
||||
- prepare
|
||||
- converge
|
||||
- verify
|
||||
- destroy
|
||||
|
||||
lint:
|
||||
enabled: false
|
||||
|
||||
verifier:
|
||||
name: testinfra
|
||||
lint:
|
||||
name: flake8
|
@ -0,0 +1,51 @@
|
||||
---
|
||||
# Copyright 2019 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
- name: Converge
|
||||
hosts: all
|
||||
|
||||
tasks:
|
||||
- name: Validate No Available Update for patch rpm
|
||||
include_role:
|
||||
name: check-latest-packages-version
|
||||
vars:
|
||||
packages:
|
||||
- patch
|
||||
|
||||
- name: Working Detection of Update for Pam package
|
||||
block:
|
||||
- include_role:
|
||||
name: check-latest-packages-version
|
||||
vars:
|
||||
packages:
|
||||
- pam
|
||||
|
||||
rescue:
|
||||
- name: Clear host errors
|
||||
meta: clear_host_errors
|
||||
|
||||
- debug:
|
||||
msg: The validation works! End the playbook run
|
||||
|
||||
- name: End play
|
||||
meta: end_play
|
||||
|
||||
- name: Fail the test
|
||||
fail:
|
||||
msg: |
|
||||
The check-latest-packages-version role should have detected
|
||||
that packages have available updates.
|
@ -0,0 +1,25 @@
|
||||
---
|
||||
# Copyright 2019 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
- name: Prepare
|
||||
hosts: all
|
||||
gather_facts: no
|
||||
|
||||
tasks:
|
||||
- name: install patch rpm
|
||||
package:
|
||||
name: patch
|
16
roles/check-latest-packages-version/tasks/main.yml
Normal file
16
roles/check-latest-packages-version/tasks/main.yml
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
- name: Get available updates for packages
|
||||
check_package_update:
|
||||
package: "{{ item }}"
|
||||
pkg_mgr: "{{ ansible_pkg_mgr }}"
|
||||
with_items: "{{ packages }}"
|
||||
register: updates
|
||||
|
||||
- name: Check if current version is the latest one
|
||||
fail:
|
||||
msg: >-
|
||||
A newer version of the {{ item.name }} package is
|
||||
available: {{ item.new_version }}-{{ item.new_release }}
|
||||
(currently {{ item.current_version }}-{{ item.current_release }})
|
||||
with_items: "{{ updates.results }}"
|
||||
when: item.new_version
|
8
roles/check-latest-packages-version/vars/main.yml
Normal file
8
roles/check-latest-packages-version/vars/main.yml
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
metadata:
|
||||
name: Check if latest version of packages is installed
|
||||
description: >
|
||||
Makes sure python-tripleoclient is at its latest version
|
||||
before starting an upgrade.
|
||||
groups:
|
||||
- pre-upgrade
|
@ -20,32 +20,18 @@ from library.check_package_update import get_package_details
|
||||
from tripleo_validations.tests import base
|
||||
|
||||
|
||||
PKG_INSTALLED = """\
|
||||
Last metadata expiration check: 1 day, 3:05:37 ago on Mon Jun 5 11:55:16 2017.
|
||||
Installed Packages
|
||||
foo-package.x86_64 2:6.1.5-1 @spideroak-one-stable
|
||||
"""
|
||||
PKG_INSTALLED = "foo-package|6.1.5|1|x86_64"
|
||||
|
||||
# This stretches the boundaries of a realistic yum list output a bit
|
||||
# but it's more explicit for testing.
|
||||
PKG_AVAILABLE = """\
|
||||
Last metadata expiration check: 1 day, 3:06:30 ago on Mon Jun 5 11:55:16 2017.
|
||||
Available Packages
|
||||
foo-package.i386 2:9.1.0-1 foo-stable
|
||||
foo-package.i386 2:6.2.3-1 foo-stable
|
||||
foo-package.x86_64 2:8.0.0-1 foo-stable
|
||||
foo-package.x86_64 2:7.0.0-1 foo-stable
|
||||
foo-package.x86_64 2:6.2.0-1 foo-stable
|
||||
foo-package.x86_64 2:6.1.6-1 foo-stable
|
||||
foo-package.x86_64 8.0.0-1 foo-stable
|
||||
"""
|
||||
|
||||
|
||||
class TestGetPackageDetails(base.TestCase):
|
||||
def setUp(self):
|
||||
super(TestGetPackageDetails, self).setUp()
|
||||
self.entry = get_package_details("""\
|
||||
foo-package.x86_64 2:6.2.0-1 spideroak-one-stable
|
||||
""")
|
||||
self.entry = get_package_details("foo-package|6.2.0|1|x86_64")
|
||||
|
||||
def test_name(self):
|
||||
self.assertEqual(self.entry.name, 'foo-package')
|
||||
@ -56,6 +42,9 @@ foo-package.x86_64 2:6.2.0-1 spideroak-one-stable
|
||||
def test_version(self):
|
||||
self.assertEqual(self.entry.version, '6.2.0')
|
||||
|
||||
def test_release(self):
|
||||
self.assertEqual(self.entry.release, '1')
|
||||
|
||||
|
||||
class TestCheckUpdate(base.TestCase):
|
||||
def setUp(self):
|
||||
@ -82,12 +71,14 @@ class TestCheckUpdate(base.TestCase):
|
||||
[PKG_INSTALLED, ''],
|
||||
[PKG_AVAILABLE, ''],
|
||||
]
|
||||
|
||||
check_update(self.module, 'foo-package', 'yum')
|
||||
self.module.exit_json.assert_called_with(changed=False,
|
||||
name='foo-package',
|
||||
current_version='6.1.5',
|
||||
latest_minor_version='6.2.0',
|
||||
latest_major_version='8.0.0')
|
||||
current_release='1',
|
||||
new_version='8.0.0',
|
||||
new_release='1')
|
||||
|
||||
@patch('library.check_package_update._command')
|
||||
def test_returns_current_version_if_no_updates(self, mock_command):
|
||||
@ -99,5 +90,6 @@ class TestCheckUpdate(base.TestCase):
|
||||
self.module.exit_json.assert_called_with(changed=False,
|
||||
name='foo-package',
|
||||
current_version='6.1.5',
|
||||
latest_minor_version='6.1.5',
|
||||
latest_major_version=None)
|
||||
current_release='1',
|
||||
new_version=None,
|
||||
new_release=None)
|
||||
|
Loading…
x
Reference in New Issue
Block a user