Ejemplo n.º 1
0
def test_user_conflict(dcos_api_session: DcosApiSession) -> None:
    # Note: the empty request body is not the decisive criterion here.
    r = dcos_api_session.put('/acs/api/v1/users/[email protected]', json={})
    assert r.status_code == 201, r.text

    r = dcos_api_session.put('/acs/api/v1/users/[email protected]', json={})
    assert r.status_code == 409, r.text
Ejemplo n.º 2
0
def test_legacy_user_creation_with_empty_json_doc(
        dcos_api_session: DcosApiSession) -> None:
    # Legacy HTTP clients built for dcos-oauth such as the web UI (up to DC/OS
    # 1.12) might insert users in the following way: uid appears to be an email
    # address, and the JSON document in the request body does not provide a
    # `public_key` or a `password` property (indicating local user), or is
    # empty. The legacy web UI would insert users like that and expect those
    # users to be remote users, usable with the legacy OIDC ID Token login
    # method through the 'https://dcos.auth0.com/' provider. This behavior is
    # maintained in Bouncer for backwards compatibility.
    r = dcos_api_session.put('/acs/api/v1/users/[email protected]', json={})
    assert r.status_code == 201, r.text

    # Bouncer annotates the created user (this is new compared to dcos-oauth).
    r = dcos_api_session.get('/acs/api/v1/users/[email protected]')
    assert r.json()['provider_type'] == 'oidc'
    assert r.json()['provider_id'] == 'https://dcos.auth0.com/'
    assert r.json()['is_remote'] is True

    # When the uid however does not appear to be an email address the more sane
    # behavior of Bouncer takes effect: an empty (meaningless) JSON body
    # results in a useful error message.
    r = dcos_api_session.put('/acs/api/v1/users/user1', json={})
    assert r.status_code == 400
    assert 'One of `password` or `public_key` must be provided' in r.text
Ejemplo n.º 3
0
def test_task_logs(dcos_api_session: DcosApiSession) -> None:
    skip_test_if_dcos_journald_log_disabled(dcos_api_session)
    test_uuid = uuid.uuid4().hex

    task_id = "integration-test-task-logs-{}".format(test_uuid)

    task_definition = {
        "id": "/{}".format(task_id),
        "cpus": 0.1,
        "instances": 1,
        "mem": 128,
        "cmd": "echo STDOUT_LOG; echo STDERR_LOG >&2;sleep 999"
    }

    with dcos_api_session.marathon.deploy_and_cleanup(task_definition, check_health=False):
        url = get_task_url(dcos_api_session, task_id)
        check_response('STDOUT_LOG', lambda: dcos_api_session.get(url + '?filter=STREAM:STDOUT'))
        check_response('STDERR_LOG', lambda: dcos_api_session.get(url + '?filter=STREAM:STDERR'))

        stream_url = get_task_url(dcos_api_session, task_id, stream=True)
        response = dcos_api_session.get(stream_url, stream=True, headers={'Accept': 'text/event-stream'})
        check_response_ok(response, {'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache'})
        lines = response.iter_lines()
        sse_id = next(lines)
        assert sse_id, 'First line must be id. Got {}'.format(sse_id)
        data = next(lines).decode('utf-8', 'ignore')
        validate_sse_entry(data)
Ejemplo n.º 4
0
def test_adminrouter_access_control_enforcement(
        dcos_api_session: DcosApiSession,
        noauth_api_session: DcosApiSession) -> None:
    reason = 'Can only test adminrouter enforcement if auth is enabled'
    if not auth_enabled():
        pytest.skip(reason)
    r = noauth_api_session.get('/acs/api/v1')
    assert r.status_code == 401
    assert r.headers['WWW-Authenticate'] in ('acsjwt', 'oauthjwt')
    # Make sure that this is UI's error page body,
    # including some JavaScript.
    assert '<html>' in r.text
    assert '</html>' in r.text
    assert 'window.location' in r.text
    # Verify that certain locations are forbidden to access
    # when not authed, but are reachable as superuser.
    for path in ('/mesos_dns/v1/config', '/service/marathon/', '/mesos/'):
        r = noauth_api_session.get(path)
        assert r.status_code == 401
        r = dcos_api_session.get(path)
        assert r.status_code == 200

    # Test authentication with auth cookie instead of Authorization header.
    authcookie = {
        'dcos-acs-auth-cookie': dcos_api_session.auth_user.auth_cookie
    }
    r = noauth_api_session.get('/service/marathon/', cookies=authcookie)
    assert r.status_code == 200
