<NME> test_composition.py
<BEF> import json
import logging
import platform
import subprocess

import dns.exception
import dns.resolver
import kazoo.client
import pytest

@pytest.mark.first
def test_cluster_is_up(cluster):
    cluster.wait_for_dcos()


def test_if_all_mesos_masters_have_registered(cluster):
@pytest.mark.first
def test_dcos_cluster_is_up() -> None:
    def _docker_info(component: str) -> str:
        # sudo is required for non-coreOS installs
        return (subprocess.check_output(['sudo', 'docker', 'version', '-f', component], timeout=60)
                .decode('utf-8')
                .rstrip()
                )

    try:
        docker_client = _docker_info('{{.Client.Version}}')
    except subprocess.TimeoutExpired:
        docker_client = "Error: docker call timed out"

    try:
        docker_server = _docker_info('{{.Server.Version}}')
    except subprocess.TimeoutExpired:
        docker_server = "Error: docker call timed out"

    cluster_environment = {
        "docker_client_version": docker_client,
        "docker_server_version": docker_server,
        "system_platform": platform.platform(),
        "system_platform_system": platform.system(),
        "system_platform_release": platform.release(),
        "system_platform_version": platform.version()
    }
    logging.info(json.dumps(cluster_environment, sort_keys=True, indent=4))


def test_leader_election(dcos_api_session: DcosApiSession) -> None:
    mesos_resolver = dns.resolver.Resolver()
    mesos_resolver.nameservers = dcos_api_session.masters
    mesos_resolver.port = 61053
    try:
        mesos_resolver.query('leader.mesos', 'A')
    except dns.exception.DNSException:
        assert False, "Cannot resolve leader.mesos"


def test_if_all_mesos_masters_have_registered(dcos_api_session: DcosApiSession) -> None:
    # Currently it is not possible to extract this information through Mesos'es
    # API, let's query zookeeper directly.
    zk_hostports = 'zk-1.zk:2181,zk-2.zk:2181,zk-3.zk:2181,zk-4.zk:2181,zk-5.zk:2181'
    zk = kazoo.client.KazooClient(hosts=zk_hostports, read_only=True)
    master_ips = []

    zk.start()
    for znode in zk.get_children("/mesos"):
        if not znode.startswith("json.info_"):
            continue
        master = json.loads(zk.get("/mesos/" + znode)[0].decode('utf-8'))
        master_ips.append(master['address']['ip'])
    zk.stop()

    assert sorted(master_ips) == dcos_api_session.masters


def test_if_all_exhibitors_are_in_sync(dcos_api_session: DcosApiSession) -> None:
    r = dcos_api_session.get('/exhibitor/exhibitor/v1/cluster/status')
    assert r.status_code == 200

    correct_data = sorted(r.json(), key=lambda k: k['hostname'])

    for master_node_ip in dcos_api_session.masters:
        # This relies on the fact that Admin Router always proxies the local
        # Exhibitor.
        resp = requests.get('http://{}/exhibitor/exhibitor/v1/cluster/status'.format(master_node_ip), verify=False)
        assert resp.status_code == 200

        tested_data = sorted(resp.json(), key=lambda k: k['hostname'])
        assert correct_data == tested_data


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'] == '*'


