adding-apt-update-functionality
* adding apt tool to update package Closes-bug: #1614600 Co-Authored-By: Anastasiya Tolochkova <atolochkova@mirantis.com> Co-Authored-By: Ilya Kharin <akscram@gmail.com> Change-Id: I6651055e798c01e22b9ba2b6fe573bb2abf7231c
This commit is contained in:
parent
183ed45496
commit
12611efbc8
136
octane/tests/test_apt.py
Normal file
136
octane/tests/test_apt.py
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from octane.util import apt
|
||||||
|
|
||||||
|
RELEASES = [
|
||||||
|
"""\
|
||||||
|
Origin: Mirantis
|
||||||
|
Label: mos9.0
|
||||||
|
Suite: mos9.0-updates
|
||||||
|
Codename: mos9.0-updates
|
||||||
|
MD5Sum: """,
|
||||||
|
"""\
|
||||||
|
Origin: Ubuntu
|
||||||
|
Label:Ubuntu
|
||||||
|
Suite: vivid
|
||||||
|
Version: 15.04
|
||||||
|
|
||||||
|
Codename: vivid
|
||||||
|
|
||||||
|
SHA1:""",
|
||||||
|
"""\
|
||||||
|
SHA256:""",
|
||||||
|
]
|
||||||
|
PARAMS = [
|
||||||
|
{'origin': 'Mirantis',
|
||||||
|
'label': 'mos9.0',
|
||||||
|
'codename': 'mos9.0-updates',
|
||||||
|
'suite': 'mos9.0-updates'},
|
||||||
|
{'origin': 'Ubuntu',
|
||||||
|
'label': 'Ubuntu',
|
||||||
|
'suite': 'vivid',
|
||||||
|
'codename': 'vivid',
|
||||||
|
'version': '15.04'},
|
||||||
|
]
|
||||||
|
REPOS = [
|
||||||
|
{'uri': 'http://mirror.fuel-infra.org/mos-repos/ubuntu/9.0/',
|
||||||
|
'suite': 'mos9.0-updates',
|
||||||
|
'type': 'deb',
|
||||||
|
'section': 'main',
|
||||||
|
'name': 'mos-updates',
|
||||||
|
'priority': 1050},
|
||||||
|
{'uri': 'http://us.archive.ubuntu.com/ubuntu/',
|
||||||
|
'suite': 'vivid',
|
||||||
|
'type': 'deb-src',
|
||||||
|
'section': 'main universe',
|
||||||
|
'name': 'ubuntu',
|
||||||
|
'priority': 601},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('repo,release,expected_params,url,code,is_error', [
|
||||||
|
(REPOS[0], RELEASES[0], PARAMS[0],
|
||||||
|
'http://mirror.fuel-infra.org/mos-repos/ubuntu/9.0/dists/'
|
||||||
|
'mos9.0-updates/Release',
|
||||||
|
200, False),
|
||||||
|
(REPOS[1], RELEASES[1], PARAMS[1],
|
||||||
|
'http://us.archive.ubuntu.com/ubuntu/dists/vivid/Release', 200, False),
|
||||||
|
(REPOS[1], RELEASES[2], {},
|
||||||
|
'http://us.archive.ubuntu.com/ubuntu/dists/vivid/Release', 200, False),
|
||||||
|
({'uri': 'https://example.com', 'suite': 'none'}, '', None,
|
||||||
|
'https://example.com/dists/none/Release', 300, True),
|
||||||
|
({'uri': 'http://example.com/ubuntu', 'suite': 'trusty'}, '', None,
|
||||||
|
'https://example.com/ubuntu/dists/none/Release', 300, True),
|
||||||
|
])
|
||||||
|
def test_fetch_release_parameters(mocker, repo, release, expected_params, url,
|
||||||
|
code, is_error):
|
||||||
|
mock_urlopen = mocker.patch('six.moves.urllib.request.urlopen')
|
||||||
|
resp = mocker.MagicMock(code=code)
|
||||||
|
resp.__iter__.return_value = iter(release.splitlines(True))
|
||||||
|
mock_urlopen.return_value = resp
|
||||||
|
|
||||||
|
if not is_error:
|
||||||
|
params = apt.fetch_release_parameters(repo)
|
||||||
|
mock_urlopen.assert_called_once_with(url)
|
||||||
|
assert params == expected_params
|
||||||
|
else:
|
||||||
|
with pytest.raises(apt.UnavailableRelease):
|
||||||
|
apt.fetch_release_parameters(repo)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('repo,filename,content', [
|
||||||
|
(REPOS[0], '/etc/apt/sources.list.d/mos-updates.list',
|
||||||
|
'deb http://mirror.fuel-infra.org/mos-repos/ubuntu/9.0/ mos9.0-updates '
|
||||||
|
'main'),
|
||||||
|
(REPOS[1], '/etc/apt/sources.list.d/ubuntu.list',
|
||||||
|
'deb-src http://us.archive.ubuntu.com/ubuntu/ vivid main universe'),
|
||||||
|
])
|
||||||
|
def test_create_repo_source(repo, filename, content):
|
||||||
|
result = apt.create_repo_source(repo)
|
||||||
|
assert result[0] == filename
|
||||||
|
assert result[1] == content
|
||||||
|
|
||||||
|
|
||||||
|
PREFERENCES = [
|
||||||
|
"""\
|
||||||
|
Package: *
|
||||||
|
Pin: release a=mos9.0-updates,o=Mirantis,n=mos9.0-updates,l=mos9.0,c=main
|
||||||
|
Pin-Priority: 1050""",
|
||||||
|
"""\
|
||||||
|
Package: *
|
||||||
|
Pin: release a=vivid,o=Ubuntu,n=vivid,l=Ubuntu,c=main,v=15.04
|
||||||
|
Pin-Priority: 601
|
||||||
|
|
||||||
|
Package: *
|
||||||
|
Pin: release a=vivid,o=Ubuntu,n=vivid,l=Ubuntu,c=universe,v=15.04
|
||||||
|
Pin-Priority: 601""",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('params,repo,filename,content', [
|
||||||
|
(PARAMS[0], REPOS[0], '/etc/apt/preferences.d/mos-updates.pref',
|
||||||
|
PREFERENCES[0]),
|
||||||
|
(PARAMS[1], REPOS[1], '/etc/apt/preferences.d/ubuntu.pref',
|
||||||
|
PREFERENCES[1]),
|
||||||
|
])
|
||||||
|
def test_create_repo_preferences(mocker, params, repo, filename, content):
|
||||||
|
mock_fetch_release = mocker.patch(
|
||||||
|
'octane.util.apt.fetch_release_parameters')
|
||||||
|
mock_fetch_release.return_value = params
|
||||||
|
result = apt.create_repo_preferences(repo)
|
||||||
|
assert result[0] == filename
|
||||||
|
assert result[1] == content
|
92
octane/util/apt.py
Normal file
92
octane/util/apt.py
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
from six.moves import urllib
|
||||||
|
|
||||||
|
from octane.util import ssh as ssh_util
|
||||||
|
|
||||||
|
PREFERENCES = [
|
||||||
|
('suite', 'a'),
|
||||||
|
('origin', 'o'),
|
||||||
|
('codename', 'n'),
|
||||||
|
('label', 'l'),
|
||||||
|
('component', 'c'),
|
||||||
|
('version', 'v'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class UnavailableRelease(Exception):
|
||||||
|
message = "Unexpected response status {0} for URL {1}."
|
||||||
|
|
||||||
|
def __init__(self, status, url):
|
||||||
|
super(UnavailableRelease, self).__init__(self.message.format(
|
||||||
|
status, url))
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade_packages(node, packages, fix_broken=False):
|
||||||
|
pkgs = ' '.join(packages)
|
||||||
|
update_cmd = ['sh', '-c',
|
||||||
|
'DEBIAN_FRONTEND=noninteractive apt-get '
|
||||||
|
'install --only-upgrade --yes --force-yes '
|
||||||
|
'-o Dpkg::Options::="--force-confdef" '
|
||||||
|
'-o Dpkg::Options::="--force-confold" '
|
||||||
|
'{0}'.format(pkgs)]
|
||||||
|
return ssh_util.call(update_cmd, node=node)
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_release_parameters(repo):
|
||||||
|
url_release_part = "./{0}".format('/'.join(('dists', repo['suite'],
|
||||||
|
'Release')))
|
||||||
|
base_url = repo['uri']
|
||||||
|
if not base_url.endswith('/'):
|
||||||
|
base_url = "{0}/".format(repo['uri'])
|
||||||
|
release_url = urllib.parse.urljoin(base_url, url_release_part)
|
||||||
|
resp = urllib.request.urlopen(release_url)
|
||||||
|
if resp.code != 200:
|
||||||
|
raise UnavailableRelease(resp.code, release_url)
|
||||||
|
params = {}
|
||||||
|
for line in resp:
|
||||||
|
key, _, value = line.partition(':')
|
||||||
|
key = key.strip().lower()
|
||||||
|
value = value.strip()
|
||||||
|
if not key or not value:
|
||||||
|
continue
|
||||||
|
# NOTE(akscram): Normal Release files contain meaningful fields
|
||||||
|
# at the beginning.
|
||||||
|
if key in ('md5sum', 'sha1', 'sha256'):
|
||||||
|
break
|
||||||
|
params[key] = value
|
||||||
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
def create_repo_source(repo):
|
||||||
|
filename = "/etc/apt/sources.list.d/{0}.list".format(repo['name'])
|
||||||
|
content = "{type} {uri} {suite} {section}".format(**repo)
|
||||||
|
return filename, content
|
||||||
|
|
||||||
|
|
||||||
|
def create_repo_preferences(repo):
|
||||||
|
filename = "/etc/apt/preferences.d/{0}.pref".format(repo['name'])
|
||||||
|
release_params = fetch_release_parameters(repo)
|
||||||
|
content = []
|
||||||
|
components = repo['section'].split()
|
||||||
|
for component in components:
|
||||||
|
params = dict(release_params, component=component)
|
||||||
|
release = ','.join("{0}={1}".format(key, params[name])
|
||||||
|
for name, key in PREFERENCES
|
||||||
|
if name in params)
|
||||||
|
content.append(
|
||||||
|
"Package: *\n"
|
||||||
|
"Pin: release {release}\n"
|
||||||
|
"Pin-Priority: {priority}"
|
||||||
|
.format(release=release, priority=repo['priority']))
|
||||||
|
return filename, '\n\n'.join(content)
|
Loading…
Reference in New Issue
Block a user