Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add indicator if SpanContext was propagated from remote parent #516

Merged

Conversation

mariojonke
Copy link
Contributor

According to the spec a SpanContext should have an indicator if it was propagated from a remote parent.

This PR introduces an is_remote flag on the SpanContext which gets set when the SpanContext is extracted in a propagator.

@mariojonke mariojonke requested a review from a team March 20, 2020 06:52
@Oberon00
Copy link
Member

Oberon00 commented Mar 20, 2020

Looks like we have an unrelated flaky test (EDIT: Actually that was pip failing to download something. Maybe we can set it to retry once):

ERROR: Exception:

Traceback (most recent call last):

  File "/home/travis/build/open-telemetry/opentelemetry-python/.tox/py34-test-ext-otcollector/lib/python3.4/site-packages/pip/_vendor/urllib3/response.py", line 360, in _error_catcher

    yield

  File "/home/travis/build/open-telemetry/opentelemetry-python/.tox/py34-test-ext-otcollector/lib/python3.4/site-packages/pip/_vendor/urllib3/response.py", line 442, in read

    data = self._fp.read(amt)

  File "/home/travis/build/open-telemetry/opentelemetry-python/.tox/py34-test-ext-otcollector/lib/python3.4/site-packages/pip/_vendor/cachecontrol/filewrapper.py", line 62, in read

    data = self.__fp.read(amt)

  File "/opt/python/3.4.8/lib/python3.4/http/client.py", line 537, in read

    return super(HTTPResponse, self).read(amt)

  File "/opt/python/3.4.8/lib/python3.4/http/client.py", line 576, in readinto

    n = self.fp.readinto(b)

  File "/opt/python/3.4.8/lib/python3.4/socket.py", line 378, in readinto

    return self._sock.recv_into(b)

  File "/opt/python/3.4.8/lib/python3.4/ssl.py", line 754, in recv_into

    return self.read(nbytes, buffer)

  File "/opt/python/3.4.8/lib/python3.4/ssl.py", line 626, in read

    v = self._sslobj.read(len, buffer)

ConnectionResetError: [Errno 104] Connection reset by peer

During handling of the above exception, another exception occurred:

Traceback (most recent call last):

  File "/home/travis/build/open-telemetry/opentelemetry-python/.tox/py34-test-ext-otcollector/lib/python3.4/site-packages/pip/_internal/cli/base_command.py", line 178, in main

    status = self.run(options, args)

  File "/home/travis/build/open-telemetry/opentelemetry-python/.tox/py34-test-ext-otcollector/lib/python3.4/site-packages/pip/_internal/commands/install.py", line 352, in run

    resolver.resolve(requirement_set)

  File "/home/travis/build/open-telemetry/opentelemetry-python/.tox/py34-test-ext-otcollector/lib/python3.4/site-packages/pip/_internal/resolve.py", line 131, in resolve

    self._resolve_one(requirement_set, req)

  File "/home/travis/build/open-telemetry/opentelemetry-python/.tox/py34-test-ext-otcollector/lib/python3.4/site-packages/pip/_internal/resolve.py", line 294, in _resolve_one

    abstract_dist = self._get_abstract_dist_for(req_to_install)

  File "/home/travis/build/open-telemetry/opentelemetry-python/.tox/py34-test-ext-otcollector/lib/python3.4/site-packages/pip/_internal/resolve.py", line 242, in _get_abstract_dist_for

    self.require_hashes

  File "/home/travis/build/open-telemetry/opentelemetry-python/.tox/py34-test-ext-otcollector/lib/python3.4/site-packages/pip/_internal/operations/prepare.py", line 347, in prepare_linked_requirement

    progress_bar=self.progress_bar

  File "/home/travis/build/open-telemetry/opentelemetry-python/.tox/py34-test-ext-otcollector/lib/python3.4/site-packages/pip/_internal/download.py", line 886, in unpack_url

    progress_bar=progress_bar

  File "/home/travis/build/open-telemetry/opentelemetry-python/.tox/py34-test-ext-otcollector/lib/python3.4/site-packages/pip/_internal/download.py", line 746, in unpack_http_url

    progress_bar)

  File "/home/travis/build/open-telemetry/opentelemetry-python/.tox/py34-test-ext-otcollector/lib/python3.4/site-packages/pip/_internal/download.py", line 954, in _download_http_url

    _download_url(resp, link, content_file, hashes, progress_bar)

  File "/home/travis/build/open-telemetry/opentelemetry-python/.tox/py34-test-ext-otcollector/lib/python3.4/site-packages/pip/_internal/download.py", line 683, in _download_url

    hashes.check_against_chunks(downloaded_chunks)

  File "/home/travis/build/open-telemetry/opentelemetry-python/.tox/py34-test-ext-otcollector/lib/python3.4/site-packages/pip/_internal/utils/hashes.py", line 62, in check_against_chunks

    for chunk in chunks:

  File "/home/travis/build/open-telemetry/opentelemetry-python/.tox/py34-test-ext-otcollector/lib/python3.4/site-packages/pip/_internal/download.py", line 651, in written_chunks

    for chunk in chunks:

  File "/home/travis/build/open-telemetry/opentelemetry-python/.tox/py34-test-ext-otcollector/lib/python3.4/site-packages/pip/_internal/utils/ui.py", line 156, in iter

    for x in it:

  File "/home/travis/build/open-telemetry/opentelemetry-python/.tox/py34-test-ext-otcollector/lib/python3.4/site-packages/pip/_internal/download.py", line 640, in resp_read

    decode_content=False):

  File "/home/travis/build/open-telemetry/opentelemetry-python/.tox/py34-test-ext-otcollector/lib/python3.4/site-packages/pip/_vendor/urllib3/response.py", line 494, in stream

    data = self.read(amt=amt, decode_content=decode_content)

  File "/home/travis/build/open-telemetry/opentelemetry-python/.tox/py34-test-ext-otcollector/lib/python3.4/site-packages/pip/_vendor/urllib3/response.py", line 459, in read

    raise IncompleteRead(self._fp_bytes_read, self.length_remaining)

  File "/opt/python/3.4.8/lib/python3.4/contextlib.py", line 77, in __exit__

    self.gen.throw(type, value, traceback)

  File "/home/travis/build/open-telemetry/opentelemetry-python/.tox/py34-test-ext-otcollector/lib/python3.4/site-packages/pip/_vendor/urllib3/response.py", line 378, in _error_catcher

    raise ProtocolError('Connection broken: %r' % e, e)

