Project

Profile

Help

Issue #2221 » pulp_smash_reproducer.py

jluza, 08/30/2016 04:19 PM

 
# coding=utf-8
"""Basic test for rpm rsync distributor.

This test do basic publish operation in rsync distributor with rpm repository
which was previously published in yum_distributor

"""
from __future__ import unicode_literals

import json
from packaging.version import Version
import os
import sys
import unittest2
import pprint

from pulp_smash import api, config, utils, cli, selectors, exceptions
from pulp_smash.compat import urljoin
from pulp_smash.constants import REPOSITORY_PATH, RPM_FEED_URL
from pulp_smash.tests.rpm.api_v2.utils import gen_distributor, gen_repo
from pulp_smash.tests.rpm.utils import set_up_module


_REPO = None


def get_remote_repo_packages(yum_distributor, rsync_distributor):
"""Return 'ls' output of packages directory of repo in remote server."""
cli_client = cli.Client(config.get_config())
repo_relative = yum_distributor['config']['relative_url']
remote_root = rsync_distributor['config']['remote']['root']
ret = cli_client.run(['ls', '%s/%s' % (remote_root, repo_relative)])
if ret.returncode:
return (False, ret.stderr)
else:
return (True, ret.stdout)


def gen_rsync_distributor():
"""Return a dict for use in creating a rsync distributor."""
return {
'auto_publish': False,
'distributor_id': utils.uuid4(),
'distributor_type_id': 'rpm_rsync_distributor',
'distributor_config': {
'remote': {
'auth_type': 'password',
'ssh_identity_file': '/etc/rsync_key',
'ssh_user': 'cdn_user',
'ssh_password': 'cdn_user',
'root': '/home/cdn_user/cdn/',
'host': 'dev'},
'remote_units_path': 'content/origin/units/',
'http': True,
'https': False,
'handler_type': 'rsync'
},
}


def setUpModule(): # pylint:disable=invalid-name
"""Prepare remote environemnt for rsync distributor.

1. Add cdn_user
2. Setup password for cdn_user
3. Setup ssh rsa-key and key authentication for ssh

Skip all tests if rsync_distributor features is not testable in pulp.
"""
set_up_module()
cfg = config.get_config()
if cfg.version < Version('2.9'):
raise unittest2.SkipTest('This module requires Pulp 2.9 or greater.')
#if selectors.bug_is_untestable(1759, cfg.version):
# raise unittest2.SkipTest(
# 'https://pulp.plan.io/issues/1759 is not testable'
# )

cli_client = cli.Client(config.get_config(), cli.echo_handler)
cli_client.run(['adduser', 'cdn_user'])
cmd = cli_client.machine['echo']['cdn_user']
cmd |= cli_client.machine['passwd']['cdn_user']['--stdin']
cmd.run()
cli_client.run(['ssh-keygen', '-f', '/etc/rsync_key',
'-t', 'rsa', '-N', '', '-P', ''])
cli_client.run('chown apache /etc/rsync_key'.split())
cli_client.run('sudo -u cdn_user mkdir -p /home/cdn_user/.ssh/'.split())
cli_client.run(('sudo -u cdn_user cp /etc/rsync_key.pub ' +
'/home/cdn_user/.ssh/authorized_keys').split())
cli_client.run('chown cdn_user /home/cdn_user/.ssh/authorized_keys'.split())


def tearDownModule(): # pylint:disable=invalid-name
"""Delete the repository created by :meth:`setUpModule`."""
cli_client = cli.Client(config.get_config())
cli_client.run('userdel cdn_user'.split())


def get_repomd_xml_path(distributor_rel_url):
"""Construct the path to a repository's ``repomd.xml`` file.

:param distributor_rel_url: A distributor's ``relative_url`` option.
:returns: An string path to a ``repomd.xml`` file.
"""
return urljoin(
urljoin('/pulp/repos/', distributor_rel_url),
'repodata/repomd.xml',
)



class RSyncTestBase(utils.BaseAPITestCase):
def get_rsynced_units_content(self, units, root, content_unit_path):
cli_client = cli.Client(config.get_config())
remote_root = root
stats = {}
for unit in units:
storage_path = unit['metadata']['_storage_path']
rel_unit_path = storage_path.replace('/var/lib/pulp/content/units/', '')
remote_path = os.path.join(remote_root, content_unit_path,
rel_unit_path)
ret = cli_client.run(['stat', remote_path])
stats[unit['metadata']['_storage_path']] = ret.returncode
return stats

def get_rsynced_units_dest(self, units):
(success, ret) = get_remote_repo_packages(self.yum_distributor,
self.rsync_distributor)
repo_packages = set([x for x in ret.split('\n') if x])
db_packages = set([unit['metadata']['filename']
for unit in units])
return (db_packages - repo_packages, repo_packages - db_packages)

