File: test_service.py

package info (click to toggle)
python-dcos 0.2.0-10
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,440 kB
  • sloc: python: 8,196; sh: 194; makefile: 36
file content (246 lines) | stat: -rw-r--r-- 7,569 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
import os
import subprocess
import time

import dcos.util as util
from dcos.util import create_schema

import pytest

from ..fixtures.service import framework_fixture
from .common import (assert_command, assert_lines, delete_zk_node,
                     delete_zk_nodes, exec_command, get_services,
                     package_install, package_uninstall, remove_app,
                     service_shutdown, ssh_output, wait_for_service,
                     watch_all_deployments)


def setup_module(module):
    package_install('chronos', True)


def teardown_module(module):
    package_uninstall(
        'chronos',
        stderr=b'Uninstalled package [chronos] version [2.4.0]\n'
               b'The Chronos DCOS Service has been uninstalled and will no '
               b'longer run.\nPlease follow the instructions at http://docs.'
               b'mesosphere.com/services/chronos/#uninstall to clean up any '
               b'persisted state\n')
    delete_zk_nodes()


def test_help():
    with open('tests/data/help/service.txt') as content:
        assert_command(['dcos', 'service', '--help'],
                       stdout=content.read().encode('utf-8'))


def test_info():
    stdout = b"Manage DCOS services\n"
    assert_command(['dcos', 'service', '--info'], stdout=stdout)


def test_service():
    services = get_services(2)

    schema = _get_schema(framework_fixture())
    for srv in services:
        assert not util.validate_json(srv, schema)


def test_service_table():
    assert_lines(['dcos', 'service'], 3)


def test_service_inactive():
    package_install('cassandra', True)

    # wait long enough for it to register
    time.sleep(5)

    # assert marathon, chronos, and cassandra are listed
    get_services(3)

    # uninstall cassandra using marathon. For now, need to explicitly remove
    # the group that is left by cassandra.  See MARATHON-144
    assert_command(['dcos', 'marathon', 'group', 'remove', '/cassandra'])

    watch_all_deployments()

    # I'm not quite sure why we have to sleep, but it seems cassandra
    # only transitions to "inactive" after a few seconds.
    time.sleep(5)

    # assert only marathon and chronos are active
    get_services(2)

    # assert marathon, chronos, and cassandra are inactive
    services = get_services(args=['--inactive'])
    assert len(services) >= 3

    # shutdown the cassandra framework
    for framework in services:
        if framework['name'] == 'cassandra.dcos':
            service_shutdown(framework['id'])

    # assert marathon, chronos are only listed with --inactive
    get_services(2, ['--inactive'])

    delete_zk_node('cassandra-mesos')

    package_uninstall('cassandra')


def test_service_completed():
    package_install('cassandra', True)

    time.sleep(5)

    services = get_services(3)

    # get cassandra's framework ID
    cassandra_id = None
    for service in services:
        if service['name'] == 'cassandra.dcos':
            cassandra_id = service['id']
            break

    assert cassandra_id is not None

    assert_command(['dcos', 'marathon', 'group', 'remove', '/cassandra'])
    service_shutdown(cassandra_id)
    delete_zk_node('cassandra-mesos')

    # assert cassandra is not running
    services = get_services(2)
    assert not any(service['id'] == cassandra_id for service in services)

    # assert cassandra is completed
    services = get_services(args=['--completed'])
    assert len(services) >= 3
    assert any(service['id'] == cassandra_id for service in services)

    package_uninstall('cassandra')


def test_log():
    returncode, stdout, stderr = exec_command(
        ['dcos', 'service', 'log', 'chronos'])

    assert returncode == 0
    assert len(stdout.decode('utf-8').split('\n')) > 1
    assert stderr == b''


def test_log_file():
    returncode, stdout, stderr = exec_command(
        ['dcos', 'service', 'log', 'chronos', 'stderr'])

    assert returncode == 0
    assert len(stdout.decode('utf-8').split('\n')) > 1
    assert stderr == b''


def test_log_marathon_file():
    assert_command(['dcos', 'service', 'log', 'marathon', 'stderr'],
                   stderr=(b'The <file> argument is invalid for marathon. ' +
                           b'The systemd journal is always used for the ' +
                           b'marathon log.\n'),
                   returncode=1)


def test_log_marathon_config():
    stdout, stderr = ssh_output(
        'dcos service log marathon ' +
        '--ssh-config-file=tests/data/node/ssh_config')

    assert stdout == b''
    assert b'ignoring bad proto spec' in stderr


def test_log_marathon():
    stdout, stderr = ssh_output(
        'dcos service log marathon ' +
        '--ssh-config-file=tests/data/service/ssh_config')

    assert len(stdout.decode('utf-8').split('\n')) > 10

    assert b"Running `" in stderr
    num_lines = len(stderr.decode('utf-8').split('\n'))
    assert ((num_lines == 2) or
            (num_lines == 3 and b'Warning: Permanently added' in stderr))


def test_log_config():
    assert_command(
        ['dcos', 'service', 'log', 'chronos', '--ssh-config-file=/path'],
        stderr=(b'The `--ssh-config-file` argument is invalid for '
                b'non-marathon services. SSH is not used.\n'),
        returncode=1)


def test_log_follow():
    wait_for_service('chronos')
    proc = subprocess.Popen(['dcos', 'service', 'log', 'chronos', '--follow'],
                            preexec_fn=os.setsid,
                            stdout=subprocess.PIPE)

    time.sleep(5)

    proc.poll()
    assert proc.returncode is None

    os.killpg(os.getpgid(proc.pid), 15)
    assert len(proc.stdout.read().decode('utf-8').split('\n')) > 3


def test_log_lines():
    assert_lines(['dcos', 'service', 'log', 'chronos', '--lines=4'], 4)


@pytest.mark.skipif(True, reason='Broken Marathon but we need to release')
def test_log_multiple_apps():
    package_install('marathon',
                    True,
                    ['--options=tests/data/service/marathon-user.json'])
    package_install('marathon',
                    True,
                    ['--options=tests/data/service/marathon-user2.json',
                     '--app-id=marathon-user2'])
    wait_for_service('marathon-user', number_of_services=2)

    try:
        stderr = (b'Multiple marathon apps found for service name ' +
                  b'[marathon-user]: [/marathon-user], [/marathon-user2]\n')
        assert_command(['dcos', 'service', 'log', 'marathon-user'],
                       returncode=1,
                       stderr=stderr)
    finally:
        # We can't use `dcos package uninstall`. The services have the same
        # name. Manually remove the dcos services.
        remove_app('marathon-user')
        remove_app('marathon-user2')
        for service in get_services():
            if service['name'] == 'marathon-user':
                service_shutdown(service['id'])

        delete_zk_node('universe')


def test_log_no_apps():
    assert_command(['dcos', 'service', 'log', 'bogus'],
                   stderr=b'No marathon apps found for service [bogus]\n',
                   returncode=1)


def _get_schema(service):
    schema = create_schema(service.dict())
    schema['required'].remove('reregistered_time')
    schema['required'].remove('pid')
    schema['required'].remove('executors')
    schema['properties']['offered_resources']['required'].remove('ports')
    schema['properties']['resources']['required'].remove('ports')
    schema['properties']['used_resources']['required'].remove('ports')

    return schema