Esempio n. 1
0
def test_sources_filetypes(symbolicator, hitcounter):
    input = dict(
        sources=[{
            "type": "http",
            "id": "microsoft",
            "filters": {
                "filetypes": ["elf_code"]
            },
            "layout": {
                "type": "symstore"
            },
            "url": f"{hitcounter.url}/msdl/",
        }],
        options={
            "dif_candidates": True,
        },
        **WINDOWS_DATA,
    )
    expected = copy.deepcopy(NO_SOURCES)
    expected["modules"][0]["candidates"] = [{
        "source": "microsoft",
        "location": "No object files listed on this source",
        "download": {
            "status": "notfound",
        },
    }]

    service = symbolicator()
    service.wait_healthcheck()

    response = service.post("/symbolicate", json=input)
    response.raise_for_status()

    assert_symbolication(response.json(), expected)
    assert not hitcounter.hits
Esempio n. 2
0
def test_malformed_objects(symbolicator, hitcounter):
    input = dict(
        sources=[{
            "type": "http",
            "id": "broken",
            "layout": {
                "type": "symstore"
            },
            "url": f"{hitcounter.url}/garbage_data/",
        }],
        options={
            "dif_candidates": True,
        },
        **WINDOWS_DATA,
    )

    service = symbolicator()
    service.wait_healthcheck()

    response = service.post("/symbolicate", json=input)
    response.raise_for_status()
    response = response.json()

    expected = _make_error_result(
        status="malformed",
        download_error={"status": "notfound"},
        source=None,
        base_url=f"{hitcounter.url}/msdl/",
    )
    assert_symbolication(response, expected)
Esempio n. 3
0
def test_no_permission(symbolicator, hitcounter, bucket_type):
    if bucket_type == "http":
        source_specific = {"url": f"{hitcounter.url}/respond_statuscode/403/"}
    elif bucket_type == "s3":
        source_specific = {
            "layout": {
                "type": "symstore"
            },
            "bucket": "symbolicator-test",
            "region": "us-east-1",
        }
    elif bucket_type == "gcs":
        source_specific = {
            "layout": {
                "type": "symstore"
            },
            "bucket": "honk",
            "private_key": "",
            "client_email": "*****@*****.**",
        }
    else:
        source_specific = {}

    source = {
        "type": bucket_type,
        "id": "broken",
        "layout": {
            "type": "symstore"
        },
    }
    source.update(source_specific)

    input = dict(
        sources=[source],
        options={
            "dif_candidates": True,
        },
        **WINDOWS_DATA,
    )

    service = symbolicator()
    service.wait_healthcheck()

    response = service.post("/symbolicate", json=input)
    response.raise_for_status()
    response = response.json()

    base_url = ("s3://symbolicator-test/" if bucket_type == "s3" else
                f"{hitcounter.url}/respond_statuscode/403/")
    expected = _make_error_result(
        status="missing",
        download_error={
            "status": "noperm",
            "details": ""
        },
        source=("broken", False),
        base_url=base_url,
    )

    assert_symbolication(response, expected, {"bucket type": bucket_type})
Esempio n. 4
0
def test_path_patterns(symbolicator, hitcounter, patterns, output):
    input = dict(
        sources=[{
            "type": "http",
            "id": "microsoft",
            "layout": {
                "type": "symstore"
            },
            "filters": {
                "path_patterns": patterns
            },
            "url": f"{hitcounter.url}/msdl/",
        }],
        options={
            "dif_candidates": True,
        },
        **WINDOWS_DATA,
    )
    if output == NO_SOURCES:
        output = copy.deepcopy(output)
        output["modules"][0]["candidates"] = [{
            "source": "microsoft",
            "location": "No object files listed on this source",
            "download": {
                "status": "notfound",
            },
        }]

    service = symbolicator()
    service.wait_healthcheck()

    response = service.post("/symbolicate", json=input)
    response.raise_for_status()

    assert_symbolication(response.json(), output)