class TestRsyncDistributorDelete(RSyncTestBase):
"""Test if files are removed from remote if they are removed from pulp repo

Test sequence:
1. Create rpm repo with feed
2. Sync repo
4. Associate yum distributor with repository
4. Associate rsync distributor with repository
3. Get list of packages in the repository
4. Publish in yum distributor
5. Publish in rsync distributor
6. Update remote_content_location of rsync distributor
7. Publish in rsync distributor

T1. Test if all synced packages are available in remote_content_location
Test if all synced packages are available in repo destination

T2. Test if all synced packages are available in remote_content_location
repository after it was updated
Test if all synced packages are available in repo destination
"""

@classmethod
def setUpClass(cls):
"""Create a repository with a distributor, and populate it.

Associate yum and rsync distributor to repository and publish
repository in those.
In addition, create several variables for use by the test methods.
"""
super(TestRsyncDistributorDelete, cls).setUpClass()
cls.responses = {}

client = api.Client(cls.cfg, api.json_handler)
body = gen_repo()
body['importer_config']['feed'] = RPM_FEED_URL
cls.repo_href = client.post(REPOSITORY_PATH, body)['_href']
cls.resources.add(cls.repo_href) # mark for deletion
cls.responses['sync'] = utils.sync_repo(cls.cfg, cls.repo_href)
rsync_dist = gen_rsync_distributor()
cls.yum_distributor = client.post(urljoin(cls.repo_href,
'distributors/'),
gen_distributor())

# Get contents of repository
cls.synced_units = client.post(
urljoin(cls.repo_href, 'search/units/'),
{'criteria': {}},
)

rsync_dist_conf = rsync_dist['distributor_config']
rsync_dist_conf['predistributor_id'] = cls.yum_distributor['id']
cls.rsync_distributor = client.post(urljoin(cls.repo_href,
'distributors/'),
rsync_dist)

cls.responses['rpm publish'] = client.post(
urljoin(cls.repo_href, 'actions/publish/'),
{'id': cls.yum_distributor['id']},
)

cls.responses['rsync publish'] = client.post(
urljoin(cls.repo_href, 'actions/publish/'),
{'id': cls.rsync_distributor['id']},
)


def test_01_removed_data(self):
""""Test if rpms are removed from remote server.

Test if packages are removed from the remote if they are
unassociated from the repo, published in yum and then published in
rsync distributor with delete == True
"""

db_rpm_units = []
for unit in self.synced_units:
if unit['metadata']['_content_type_id'] != 'rpm':
continue
db_rpm_units.append(unit)

filenames = []
for unit in db_rpm_units[:2]:
filenames.append(unit["metadata"]["filename"])

client = api.Client(self.cfg, api.json_handler)

criteria = {"type_ids": ["rpm"],
"filters": {"unit": {"filename": {"$in": filenames}}}}
report = client.post(urljoin(self.repo_href, 'actions/unassociate/'),
{"criteria": criteria})
unassociate_result = next(api.poll_spawned_tasks(self.cfg, report))

publish_ret1 = client.post(urljoin(self.repo_href, 'actions/publish/'),
{'id': self.yum_distributor['id']})
publish_ret2 = client.post(urljoin(self.repo_href, 'actions/publish/'),
{'id': self.rsync_distributor['id'],
'override_config': {"delete": True}})

(success, ret) = get_remote_repo_packages(self.yum_distributor,
self.rsync_distributor)

missing_in_remote, missing_in_db = self.get_rsynced_units_dest(db_rpm_units)

self.assertEqual(set(missing_in_remote), set(filenames))
self.assertEqual(set(missing_in_db), set(['repodata']), missing_in_db)
def test_02_repo_clean(self):
""""Test if it's possible to clean up whole repo and rsync empty.

Test if it's possible to rsync empty repo to remote server.
"""

db_rpm_units = []
filenames = []
for unit in self.synced_units:
if unit['metadata']['_content_type_id'] != 'rpm':
continue
db_rpm_units.append(unit)
filenames.append(unit["metadata"]["filename"])
client = api.Client(self.cfg, api.json_handler)

criteria = {"type_ids": ["rpm"]}
report = client.post(urljoin(self.repo_href, 'actions/unassociate/'),
{"criteria": criteria})
unassociate_result = next(api.poll_spawned_tasks(self.cfg, report))

publish_ret1 = client.post(urljoin(self.repo_href, 'actions/publish/'),
{'id': self.yum_distributor['id']})
print >> sys.stderr, publish_ret1
publish_ret2 = client.post(urljoin(self.repo_href, 'actions/publish/'),
{'id': self.rsync_distributor['id'],
'override_config': {"delete": True}})
print >> sys.stderr, publish_ret2

(success, ret) = get_remote_repo_packages(self.yum_distributor,
self.rsync_distributor)

missing_in_remote, missing_in_db = self.get_rsynced_units_dest(db_rpm_units)

self.assertEqual(set(missing_in_remote), set(filenames))
self.assertEqual(set(missing_in_db), set(['repodata']), missing_in_db)
(1-1/3)