From 5c63dcd6de5b448c834ced3ec729231749d3eba6 Mon Sep 17 00:00:00 2001
From: Alex Boten <223565+codeboten@users.noreply.github.com>
Date: Wed, 17 Apr 2024 13:01:03 -0700
Subject: [PATCH 1/9] new(opentelemetry-processor-baggage): add new component

Fixes #2428

Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com>
---
 .../opentelemetry-processor-baggage/LICENSE   | 201 ++++++++++++++++++
 .../README.rst                                |  22 ++
 .../pyproject.toml                            |  44 ++++
 .../processors/baggage/__init__.py            |  20 ++
 .../processors/baggage/version.py             |  15 ++
 .../tests/__init__.py                         |  13 ++
 6 files changed, 315 insertions(+)
 create mode 100644 processor/opentelemetry-processor-baggage/LICENSE
 create mode 100644 processor/opentelemetry-processor-baggage/README.rst
 create mode 100644 processor/opentelemetry-processor-baggage/pyproject.toml
 create mode 100644 processor/opentelemetry-processor-baggage/src/opentelemetry/processors/baggage/__init__.py
 create mode 100644 processor/opentelemetry-processor-baggage/src/opentelemetry/processors/baggage/version.py
 create mode 100644 processor/opentelemetry-processor-baggage/tests/__init__.py

diff --git a/processor/opentelemetry-processor-baggage/LICENSE b/processor/opentelemetry-processor-baggage/LICENSE
new file mode 100644
index 0000000000..1ef7dad2c5
--- /dev/null
+++ b/processor/opentelemetry-processor-baggage/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright The OpenTelemetry Authors
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/processor/opentelemetry-processor-baggage/README.rst b/processor/opentelemetry-processor-baggage/README.rst
new file mode 100644
index 0000000000..2768758a99
--- /dev/null
+++ b/processor/opentelemetry-processor-baggage/README.rst
@@ -0,0 +1,22 @@
+OpenTelemetry Baggage Span Processor
+====================================
+
+The BaggageSpanProcessor reads entries stored in Baggage
+from the parent context and adds the baggage entries' keys and
+values to the span as attributes on span start.
+
+Add this span processor to a tracer provider.
+
+Keys and values added to Baggage will appear on subsequent child
+spans for a trace within this service *and* be propagated to external
+services in accordance with any configured propagation formats
+configured. If the external services also have a Baggage span
+processor, the keys and values will appear in those child spans as
+well.
+
+⚠ Warning ⚠️
+
+Do not put sensitive information in Baggage.
+
+To repeat: a consequence of adding data to Baggage is that the keys and
+values will appear in all outgoing HTTP headers from the application.
diff --git a/processor/opentelemetry-processor-baggage/pyproject.toml b/processor/opentelemetry-processor-baggage/pyproject.toml
new file mode 100644
index 0000000000..a561a337d4
--- /dev/null
+++ b/processor/opentelemetry-processor-baggage/pyproject.toml
@@ -0,0 +1,44 @@
+[build-system]
+requires = ["hatchling"]
+build-backend = "hatchling.build"
+
+[project]
+name = "opentelemetry-processor-baggage"
+dynamic = ["version"]
+description = "OpenTelemetry Baggage Span Processor"
+readme = "README.rst"
+license = "Apache-2.0"
+requires-python = ">=3.8"
+authors = [
+  { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
+]
+classifiers = [
+  "Development Status :: 4 - Beta",
+  "Intended Audience :: Developers",
+  "License :: OSI Approved :: Apache Software License",
+  "Programming Language :: Python",
+  "Programming Language :: Python :: 3",
+  "Programming Language :: Python :: 3.8",
+  "Programming Language :: Python :: 3.9",
+  "Programming Language :: Python :: 3.10",
+  "Programming Language :: Python :: 3.11",
+]
+dependencies = [
+  "opentelemetry-api ~= 1.5",
+  "wrapt >= 1.0.0, < 2.0.0",
+]
+
+[project.urls]
+Homepage = "https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/processor/opentelemetry-processor-baggage"
+
+[tool.hatch.version]
+path = "src/opentelemetry/processors/baggage/version.py"
+
+[tool.hatch.build.targets.sdist]
+include = [
+  "/src",
+  "/tests",
+]
+
+[tool.hatch.build.targets.wheel]
+packages = ["src/opentelemetry"]
diff --git a/processor/opentelemetry-processor-baggage/src/opentelemetry/processors/baggage/__init__.py b/processor/opentelemetry-processor-baggage/src/opentelemetry/processors/baggage/__init__.py
new file mode 100644
index 0000000000..a740c66491
--- /dev/null
+++ b/processor/opentelemetry-processor-baggage/src/opentelemetry/processors/baggage/__init__.py
@@ -0,0 +1,20 @@
+# Copyright The OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# pylint: disable=import-error
+
+from .processor import BaggageSpanProcessor
+from .version import __version__
+
+__all__ = ["BaggageSpanProcessor", "__version__"]
diff --git a/processor/opentelemetry-processor-baggage/src/opentelemetry/processors/baggage/version.py b/processor/opentelemetry-processor-baggage/src/opentelemetry/processors/baggage/version.py
new file mode 100644
index 0000000000..ff4933b20b
--- /dev/null
+++ b/processor/opentelemetry-processor-baggage/src/opentelemetry/processors/baggage/version.py
@@ -0,0 +1,15 @@
+# Copyright The OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+__version__ = "0.46b0.dev"
diff --git a/processor/opentelemetry-processor-baggage/tests/__init__.py b/processor/opentelemetry-processor-baggage/tests/__init__.py
new file mode 100644
index 0000000000..b0a6f42841
--- /dev/null
+++ b/processor/opentelemetry-processor-baggage/tests/__init__.py
@@ -0,0 +1,13 @@
+# Copyright The OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.

From f034487759b906d61732b43710cfde1476b8fdf4 Mon Sep 17 00:00:00 2001
From: Alex Boten <223565+codeboten@users.noreply.github.com>
Date: Tue, 23 Apr 2024 13:45:36 -0700
Subject: [PATCH 2/9] add tests

Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com>
---
 .../processors/baggage/processor.py           | 56 ++++++++++++
 .../test-requirements.txt                     |  2 +
 .../tests/test_baggage_processor.py           | 85 +++++++++++++++++++
 tox.ini                                       |  7 ++
 4 files changed, 150 insertions(+)
 create mode 100644 processor/opentelemetry-processor-baggage/src/opentelemetry/processors/baggage/processor.py
 create mode 100644 processor/opentelemetry-processor-baggage/test-requirements.txt
 create mode 100644 processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py

diff --git a/processor/opentelemetry-processor-baggage/src/opentelemetry/processors/baggage/processor.py b/processor/opentelemetry-processor-baggage/src/opentelemetry/processors/baggage/processor.py
new file mode 100644
index 0000000000..603a41f3ba
--- /dev/null
+++ b/processor/opentelemetry-processor-baggage/src/opentelemetry/processors/baggage/processor.py
@@ -0,0 +1,56 @@
+# Copyright The OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from typing import Optional
+from opentelemetry.baggage import get_all as get_all_baggage
+from opentelemetry.sdk.trace.export import SpanProcessor
+from opentelemetry.trace import Span
+from opentelemetry.context import Context
+
+
+class BaggageSpanProcessor(SpanProcessor):
+    """
+     The BaggageSpanProcessor reads entries stored in Baggage
+     from the parent context and adds the baggage entries' keys and
+     values to the span as attributes on span start.
+
+     Add this span processor to a tracer provider.
+
+     Keys and values added to Baggage will appear on subsequent child
+     spans for a trace within this service *and* be propagated to external
+     services in accordance with any configured propagation formats
+     configured. If the external services also have a Baggage span
+     processor, the keys and values will appear in those child spans as
+     well.
+
+     ⚠ Warning ⚠️
+
+     Do not put sensitive information in Baggage.
+
+     To repeat: a consequence of adding data to Baggage is that the keys and
+     values will appear in all outgoing HTTP headers from the application.
+
+    """
+
+    def __init__(self) -> None:
+        pass
+
+    def on_start(
+        self,
+        span: "Span",
+        parent_context: Optional[Context] = None
+    ) -> None:
+        baggage = get_all_baggage(parent_context)
+        for key, value in baggage.items():
+            span.set_attribute(key, value)
\ No newline at end of file
diff --git a/processor/opentelemetry-processor-baggage/test-requirements.txt b/processor/opentelemetry-processor-baggage/test-requirements.txt
new file mode 100644
index 0000000000..fa7ad3d793
--- /dev/null
+++ b/processor/opentelemetry-processor-baggage/test-requirements.txt
@@ -0,0 +1,2 @@
+
+-e processor/opentelemetry-processor-baggage
\ No newline at end of file
diff --git a/processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py b/processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py
new file mode 100644
index 0000000000..cfedee2050
--- /dev/null
+++ b/processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py
@@ -0,0 +1,85 @@
+# Copyright The OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+
+from opentelemetry.sdk.trace.export import SpanProcessor
+from opentelemetry.sdk.trace import TracerProvider
+from opentelemetry.trace import (
+    Span,
+    Tracer
+)
+from opentelemetry.baggage import (
+    get_all as get_all_baggage,
+    set_baggage
+)
+from opentelemetry.context import attach, detach
+from opentelemetry.processors.baggage import BaggageSpanProcessor
+
+class BaggageSpanProcessorTest(unittest.TestCase):
+
+    def test_check_the_baggage(self):
+        baggageProcessor = BaggageSpanProcessor()
+        assert isinstance(baggageProcessor, SpanProcessor)
+
+
+    def test_set_baggage_attaches_to_child_spans_and_detaches_properly_with_context(self):
+        tracer_provider = TracerProvider()
+        tracer_provider.add_span_processor(BaggageSpanProcessor())
+
+        # tracer has no baggage to start
+        tracer = tracer_provider.get_tracer("my-tracer")
+        assert isinstance(tracer, Tracer)
+        assert get_all_baggage() == {}
+        # set baggage in context
+        ctx = set_baggage("queen", "bee")
+        with tracer.start_as_current_span(name="bumble", context=ctx) as bumble_span:
+            # span should have baggage key-value pair in context
+            assert get_all_baggage(ctx) == {"queen": "bee"}
+            # span should have baggage key-value pair in attribute
+            assert bumble_span._attributes["queen"] == "bee"
+            with tracer.start_as_current_span(name="child_span", context=ctx) as child_span:
+                assert isinstance(child_span, Span)
+                # child span should have baggage key-value pair in context
+                assert get_all_baggage(ctx) == {"queen": "bee"}
+                # child span should have baggage key-value pair in attribute
+                assert child_span._attributes["queen"] == "bee"
+
+
+    def test_set_baggage_attaches_to_child_spans_and_detaches_properly_with_token(self):
+        tracer_provider = TracerProvider()
+        tracer_provider.add_span_processor(BaggageSpanProcessor())
+
+        # tracer has no baggage to start
+        tracer = tracer_provider.get_tracer("my-tracer")
+        assert isinstance(tracer, Tracer)
+        assert get_all_baggage() == {}
+        # create a context token and set baggage
+        honey_token = attach(set_baggage("bumble", "bee"))
+        assert get_all_baggage() == {"bumble": "bee"}
+        # in a new span, ensure the baggage is there
+        with tracer.start_as_current_span("parent") as span:
+            assert get_all_baggage() == {"bumble": "bee"}
+            assert span._attributes["bumble"] == "bee"
+            # create a second context token and set more baggage
+            moar_token = attach(set_baggage("moar", "bee"))
+            assert get_all_baggage() == {"bumble": "bee", "moar": "bee"}
+            # in a child span, ensure all baggage is there as attributes
+            with tracer.start_as_current_span("child") as child_span:
+                assert get_all_baggage() == {"bumble": "bee", "moar": "bee"}
+                assert child_span._attributes["bumble"] == "bee"
+                assert child_span._attributes["moar"] == "bee"
+            detach(moar_token)
+        detach(honey_token)
+        assert get_all_baggage() == {}
diff --git a/tox.ini b/tox.ini
index 3583b7319f..b26243a321 100644
--- a/tox.ini
+++ b/tox.ini
@@ -293,6 +293,10 @@ envlist =
     py3{8,9,10,11}-test-instrumentation-cassandra
     pypy3-test-instrumentation-cassandra
 
+    ; opentelemetry-processor-baggage
+    py3{8,9,10,11}-test-processor-baggage
+    pypy3-test-processor-baggage
+
     lint
     spellcheck
     docker-tests
@@ -475,6 +479,8 @@ commands_pre =
 
   propagator-aws-xray: pip install -r {toxinidir}/propagator/opentelemetry-propagator-aws-xray/test-requirements.txt
 
+  processor-baggage: pip install -r {toxinidir}/processor/opentelemetry-processor-baggage/test-requirements.txt
+
 ; we have to install packages in editable mode.
   coverage: python {toxinidir}/scripts/eachdist.py install --editable
 
@@ -532,6 +538,7 @@ commands =
   test-util-http: pytest {toxinidir}/util/opentelemetry-util-http/tests {posargs}
   test-sdk-extension-aws: pytest {toxinidir}/sdk-extension/opentelemetry-sdk-extension-aws/tests {posargs}
   test-resource-detector-container: pytest {toxinidir}/resource/opentelemetry-resource-detector-container/tests {posargs}
+  test-processor-baggage: pytest {toxinidir}/processor/opentelemetry-processor-baggage/tests {posargs}
   test-propagator-aws: pytest {toxinidir}/propagator/opentelemetry-propagator-aws-xray/tests {posargs}
   test-propagator-ot-trace: pytest {toxinidir}/propagator/opentelemetry-propagator-ot-trace/tests {posargs}
   test-exporter-richconsole: pytest {toxinidir}/exporter/opentelemetry-exporter-richconsole/tests {posargs}

From 3fc4128e647a36cfb7f0832f8f133f368c89c11c Mon Sep 17 00:00:00 2001
From: Alex Boten <223565+codeboten@users.noreply.github.com>
Date: Tue, 23 Apr 2024 13:48:32 -0700
Subject: [PATCH 3/9] update changelog

Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com>
---
 CHANGELOG.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 89495abe73..723ad7a5a4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 - `opentelemetry-instrumentation-threading` Initial release for threading
   ([#2253](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2253))
+- `opentelemetry-processor-baggage` Initial release
+  ([#2436](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2436))
 
 ## Version 1.24.0/0.45b0 (2024-03-28)
 

From d6406c6949e91a86eaed5f4e4b947630e401eb15 Mon Sep 17 00:00:00 2001
From: Alex Boten <223565+codeboten@users.noreply.github.com>
Date: Tue, 23 Apr 2024 14:24:36 -0700
Subject: [PATCH 4/9] update component owners

Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com>
---
 .github/component_owners.yml | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/.github/component_owners.yml b/.github/component_owners.yml
index ab14a41aec..efd15a6775 100644
--- a/.github/component_owners.yml
+++ b/.github/component_owners.yml
@@ -73,3 +73,6 @@ components:
 
   instrumentation/opentelemetry-instrumentation-psycopg:
     - federicobond
+
+  processor/opentelemetry-processor-baggage:
+    - codeboten

From 301e0eaf64c87c2b714525ade6143d7c5502e871 Mon Sep 17 00:00:00 2001
From: Alex Boten <223565+codeboten@users.noreply.github.com>
Date: Wed, 24 Apr 2024 06:59:00 -0700
Subject: [PATCH 5/9] lint

Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com>
---
 .../processors/baggage/processor.py           | 37 +++++++++----------
 .../tests/test_baggage_processor.py           | 35 +++++++++---------
 2 files changed, 36 insertions(+), 36 deletions(-)

diff --git a/processor/opentelemetry-processor-baggage/src/opentelemetry/processors/baggage/processor.py b/processor/opentelemetry-processor-baggage/src/opentelemetry/processors/baggage/processor.py
index 603a41f3ba..36df06a94c 100644
--- a/processor/opentelemetry-processor-baggage/src/opentelemetry/processors/baggage/processor.py
+++ b/processor/opentelemetry-processor-baggage/src/opentelemetry/processors/baggage/processor.py
@@ -13,33 +13,34 @@
 # limitations under the License.
 
 from typing import Optional
+
 from opentelemetry.baggage import get_all as get_all_baggage
+from opentelemetry.context import Context
 from opentelemetry.sdk.trace.export import SpanProcessor
 from opentelemetry.trace import Span
-from opentelemetry.context import Context
 
 
 class BaggageSpanProcessor(SpanProcessor):
     """
-     The BaggageSpanProcessor reads entries stored in Baggage
-     from the parent context and adds the baggage entries' keys and
-     values to the span as attributes on span start.
+    The BaggageSpanProcessor reads entries stored in Baggage
+    from the parent context and adds the baggage entries' keys and
+    values to the span as attributes on span start.
 
-     Add this span processor to a tracer provider.
+    Add this span processor to a tracer provider.
 
-     Keys and values added to Baggage will appear on subsequent child
-     spans for a trace within this service *and* be propagated to external
-     services in accordance with any configured propagation formats
-     configured. If the external services also have a Baggage span
-     processor, the keys and values will appear in those child spans as
-     well.
+    Keys and values added to Baggage will appear on subsequent child
+    spans for a trace within this service *and* be propagated to external
+    services in accordance with any configured propagation formats
+    configured. If the external services also have a Baggage span
+    processor, the keys and values will appear in those child spans as
+    well.
 
-     ⚠ Warning ⚠️
+    ⚠ Warning ⚠️
 
-     Do not put sensitive information in Baggage.
+    Do not put sensitive information in Baggage.
 
-     To repeat: a consequence of adding data to Baggage is that the keys and
-     values will appear in all outgoing HTTP headers from the application.
+    To repeat: a consequence of adding data to Baggage is that the keys and
+    values will appear in all outgoing HTTP headers from the application.
 
     """
 
@@ -47,10 +48,8 @@ def __init__(self) -> None:
         pass
 
     def on_start(
-        self,
-        span: "Span",
-        parent_context: Optional[Context] = None
+        self, span: "Span", parent_context: Optional[Context] = None
     ) -> None:
         baggage = get_all_baggage(parent_context)
         for key, value in baggage.items():
-            span.set_attribute(key, value)
\ No newline at end of file
+            span.set_attribute(key, value)
diff --git a/processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py b/processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py
index cfedee2050..0ff4731303 100644
--- a/processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py
+++ b/processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py
@@ -14,27 +14,23 @@
 
 import unittest
 
-from opentelemetry.sdk.trace.export import SpanProcessor
-from opentelemetry.sdk.trace import TracerProvider
-from opentelemetry.trace import (
-    Span,
-    Tracer
-)
-from opentelemetry.baggage import (
-    get_all as get_all_baggage,
-    set_baggage
-)
+from opentelemetry.baggage import get_all as get_all_baggage
+from opentelemetry.baggage import set_baggage
 from opentelemetry.context import attach, detach
 from opentelemetry.processors.baggage import BaggageSpanProcessor
+from opentelemetry.sdk.trace import TracerProvider
+from opentelemetry.sdk.trace.export import SpanProcessor
+from opentelemetry.trace import Span, Tracer
 
-class BaggageSpanProcessorTest(unittest.TestCase):
 
+class BaggageSpanProcessorTest(unittest.TestCase):
     def test_check_the_baggage(self):
         baggageProcessor = BaggageSpanProcessor()
         assert isinstance(baggageProcessor, SpanProcessor)
 
-
-    def test_set_baggage_attaches_to_child_spans_and_detaches_properly_with_context(self):
+    def test_set_baggage_attaches_to_child_spans_and_detaches_properly_with_context(
+        self,
+    ):
         tracer_provider = TracerProvider()
         tracer_provider.add_span_processor(BaggageSpanProcessor())
 
@@ -44,20 +40,25 @@ def test_set_baggage_attaches_to_child_spans_and_detaches_properly_with_context(
         assert get_all_baggage() == {}
         # set baggage in context
         ctx = set_baggage("queen", "bee")
-        with tracer.start_as_current_span(name="bumble", context=ctx) as bumble_span:
+        with tracer.start_as_current_span(
+            name="bumble", context=ctx
+        ) as bumble_span:
             # span should have baggage key-value pair in context
             assert get_all_baggage(ctx) == {"queen": "bee"}
             # span should have baggage key-value pair in attribute
             assert bumble_span._attributes["queen"] == "bee"
-            with tracer.start_as_current_span(name="child_span", context=ctx) as child_span:
+            with tracer.start_as_current_span(
+                name="child_span", context=ctx
+            ) as child_span:
                 assert isinstance(child_span, Span)
                 # child span should have baggage key-value pair in context
                 assert get_all_baggage(ctx) == {"queen": "bee"}
                 # child span should have baggage key-value pair in attribute
                 assert child_span._attributes["queen"] == "bee"
 
-
-    def test_set_baggage_attaches_to_child_spans_and_detaches_properly_with_token(self):
+    def test_set_baggage_attaches_to_child_spans_and_detaches_properly_with_token(
+        self,
+    ):
         tracer_provider = TracerProvider()
         tracer_provider.add_span_processor(BaggageSpanProcessor())
 

From 48b5f2995c859b2607bd970b8d6a64161b116575 Mon Sep 17 00:00:00 2001
From: Alex Boten <223565+codeboten@users.noreply.github.com>
Date: Wed, 24 Apr 2024 07:00:19 -0700
Subject: [PATCH 6/9] update license

Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com>
---
 processor/opentelemetry-processor-baggage/LICENSE | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/processor/opentelemetry-processor-baggage/LICENSE b/processor/opentelemetry-processor-baggage/LICENSE
index 1ef7dad2c5..261eeb9e9f 100644
--- a/processor/opentelemetry-processor-baggage/LICENSE
+++ b/processor/opentelemetry-processor-baggage/LICENSE
@@ -186,7 +186,7 @@
       same "printed page" as the copyright notice for easier
       identification within third-party archives.
 
-   Copyright The OpenTelemetry Authors
+   Copyright [yyyy] [name of copyright owner]
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.

From bd6f9194ea281d2a2143551526dafcc3cc7470a9 Mon Sep 17 00:00:00 2001
From: Alex Boten <223565+codeboten@users.noreply.github.com>
Date: Wed, 24 Apr 2024 08:42:39 -0700
Subject: [PATCH 7/9] fix lint

Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com>
---
 .../tests/test_baggage_processor.py           | 37 +++++++++----------
 1 file changed, 18 insertions(+), 19 deletions(-)

diff --git a/processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py b/processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py
index 0ff4731303..a3788d53a0 100644
--- a/processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py
+++ b/processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py
@@ -25,8 +25,7 @@
 
 class BaggageSpanProcessorTest(unittest.TestCase):
     def test_check_the_baggage(self):
-        baggageProcessor = BaggageSpanProcessor()
-        assert isinstance(baggageProcessor, SpanProcessor)
+        self.assertIsInstance(BaggageSpanProcessor(), SpanProcessor)
 
     def test_set_baggage_attaches_to_child_spans_and_detaches_properly_with_context(
         self,
@@ -36,25 +35,25 @@ def test_set_baggage_attaches_to_child_spans_and_detaches_properly_with_context(
 
         # tracer has no baggage to start
         tracer = tracer_provider.get_tracer("my-tracer")
-        assert isinstance(tracer, Tracer)
-        assert get_all_baggage() == {}
+        self.assertIsInstance(tracer, Tracer)
+        self.assertEqual(get_all_baggage(), {})
         # set baggage in context
         ctx = set_baggage("queen", "bee")
         with tracer.start_as_current_span(
             name="bumble", context=ctx
         ) as bumble_span:
             # span should have baggage key-value pair in context
-            assert get_all_baggage(ctx) == {"queen": "bee"}
+            self.assertEqual(get_all_baggage(ctx), {"queen": "bee"})
             # span should have baggage key-value pair in attribute
-            assert bumble_span._attributes["queen"] == "bee"
+            self.assertEqual(bumble_span._attributes["queen"], "bee")
             with tracer.start_as_current_span(
                 name="child_span", context=ctx
             ) as child_span:
-                assert isinstance(child_span, Span)
+                self.assertIsInstance(child_span, Span)
                 # child span should have baggage key-value pair in context
-                assert get_all_baggage(ctx) == {"queen": "bee"}
+                self.assertEqual(get_all_baggage(ctx), {"queen": "bee"})
                 # child span should have baggage key-value pair in attribute
-                assert child_span._attributes["queen"] == "bee"
+                self.assertEqual(child_span._attributes["queen"], "bee")
 
     def test_set_baggage_attaches_to_child_spans_and_detaches_properly_with_token(
         self,
@@ -64,23 +63,23 @@ def test_set_baggage_attaches_to_child_spans_and_detaches_properly_with_token(
 
         # tracer has no baggage to start
         tracer = tracer_provider.get_tracer("my-tracer")
-        assert isinstance(tracer, Tracer)
-        assert get_all_baggage() == {}
+        self.assertIsInstance(tracer, Tracer)
+        self.assertEqual(get_all_baggage(), {})
         # create a context token and set baggage
         honey_token = attach(set_baggage("bumble", "bee"))
-        assert get_all_baggage() == {"bumble": "bee"}
+        self.assertEqual(get_all_baggage(), {"bumble": "bee"})
         # in a new span, ensure the baggage is there
         with tracer.start_as_current_span("parent") as span:
-            assert get_all_baggage() == {"bumble": "bee"}
-            assert span._attributes["bumble"] == "bee"
+            self.assertEqual(get_all_baggage(), {"bumble": "bee"})
+            self.assertEqual(span._attributes["bumble"], "bee")
             # create a second context token and set more baggage
             moar_token = attach(set_baggage("moar", "bee"))
-            assert get_all_baggage() == {"bumble": "bee", "moar": "bee"}
+            self.assertEqual(get_all_baggage(), {"bumble": "bee", "moar": "bee"})
             # in a child span, ensure all baggage is there as attributes
             with tracer.start_as_current_span("child") as child_span:
-                assert get_all_baggage() == {"bumble": "bee", "moar": "bee"}
-                assert child_span._attributes["bumble"] == "bee"
-                assert child_span._attributes["moar"] == "bee"
+                self.assertEqual(get_all_baggage(), {"bumble": "bee", "moar": "bee"})
+                self.assertEqual(child_span._attributes["bumble"], "bee")
+                self.assertEqual(child_span._attributes["moar"], "bee")
             detach(moar_token)
         detach(honey_token)
-        assert get_all_baggage() == {}
+        self.assertEqual(get_all_baggage(), {})

From a4c495c649f2508f4db2af8a6aed6ffd8d755412 Mon Sep 17 00:00:00 2001
From: Alex Boten <223565+codeboten@users.noreply.github.com>
Date: Wed, 24 Apr 2024 08:55:58 -0700
Subject: [PATCH 8/9] lint

Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com>
---
 .../tests/test_baggage_processor.py                       | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py b/processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py
index a3788d53a0..be5f65a979 100644
--- a/processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py
+++ b/processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py
@@ -74,10 +74,14 @@ def test_set_baggage_attaches_to_child_spans_and_detaches_properly_with_token(
             self.assertEqual(span._attributes["bumble"], "bee")
             # create a second context token and set more baggage
             moar_token = attach(set_baggage("moar", "bee"))
-            self.assertEqual(get_all_baggage(), {"bumble": "bee", "moar": "bee"})
+            self.assertEqual(
+                get_all_baggage(), {"bumble": "bee", "moar": "bee"}
+            )
             # in a child span, ensure all baggage is there as attributes
             with tracer.start_as_current_span("child") as child_span:
-                self.assertEqual(get_all_baggage(), {"bumble": "bee", "moar": "bee"})
+                self.assertEqual(
+                    get_all_baggage(), {"bumble": "bee", "moar": "bee"}
+                )
                 self.assertEqual(child_span._attributes["bumble"], "bee")
                 self.assertEqual(child_span._attributes["moar"], "bee")
             detach(moar_token)

From 6f0c4a8e777aba1ea8de9252b81afcf2f99fdc27 Mon Sep 17 00:00:00 2001
From: Alex Boten <223565+codeboten@users.noreply.github.com>
Date: Wed, 24 Apr 2024 10:20:44 -0700
Subject: [PATCH 9/9] rename processors dir to processor

Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com>
---
 processor/opentelemetry-processor-baggage/pyproject.toml        | 2 +-
 .../opentelemetry/{processors => processor}/baggage/__init__.py | 0
 .../{processors => processor}/baggage/processor.py              | 0
 .../opentelemetry/{processors => processor}/baggage/version.py  | 0
 .../tests/test_baggage_processor.py                             | 2 +-
 tox.ini                                                         | 1 +
 6 files changed, 3 insertions(+), 2 deletions(-)
 rename processor/opentelemetry-processor-baggage/src/opentelemetry/{processors => processor}/baggage/__init__.py (100%)
 rename processor/opentelemetry-processor-baggage/src/opentelemetry/{processors => processor}/baggage/processor.py (100%)
 rename processor/opentelemetry-processor-baggage/src/opentelemetry/{processors => processor}/baggage/version.py (100%)

diff --git a/processor/opentelemetry-processor-baggage/pyproject.toml b/processor/opentelemetry-processor-baggage/pyproject.toml
index a561a337d4..0ef5392fdb 100644
--- a/processor/opentelemetry-processor-baggage/pyproject.toml
+++ b/processor/opentelemetry-processor-baggage/pyproject.toml
@@ -32,7 +32,7 @@ dependencies = [
 Homepage = "https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/processor/opentelemetry-processor-baggage"
 
 [tool.hatch.version]
-path = "src/opentelemetry/processors/baggage/version.py"
+path = "src/opentelemetry/processor/baggage/version.py"
 
 [tool.hatch.build.targets.sdist]
 include = [
diff --git a/processor/opentelemetry-processor-baggage/src/opentelemetry/processors/baggage/__init__.py b/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/__init__.py
similarity index 100%
rename from processor/opentelemetry-processor-baggage/src/opentelemetry/processors/baggage/__init__.py
rename to processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/__init__.py
diff --git a/processor/opentelemetry-processor-baggage/src/opentelemetry/processors/baggage/processor.py b/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/processor.py
similarity index 100%
rename from processor/opentelemetry-processor-baggage/src/opentelemetry/processors/baggage/processor.py
rename to processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/processor.py
diff --git a/processor/opentelemetry-processor-baggage/src/opentelemetry/processors/baggage/version.py b/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/version.py
similarity index 100%
rename from processor/opentelemetry-processor-baggage/src/opentelemetry/processors/baggage/version.py
rename to processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/version.py
diff --git a/processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py b/processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py
index be5f65a979..63a71c3cba 100644
--- a/processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py
+++ b/processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py
@@ -17,7 +17,7 @@
 from opentelemetry.baggage import get_all as get_all_baggage
 from opentelemetry.baggage import set_baggage
 from opentelemetry.context import attach, detach
-from opentelemetry.processors.baggage import BaggageSpanProcessor
+from opentelemetry.processor.baggage import BaggageSpanProcessor
 from opentelemetry.sdk.trace import TracerProvider
 from opentelemetry.sdk.trace.export import SpanProcessor
 from opentelemetry.trace import Span, Tracer
diff --git a/tox.ini b/tox.ini
index b5b6b80143..0fb11855e8 100644
--- a/tox.ini
+++ b/tox.ini
@@ -629,6 +629,7 @@ commands_pre =
   pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-tortoiseorm/test-requirements.txt
   # requires snappy headers to be available on the system
   pip install -r {toxinidir}/resource/opentelemetry-resource-detector-container/test-requirements.txt
+  pip install -r {toxinidir}/processor/opentelemetry-processor-baggage/test-requirements.txt
   pip install -r {toxinidir}/propagator/opentelemetry-propagator-aws-xray/test-requirements.txt
   pip install -r {toxinidir}/propagator/opentelemetry-propagator-ot-trace/test-requirements.txt
   pip install -r {toxinidir}/sdk-extension/opentelemetry-sdk-extension-aws/test-requirements.txt