Esempio n. 5
0
def test_reserved_ip_addresses(symbolicator, hitcounter, allow_reserved_ip,
                               hostname):
    service = symbolicator(connect_to_reserved_ips=allow_reserved_ip)
    service.wait_healthcheck()

    url = hitcounter.url.replace("localhost",
                                 hostname).replace("127.0.0.1", hostname)
    assert hostname in url

    input = dict(
        sources=[{
            "type": "http",
            "id": "microsoft",
            "layout": {
                "type": "symstore"
            },
            "url": f"{url}/msdl/",
        }],
        options={
            "dif_candidates": True,
        },
        **WINDOWS_DATA,
    )

    response = service.post("/symbolicate", json=input)
    response.raise_for_status()

    if allow_reserved_ip:
        assert hitcounter.hits
        assert_symbolication(response.json(), SUCCESS_WINDOWS)
    else:
        assert not hitcounter.hits
        assert_symbolication(response.json(), MISSING_FILE)
Esempio n. 6
0
def test_path_patterns(symbolicator, hitcounter, patterns, expected_output):
    input = dict(
        sources=[{
            "type": "http",
            "id": "microsoft",
            "layout": {
                "type": "symstore"
            },
            "filters": {
                "path_patterns": patterns
            },
            "url": f"{hitcounter.url}/msdl/",
        }],
        options={
            "dif_candidates": True,
        },
        **WINDOWS_DATA,
    )
    if expected_output == "no sources":
        expected_output = _make_error_result(
            status="missing",
            download_error={"status": "notfound"},
            source=("microsoft", True),
            base_url=f"{hitcounter.url}/msdl/",
        )

    service = symbolicator()
    service.wait_healthcheck()

    response = service.post("/symbolicate", json=input)
    response.raise_for_status()

    assert_symbolication(response.json(), expected_output)
Esempio n. 7
0
def test_redirects(symbolicator, hitcounter):
    input = dict(
        sources=[{
            "type": "http",
            "id": "microsoft",
            "layout": {
                "type": "symstore"
            },
            "url": f"{hitcounter.url}/redirect/msdl/",
        }],
        options={
            "dif_candidates": True,
        },
        **WINDOWS_DATA,
    )

    service = symbolicator()
    service.wait_healthcheck()

    response = service.post("/symbolicate", json=input)
    response.raise_for_status()

    expected = copy.deepcopy(SUCCESS_WINDOWS)
    for module in expected.get("modules", []):
        for candidate in module.get("candidates", []):
            if "location" in candidate:
                candidate["location"] = candidate["location"].replace(
                    "/msdl/", "/redirect/msdl/")

    assert_symbolication(response.json(), expected)
Esempio n. 8
0
def test_unknown_source_config(symbolicator, hitcounter):
    # Requests could contain invalid data which should not stop symbolication,
    # name is sent by Sentry and is unknown.
    input = dict(
        sources=[{
            "type": "http",
            "id": "unknown",
            "layout": {
                "type": "symstore"
            },
            "url": f"{hitcounter.url}/respond_statuscode/400",
            "name": "not a known field",
            "not-a-field": "more unknown fields",
        }],
        options={
            "dif_candidates": True,
        },
        **WINDOWS_DATA,
    )

    service = symbolicator()
    service.wait_healthcheck()

    response = service.post("/symbolicate", json=input)
    response.raise_for_status()

    expected = copy.deepcopy(UNKNOWN_SOURCE)
    for module in expected.get("modules", []):
        for candidate in module.get("candidates", []):
            if "location" in candidate:
                candidate["location"] = candidate["location"].replace(
                    "/msdl/",
                    "/respond_statuscode/400/",
                )
    assert_symbolication(response.json(), expected)
Esempio n. 9
0
def test_unknown_source_config(symbolicator, hitcounter):
    # Requests could contain invalid data which should not stop symbolication,
    # name is sent by Sentry and is unknown.
    input = dict(
        sources=[{
            "type": "http",
            "id": "unknown",
            "layout": {
                "type": "symstore"
            },
            "url": f"{hitcounter.url}/respond_statuscode/400",
            "name": "not a known field",
            "not-a-field": "more unknown fields",
        }],
        options={
            "dif_candidates": True,
        },
        **WINDOWS_DATA,
    )

    service = symbolicator()
    service.wait_healthcheck()

    response = service.post("/symbolicate", json=input)
    response.raise_for_status()

    expected = _make_error_result(
        status="missing",
        download_error={"status": "notfound"},
        source=("unknown", False),
        base_url=f"{hitcounter.url}/respond_statuscode/400/",
    )
    assert_symbolication(response.json(), expected)
Esempio n. 10
0
def test_no_dif_candidates(symbolicator, hitcounter):
    # Asserts that disabling requesting for DIF candidates info works.
    service = symbolicator()
    service.wait_healthcheck()

    request_data = dict(
        **WINDOWS_DATA,
        sources=[{
            "type": "http",
            "id": "microsoft",
            "layout": {
                "type": "symstore"
            },
            "filters": {
                "filetypes": ["pdb", "pe"]
            },
            "url": f"{hitcounter.url}/msdl/",
        }],
    )

    response = service.post("/symbolicate", json=request_data)
    response.raise_for_status()

    success_response = copy.deepcopy(SUCCESS_WINDOWS_FILTERED)
    for module in success_response["modules"]:
        del module["candidates"]

    assert hitcounter.hits
    assert_symbolication(response.json(), success_response)
Esempio n. 11
0
def test_no_sources(symbolicator, cache_dir_param):
    input = dict(**WINDOWS_DATA, sources=[])

    service = symbolicator(cache_dir=cache_dir_param)
    service.wait_healthcheck()

    response = service.post("/symbolicate", json=input)
    response.raise_for_status()

    assert_symbolication(response.json(), NO_SOURCES)

    if cache_dir_param:
        assert not cache_dir_param.join("objects/global").exists()
        assert not cache_dir_param.join("symcaches/global").exists()
Esempio n. 12
0
def test_lookup_deduplication(symbolicator, hitcounter, is_public):
    input = dict(
        **WINDOWS_DATA,
        sources=[{
            "type": "http",
            "id": "microsoft",
            "filters": {
                "filetypes": ["pdb", "pe"]
            },
            "layout": {
                "type": "symstore"
            },
            "url": f"{hitcounter.url}/msdl/",
            "is_public": is_public,
        }],
        options={
            "dif_candidates": True,
        },
    )

    service = symbolicator(cache_dir=None)
    service.wait_healthcheck()
    responses = []

    def f():
        response = service.post("/symbolicate", json=input)
        response.raise_for_status()
        responses.append(response.json())

    ts = []
    for _ in range(20):
        t = threading.Thread(target=f)
        t.start()
        ts.append(t)

    for t in ts:
        t.join()

    for response in responses:
        assert_symbolication(response, SUCCESS_WINDOWS_FILTERED)

    assert set(hitcounter.hits) == {
        "/msdl/wkernel32.pdb/FF9F9F7841DB88F0CDEDA9E1E9BFF3B51/wkernel32.pd_",
        "/msdl/wkernel32.pdb/FF9F9F7841DB88F0CDEDA9E1E9BFF3B51/wkernel32.pdb",
    }

    for key, count in hitcounter.hits.items():
        assert count < 20, (key, count)
Esempio n. 13
0
def test_unreachable_bucket(symbolicator, hitcounter, statuscode, bucket_type):
    input = dict(
        sources=[{
            "type": bucket_type,
            "id": "broken",
            "layout": {
                "type": "symstore"
            },  # only relevant for http type
            "url": f"{hitcounter.url}/respond_statuscode/{statuscode}/",
            "token": "123abc",  # only relevant for sentry type
        }],
        options={
            "dif_candidates": True,
        },
        **WINDOWS_DATA,
    )

    service = symbolicator()
    service.wait_healthcheck()

    response = service.post("/symbolicate", json=input)
    response.raise_for_status()
    response = response.json()

    # TODO(markus): Better error reporting
    if bucket_type == "sentry":
        source = ("broken", True)
        download_error = {"status": "notfound"}
    elif statuscode == 500:
        source = ("broken", False)
        download_error = {
            "status": "error",
            "details": "failed to download: 500 Internal Server Error",
        }
    else:
        source = ("broken", False)
        download_error = {"status": "notfound"}

    expected = _make_error_result(
        status="missing",
        download_error=download_error,
        source=source,
        base_url=f"{hitcounter.url}/respond_statuscode/{statuscode}/",
    )
    assert_symbolication(response, expected, {
        "status code": statuscode,
        "bucket type": bucket_type
    })
Esempio n. 14
0
def test_unreachable_bucket(symbolicator, hitcounter, statuscode, bucket_type):
    input = dict(
        sources=[{
            "type": bucket_type,
            "id": "broken",
            "layout": {
                "type": "symstore"
            },  # only relevant for http type
            "url": f"{hitcounter.url}/respond_statuscode/{statuscode}/",
            "token": "123abc",  # only relevant for sentry type
        }],
        options={
            "dif_candidates": True,
        },
        **WINDOWS_DATA,
    )

    service = symbolicator()
    service.wait_healthcheck()

    response = service.post("/symbolicate", json=input)
    response.raise_for_status()
    response = response.json()
    # TODO(markus): Better error reporting
    if bucket_type == "sentry":
        expected = copy.deepcopy(NO_SOURCES)
        expected["modules"][0]["candidates"] = [{
            "source": "broken",
            "location": "No object files listed on this source",
            "download": {
                "status": "notfound",
            },
        }]
        assert_symbolication(response, expected)
    else:
        expected = _make_unsuccessful_result(status="missing", source="broken")
        for module in expected.get("modules", []):
            for candidate in module.get("candidates", []):
                if "location" in candidate:
                    candidate["location"] = candidate["location"].replace(
                        "/msdl/",
                        f"/respond_statuscode/{statuscode}/",
                    )

        assert_symbolication(response, expected)
Esempio n. 15
0
def test_reserved_ip_addresses(symbolicator, hitcounter, allow_reserved_ip,
                               hostname):
    service = symbolicator(connect_to_reserved_ips=allow_reserved_ip)
    service.wait_healthcheck()

    url = hitcounter.url.replace("localhost",
                                 hostname).replace("127.0.0.1", hostname)
    assert hostname in url

    input = dict(
        sources=[{
            "type": "http",
            "id": "microsoft",
            "layout": {
                "type": "symstore"
            },
            "url": f"{url}/msdl/",
        }],
        options={
            "dif_candidates": True,
        },
        **WINDOWS_DATA,
    )

    response = service.post("/symbolicate", json=input)
    response.raise_for_status()

    if allow_reserved_ip:
        assert hitcounter.hits
        assert_symbolication(response.json(), SUCCESS_WINDOWS)
    else:
        assert not hitcounter.hits
        restricted_download_failure = _make_error_result(
            status="missing",
            download_error={
                "status": "error",
                "details": "failed to stream file"
            },
            source=("microsoft", False),
            base_url=f"{url}/msdl/",
        )
        assert_symbolication(response.json(), restricted_download_failure)
Esempio n. 16
0
def test_no_sources(symbolicator, cache_dir_param):
    input = dict(**WINDOWS_DATA, sources=[])

    service = symbolicator(cache_dir=cache_dir_param)
    service.wait_healthcheck()

    response = service.post("/symbolicate", json=input)
    response.raise_for_status()

    expected = _make_error_result(
        status="missing",
        download_error={"status": "notfound"},
        source=None,
        base_url=f"{service.url}/msdl/",
    )
    assert_symbolication(response.json(), expected)

    if cache_dir_param:
        assert not cache_dir_param.join("objects/global").exists()
        assert not cache_dir_param.join("symcaches/global").exists()
Esempio n. 17
0
def test_malformed_objects(symbolicator, hitcounter):
    input = dict(
        sources=[{
            "type": "http",
            "id": "broken",
            "layout": {
                "type": "symstore"
            },
            "url": f"{hitcounter.url}/garbage_data/",
        }],
        options={
            "dif_candidates": True,
        },
        **WINDOWS_DATA,
    )

    service = symbolicator()
    service.wait_healthcheck()

    response = service.post("/symbolicate", json=input)
    response.raise_for_status()
    response = response.json()
    assert_symbolication(response, MALFORMED_NO_SOURCES)