pip._vendor.urllib3.exceptions.ProtocolError: ("Connection broken: ConnectionResetError(104, 'Connection reset by peer')", ConnectionResetError(104, 'Connection reset by peer'))

ERROR: InvocationError for command /home/travis/build/open-telemetry/opentelemetry-python/.tox/py34-test-ext-otcollector/bin/pip install /home/travis/build/open-telemetry/opentelemetry-python/ext/opentelemetry-ext-otcollector (exited with code 2)

I re-triggered the build.

Copy link
Member

@mauriciovasquezbernal mauriciovasquezbernal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two small -not blocking- nits. LGTM!

@@ -86,10 +86,12 @@ def test_extract_multi_header(self):
new_carrier[FORMAT.SPAN_ID_KEY],
b3_format.format_span_id(child.context.span_id),
)
self.assertFalse(child.context.is_remote)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this check is needed. child is created from a SpanContext extracted from the carrier, so whether is_remote is False or True depends on the Span implementation (that you are already testing in test_span_context_remote_flag) and not on the propagator one.

Same consideration for all the cases where child.is_remote is tested.

Copy link
Member

@toumorokoshi toumorokoshi Mar 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's an interesting nuance. Are we expecting that different Span SDKs will handle this behavior differently? I guess I understood it as even different Span implementations SHOULD still adhere to the span propagation behavior.

Even the link this PR is referring to lives in the "API" section: https:/open-telemetry/opentelemetry-specification/blob/master/specification/api-tracing.md#spancontext. So theoretically different implementations should still provide similar behavior.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that a test checking that the is_remote flag is not propagated to children is useful, however this is not the place to such test, that behavior depends on the tracer implementation and not in the propagator one.

@@ -319,6 +319,7 @@ class SpanContext:
span_id: This span's ID.
trace_flags: Trace options to propagate.
trace_state: Tracing-system-specific info to propagate.
is_remote: Indicator if propagated from a remote parent

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
is_remote: Indicator if propagated from a remote parent
is_remote: Indicator if propagated from a remote parent.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
is_remote: Indicator if propagated from a remote parent
is_remote: True if propagated from a remote parent.

* remove redundant assertions in tests
* adapt doc string for is_remote flag in SpanContext
@@ -378,6 +378,12 @@ def test_default_span_resource(self):
# pylint: disable=protected-access
self.assertIs(span.resource, resources._EMPTY_RESOURCE)