def test_systemd_units_are_healthy(dcos_api_session: DcosApiSession) -> None:
    """
    Test that the system is healthy at the arbitrary point in time
    that this test runs. This test has caught several issues in the past
    as it serves as a very high-level assertion about the system state.
    It seems very random, but it has proven very valuable.

    We are explicit about the list of units that are expected to be present
    in order to test against a static, known reference in order to avoid
    dynamically generated output (e.g., from /health) not matching our
    real world expectations and the test pass while errors occur silently.

    First, we loop through the nodes returned from
    the /system/health/v1/report endpoint and print the report if anything
    is unhealthy.

    Secondly, we check that the list of expected units matches the list
    of units on every node.
    """
    # Insert all the diagnostics data programmatically
    master_units = [
        'dcos-adminrouter.service',
        'dcos-cockroach.service',
        'dcos-cockroachdb-config-change.service',
        'dcos-cockroachdb-config-change.timer',
        'dcos-cosmos.service',
        'dcos-etcd.service',
        'dcos-exhibitor.service',
        'dcos-log-master.service',
        'dcos-log-master.socket',
        'dcos-logrotate-master.service',
        'dcos-logrotate-master.timer',
        'dcos-marathon.service',
        'dcos-mesos-dns.service',
        'dcos-mesos-master.service',
        'dcos-metronome.service',
        'dcos-bouncer.service',
        'dcos-bouncer-migrate-users.service',
        'dcos-ui-update-service.service',
        'dcos-ui-update-service.socket',
        'dcos-diagnostics-mesos-state.service',
        'dcos-diagnostics-mesos-state.timer',
    ]
    all_node_units = [
        'dcos-calico-bird.service',
        'dcos-calico-felix.service',
        'dcos-calico-confd.service',
        'dcos-checks-api.service',
        'dcos-checks-api.socket',
        'dcos-diagnostics.service',
        'dcos-diagnostics.socket',
        'dcos-gen-resolvconf.service',
        'dcos-gen-resolvconf.timer',
        'dcos-net.service',
        'dcos-net-watchdog.service',
        'dcos-pkgpanda-api.service',
        'dcos-checks-poststart.service',
        'dcos-checks-poststart.timer',
        'dcos-telegraf.service',
        'dcos-telegraf.socket',
        'dcos-fluent-bit.service']
    slave_units = [
        'dcos-mesos-slave.service',
        'dcos-mesos-slave.socket']
    public_slave_units = [
        'dcos-mesos-slave-public.service',
        'dcos-mesos-slave-public.socket']
    all_slave_units = [
        'dcos-adminrouter-agent.service',
        'dcos-calico-libnetwork-plugin.service',
        'dcos-docker-gc.service',
        'dcos-docker-gc.timer',
        'dcos-log-agent.service',
        'dcos-log-agent.socket',
        'dcos-logrotate-agent.service',
        'dcos-logrotate-agent.timer',
        'dcos-rexray.service']

    expected_units = {
        "master": set(all_node_units + master_units),
        "agent": set(all_node_units + all_slave_units + slave_units),
        "agent_public": set(all_node_units + all_slave_units + public_slave_units),
    }

    # Collect the dcos-diagnostics output that to determine
    # whether or not there are failed units.
    resp = dcos_api_session.get('/system/health/v1/report?cache=0')
    # We expect reading the health report to succeed.
    resp.raise_for_status()
    # Parse the response into JSON.
    health_report = resp.json()
    # The format of the /health/v1/report output is as follows:
    # {
    #     "Nodes": { ... },
    #     "Units": {
    #         "dcos-unit-foo.service": {
    #             "UnitName": "dcos-unit-foo.service",
    #             "Nodes": [
    #                 {
    #                     "Role": "agent" (or "agent_public", or "master")
    #                     "IP": "172.17.0.2",
    #                     "Host": "dcos-e2e-7dd6638e-a6f5-4276-bf6b-c9a4d6066ea4-master-2",
    #                     "Health": 0 if node is healthy, 1 if unhealthy,
    #                     "Output": {
    #                         "dcos-unit-bar.service": "" (empty string if healthy),
    #                         "dcos-unit-foo.service": "journalctl output" (if unhealthy),
    #                     }
    #                 },
    #                 ...
    #             ]
    #         }
    #     }
    # }

    # Test that all nodes have the correct set of dcos-* systemd units.
    units_per_node = {}
    exp_units_per_node = {}
    for node, node_health in health_report["Nodes"].items():
        role = node_health["Role"]  # Is one of master, agent, agent_public
        units_per_node[node] = set(node_health["Output"])
        exp_units_per_node[node] = expected_units[role]
    assert units_per_node == exp_units_per_node

    # Test that there are no unhealthy nodes.
    unhealthy_nodes = 0
    for node, node_health in health_report["Nodes"].items():
        # Assert that this node is healthy.
        if node_health["Health"] != 0:
            logging.info("Node {} was unhealthy: {}".format(
                node, json.dumps(node_health, indent=4, sort_keys=True)))
            unhealthy_nodes += 1
    assert unhealthy_nodes == 0

<MSG> Always authenticate before any test

wait_for_dcos needs to be in the fixture or else
running tests in arbitrary order will not work

<DFF> @@ -10,7 +10,7 @@ import requests
 
 @pytest.mark.first
 def test_cluster_is_up(cluster):
-    cluster.wait_for_dcos()
+    pass
 
 
 def test_if_all_mesos_masters_have_registered(cluster):

{'commit': 'diff --git a/packages/dcos-integration-test/extra/conftest.py b/packages/dcos-integration-test/extra/conftest.py\nindex 9e3c56dc..1af8f456 100644\n--- a/packages/dcos-integration-test/extra/conftest.py\n+++ b/packages/dcos-integration-test/extra/conftest.py\n@@ -73,7 +73,7 @@ def cluster():\n     logging.getLogger("requests").setLevel(logging.WARNING)\n     logging.getLogger("botocore").setLevel(logging.WARNING)\n \n-    return test_util.cluster_api.ClusterApi(\n+    cluster_api = test_util.cluster_api.ClusterApi(\n         dcos_uri=os.environ[\'DCOS_DNS_ADDRESS\'],\n         masters=os.environ[\'MASTER_HOSTS\'].split(\',\'),\n         public_masters=os.environ[\'PUBLIC_MASTER_HOSTS\'].split(\',\'),\n@@ -84,3 +84,5 @@ def cluster():\n         auth_enabled=os.getenv(\'DCOS_AUTH_ENABLED\', \'true\') == \'true\',\n         username=os.getenv(\'DCOS_LOGIN_UNAME\', None),\n         password=os.getenv(\'DCOS_LOGIN_PW\', None))\n+    cluster_api.wait_for_dcos()\n+    return cluster_api\ndiff --git a/packages/dcos-integration-test/extra/test_composition.py b/packages/dcos-integration-test/extra/test_composition.py\nindex 16b04073..68e01e12 100644\n--- a/packages/dcos-integration-test/extra/test_composition.py\n+++ b/packages/dcos-integration-test/extra/test_composition.py\n@@ -10,7 +10,7 @@ import requests\n \n @pytest.mark.first\n def test_cluster_is_up(cluster):\n-    cluster.wait_for_dcos()\n+    pass\n \n \n def test_if_all_mesos_masters_have_registered(cluster):\n', 'commit_subject': 'Always authenticate before any test', 'repo_name': 'dcos/dcos', 'committer': {'name': 'Michael Ellenburg', 'email': 'b33916b486c86fa0acc2d3d1741f6ccc23ce8f2c@mesosphere.io', 'time_sec': '1474578734', 'tz_offset': '-420', 'date': {'seconds': '1474578734'}}, 'commit_tree': '765deb778994ebb8bbb049407e7c99f2c81f8ca2', 'commit_message': 'Always authenticate before any test\n\nwait_for_dcos needs to be in the fixture or else\nrunning tests in arbitrary order will not work\n', 'language_name': 'HTML', 'license': 'apache-2.0', 'hunks': '@@ -10,7 +10,7 @@ import requests\n \n @pytest.mark.first\n def test_cluster_is_up(cluster):\n-    cluster.wait_for_dcos()\n+    pass\n \n \n def test_if_all_mesos_masters_have_registered(cluster):\n', 'addition_count': 1, 'deletion_count': 1, 'src_file': 'a/packages/dcos-integration-test/extra/test_composition.py', 'tgt_file': 'b/packages/dcos-integration-test/extra/test_composition.py', 'file_extension': '.py', 'before_file': 'import json\nimport logging\nimport platform\nimport subprocess\n\nimport dns.exception\nimport dns.resolver\nimport kazoo.client\nimport pytest\n\n@pytest.mark.first\ndef test_cluster_is_up(cluster):\n    cluster.wait_for_dcos()\n\n\ndef test_if_all_mesos_masters_have_registered(cluster):\n@pytest.mark.first\ndef test_dcos_cluster_is_up() -> None:\n    def _docker_info(component: str) -> str:\n        # sudo is required for non-coreOS installs\n        return (subprocess.check_output([\'sudo\', \'docker\', \'version\', \'-f\', component], timeout=60)\n                .decode(\'utf-8\')\n                .rstrip()\n                )\n\n    try:\n        docker_client = _docker_info(\'{{.Client.Version}}\')\n    except subprocess.TimeoutExpired:\n        docker_client = "Error: docker call timed out"\n\n    try:\n        docker_server = _docker_info(\'{{.Server.Version}}\')\n    except subprocess.TimeoutExpired:\n        docker_server = "Error: docker call timed out"\n\n    cluster_environment = {\n        "docker_client_version": docker_client,\n        "docker_server_version": docker_server,\n        "system_platform": platform.platform(),\n        "system_platform_system": platform.system(),\n        "system_platform_release": platform.release(),\n        "system_platform_version": platform.version()\n    }\n    logging.info(json.dumps(cluster_environment, sort_keys=True, indent=4))\n\n\ndef test_leader_election(dcos_api_session: DcosApiSession) -> None:\n    mesos_resolver = dns.resolver.Resolver()\n    mesos_resolver.nameservers = dcos_api_session.masters\n    mesos_resolver.port = 61053\n    try:\n        mesos_resolver.query(\'leader.mesos\', \'A\')\n    except dns.exception.DNSException:\n        assert False, "Cannot resolve leader.mesos"\n\n\ndef test_if_all_mesos_masters_have_registered(dcos_api_session: DcosApiSession) -> None:\n    # Currently it is not possible to extract this information through Mesos\'es\n    # API, let\'s query zookeeper directly.\n    zk_hostports = \'zk-1.zk:2181,zk-2.zk:2181,zk-3.zk:2181,zk-4.zk:2181,zk-5.zk:2181\'\n    zk = kazoo.client.KazooClient(hosts=zk_hostports, read_only=True)\n    master_ips = []\n\n    zk.start()\n    for znode in zk.get_children("/mesos"):\n        if not znode.startswith("json.info_"):\n            continue\n        master = json.loads(zk.get("/mesos/" + znode)[0].decode(\'utf-8\'))\n        master_ips.append(master[\'address\'][\'ip\'])\n    zk.stop()\n\n    assert sorted(master_ips) == dcos_api_session.masters\n\n\ndef test_if_all_exhibitors_are_in_sync(dcos_api_session: DcosApiSession) -> None:\n    r = dcos_api_session.get(\'/exhibitor/exhibitor/v1/cluster/status\')\n    assert r.status_code == 200\n\n    correct_data = sorted(r.json(), key=lambda k: k[\'hostname\'])\n\n    for master_node_ip in dcos_api_session.masters:\n        # This relies on the fact that Admin Router always proxies the local\n        # Exhibitor.\n        resp = requests.get(\'http://{}/exhibitor/exhibitor/v1/cluster/status\'.format(master_node_ip), verify=False)\n        assert resp.status_code == 200\n\n        tested_data = sorted(resp.json(), key=lambda k: k[\'hostname\'])\n        assert correct_data == tested_data\n\n\ndef test_mesos_agent_role_assignment(dcos_api_session: DcosApiSession) -> None:\n    state_endpoint = \'/state\'\n    for agent in dcos_api_session.public_slaves:\n        r = dcos_api_session.get(state_endpoint, host=agent, port=5051)\n        assert r.json()[\'flags\'][\'default_role\'] == \'slave_public\'\n    for agent in dcos_api_session.slaves:\n        r = dcos_api_session.get(state_endpoint, host=agent, port=5051)\n        assert r.json()[\'flags\'][\'default_role\'] == \'*\'\n\n\ndef test_systemd_units_are_healthy(dcos_api_session: DcosApiSession) -> None:\n    """\n    Test that the system is healthy at the arbitrary point in time\n    that this test runs. This test has caught several issues in the past\n    as it serves as a very high-level assertion about the system state.\n    It seems very random, but it has proven very valuable.\n\n    We are explicit about the list of units that are expected to be present\n    in order to test against a static, known reference in order to avoid\n    dynamically generated output (e.g., from /health) not matching our\n    real world expectations and the test pass while errors occur silently.\n\n    First, we loop through the nodes returned from\n    the /system/health/v1/report endpoint and print the report if anything\n    is unhealthy.\n\n    Secondly, we check that the list of expected units matches the list\n    of units on every node.\n    """\n    # Insert all the diagnostics data programmatically\n    master_units = [\n        \'dcos-adminrouter.service\',\n        \'dcos-cockroach.service\',\n        \'dcos-cockroachdb-config-change.service\',\n        \'dcos-cockroachdb-config-change.timer\',\n        \'dcos-cosmos.service\',\n        \'dcos-etcd.service\',\n        \'dcos-exhibitor.service\',\n        \'dcos-log-master.service\',\n        \'dcos-log-master.socket\',\n        \'dcos-logrotate-master.service\',\n        \'dcos-logrotate-master.timer\',\n        \'dcos-marathon.service\',\n        \'dcos-mesos-dns.service\',\n        \'dcos-mesos-master.service\',\n        \'dcos-metronome.service\',\n        \'dcos-bouncer.service\',\n        \'dcos-bouncer-migrate-users.service\',\n        \'dcos-ui-update-service.service\',\n        \'dcos-ui-update-service.socket\',\n        \'dcos-diagnostics-mesos-state.service\',\n        \'dcos-diagnostics-mesos-state.timer\',\n    ]\n    all_node_units = [\n        \'dcos-calico-bird.service\',\n        \'dcos-calico-felix.service\',\n        \'dcos-calico-confd.service\',\n        \'dcos-checks-api.service\',\n        \'dcos-checks-api.socket\',\n        \'dcos-diagnostics.service\',\n        \'dcos-diagnostics.socket\',\n        \'dcos-gen-resolvconf.service\',\n        \'dcos-gen-resolvconf.timer\',\n        \'dcos-net.service\',\n        \'dcos-net-watchdog.service\',\n        \'dcos-pkgpanda-api.service\',\n        \'dcos-checks-poststart.service\',\n        \'dcos-checks-poststart.timer\',\n        \'dcos-telegraf.service\',\n        \'dcos-telegraf.socket\',\n        \'dcos-fluent-bit.service\']\n    slave_units = [\n        \'dcos-mesos-slave.service\',\n        \'dcos-mesos-slave.socket\']\n    public_slave_units = [\n        \'dcos-mesos-slave-public.service\',\n        \'dcos-mesos-slave-public.socket\']\n    all_slave_units = [\n        \'dcos-adminrouter-agent.service\',\n        \'dcos-calico-libnetwork-plugin.service\',\n        \'dcos-docker-gc.service\',\n        \'dcos-docker-gc.timer\',\n        \'dcos-log-agent.service\',\n        \'dcos-log-agent.socket\',\n        \'dcos-logrotate-agent.service\',\n        \'dcos-logrotate-agent.timer\',\n        \'dcos-rexray.service\']\n\n    expected_units = {\n        "master": set(all_node_units + master_units),\n        "agent": set(all_node_units + all_slave_units + slave_units),\n        "agent_public": set(all_node_units + all_slave_units + public_slave_units),\n    }\n\n    # Collect the dcos-diagnostics output that to determine\n    # whether or not there are failed units.\n    resp = dcos_api_session.get(\'/system/health/v1/report?cache=0\')\n    # We expect reading the health report to succeed.\n    resp.raise_for_status()\n    # Parse the response into JSON.\n    health_report = resp.json()\n    # The format of the /health/v1/report output is as follows:\n    # {\n    #     "Nodes": { ... },\n    #     "Units": {\n    #         "dcos-unit-foo.service": {\n    #             "UnitName": "dcos-unit-foo.service",\n    #             "Nodes": [\n    #                 {\n    #                     "Role": "agent" (or "agent_public", or "master")\n    #                     "IP": "172.17.0.2",\n    #                     "Host": "dcos-e2e-7dd6638e-a6f5-4276-bf6b-c9a4d6066ea4-master-2",\n    #                     "Health": 0 if node is healthy, 1 if unhealthy,\n    #                     "Output": {\n    #                         "dcos-unit-bar.service": "" (empty string if healthy),\n    #                         "dcos-unit-foo.service": "journalctl output" (if unhealthy),\n    #                     }\n    #                 },\n    #                 ...\n    #             ]\n    #         }\n    #     }\n    # }\n\n    # Test that all nodes have the correct set of dcos-* systemd units.\n    units_per_node = {}\n    exp_units_per_node = {}\n    for node, node_health in health_report["Nodes"].items():\n        role = node_health["Role"]  # Is one of master, agent, agent_public\n        units_per_node[node] = set(node_health["Output"])\n        exp_units_per_node[node] = expected_units[role]\n    assert units_per_node == exp_units_per_node\n\n    # Test that there are no unhealthy nodes.\n    unhealthy_nodes = 0\n    for node, node_health in health_report["Nodes"].items():\n        # Assert that this node is healthy.\n        if node_health["Health"] != 0:\n            logging.info("Node {} was unhealthy: {}".format(\n                node, json.dumps(node_health, indent=4, sort_keys=True)))\n            unhealthy_nodes += 1\n    assert unhealthy_nodes == 0\n', 'message': 'Always authenticate before any test\n\nwait_for_dcos needs to be in the fixture or else\nrunning tests in arbitrary order will not work\n', 'author_name': 'Michael Ellenburg', 'lang': 'py'}