Esempio n. 18
0
def test_basic_windows(symbolicator, cache_dir_param, is_public, hitcounter):
    scope = "myscope"

    input = dict(
        **WINDOWS_DATA,
        sources=[{
            "type": "http",
            "id": "microsoft",
            "layout": {
                "type": "symstore"
            },
            "filters": {
                "filetypes": ["pdb", "pe"]
            },
            "url": f"{hitcounter.url}/msdl/",
            "is_public": is_public,
        }],
        options={
            "dif_candidates": True,
        },
    )

    # i = 0: Cache miss
    # i = 1: Cache hit
    for i in range(2):
        service = symbolicator(cache_dir=cache_dir_param)
        service.wait_healthcheck()

        response = service.post(f"/symbolicate?scope={scope}", json=input)
        response.raise_for_status()

        assert_symbolication(response.json(), SUCCESS_WINDOWS_FILTERED)

        if cache_dir_param:
            stored_in_scope = "global" if is_public else scope
            assert {
                o.basename: o.size()
                for o in cache_dir_param.join("objects").join(
                    stored_in_scope).listdir()
            } == {
                "microsoft_wkernel32_pdb_FF9F9F7841DB88F0CDEDA9E1E9BFF3B51_wkernel32_pd_":
                0,
                "microsoft_wkernel32_pdb_FF9F9F7841DB88F0CDEDA9E1E9BFF3B51_wkernel32_pdb":
                846_848,
            }

            (symcache, ) = (cache_dir_param.join("symcaches").join(
                stored_in_scope).listdir())
            assert (
                symcache.basename ==
                "microsoft_wkernel32_pdb_FF9F9F7841DB88F0CDEDA9E1E9BFF3B51_wkernel32_pdb"
            )
            assert symcache.size() > 0

        if cache_dir_param:
            hit_count = miss_count = 1
        else:
            miss_count = i + 1
            # XXX(markus): Symbolicator opens a cachefile twice if it maps
            # successfully. With caches this doesn't matter, but without caches
            # enabled Symbolicator effectively downloads every item twice
            hit_count = 2 * (i + 1)

        assert hitcounter.hits == {
            "/msdl/wkernel32.pdb/FF9F9F7841DB88F0CDEDA9E1E9BFF3B51/wkernel32.pd_":
            miss_count,
            "/msdl/wkernel32.pdb/FF9F9F7841DB88F0CDEDA9E1E9BFF3B51/wkernel32.pdb":
            hit_count,
        }
Esempio n. 19
0
def test_timeouts(symbolicator, hitcounter):
    hitcounter.before_request = lambda: time.sleep(3)

    request_id = None

    responses = []

    service = symbolicator()
    service.wait_healthcheck()

    for _ in range(10):
        if request_id:
            response = service.get("/requests/{}?timeout=1".format(request_id))
        else:
            input = dict(
                sources=[{
                    "type": "http",
                    "id": "microsoft",
                    "filters": {
                        "filetypes": ["pdb", "pe"]
                    },
                    "layout": {
                        "type": "symstore"
                    },
                    "url": f"{hitcounter.url}/msdl/",
                }],
                options={
                    "dif_candidates": True,
                },
                **WINDOWS_DATA,
            )
            response = service.post("/symbolicate?timeout=1", json=input)

        response.raise_for_status()
        response = response.json()
        responses.append(response)
        if response["status"] == "completed":
            break
        elif response["status"] == "pending":
            request_id = response["request_id"]
        else:
            assert False
        time.sleep(
            0.3)  # 0.3 * 10 iterations = 3s => expect timeout on 4th iteration

    for response in responses[:-1]:
        assert response["status"] == "pending"
        assert response["request_id"] == request_id

    assert_symbolication(responses[-1], SUCCESS_WINDOWS_FILTERED)
    assert len(responses) > 1

    assert hitcounter.hits == {
        "/msdl/wkernel32.pdb/FF9F9F7841DB88F0CDEDA9E1E9BFF3B51/wkernel32.pd_":
        1,
        # XXX(markus): Symbolicator opens a cachefile twice if it maps
        # successfully. With caches this doesn't matter, but without caches
        # enabled Symbolicator effectively downloads every item twice
        "/msdl/wkernel32.pdb/FF9F9F7841DB88F0CDEDA9E1E9BFF3B51/wkernel32.pdb":
        2,
    }