def test_span_context_remote_flag(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe add a docstring saying that the expected behavior is is_remote is False by default? I guess it's implicit, I just like having some human description so I can verify the intended behavior.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I think there should not be a default for is_remote. And if any, the default should be true. This flag might have security implications, e.g. samplers may trust a sampled=True flag on a is_remote=False SpanContext. If the is_remote flag is wrongly False, this might lead to a Denial of Service vulnerability.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can definitely see that argument, although I'm wondering if denial of service is a concern, there isn't a more robust way of dealing with that. This also assumes that the adapters (include internal/custom ones) also properly use the is_remote flag, which is impossible to enforce.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on the other hand, I feel like is_remote=True as the default is unusual behavior, as the remote span is much more of a special case, and currently users are creating/starting spans with no arguments, which would then make then remote spans by default unless one is aware of that flag, and sets it everywhere.

We may need more opinions here, but I think the default being false ensures minimal friction for usage of the opentelemetry APIs.

Copy link
Member

@Oberon00 Oberon00 Mar 25, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, this was a misunderstanding on my side! 🙈
I thought we were talking about manually creating a SpanContext. For starting a new span, having is_remote=False on its SpanContext is the only sane thing to do (and this should not be configurable).

@@ -86,10 +86,12 @@ def test_extract_multi_header(self):
new_carrier[FORMAT.SPAN_ID_KEY],
b3_format.format_span_id(child.context.span_id),
)
self.assertFalse(child.context.is_remote)
Copy link
Member

@toumorokoshi toumorokoshi Mar 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's an interesting nuance. Are we expecting that different Span SDKs will handle this behavior differently? I guess I understood it as even different Span implementations SHOULD still adhere to the span propagation behavior.

Even the link this PR is referring to lives in the "API" section: https:/open-telemetry/opentelemetry-specification/blob/master/specification/api-tracing.md#spancontext. So theoretically different implementations should still provide similar behavior.

Copy link
Member

@toumorokoshi toumorokoshi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the hard work! I think there's a little bit of clarification in the Spec I'd like to verify before merging, but I think this does the current intended behavior.

@mauriciovasquezbernal I'd argue the test that was removed should be re-added, but maybe that needs clarification in the spec. issue filed: open-telemetry/opentelemetry-specification#523

@codecov-io
Copy link

codecov-io commented Mar 24, 2020

Codecov Report

Merging #516 into master will increase coverage by 0.00%.
The diff coverage is 50.00%.

Impacted file tree graph

@@           Coverage Diff           @@
##           master     #516   +/-   ##
=======================================
  Coverage   89.47%   89.47%           
=======================================
  Files          43       43           
  Lines        2213     2214    +1     
  Branches      250      250           
=======================================
+ Hits         1980     1981    +1     
  Misses        161      161           
  Partials       72       72           
Impacted Files Coverage Δ
...ry/trace/propagation/tracecontexthttptextformat.py 84.28% <ø> (ø)
...opentelemetry/sdk/context/propagation/b3_format.py 87.27% <ø> (ø)
...ntelemetry-api/src/opentelemetry/trace/__init__.py 83.12% <50.00%> (+0.10%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update fbfafa4...eb5be02. Read the comment docs.

Comment on lines 328 to 331
span_id: int,
trace_flags: "TraceFlags" = DEFAULT_TRACE_OPTIONS,
trace_state: "TraceState" = DEFAULT_TRACE_STATE,
is_remote: bool = False,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest not having a default here, for the reasons outlined in https:/open-telemetry/opentelemetry-python/pull/516/files/9b92b7aa1e08be55d309ff2b0de266df0ee5fb91#r396611236

Suggested change
span_id: int,
trace_flags: "TraceFlags" = DEFAULT_TRACE_OPTIONS,
trace_state: "TraceState" = DEFAULT_TRACE_STATE,
is_remote: bool = False,
span_id: int,
is_remote: bool,
trace_flags: "TraceFlags" = DEFAULT_TRACE_OPTIONS,
trace_state: "TraceState" = DEFAULT_TRACE_STATE,

@toumorokoshi toumorokoshi merged commit 144ab39 into open-telemetry:master Mar 26, 2020
c24t pushed a commit to c24t/opentelemetry-python that referenced this pull request Mar 26, 2020
…telemetry#516)

According to the spec a SpanContext should have an indicator if it was propagated from a remote parent.

Introduces an is_remote flag on the SpanContext which gets set when the SpanContext is extracted in a propagaton.
@mariojonke mariojonke deleted the spancontext-isremote-flag branch March 27, 2020 06:23
srikanthccv pushed a commit to srikanthccv/opentelemetry-python that referenced this pull request Nov 1, 2020
* fix: add missing dev dependencies

* fix: lint
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants