From: Alfredo Deza Date: Wed, 17 Jul 2013 20:31:23 +0000 (-0400) Subject: moved test to tests to follow best practices X-Git-Tag: v1.2~31^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=38316e41a199349a90c7befd47a7757cacee818c;p=ceph-deploy.git moved test to tests to follow best practices --- diff --git a/ceph_deploy/test/__init__.py b/ceph_deploy/test/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/ceph_deploy/test/conftest.py b/ceph_deploy/test/conftest.py deleted file mode 100644 index 819fc34..0000000 --- a/ceph_deploy/test/conftest.py +++ /dev/null @@ -1,98 +0,0 @@ -import logging -import os -import subprocess -import sys - - -LOG = logging.getLogger(__name__) - - -def _prepend_path(env): - """ - Make sure the PATH contains the location where the Python binary - lives. This makes sure cli tools installed in a virtualenv work. - """ - if env is None: - env = os.environ - env = dict(env) - new = os.path.dirname(sys.executable) - path = env.get('PATH') - if path is not None: - new = new + ':' + path - env['PATH'] = new - return env - - -class CLIFailed(Exception): - """CLI tool failed""" - - def __init__(self, args, status): - self.args = args - self.status = status - - def __str__(self): - return '{doc}: {args}: exited with status {status}'.format( - doc=self.__doc__, - args=self.args, - status=self.status, - ) - - -class CLIProcess(object): - def __init__(self, **kw): - self.kw = kw - - def __enter__(self): - try: - self.p = subprocess.Popen(**self.kw) - except OSError as e: - raise AssertionError( - 'CLI tool {args!r} does not work: {err}'.format( - args=self.kw['args'], - err=e, - ), - ) - else: - return self.p - - def __exit__(self, exc_type, exc_val, exc_tb): - self.p.wait() - if self.p.returncode != 0: - err = CLIFailed( - args=self.kw['args'], - status=self.p.returncode, - ) - if exc_type is None: - # nothing else raised, so we should complain; if - # something else failed, we'll just log - raise err - else: - LOG.error(str(err)) - - -class CLITester(object): - # provide easy way for caller to access the exception class - # without importing us - Failed = CLIFailed - - def __init__(self, tmpdir): - self.tmpdir = tmpdir - - def __call__(self, **kw): - kw.setdefault('cwd', str(self.tmpdir)) - kw['env'] = _prepend_path(kw.get('env')) - kw['env']['COLUMNS'] = '80' - return CLIProcess(**kw) - - -def pytest_funcarg__cli(request): - """ - Test command line behavior. - """ - - # the tmpdir here will be the same value as the test function - # sees; we rely on that to let caller prepare and introspect - # any files the cli tool will read or create - tmpdir = request.getfuncargvalue('tmpdir') - - return CLITester(tmpdir=tmpdir) diff --git a/ceph_deploy/test/directory.py b/ceph_deploy/test/directory.py deleted file mode 100644 index 81d3e19..0000000 --- a/ceph_deploy/test/directory.py +++ /dev/null @@ -1,13 +0,0 @@ -import contextlib -import os - - -@contextlib.contextmanager -def directory(path): - prev = os.open('.', os.O_RDONLY | os.O_DIRECTORY) - try: - os.chdir(path) - yield - finally: - os.fchdir(prev) - os.close(prev) diff --git a/ceph_deploy/test/fakes.py b/ceph_deploy/test/fakes.py deleted file mode 100644 index a96bcf7..0000000 --- a/ceph_deploy/test/fakes.py +++ /dev/null @@ -1,5 +0,0 @@ - - -def fake_getaddrinfo(*a, **kw): - return_host = kw.get('return_host', 'host1') - return [[0,0,0,0, return_host]] diff --git a/ceph_deploy/test/test_cli.py b/ceph_deploy/test/test_cli.py deleted file mode 100644 index 0e28378..0000000 --- a/ceph_deploy/test/test_cli.py +++ /dev/null @@ -1,38 +0,0 @@ -import pytest -import subprocess - - -def test_help(tmpdir, cli): - with cli( - args=['ceph-deploy', '--help'], - stdout=subprocess.PIPE, - ) as p: - result = p.stdout.read() - assert 'usage: ceph-deploy' in result - assert 'Deploy Ceph' in result - assert 'optional arguments:' in result - assert 'commands:' in result - - -def test_bad_command(tmpdir, cli): - with pytest.raises(cli.Failed) as err: - with cli( - args=['ceph-deploy', 'bork'], - stderr=subprocess.PIPE, - ) as p: - result = p.stderr.read() - assert 'usage: ceph-deploy' in result - assert err.value.status == 2 - assert {p.basename for p in tmpdir.listdir()} == set() - - -def test_bad_cluster(tmpdir, cli): - with pytest.raises(cli.Failed) as err: - with cli( - args=['ceph-deploy', '--cluster=/evil-this-should-not-be-created', 'new'], - stderr=subprocess.PIPE, - ) as p: - result = p.stderr.read() - assert 'usage: ceph-deploy' in result - assert err.value.status == 2 - assert {p.basename for p in tmpdir.listdir()} == set() diff --git a/ceph_deploy/test/test_cli_install.py b/ceph_deploy/test/test_cli_install.py deleted file mode 100644 index 0e8dcb0..0000000 --- a/ceph_deploy/test/test_cli_install.py +++ /dev/null @@ -1,63 +0,0 @@ -import argparse -import collections -import mock -import pytest -import subprocess - -from ..cli import main -from .. import install - -from .directory import directory - - -def test_help(tmpdir, cli): - with cli( - args=['ceph-deploy', 'install', '--help'], - stdout=subprocess.PIPE, - ) as p: - result = p.stdout.read() - assert 'usage: ceph-deploy' in result - assert 'positional arguments:' in result - assert 'optional arguments:' in result - - -def test_bad_no_host(tmpdir, cli): - with pytest.raises(cli.Failed) as err: - with cli( - args=['ceph-deploy', 'install'], - stderr=subprocess.PIPE, - ) as p: - result = p.stderr.read() - assert 'usage: ceph-deploy install' in result - assert 'too few arguments' in result - assert err.value.status == 2 - - -def test_simple(tmpdir): - ns = argparse.Namespace() - ns.pushy = mock.Mock() - conn = mock.NonCallableMock(name='PushyClient') - ns.pushy.return_value = conn - - mock_compiled = collections.defaultdict(mock.Mock) - conn.compile.return_value = mock.Mock(return_value = ('Ubuntu', 'precise','cuttlefish')) - - - try: - with directory(str(tmpdir)): - main( - args=['-v', 'install', 'storehost1'], - namespace=ns, - ) - except SystemExit as e: - raise AssertionError('Unexpected exit: %s', e) - - ns.pushy.assert_has_calls([ - mock.call('ssh+sudo:storehost1'), - ]) - - call_list = conn.compile.call_args_list - mock.call(install.lsb.check_lsb_release) == call_list[0] - mock.call(install.lsb.lsb_release) == call_list[1] - mock.call(install.install_debian) == call_list[2] - assert len(call_list) == 3 diff --git a/ceph_deploy/test/test_cli_mon.py b/ceph_deploy/test/test_cli_mon.py deleted file mode 100644 index 6df9096..0000000 --- a/ceph_deploy/test/test_cli_mon.py +++ /dev/null @@ -1,94 +0,0 @@ -import argparse -import collections -import mock -import pytest -import subprocess - -from ..cli import main -from .. import mon - -from .directory import directory -from .fakes import fake_getaddrinfo - -def test_help(tmpdir, cli): - with cli( - args=['ceph-deploy', 'mon', '--help'], - stdout=subprocess.PIPE, - ) as p: - result = p.stdout.read() - assert 'usage: ceph-deploy' in result - assert 'Deploy ceph monitor on remote hosts.' in result - assert 'positional arguments:' - assert 'optional arguments:' - - -def test_bad_no_conf(tmpdir, cli): - with pytest.raises(cli.Failed) as err: - with cli( - args=['ceph-deploy', 'mon'], - stderr=subprocess.PIPE, - ) as p: - result = p.stderr.read() - assert 'usage: ceph-deploy' in result - assert 'too few arguments' in result - assert err.value.status == 2 - - -def test_bad_no_mon(tmpdir, cli): - with tmpdir.join('ceph.conf').open('w'): - pass - with pytest.raises(cli.Failed) as err: - with cli( - args=['ceph-deploy', 'mon'], - stderr=subprocess.PIPE, - ) as p: - result = p.stderr.read() - assert 'usage: ceph-deploy mon' in result - assert 'too few arguments' in result - assert err.value.status == 2 - - -def test_simple(tmpdir, capsys): - with tmpdir.join('ceph.conf').open('w') as f: - f.write("""\ -[global] -fsid = 6ede5564-3cf1-44b5-aa96-1c77b0c3e1d0 -mon initial members = host1 -""") - - ns = argparse.Namespace() - ns.pushy = mock.Mock() - conn = mock.NonCallableMock(name='PushyClient') - ns.pushy.return_value = conn - - mock_compiled = collections.defaultdict(mock.Mock) - conn.compile.side_effect = mock_compiled.__getitem__ - - MON_SECRET = 'AQBWDj5QAP6LHhAAskVBnUkYHJ7eYREmKo5qKA==' - - def _create_mon(cluster, get_monitor_secret): - secret = get_monitor_secret() - assert secret == MON_SECRET - - mock_compiled[mon.create_mon].side_effect = _create_mon - - try: - with mock.patch('socket.getaddrinfo', fake_getaddrinfo): - with directory(str(tmpdir)): - main( - args=['-v', 'new', 'host1'], - namespace=ns, - ) - main( - args=['-v', 'mon', 'create', 'host1'], - namespace=ns, - ) - except SystemExit as e: - raise AssertionError('Unexpected exit: %s', e) - out, err = capsys.readouterr() - err = err.lower() - assert 'creating new cluster named ceph' in err - assert 'monitor host1 at h' in err - assert 'resolving host host1' in err - assert "monitor initial members are ['host1']" in err - assert "monitor addrs are ['h']" in err diff --git a/ceph_deploy/test/test_cli_new.py b/ceph_deploy/test/test_cli_new.py deleted file mode 100644 index 182729e..0000000 --- a/ceph_deploy/test/test_cli_new.py +++ /dev/null @@ -1,75 +0,0 @@ -import pytest -from mock import patch -import re -import subprocess -import uuid - -from .. import conf -from ..cli import main -from .directory import directory -from .fakes import fake_getaddrinfo - - -def test_help(tmpdir, cli): - with cli( - args=['ceph-deploy', 'new', '--help'], - stdout=subprocess.PIPE, - ) as p: - result = p.stdout.read() - assert 'usage: ceph-deploy new' in result - assert 'positional arguments' in result - assert 'optional arguments' in result - - -def test_write_global_conf_section(tmpdir, cli): - with patch('ceph_deploy.new.socket.getaddrinfo', fake_getaddrinfo): - with directory(str(tmpdir)): - main(args=['new', 'host1']) - with tmpdir.join('ceph.conf').open() as f: - cfg = conf.parse(f) - assert cfg.sections() == ['global'] - - -def pytest_funcarg__newcfg(request): - tmpdir = request.getfuncargvalue('tmpdir') - cli = request.getfuncargvalue('cli') - - def new(*args): - with patch('ceph_deploy.new.socket.getaddrinfo', fake_getaddrinfo): - with directory(str(tmpdir)): - main( args=['new'] + list(args)) - with tmpdir.join('ceph.conf').open() as f: - cfg = conf.parse(f) - return cfg - return new - - -def test_uuid(newcfg): - cfg = newcfg('host1') - fsid = cfg.get('global', 'fsid') - # make sure it's a valid uuid - uuid.UUID(hex=fsid) - # make sure it looks pretty, too - UUID_RE = re.compile( - r'^[0-9a-f]{8}-' - + r'[0-9a-f]{4}-' - # constant 4 here, we want to enforce randomness and not leak - # MACs or time - + r'4[0-9a-f]{3}-' - + r'[0-9a-f]{4}-' - + r'[0-9a-f]{12}$', - ) - assert UUID_RE.match(fsid) - - -def test_mons(newcfg): - cfg = newcfg('node01', 'node07', 'node34') - mon_initial_members = cfg.get('global', 'mon_initial_members') - assert mon_initial_members == 'node01, node07, node34' - - -def test_defaults(newcfg): - cfg = newcfg('host1') - assert cfg.get('global', 'auth_supported') == 'cephx' - assert cfg.get('global', 'osd_journal_size') == '1024' - assert cfg.get('global', 'filestore_xattr_use_omap') == 'true' diff --git a/ceph_deploy/test/test_cli_osd.py b/ceph_deploy/test/test_cli_osd.py deleted file mode 100644 index 25045af..0000000 --- a/ceph_deploy/test/test_cli_osd.py +++ /dev/null @@ -1,104 +0,0 @@ -import argparse -import collections -import mock -import pytest -import subprocess - -from ..cli import main -from .. import osd - -from .directory import directory - - -def test_help(tmpdir, cli): - with cli( - args=['ceph-deploy', 'osd', '--help'], - stdout=subprocess.PIPE, - ) as p: - result = p.stdout.read() - assert 'usage: ceph-deploy osd' in result - assert 'positional arguments' in result - assert 'optional arguments' in result - - -def test_bad_no_conf(tmpdir, cli): - with pytest.raises(cli.Failed) as err: - with cli( - args=['ceph-deploy', 'osd', 'fakehost:/does-not-exist'], - stderr=subprocess.PIPE, - ) as p: - result = p.stderr.read() - assert 'ceph-deploy osd: error' in result - assert 'invalid choice' in result - assert err.value.status == 2 - - -def test_bad_no_disk(tmpdir, cli): - with tmpdir.join('ceph.conf').open('w'): - pass - with pytest.raises(cli.Failed) as err: - with cli( - args=['ceph-deploy', 'osd'], - stderr=subprocess.PIPE, - ) as p: - result = p.stderr.read() - assert 'usage: ceph-deploy osd' in result - assert err.value.status == 2 - - -def test_simple(tmpdir, capsys): - with tmpdir.join('ceph.conf').open('w') as f: - f.write("""\ -[global] -fsid = 6ede5564-3cf1-44b5-aa96-1c77b0c3e1d0 -mon host = host1 -""") - - ns = argparse.Namespace() - - conn_osd = mock.NonCallableMock(name='PushyClient-osd') - mock_compiled_osd = collections.defaultdict(mock.Mock) - #conn_osd.compile.side_effect = mock_compiled_osd.__getitem__ - conn_osd.compile.return_value = mock.Mock(return_value='fakekeyring') - - conn_mon = mock.NonCallableMock(name='PushyClient-mon') - mock_compiled_mon = collections.defaultdict(mock.Mock) - conn_mon.compile.side_effect = mock_compiled_mon.__getitem__ - - ns.pushy = mock.Mock(name='pushy namespace') - - def _conn(url): - if url == 'ssh+sudo:host1': - return conn_mon - elif url == 'ssh+sudo:storehost1:sdc': - return conn_osd - else: - raise AssertionError('Unexpected connection url: %r', url) - ns.pushy.side_effect = _conn - - BOOTSTRAP_KEY = 'fakekeyring' - - mock_compiled_mon[osd.get_bootstrap_osd_key].side_effect = BOOTSTRAP_KEY - - def _create_osd(cluster, find_key): - key = find_key() - assert key == BOOTSTRAP_KEY - - mock_compiled_osd[osd.create_osd].side_effect = _create_osd - - with directory(str(tmpdir)): - main( - args=['-v', 'gatherkeys', 'storehost1:sdc'], - namespace=ns, - ) - main( - args=['-v', 'osd', 'prepare', 'storehost1:sdc'], - namespace=ns, - ) - out, err = capsys.readouterr() - err = err.lower() - assert 'have ceph.mon.keyring' in err - assert 'have ceph.client.admin.keyring' in err - assert 'have ceph.bootstrap-osd.keyring' in err - assert 'got ceph.bootstrap-mds.keyring key from storehost1:sdc' in err - assert 'got ceph.bootstrap-osd.keyring key from storehost1:sdc' in err diff --git a/ceph_deploy/test/test_conf.py b/ceph_deploy/test/test_conf.py deleted file mode 100644 index faa3688..0000000 --- a/ceph_deploy/test/test_conf.py +++ /dev/null @@ -1,59 +0,0 @@ -from cStringIO import StringIO -from .. import conf - - -def test_simple(): - f = StringIO("""\ -[foo] -bar = baz -""") - cfg = conf.parse(f) - assert cfg.get('foo', 'bar') == 'baz' - - -def test_indent_space(): - f = StringIO("""\ -[foo] - bar = baz -""") - cfg = conf.parse(f) - assert cfg.get('foo', 'bar') == 'baz' - - -def test_indent_tab(): - f = StringIO("""\ -[foo] -\tbar = baz -""") - cfg = conf.parse(f) - assert cfg.get('foo', 'bar') == 'baz' - - -def test_words_underscore(): - f = StringIO("""\ -[foo] -bar_thud = baz -""") - cfg = conf.parse(f) - assert cfg.get('foo', 'bar_thud') == 'baz' - assert cfg.get('foo', 'bar thud') == 'baz' - - -def test_words_space(): - f = StringIO("""\ -[foo] -bar thud = baz -""") - cfg = conf.parse(f) - assert cfg.get('foo', 'bar_thud') == 'baz' - assert cfg.get('foo', 'bar thud') == 'baz' - - -def test_words_many(): - f = StringIO("""\ -[foo] -bar__ thud quux = baz -""") - cfg = conf.parse(f) - assert cfg.get('foo', 'bar_thud_quux') == 'baz' - assert cfg.get('foo', 'bar thud quux') == 'baz' diff --git a/ceph_deploy/tests/__init__.py b/ceph_deploy/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ceph_deploy/tests/conftest.py b/ceph_deploy/tests/conftest.py new file mode 100644 index 0000000..819fc34 --- /dev/null +++ b/ceph_deploy/tests/conftest.py @@ -0,0 +1,98 @@ +import logging +import os +import subprocess +import sys + + +LOG = logging.getLogger(__name__) + + +def _prepend_path(env): + """ + Make sure the PATH contains the location where the Python binary + lives. This makes sure cli tools installed in a virtualenv work. + """ + if env is None: + env = os.environ + env = dict(env) + new = os.path.dirname(sys.executable) + path = env.get('PATH') + if path is not None: + new = new + ':' + path + env['PATH'] = new + return env + + +class CLIFailed(Exception): + """CLI tool failed""" + + def __init__(self, args, status): + self.args = args + self.status = status + + def __str__(self): + return '{doc}: {args}: exited with status {status}'.format( + doc=self.__doc__, + args=self.args, + status=self.status, + ) + + +class CLIProcess(object): + def __init__(self, **kw): + self.kw = kw + + def __enter__(self): + try: + self.p = subprocess.Popen(**self.kw) + except OSError as e: + raise AssertionError( + 'CLI tool {args!r} does not work: {err}'.format( + args=self.kw['args'], + err=e, + ), + ) + else: + return self.p + + def __exit__(self, exc_type, exc_val, exc_tb): + self.p.wait() + if self.p.returncode != 0: + err = CLIFailed( + args=self.kw['args'], + status=self.p.returncode, + ) + if exc_type is None: + # nothing else raised, so we should complain; if + # something else failed, we'll just log + raise err + else: + LOG.error(str(err)) + + +class CLITester(object): + # provide easy way for caller to access the exception class + # without importing us + Failed = CLIFailed + + def __init__(self, tmpdir): + self.tmpdir = tmpdir + + def __call__(self, **kw): + kw.setdefault('cwd', str(self.tmpdir)) + kw['env'] = _prepend_path(kw.get('env')) + kw['env']['COLUMNS'] = '80' + return CLIProcess(**kw) + + +def pytest_funcarg__cli(request): + """ + Test command line behavior. + """ + + # the tmpdir here will be the same value as the test function + # sees; we rely on that to let caller prepare and introspect + # any files the cli tool will read or create + tmpdir = request.getfuncargvalue('tmpdir') + + return CLITester(tmpdir=tmpdir) diff --git a/ceph_deploy/tests/directory.py b/ceph_deploy/tests/directory.py new file mode 100644 index 0000000..81d3e19 --- /dev/null +++ b/ceph_deploy/tests/directory.py @@ -0,0 +1,13 @@ +import contextlib +import os + + +@contextlib.contextmanager +def directory(path): + prev = os.open('.', os.O_RDONLY | os.O_DIRECTORY) + try: + os.chdir(path) + yield + finally: + os.fchdir(prev) + os.close(prev) diff --git a/ceph_deploy/tests/fakes.py b/ceph_deploy/tests/fakes.py new file mode 100644 index 0000000..a96bcf7 --- /dev/null +++ b/ceph_deploy/tests/fakes.py @@ -0,0 +1,5 @@ + + +def fake_getaddrinfo(*a, **kw): + return_host = kw.get('return_host', 'host1') + return [[0,0,0,0, return_host]] diff --git a/ceph_deploy/tests/test_cli.py b/ceph_deploy/tests/test_cli.py new file mode 100644 index 0000000..0e28378 --- /dev/null +++ b/ceph_deploy/tests/test_cli.py @@ -0,0 +1,38 @@ +import pytest +import subprocess + + +def test_help(tmpdir, cli): + with cli( + args=['ceph-deploy', '--help'], + stdout=subprocess.PIPE, + ) as p: + result = p.stdout.read() + assert 'usage: ceph-deploy' in result + assert 'Deploy Ceph' in result + assert 'optional arguments:' in result + assert 'commands:' in result + + +def test_bad_command(tmpdir, cli): + with pytest.raises(cli.Failed) as err: + with cli( + args=['ceph-deploy', 'bork'], + stderr=subprocess.PIPE, + ) as p: + result = p.stderr.read() + assert 'usage: ceph-deploy' in result + assert err.value.status == 2 + assert {p.basename for p in tmpdir.listdir()} == set() + + +def test_bad_cluster(tmpdir, cli): + with pytest.raises(cli.Failed) as err: + with cli( + args=['ceph-deploy', '--cluster=/evil-this-should-not-be-created', 'new'], + stderr=subprocess.PIPE, + ) as p: + result = p.stderr.read() + assert 'usage: ceph-deploy' in result + assert err.value.status == 2 + assert {p.basename for p in tmpdir.listdir()} == set() diff --git a/ceph_deploy/tests/test_cli_install.py b/ceph_deploy/tests/test_cli_install.py new file mode 100644 index 0000000..0e8dcb0 --- /dev/null +++ b/ceph_deploy/tests/test_cli_install.py @@ -0,0 +1,63 @@ +import argparse +import collections +import mock +import pytest +import subprocess + +from ..cli import main +from .. import install + +from .directory import directory + + +def test_help(tmpdir, cli): + with cli( + args=['ceph-deploy', 'install', '--help'], + stdout=subprocess.PIPE, + ) as p: + result = p.stdout.read() + assert 'usage: ceph-deploy' in result + assert 'positional arguments:' in result + assert 'optional arguments:' in result + + +def test_bad_no_host(tmpdir, cli): + with pytest.raises(cli.Failed) as err: + with cli( + args=['ceph-deploy', 'install'], + stderr=subprocess.PIPE, + ) as p: + result = p.stderr.read() + assert 'usage: ceph-deploy install' in result + assert 'too few arguments' in result + assert err.value.status == 2 + + +def test_simple(tmpdir): + ns = argparse.Namespace() + ns.pushy = mock.Mock() + conn = mock.NonCallableMock(name='PushyClient') + ns.pushy.return_value = conn + + mock_compiled = collections.defaultdict(mock.Mock) + conn.compile.return_value = mock.Mock(return_value = ('Ubuntu', 'precise','cuttlefish')) + + + try: + with directory(str(tmpdir)): + main( + args=['-v', 'install', 'storehost1'], + namespace=ns, + ) + except SystemExit as e: + raise AssertionError('Unexpected exit: %s', e) + + ns.pushy.assert_has_calls([ + mock.call('ssh+sudo:storehost1'), + ]) + + call_list = conn.compile.call_args_list + mock.call(install.lsb.check_lsb_release) == call_list[0] + mock.call(install.lsb.lsb_release) == call_list[1] + mock.call(install.install_debian) == call_list[2] + assert len(call_list) == 3 diff --git a/ceph_deploy/tests/test_cli_mon.py b/ceph_deploy/tests/test_cli_mon.py new file mode 100644 index 0000000..6df9096 --- /dev/null +++ b/ceph_deploy/tests/test_cli_mon.py @@ -0,0 +1,94 @@ +import argparse +import collections +import mock +import pytest +import subprocess + +from ..cli import main +from .. import mon + +from .directory import directory +from .fakes import fake_getaddrinfo + +def test_help(tmpdir, cli): + with cli( + args=['ceph-deploy', 'mon', '--help'], + stdout=subprocess.PIPE, + ) as p: + result = p.stdout.read() + assert 'usage: ceph-deploy' in result + assert 'Deploy ceph monitor on remote hosts.' in result + assert 'positional arguments:' + assert 'optional arguments:' + + +def test_bad_no_conf(tmpdir, cli): + with pytest.raises(cli.Failed) as err: + with cli( + args=['ceph-deploy', 'mon'], + stderr=subprocess.PIPE, + ) as p: + result = p.stderr.read() + assert 'usage: ceph-deploy' in result + assert 'too few arguments' in result + assert err.value.status == 2 + + +def test_bad_no_mon(tmpdir, cli): + with tmpdir.join('ceph.conf').open('w'): + pass + with pytest.raises(cli.Failed) as err: + with cli( + args=['ceph-deploy', 'mon'], + stderr=subprocess.PIPE, + ) as p: + result = p.stderr.read() + assert 'usage: ceph-deploy mon' in result + assert 'too few arguments' in result + assert err.value.status == 2 + + +def test_simple(tmpdir, capsys): + with tmpdir.join('ceph.conf').open('w') as f: + f.write("""\ +[global] +fsid = 6ede5564-3cf1-44b5-aa96-1c77b0c3e1d0 +mon initial members = host1 +""") + + ns = argparse.Namespace() + ns.pushy = mock.Mock() + conn = mock.NonCallableMock(name='PushyClient') + ns.pushy.return_value = conn + + mock_compiled = collections.defaultdict(mock.Mock) + conn.compile.side_effect = mock_compiled.__getitem__ + + MON_SECRET = 'AQBWDj5QAP6LHhAAskVBnUkYHJ7eYREmKo5qKA==' + + def _create_mon(cluster, get_monitor_secret): + secret = get_monitor_secret() + assert secret == MON_SECRET + + mock_compiled[mon.create_mon].side_effect = _create_mon + + try: + with mock.patch('socket.getaddrinfo', fake_getaddrinfo): + with directory(str(tmpdir)): + main( + args=['-v', 'new', 'host1'], + namespace=ns, + ) + main( + args=['-v', 'mon', 'create', 'host1'], + namespace=ns, + ) + except SystemExit as e: + raise AssertionError('Unexpected exit: %s', e) + out, err = capsys.readouterr() + err = err.lower() + assert 'creating new cluster named ceph' in err + assert 'monitor host1 at h' in err + assert 'resolving host host1' in err + assert "monitor initial members are ['host1']" in err + assert "monitor addrs are ['h']" in err diff --git a/ceph_deploy/tests/test_cli_new.py b/ceph_deploy/tests/test_cli_new.py new file mode 100644 index 0000000..182729e --- /dev/null +++ b/ceph_deploy/tests/test_cli_new.py @@ -0,0 +1,75 @@ +import pytest +from mock import patch +import re +import subprocess +import uuid + +from .. import conf +from ..cli import main +from .directory import directory +from .fakes import fake_getaddrinfo + + +def test_help(tmpdir, cli): + with cli( + args=['ceph-deploy', 'new', '--help'], + stdout=subprocess.PIPE, + ) as p: + result = p.stdout.read() + assert 'usage: ceph-deploy new' in result + assert 'positional arguments' in result + assert 'optional arguments' in result + + +def test_write_global_conf_section(tmpdir, cli): + with patch('ceph_deploy.new.socket.getaddrinfo', fake_getaddrinfo): + with directory(str(tmpdir)): + main(args=['new', 'host1']) + with tmpdir.join('ceph.conf').open() as f: + cfg = conf.parse(f) + assert cfg.sections() == ['global'] + + +def pytest_funcarg__newcfg(request): + tmpdir = request.getfuncargvalue('tmpdir') + cli = request.getfuncargvalue('cli') + + def new(*args): + with patch('ceph_deploy.new.socket.getaddrinfo', fake_getaddrinfo): + with directory(str(tmpdir)): + main( args=['new'] + list(args)) + with tmpdir.join('ceph.conf').open() as f: + cfg = conf.parse(f) + return cfg + return new + + +def test_uuid(newcfg): + cfg = newcfg('host1') + fsid = cfg.get('global', 'fsid') + # make sure it's a valid uuid + uuid.UUID(hex=fsid) + # make sure it looks pretty, too + UUID_RE = re.compile( + r'^[0-9a-f]{8}-' + + r'[0-9a-f]{4}-' + # constant 4 here, we want to enforce randomness and not leak + # MACs or time + + r'4[0-9a-f]{3}-' + + r'[0-9a-f]{4}-' + + r'[0-9a-f]{12}$', + ) + assert UUID_RE.match(fsid) + + +def test_mons(newcfg): + cfg = newcfg('node01', 'node07', 'node34') + mon_initial_members = cfg.get('global', 'mon_initial_members') + assert mon_initial_members == 'node01, node07, node34' + + +def test_defaults(newcfg): + cfg = newcfg('host1') + assert cfg.get('global', 'auth_supported') == 'cephx' + assert cfg.get('global', 'osd_journal_size') == '1024' + assert cfg.get('global', 'filestore_xattr_use_omap') == 'true' diff --git a/ceph_deploy/tests/test_cli_osd.py b/ceph_deploy/tests/test_cli_osd.py new file mode 100644 index 0000000..25045af --- /dev/null +++ b/ceph_deploy/tests/test_cli_osd.py @@ -0,0 +1,104 @@ +import argparse +import collections +import mock +import pytest +import subprocess + +from ..cli import main +from .. import osd + +from .directory import directory + + +def test_help(tmpdir, cli): + with cli( + args=['ceph-deploy', 'osd', '--help'], + stdout=subprocess.PIPE, + ) as p: + result = p.stdout.read() + assert 'usage: ceph-deploy osd' in result + assert 'positional arguments' in result + assert 'optional arguments' in result + + +def test_bad_no_conf(tmpdir, cli): + with pytest.raises(cli.Failed) as err: + with cli( + args=['ceph-deploy', 'osd', 'fakehost:/does-not-exist'], + stderr=subprocess.PIPE, + ) as p: + result = p.stderr.read() + assert 'ceph-deploy osd: error' in result + assert 'invalid choice' in result + assert err.value.status == 2 + + +def test_bad_no_disk(tmpdir, cli): + with tmpdir.join('ceph.conf').open('w'): + pass + with pytest.raises(cli.Failed) as err: + with cli( + args=['ceph-deploy', 'osd'], + stderr=subprocess.PIPE, + ) as p: + result = p.stderr.read() + assert 'usage: ceph-deploy osd' in result + assert err.value.status == 2 + + +def test_simple(tmpdir, capsys): + with tmpdir.join('ceph.conf').open('w') as f: + f.write("""\ +[global] +fsid = 6ede5564-3cf1-44b5-aa96-1c77b0c3e1d0 +mon host = host1 +""") + + ns = argparse.Namespace() + + conn_osd = mock.NonCallableMock(name='PushyClient-osd') + mock_compiled_osd = collections.defaultdict(mock.Mock) + #conn_osd.compile.side_effect = mock_compiled_osd.__getitem__ + conn_osd.compile.return_value = mock.Mock(return_value='fakekeyring') + + conn_mon = mock.NonCallableMock(name='PushyClient-mon') + mock_compiled_mon = collections.defaultdict(mock.Mock) + conn_mon.compile.side_effect = mock_compiled_mon.__getitem__ + + ns.pushy = mock.Mock(name='pushy namespace') + + def _conn(url): + if url == 'ssh+sudo:host1': + return conn_mon + elif url == 'ssh+sudo:storehost1:sdc': + return conn_osd + else: + raise AssertionError('Unexpected connection url: %r', url) + ns.pushy.side_effect = _conn + + BOOTSTRAP_KEY = 'fakekeyring' + + mock_compiled_mon[osd.get_bootstrap_osd_key].side_effect = BOOTSTRAP_KEY + + def _create_osd(cluster, find_key): + key = find_key() + assert key == BOOTSTRAP_KEY + + mock_compiled_osd[osd.create_osd].side_effect = _create_osd + + with directory(str(tmpdir)): + main( + args=['-v', 'gatherkeys', 'storehost1:sdc'], + namespace=ns, + ) + main( + args=['-v', 'osd', 'prepare', 'storehost1:sdc'], + namespace=ns, + ) + out, err = capsys.readouterr() + err = err.lower() + assert 'have ceph.mon.keyring' in err + assert 'have ceph.client.admin.keyring' in err + assert 'have ceph.bootstrap-osd.keyring' in err + assert 'got ceph.bootstrap-mds.keyring key from storehost1:sdc' in err + assert 'got ceph.bootstrap-osd.keyring key from storehost1:sdc' in err diff --git a/ceph_deploy/tests/test_conf.py b/ceph_deploy/tests/test_conf.py new file mode 100644 index 0000000..faa3688 --- /dev/null +++ b/ceph_deploy/tests/test_conf.py @@ -0,0 +1,59 @@ +from cStringIO import StringIO +from .. import conf + + +def test_simple(): + f = StringIO("""\ +[foo] +bar = baz +""") + cfg = conf.parse(f) + assert cfg.get('foo', 'bar') == 'baz' + + +def test_indent_space(): + f = StringIO("""\ +[foo] + bar = baz +""") + cfg = conf.parse(f) + assert cfg.get('foo', 'bar') == 'baz' + + +def test_indent_tab(): + f = StringIO("""\ +[foo] +\tbar = baz +""") + cfg = conf.parse(f) + assert cfg.get('foo', 'bar') == 'baz' + + +def test_words_underscore(): + f = StringIO("""\ +[foo] +bar_thud = baz +""") + cfg = conf.parse(f) + assert cfg.get('foo', 'bar_thud') == 'baz' + assert cfg.get('foo', 'bar thud') == 'baz' + + +def test_words_space(): + f = StringIO("""\ +[foo] +bar thud = baz +""") + cfg = conf.parse(f) + assert cfg.get('foo', 'bar_thud') == 'baz' + assert cfg.get('foo', 'bar thud') == 'baz' + + +def test_words_many(): + f = StringIO("""\ +[foo] +bar__ thud quux = baz +""") + cfg = conf.parse(f) + assert cfg.get('foo', 'bar_thud_quux') == 'baz' + assert cfg.get('foo', 'bar thud quux') == 'baz'