Ejemplo n.º 5
0
def test_pod_logs(dcos_api_session: DcosApiSession) -> None:
    skip_test_if_dcos_journald_log_disabled(dcos_api_session)
    test_uuid = uuid.uuid4().hex

    pod_id = 'integration-test-pod-logs-{}'.format(test_uuid)

    pod_definition = {
        'id': '/{}'.format(pod_id),
        'scaling': {'kind': 'fixed', 'instances': 1},
        'environment': {'PING': 'PONG'},
        'containers': [
            {
                'name': 'sleep1',
                'exec': {'command': {'shell': 'echo $PING > foo;echo STDOUT_LOG;echo STDERR_LOG >&2;sleep 10000'}},
                'resources': {'cpus': 0.1, 'mem': 32},
                'healthcheck': {'command': {'shell': 'test $PING = `cat foo`'}}
            }
        ],
        'networks': [{'mode': 'host'}]
    }

    with dcos_api_session.marathon.deploy_pod_and_cleanup(pod_definition):
        url = get_task_url(dcos_api_session, pod_id)
        container_id = url.split('/')[-1]

        check_response('STDOUT_LOG', lambda: dcos_api_session.get(url + '?filter=STREAM:STDOUT'))
        check_response('STDERR_LOG', lambda: dcos_api_session.get(url + '?filter=STREAM:STDERR'))

        response = dcos_api_session.get(url + '/download', query='limit=10&postfix=stdout')
        log_file_name = 'task-{}-stdout.log.gz'.format(container_id)
        check_response_ok(response, {'Content-Disposition': 'attachment; filename={}'.format(log_file_name)})
Ejemplo n.º 6
0
def test_mesos_agent_role_assignment(dcos_api_session: DcosApiSession) -> None:
    state_endpoint = '/state'
    for agent in dcos_api_session.public_slaves:
        r = dcos_api_session.get(state_endpoint, host=agent, port=5051)
        assert r.json()['flags']['default_role'] == 'slave_public'
    for agent in dcos_api_session.slaves:
        r = dcos_api_session.get(state_endpoint, host=agent, port=5051)
        assert r.json()['flags']['default_role'] == '*'
Ejemplo n.º 7
0
def test_user_delete(dcos_api_session: DcosApiSession) -> None:
    r = dcos_api_session.put('/acs/api/v1/users/[email protected]', json={})
    r.raise_for_status()
    assert r.status_code == 201

    r = dcos_api_session.delete('/acs/api/v1/users/[email protected]')
    r.raise_for_status()
    assert r.status_code == 204

    users = get_users(dcos_api_session)
    assert '*****@*****.**' not in users
Ejemplo n.º 8
0
def test_log_proxy(dcos_api_session: DcosApiSession) -> None:
    r = dcos_api_session.get('/mesos/master/slaves')
    check_response_ok(r, {})

    data = r.json()
    slaves_ids = sorted(x['id'] for x in data['slaves'] if x['hostname'] in dcos_api_session.all_slaves)

    for slave_id in slaves_ids:
        response = dcos_api_session.get('/system/v1/agent/{}/logs/v1/range/?skip_prev=10&limit=10'.format(slave_id))
        check_response_ok(response, {'Content-Type': 'text/plain'})
        lines = list(filter(lambda x: x != '', response.text.split('\n')))
        assert len(lines) == 10, 'Expect 10 log entries. Got {}. All lines {}'.format(len(lines), lines)
Ejemplo n.º 9
0
    def wait_for_dcos_oss(self) -> None:
        """
        Wait until the DC/OS OSS boot process has completed.

        Raises:
            RetryError: Raised if any cluster component did not become
                healthy in time.
        """

        self._wait_for_dcos_diagnostics()

        # The dcos-diagnostics check is not yet sufficient to determine
        # when a CLI login would be possible with DC/OS OSS. It only
        # checks the healthy state of the systemd units, not reachability
        # of services through HTTP.

        # Since DC/OS uses a Single-Sign-On flow with Identity Providers
        # outside the cluster for the login and Admin Router only rewrites
        # requests to them, the login endpoint does not provide anything.

        # Current solution to guarantee the security CLI login:

        # Try until one can login successfully with a long lived token
        # (dirty hack in dcos-test-utils wait_for_dcos). This is to avoid
        # having to simulate a browser that does the SSO flow.

        # Suggestion for replacing this with a DC/OS check for CLI login:

        # Determine and wait for all dependencies of the SSO OAuth login
        # inside of DC/OS. This should include Admin Router, ZooKeeper and
        # the DC/OS OAuth login service. Note that this may only guarantee
        # that the login could work, however not that is actually works.

        # In order to fully replace this method one would need to have
        # DC/OS checks for every HTTP endpoint exposed by Admin Router.

        any_master = next(iter(self.masters))

        api_session = DcosApiSession(
            dcos_url='http://{ip}'.format(ip=any_master.public_ip_address),
            masters=[str(n.public_ip_address) for n in self.masters],
            slaves=[str(n.public_ip_address) for n in self.agents],
            public_slaves=[
                str(n.public_ip_address) for n in self.public_agents
            ],
            auth_user=DcosUser(credentials=CI_CREDENTIALS),
        )

        api_session.wait_for_dcos()
Ejemplo n.º 10
0
def test_checks_api(dcos_api_session: DcosApiSession) -> None:
    """
    Test the checks API at /system/checks/
    This will test that all checks run on all agents return a normal status. A
    failure in this test may be an indicator that some unrelated component
    failed and dcos-checks functioned properly.
    """
    checks_uri = '/system/checks/v1/'
    # Test that we can list and run node and cluster checks on a master, agent, and public agent.
    check_nodes = []
    for nodes in [
            dcos_api_session.masters, dcos_api_session.slaves,
            dcos_api_session.public_slaves
    ]:
        if nodes:
            check_nodes.append(random.choice(nodes))
    logging.info('Testing %s on these nodes: %s', checks_uri,
                 ', '.join(check_nodes))

    for node in check_nodes:
        for check_type in ['node', 'cluster']:
            uri = '{}{}/'.format(checks_uri, check_type)
            logging.info('Testing %s on %s', uri, node)

            # List checks
            r = dcos_api_session.get(uri, node=node)
            assert r.status_code == 200
            checks = r.json()
            assert isinstance(checks, dict)

            # Run checks
            r = dcos_api_session.post(uri, node=node)
            assert r.status_code == 200
            results = r.json()
            assert isinstance(results, dict)

            # check that the returned statuses of each check is 0
            expected_status = {c: 0 for c in checks.keys()}
            response_status = {
                c: v['status']
                for c, v in results['checks'].items()
            }

            # print out the response for debugging
            logging.info('Response: {}'.format(results))
            assert expected_status == response_status

            # check that overall status is also 0
            assert results['status'] == 0
Ejemplo n.º 11
0
def test_metronome(dcos_api_session: DcosApiSession) -> None:
    job = {
        'description': 'Test Metronome API regressions',
        'id': 'test.metronome',
        'run': {
            'cmd': 'ls',
            'docker': {'image': 'busybox:latest'},
            'cpus': 1,
            'mem': 512,
            'disk': 0,
            'user': '******',
            'restart': {'policy': 'ON_FAILURE'}
        }
    }
    dcos_api_session.metronome_one_off(job)
Ejemplo n.º 12
0
def test_if_marathon_is_up(dcos_api_session: DcosApiSession) -> None:
    r = dcos_api_session.get('/marathon/v2/info')

    assert r.status_code == 200
    response_json = r.json()
    assert "name" in response_json
    assert "marathon" == response_json["name"]
Ejemplo n.º 13
0
def test_if_overlay_master_is_up(dcos_api_session: DcosApiSession) -> None:
    r = dcos_api_session.get('/mesos/overlay-master/state')
    assert r.ok, "status_code: {}, content: {}".format(r.status_code,
                                                       r.content)

    # Make sure the `dcos` and `dcos6` overlays have been configured.
    json = r.json()

    dcos_overlay_network = {
        'vtep_subnet':
        '44.128.0.0/20',
        'vtep_subnet6':
        'fd01:a::/64',
        'vtep_mac_oui':
        '70:B3:D5:00:00:00',
        'overlays': [{
            'name': 'dcos',
            'subnet': '9.0.0.0/8',
            'prefix': 24
        }, {
            'name': 'dcos6',
            'subnet6': 'fd01:b::/64',
            'prefix6': 80
        }]
    }

    assert nested_match(dcos_overlay_network, json['network'])
Ejemplo n.º 14
0
def test_dcos_add_user(dcos_api_session: DcosApiSession,
                       new_dcos_cli: DcosCli) -> None:
    """
    dcos_add_user.py script adds a user to IAM using the
    script dcos_add_user.py.
    """

    email_address = uuid.uuid4().hex + '@example.com'
    command = ['python', '/opt/mesosphere/bin/dcos_add_user.py', email_address]
    new_dcos_cli.exec_command(command)

    try:
        r = dcos_api_session.get('/acs/api/v1/users')
        r.raise_for_status()
        expected_user_data = {
            "uid": email_address,
            "description": "",
            "url": "/acs/api/v1/users/" + email_address,
            "is_remote": True,
            "is_service": False,
            "provider_type": "oidc",
            "provider_id": "https://dcos.auth0.com/"
        }
        assert expected_user_data in r.json()['array']
    finally:
        delete_user(dcos_api_session, email_address)
Ejemplo n.º 15
0
def test_containerizer_debug_endpoint(
        dcos_api_session: DcosApiSession) -> None:
    # Test that we can poll `/containerizer/debug` endpoint exposed by the agent.
    agent = dcos_api_session.slaves[0]
    r = dcos_api_session.get('/containerizer/debug', host=agent, port=5051)
    assert r.status_code == 200
    assert r.json() == {'pending': []}
Ejemplo n.º 16
0
 def test_accept_gzip(self, dcos_api_session: DcosApiSession) -> None:
     """
     Clients that send "Accept-Encoding: gzip" get gzipped responses
     for some assets.
     """
     r = dcos_api_session.get('/')
     r.raise_for_status()
     filenames = self.pat.findall(r.text)
     assert len(filenames) > 0
     for filename in set(filenames):
         log.info('Load %r', filename)
         r = dcos_api_session.head(filename,
                                   headers={'Accept-Encoding': 'gzip'})
         r.raise_for_status()
         log.info('Response headers: %s', repr(r.headers))
         assert r.headers.get('content-encoding') == 'gzip'
Ejemplo n.º 17
0
def test_dcos_diagnostics_bundle_create_download_delete(
        dcos_api_session: DcosApiSession, use_legacy_api: bool) -> None:
    """
    test bundle create, read, delete workflow
    """

    health_url = dcos_api_session.default_url.copy(
        query='cache=0',
        path='system/health/v1',
    )

    diagnostics = Diagnostics(
        default_url=health_url,
        masters=dcos_api_session.masters,
        all_slaves=dcos_api_session.all_slaves,
        session=dcos_api_session.copy().session,
        use_legacy_api=use_legacy_api,
    )

    app, test_uuid = test_helpers.marathon_test_docker_app('diag-bundle',
                                                           constraints=[])
    with dcos_api_session.marathon.deploy_and_cleanup(app, timeout=120):
        bundle = _create_bundle(diagnostics)
        _check_diagnostics_bundle_status(dcos_api_session)
        _download_and_extract_bundle(dcos_api_session, bundle, diagnostics)
        _download_and_extract_bundle_from_another_master(
            dcos_api_session, bundle, diagnostics)
        _delete_bundle(diagnostics, bundle)
Ejemplo n.º 18
0
def test_memory_profiling(dcos_api_session: DcosApiSession) -> None:
    # Test that we can fetch raw memory profiles
    master_ip = dcos_api_session.masters[0]
    r0 = dcos_api_session.get('/memory-profiler/start',
                              host=master_ip,
                              port=5050)
    assert r0.status_code == 200, r0.text

    r1 = dcos_api_session.get('/memory-profiler/stop',
                              host=master_ip,
                              port=5050)
    assert r1.status_code == 200, r1.text

    r2 = dcos_api_session.get('/memory-profiler/download/raw',
                              host=master_ip,
                              port=5050)
    assert r2.status_code == 200, r2.text
Ejemplo n.º 19
0
def test_if_dcos_ui_is_up(dcos_api_session: DcosApiSession) -> None:
    r = dcos_api_session.get('/')

    assert r.status_code == 200
    assert len(r.text) > 100
    assert 'DC/OS' in r.text

    # Not sure if it's really needed, seems a bit of an overkill:
    soup = bs4.BeautifulSoup(r.text, "html.parser")
    for link in soup.find_all(['link', 'a'], href=True):
        if urllib.parse.urlparse(link.attrs['href']).netloc:
            # Relative URLs only, others are to complex to handle here
            continue
        # Some links might start with a dot (e.g. ./img/...). Remove.
        href = link.attrs['href'].lstrip('.')
        link_response = dcos_api_session.head(href)
        assert link_response.status_code == 200
Ejemplo n.º 20
0
 def test_not_accept_gzip(self, dcos_api_session: DcosApiSession) -> None:
     """
     Clients that do not send "Accept-Encoding: gzip" do not get gzipped
     responses.
     """
     r = dcos_api_session.get('/')
     r.raise_for_status()
     filenames = self.pat.findall(r.text)
     assert len(filenames) > 0
     for filename in set(filenames):
         log.info('Load %r', filename)
         # Set a benign `Accept-Encoding` header to prevent underlying
         # libraries setting their own header based on their capabilities.
         r = dcos_api_session.head(filename,
                                   headers={'Accept-Encoding': 'identity'})
         r.raise_for_status()
         log.info('Response headers: %s', repr(r.headers))
         assert 'content-encoding' not in r.headers
Ejemplo n.º 21
0
def test_logout(dcos_api_session: DcosApiSession) -> None:
    """Test logout endpoint. It's a soft logout, instructing
    the user agent to delete the authentication cookie, i.e. this test
    does not have side effects on other tests.
    """
    r = dcos_api_session.get('/acs/api/v1/auth/logout')
    cookieheader = r.headers['set-cookie']
    assert 'dcos-acs-auth-cookie=;' in cookieheader or 'dcos-acs-auth-cookie="";' in cookieheader
    assert 'expires' in cookieheader.lower()
Ejemplo n.º 22
0
def test_if_all_mesos_slaves_have_registered(
        dcos_api_session: DcosApiSession) -> None:
    r = dcos_api_session.get('/mesos/master/slaves')
    assert r.status_code == 200

    data = r.json()
    slaves_ips = sorted(x['hostname'] for x in data['slaves'])

    assert slaves_ips == dcos_api_session.all_slaves
Ejemplo n.º 23
0
def skip_test_if_dcos_journald_log_disabled(dcos_api_session: DcosApiSession) -> None:
    response = dcos_api_session.get('/dcos-metadata/ui-config.json').json()
    try:
        strategy = response['uiConfiguration']['plugins']['mesos']['logging-strategy']
    except Exception:
        log.exception('Unable to find logging strategy')
        raise
    if not strategy.startswith('journald'):
        pytest.skip('Skipping a test since journald logging is disabled')
Ejemplo n.º 24
0
def test_user_put_email_uid_and_description(
        dcos_api_session: DcosApiSession) -> None:
    r = dcos_api_session.put('/acs/api/v1/users/[email protected]',
                             json={'description': 'integration test user'})
    assert r.status_code == 201, r.text

    users = get_users(dcos_api_session)
    assert len(users) > 1
    assert '*****@*****.**' in users
Ejemplo n.º 25
0
def test_if_pkgpanda_metadata_is_available(
        dcos_api_session: DcosApiSession) -> None:
    r = dcos_api_session.get('/pkgpanda/active.buildinfo.full.json')
    assert r.status_code == 200

    data = r.json()
    assert 'mesos' in data
    assert len(
        data
    ) > 5  # (prozlach) We can try to put minimal number of pacakages required
Ejemplo n.º 26
0
def test_if_srouter_service_endpoint_works(
        dcos_api_session: DcosApiSession) -> None:
    r = dcos_api_session.get('/service/marathon/v2/info')

    assert r.status_code == 200
    assert len(r.text) > 100
    response_json = r.json()
    assert "name" in response_json
    assert "marathon" == response_json["name"]
    assert "version" in response_json
Ejemplo n.º 27
0
def test_iam_migration(dcos_api_session: DcosApiSession) -> None:
    check_call(['sudo', 'systemctl', 'stop', 'dcos-bouncer-migrate-users.service'])

    def _filter_test_uids(r):
        return [
            u['uid'] for u in r.json()['array'] if '@example.com' in u['uid']]

    r = dcos_api_session.get('/acs/api/v1/users')
    test_uids = _filter_test_uids(r)
    assert len(test_uids) == 0

    check_call(['sudo', 'systemctl', 'start', 'dcos-bouncer-migrate-users.service'])
    # Sleep for 5 seconds and let the migration script run
    time.sleep(5)

    r = dcos_api_session.get('/acs/api/v1/users')
    test_uids = _filter_test_uids(r)
    assert len(test_uids) == 2
    assert '*****@*****.**' in test_uids
    assert '*****@*****.**' in test_uids
Ejemplo n.º 28
0
def test_if_we_have_capabilities(dcos_api_session: DcosApiSession) -> None:
    """Indirectly test that Cosmos is up since this call is handled by Cosmos.
    """
    r = dcos_api_session.get(
        '/capabilities',
        headers={
            'Accept':
            'application/vnd.dcos.capabilities+json;charset=utf-8;version=v1'
        })
    assert r.status_code == 200
    assert {'name': 'PACKAGE_MANAGEMENT'} in r.json()['capabilities']
Ejemplo n.º 29
0
def test_if_zookeeper_cluster_is_up(dcos_api_session: DcosApiSession) -> None:
    r = dcos_api_session.get('/exhibitor/exhibitor/v1/cluster/status')
    assert r.status_code == 200

    data = r.json()
    serving_zks = sum(1 for x in data if x['code'] == 3)
    zks_ips = sorted(x['hostname'] for x in data)
    zks_leaders = sum(1 for x in data if x['isLeader'])

    assert zks_ips == dcos_api_session.masters
    assert serving_zks == len(dcos_api_session.masters)
    assert zks_leaders == 1
Ejemplo n.º 30
0
def test_if_cosmos_is_only_available_locally(
        dcos_api_session: DcosApiSession) -> None:
    # One should not be able to connect to the cosmos HTTP and admin ports
    # over non-lo interfaces
    msg = "Cosmos reachable from non-lo interface"
    with pytest.raises(ConnectionError, message=msg):
        dcos_api_session.get('/',
                             host=dcos_api_session.masters[0],
                             port=7070,
                             scheme='http')
    with pytest.raises(ConnectionError, message=msg):
        dcos_api_session.get('/',
                             host=dcos_api_session.masters[0],
                             port=9990,
                             scheme='http')

    # One should be able to connect to the cosmos HTTP and admin ports at
    # 127.0.0.1:7070 and 127.0.0.1:9990.
    # Getting HTTP error codes shows that we made it all the way to
    # cosmos which is exactly what we're testing.
    r = dcos_api_session.get('/', host="127.0.0.1", port=7070, scheme='http')
    assert r.status_code == 404

    # In this case localhost:9990/ redirects to localhost:9990/admin so we
    # we expect a 200
    r = dcos_api_session.get('/', host="127.0.0.1", port=9990, scheme='http')
    assert r.status_code == 200