From cfd9225f8ea6599cadd322f2a8818b1cb502a0ca Mon Sep 17 00:00:00 2001 From: Aaron Abbott Date: Thu, 16 Jul 2020 18:07:59 +0000 Subject: [PATCH 1/2] Add instructions to proto package README (#905) --- opentelemetry-proto/README.rst | 11 ++++++++--- scripts/proto_codegen.sh | 20 ++++++++++++++------ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/opentelemetry-proto/README.rst b/opentelemetry-proto/README.rst index 48b9661b206..6825aa88238 100644 --- a/opentelemetry-proto/README.rst +++ b/opentelemetry-proto/README.rst @@ -16,11 +16,15 @@ Installation Code Generation --------------- -These files were generated automatically. More details on how to generate them -are available here_. +These files were generated automatically from code in opentelemetry-proto_. +To regenerate the code, run ``../scripts/proto_codegen.sh``. +To build against a new release or specific commit of opentelemetry-proto_, +update the ``PROTO_REPO_BRANCH_OR_COMMIT`` variable in +``../scripts/proto_codegen.sh``. Then run the script and commit the changes +as well as any fixes needed in the OTLP exporter. -.. _here: https://github.com/open-telemetry/opentelemetry-proto +.. _opentelemetry-proto: https://github.com/open-telemetry/opentelemetry-proto References @@ -28,3 +32,4 @@ References * `OpenTelemetry Project `_ * `OpenTelemetry Proto `_ +* `proto_codegen.sh script `_ diff --git a/scripts/proto_codegen.sh b/scripts/proto_codegen.sh index b840f8452b8..f7742db6f68 100755 --- a/scripts/proto_codegen.sh +++ b/scripts/proto_codegen.sh @@ -16,16 +16,24 @@ PROTO_REPO_BRANCH_OR_COMMIT="v0.4.0" set -e -if [ -z "$VIRTUAL_ENV" ]; then - echo '$VIRTUAL_ENV is not set, you probably forgot to source it. Exiting...' - exit 1 -fi - PROTO_REPO_DIR=${PROTO_REPO_DIR:-"/tmp/opentelemetry-proto"} # root of opentelemetry-python repo repo_root="$(git rev-parse --show-toplevel)" +venv_dir="/tmp/proto_codegen_venv" + +# run on exit even if crash +cleanup() { + echo "Deleting $venv_dir" + rm -rf $venv_dir +} +trap cleanup EXIT -python -m pip install -r $repo_root/dev-requirements.txt +echo "Creating temporary virtualenv at $venv_dir using $(python3 --version)" +python3 -m venv $venv_dir +source $venv_dir/bin/activate +python -m pip install \ + -c $repo_root/dev-requirements.txt \ + grpcio-tools mypy-protobuf # Clone the proto repo if it doesn't exist if [ ! -d "$PROTO_REPO_DIR" ]; then From 23e577e5504384432ed37b31149b61627004e3e3 Mon Sep 17 00:00:00 2001 From: Andrew Xue Date: Thu, 16 Jul 2020 16:12:44 -0400 Subject: [PATCH 2/2] remove google exporter files (#918) --- docs-requirements.txt | 6 +- .../CHANGELOG.md | 21 - .../LICENSE | 201 ------- .../MANIFEST.in | 9 - .../README.rst | 18 - .../setup.cfg | 51 -- .../setup.py | 31 -- .../exporter/cloud_monitoring/__init__.py | 236 -------- .../exporter/cloud_monitoring/version.py | 15 - .../tests/__init__.py | 0 .../tests/test_cloud_monitoring.py | 436 --------------- .../CHANGELOG.md | 25 - .../LICENSE | 201 ------- .../MANIFEST.in | 9 - .../README.rst | 43 -- .../setup.cfg | 51 -- .../setup.py | 26 - .../exporter/cloud_trace/__init__.py | 382 ------------- .../cloud_trace/cloud_trace_propagator.py | 89 --- .../exporter/cloud_trace/version.py | 15 - .../opentelemetry/tools/resource_detector.py | 47 -- .../tests/__init__.py | 0 .../tests/test_cloud_trace_exporter.py | 508 ------------------ .../tests/test_cloud_trace_propagator.py | 238 -------- .../tests/test_gcp_resource_detector.py | 84 --- scripts/coverage.sh | 2 - tox.ini | 13 - 27 files changed, 3 insertions(+), 2754 deletions(-) delete mode 100644 ext/opentelemetry-exporter-cloud-monitoring/CHANGELOG.md delete mode 100644 ext/opentelemetry-exporter-cloud-monitoring/LICENSE delete mode 100644 ext/opentelemetry-exporter-cloud-monitoring/MANIFEST.in delete mode 100644 ext/opentelemetry-exporter-cloud-monitoring/README.rst delete mode 100644 ext/opentelemetry-exporter-cloud-monitoring/setup.cfg delete mode 100644 ext/opentelemetry-exporter-cloud-monitoring/setup.py delete mode 100644 ext/opentelemetry-exporter-cloud-monitoring/src/opentelemetry/exporter/cloud_monitoring/__init__.py delete mode 100644 ext/opentelemetry-exporter-cloud-monitoring/src/opentelemetry/exporter/cloud_monitoring/version.py delete mode 100644 ext/opentelemetry-exporter-cloud-monitoring/tests/__init__.py delete mode 100644 ext/opentelemetry-exporter-cloud-monitoring/tests/test_cloud_monitoring.py delete mode 100644 ext/opentelemetry-exporter-cloud-trace/CHANGELOG.md delete mode 100644 ext/opentelemetry-exporter-cloud-trace/LICENSE delete mode 100644 ext/opentelemetry-exporter-cloud-trace/MANIFEST.in delete mode 100644 ext/opentelemetry-exporter-cloud-trace/README.rst delete mode 100644 ext/opentelemetry-exporter-cloud-trace/setup.cfg delete mode 100644 ext/opentelemetry-exporter-cloud-trace/setup.py delete mode 100644 ext/opentelemetry-exporter-cloud-trace/src/opentelemetry/exporter/cloud_trace/__init__.py delete mode 100644 ext/opentelemetry-exporter-cloud-trace/src/opentelemetry/exporter/cloud_trace/cloud_trace_propagator.py delete mode 100644 ext/opentelemetry-exporter-cloud-trace/src/opentelemetry/exporter/cloud_trace/version.py delete mode 100644 ext/opentelemetry-exporter-cloud-trace/src/opentelemetry/tools/resource_detector.py delete mode 100644 ext/opentelemetry-exporter-cloud-trace/tests/__init__.py delete mode 100644 ext/opentelemetry-exporter-cloud-trace/tests/test_cloud_trace_exporter.py delete mode 100644 ext/opentelemetry-exporter-cloud-trace/tests/test_cloud_trace_propagator.py delete mode 100644 ext/opentelemetry-exporter-cloud-trace/tests/test_gcp_resource_detector.py diff --git a/docs-requirements.txt b/docs-requirements.txt index 169bbb7f0b1..0fa078ef233 100644 --- a/docs-requirements.txt +++ b/docs-requirements.txt @@ -25,8 +25,8 @@ wrapt>=1.0.0,<2.0.0 celery>=4.0 psutil~=5.7.0 boto~=2.0 -google-cloud-trace >=0.23.0 -google-cloud-monitoring>=0.36.0 botocore~=1.0 starlette~=0.13 -fastapi~=0.58.1 \ No newline at end of file +fastapi~=0.58.1 +opentelemetry-exporter-cloud-trace==0.10b0 +opentelemetry-exporter-cloud-monitoring==0.10b0 \ No newline at end of file diff --git a/ext/opentelemetry-exporter-cloud-monitoring/CHANGELOG.md b/ext/opentelemetry-exporter-cloud-monitoring/CHANGELOG.md deleted file mode 100644 index e5f01ee5b02..00000000000 --- a/ext/opentelemetry-exporter-cloud-monitoring/CHANGELOG.md +++ /dev/null @@ -1,21 +0,0 @@ -# Changelog - -## Unreleased - -- Add support for resources - ([#853](https://github.com/open-telemetry/opentelemetry-python/pull/853)) - -## Version 0.10b0 - -Released 2020-06-23 - -- Add ability for exporter to add unique identifier - ([#841](https://github.com/open-telemetry/opentelemetry-python/pull/841)) -- Added tests to tox coverage files - ([#804](https://github.com/open-telemetry/opentelemetry-python/pull/804)) - -## 0.9b0 - -Released 2020-06-10 - -- Initial release diff --git a/ext/opentelemetry-exporter-cloud-monitoring/LICENSE b/ext/opentelemetry-exporter-cloud-monitoring/LICENSE deleted file mode 100644 index 261eeb9e9f8..00000000000 --- a/ext/opentelemetry-exporter-cloud-monitoring/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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 [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. - 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/ext/opentelemetry-exporter-cloud-monitoring/MANIFEST.in b/ext/opentelemetry-exporter-cloud-monitoring/MANIFEST.in deleted file mode 100644 index aed3e33273b..00000000000 --- a/ext/opentelemetry-exporter-cloud-monitoring/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -graft src -graft tests -global-exclude *.pyc -global-exclude *.pyo -global-exclude __pycache__/* -include CHANGELOG.md -include MANIFEST.in -include README.rst -include LICENSE diff --git a/ext/opentelemetry-exporter-cloud-monitoring/README.rst b/ext/opentelemetry-exporter-cloud-monitoring/README.rst deleted file mode 100644 index f1fd52528c9..00000000000 --- a/ext/opentelemetry-exporter-cloud-monitoring/README.rst +++ /dev/null @@ -1,18 +0,0 @@ -OpenTelemetry Cloud Monitoring Exporters -======================================== - -This library provides classes for exporting metrics data to Google Cloud Monitoring. - -Installation ------------- - -:: - - pip install opentelemetry-exporter-cloud-monitoring - -References ----------- - -* `OpenTelemetry Cloud Monitoring Exporter `_ -* `Cloud Monitoring `_ -* `OpenTelemetry Project `_ \ No newline at end of file diff --git a/ext/opentelemetry-exporter-cloud-monitoring/setup.cfg b/ext/opentelemetry-exporter-cloud-monitoring/setup.cfg deleted file mode 100644 index 8875937751f..00000000000 --- a/ext/opentelemetry-exporter-cloud-monitoring/setup.cfg +++ /dev/null @@ -1,51 +0,0 @@ - -# Copyright 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. -# -[metadata] -name = opentelemetry-exporter-cloud-monitoring -description = Cloud Monitoring integration for OpenTelemetry -long_description = file: README.rst -long_description_content_type = text/x-rst -author = OpenTelemetry Authors -author_email = cncf-opentelemetry-contributors@lists.cncf.io -url = https://github.com/open-telemetry/opentelemetry-python/ext/opentelemetry-exporter-cloud-monitoring -platforms = any -license = Apache-2.0 -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.4 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - -[options] -python_requires = >=3.4 -package_dir= - =src -packages=find_namespace: -install_requires = - opentelemetry-api - opentelemetry-sdk - google-cloud-monitoring - -[options.packages.find] -where = src - -[options.extras_require] -test = diff --git a/ext/opentelemetry-exporter-cloud-monitoring/setup.py b/ext/opentelemetry-exporter-cloud-monitoring/setup.py deleted file mode 100644 index 0ca88bc330f..00000000000 --- a/ext/opentelemetry-exporter-cloud-monitoring/setup.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright 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 os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, - "src", - "opentelemetry", - "exporter", - "cloud_monitoring", - "version.py", -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/ext/opentelemetry-exporter-cloud-monitoring/src/opentelemetry/exporter/cloud_monitoring/__init__.py b/ext/opentelemetry-exporter-cloud-monitoring/src/opentelemetry/exporter/cloud_monitoring/__init__.py deleted file mode 100644 index 2c6e3abdb09..00000000000 --- a/ext/opentelemetry-exporter-cloud-monitoring/src/opentelemetry/exporter/cloud_monitoring/__init__.py +++ /dev/null @@ -1,236 +0,0 @@ -import logging -import random -from typing import Optional, Sequence - -import google.auth -from google.api.label_pb2 import LabelDescriptor -from google.api.metric_pb2 import MetricDescriptor -from google.api.monitored_resource_pb2 import MonitoredResource -from google.cloud.monitoring_v3 import MetricServiceClient -from google.cloud.monitoring_v3.proto.metric_pb2 import TimeSeries - -from opentelemetry.sdk.metrics.export import ( - MetricRecord, - MetricsExporter, - MetricsExportResult, -) -from opentelemetry.sdk.metrics.export.aggregate import SumAggregator -from opentelemetry.sdk.resources import Resource - -logger = logging.getLogger(__name__) -MAX_BATCH_WRITE = 200 -WRITE_INTERVAL = 10 -UNIQUE_IDENTIFIER_KEY = "opentelemetry_id" - -OT_RESOURCE_LABEL_TO_GCP = { - "gce_instance": { - "cloud.account.id": "project_id", - "host.id": "instance_id", - "cloud.zone": "zone", - } -} - - -# pylint is unable to resolve members of protobuf objects -# pylint: disable=no-member -class CloudMonitoringMetricsExporter(MetricsExporter): - """ Implementation of Metrics Exporter to Google Cloud Monitoring - - You can manually pass in project_id and client, or else the - Exporter will take that information from Application Default - Credentials. - - Args: - project_id: project id of your Google Cloud project. - client: Client to upload metrics to Google Cloud Monitoring. - add_unique_identifier: Add an identifier to each exporter metric. This - must be used when there exist two (or more) exporters that may - export to the same metric name within WRITE_INTERVAL seconds of - each other. - """ - - def __init__( - self, project_id=None, client=None, add_unique_identifier=False - ): - self.client = client or MetricServiceClient() - if not project_id: - _, self.project_id = google.auth.default() - else: - self.project_id = project_id - self.project_name = self.client.project_path(self.project_id) - self._metric_descriptors = {} - self._last_updated = {} - self.unique_identifier = None - if add_unique_identifier: - self.unique_identifier = "{:08x}".format( - random.randint(0, 16 ** 8) - ) - - @staticmethod - def _get_monitored_resource( - resource: Resource, - ) -> Optional[MonitoredResource]: - """Add Google resource specific information (e.g. instance id, region). - - See - https://cloud.google.com/monitoring/custom-metrics/creating-metrics#custom-metric-resources - for supported types - Args: - series: ProtoBuf TimeSeries - """ - - if resource.labels.get("cloud.provider") != "gcp": - return None - resource_type = resource.labels["gcp.resource_type"] - if resource_type not in OT_RESOURCE_LABEL_TO_GCP: - return None - return MonitoredResource( - type=resource_type, - labels={ - gcp_label: str(resource.labels[ot_label]) - for ot_label, gcp_label in OT_RESOURCE_LABEL_TO_GCP[ - resource_type - ].items() - }, - ) - - def _batch_write(self, series: TimeSeries) -> None: - """ Cloud Monitoring allows writing up to 200 time series at once - - :param series: ProtoBuf TimeSeries - :return: - """ - write_ind = 0 - while write_ind < len(series): - self.client.create_time_series( - self.project_name, - series[write_ind : write_ind + MAX_BATCH_WRITE], - ) - write_ind += MAX_BATCH_WRITE - - def _get_metric_descriptor( - self, record: MetricRecord - ) -> Optional[MetricDescriptor]: - """ We can map Metric to MetricDescriptor using Metric.name or - MetricDescriptor.type. We create the MetricDescriptor if it doesn't - exist already and cache it. Note that recreating MetricDescriptors is - a no-op if it already exists. - - :param record: - :return: - """ - instrument = record.instrument - descriptor_type = "custom.googleapis.com/OpenTelemetry/{}".format( - instrument.name - ) - if descriptor_type in self._metric_descriptors: - return self._metric_descriptors[descriptor_type] - descriptor = { - "name": None, - "type": descriptor_type, - "display_name": instrument.name, - "description": instrument.description, - "labels": [], - } - for key, value in record.labels: - if isinstance(value, str): - descriptor["labels"].append( - LabelDescriptor(key=key, value_type="STRING") - ) - elif isinstance(value, bool): - descriptor["labels"].append( - LabelDescriptor(key=key, value_type="BOOL") - ) - elif isinstance(value, int): - descriptor["labels"].append( - LabelDescriptor(key=key, value_type="INT64") - ) - else: - logger.warning( - "Label value %s is not a string, bool or integer", value - ) - - if self.unique_identifier: - descriptor["labels"].append( - LabelDescriptor(key=UNIQUE_IDENTIFIER_KEY, value_type="STRING") - ) - - if isinstance(record.aggregator, SumAggregator): - descriptor["metric_kind"] = MetricDescriptor.MetricKind.GAUGE - else: - logger.warning( - "Unsupported aggregation type %s, ignoring it", - type(record.aggregator).__name__, - ) - return None - if instrument.value_type == int: - descriptor["value_type"] = MetricDescriptor.ValueType.INT64 - elif instrument.value_type == float: - descriptor["value_type"] = MetricDescriptor.ValueType.DOUBLE - proto_descriptor = MetricDescriptor(**descriptor) - try: - descriptor = self.client.create_metric_descriptor( - self.project_name, proto_descriptor - ) - # pylint: disable=broad-except - except Exception as ex: - logger.error( - "Failed to create metric descriptor %s", - proto_descriptor, - exc_info=ex, - ) - return None - self._metric_descriptors[descriptor_type] = descriptor - return descriptor - - def export( - self, metric_records: Sequence[MetricRecord] - ) -> "MetricsExportResult": - all_series = [] - for record in metric_records: - instrument = record.instrument - metric_descriptor = self._get_metric_descriptor(record) - if not metric_descriptor: - continue - series = TimeSeries( - resource=self._get_monitored_resource( - record.instrument.meter.resource - ) - ) - series.metric.type = metric_descriptor.type - for key, value in record.labels: - series.metric.labels[key] = str(value) - - if self.unique_identifier: - series.metric.labels[ - UNIQUE_IDENTIFIER_KEY - ] = self.unique_identifier - - point = series.points.add() - if instrument.value_type == int: - point.value.int64_value = record.aggregator.checkpoint - elif instrument.value_type == float: - point.value.double_value = record.aggregator.checkpoint - seconds, nanos = divmod( - record.aggregator.last_update_timestamp, 1e9 - ) - - # Cloud Monitoring API allows, for any combination of labels and - # metric name, one update per WRITE_INTERVAL seconds - updated_key = (metric_descriptor.type, record.labels) - last_updated_seconds = self._last_updated.get(updated_key, 0) - if seconds <= last_updated_seconds + WRITE_INTERVAL: - continue - self._last_updated[updated_key] = seconds - point.interval.end_time.seconds = int(seconds) - point.interval.end_time.nanos = int(nanos) - all_series.append(series) - try: - self._batch_write(all_series) - # pylint: disable=broad-except - except Exception as ex: - logger.error( - "Error while writing to Cloud Monitoring", exc_info=ex - ) - return MetricsExportResult.FAILURE - return MetricsExportResult.SUCCESS diff --git a/ext/opentelemetry-exporter-cloud-monitoring/src/opentelemetry/exporter/cloud_monitoring/version.py b/ext/opentelemetry-exporter-cloud-monitoring/src/opentelemetry/exporter/cloud_monitoring/version.py deleted file mode 100644 index 8d3a82b3e54..00000000000 --- a/ext/opentelemetry-exporter-cloud-monitoring/src/opentelemetry/exporter/cloud_monitoring/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 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.11.dev0" diff --git a/ext/opentelemetry-exporter-cloud-monitoring/tests/__init__.py b/ext/opentelemetry-exporter-cloud-monitoring/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/ext/opentelemetry-exporter-cloud-monitoring/tests/test_cloud_monitoring.py b/ext/opentelemetry-exporter-cloud-monitoring/tests/test_cloud_monitoring.py deleted file mode 100644 index bc695f6d1f6..00000000000 --- a/ext/opentelemetry-exporter-cloud-monitoring/tests/test_cloud_monitoring.py +++ /dev/null @@ -1,436 +0,0 @@ -# Copyright 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 unittest import mock - -from google.api.label_pb2 import LabelDescriptor -from google.api.metric_pb2 import MetricDescriptor -from google.api.monitored_resource_pb2 import MonitoredResource -from google.cloud.monitoring_v3.proto.metric_pb2 import TimeSeries - -from opentelemetry.exporter.cloud_monitoring import ( - MAX_BATCH_WRITE, - UNIQUE_IDENTIFIER_KEY, - WRITE_INTERVAL, - CloudMonitoringMetricsExporter, -) -from opentelemetry.sdk.metrics.export import MetricRecord -from opentelemetry.sdk.metrics.export.aggregate import SumAggregator -from opentelemetry.sdk.resources import Resource - - -class UnsupportedAggregator: - pass - - -class MockMeter: - def __init__(self, resource=Resource.create_empty()): - self.resource = resource - - -class MockMetric: - def __init__( - self, - name="name", - description="description", - value_type=int, - meter=None, - ): - self.name = name - self.description = description - self.value_type = value_type - self.meter = meter or MockMeter() - - -# pylint: disable=protected-access -# pylint can't deal with ProtoBuf object members -# pylint: disable=no-member - - -class TestCloudMonitoringMetricsExporter(unittest.TestCase): - def setUp(self): - self.client_patcher = mock.patch( - "opentelemetry.exporter.cloud_monitoring.MetricServiceClient" - ) - self.client_patcher.start() - self.project_id = "PROJECT" - self.project_name = "PROJECT_NAME" - - def tearDown(self): - self.client_patcher.stop() - - def test_constructor_default(self): - exporter = CloudMonitoringMetricsExporter(self.project_id) - self.assertEqual(exporter.project_id, self.project_id) - - def test_constructor_explicit(self): - client = mock.Mock() - exporter = CloudMonitoringMetricsExporter( - self.project_id, client=client - ) - - self.assertIs(exporter.client, client) - self.assertEqual(exporter.project_id, self.project_id) - - def test_batch_write(self): - client = mock.Mock() - exporter = CloudMonitoringMetricsExporter( - project_id=self.project_id, client=client - ) - exporter.project_name = self.project_name - exporter._batch_write(range(2 * MAX_BATCH_WRITE + 1)) - client.create_time_series.assert_has_calls( - [ - mock.call(self.project_name, range(MAX_BATCH_WRITE)), - mock.call( - self.project_name, - range(MAX_BATCH_WRITE, 2 * MAX_BATCH_WRITE), - ), - mock.call( - self.project_name, - range(2 * MAX_BATCH_WRITE, 2 * MAX_BATCH_WRITE + 1), - ), - ] - ) - - exporter._batch_write(range(MAX_BATCH_WRITE)) - client.create_time_series.assert_has_calls( - [mock.call(self.project_name, range(MAX_BATCH_WRITE))] - ) - - exporter._batch_write(range(MAX_BATCH_WRITE - 1)) - client.create_time_series.assert_has_calls( - [mock.call(self.project_name, range(MAX_BATCH_WRITE - 1))] - ) - - def test_get_metric_descriptor(self): - client = mock.Mock() - exporter = CloudMonitoringMetricsExporter( - project_id=self.project_id, client=client - ) - exporter.project_name = self.project_name - - self.assertIsNone( - exporter._get_metric_descriptor( - MetricRecord(MockMetric(), (), UnsupportedAggregator()) - ) - ) - - record = MetricRecord( - MockMetric(), (("label1", "value1"),), SumAggregator(), - ) - metric_descriptor = exporter._get_metric_descriptor(record) - client.create_metric_descriptor.assert_called_with( - self.project_name, - MetricDescriptor( - **{ - "name": None, - "type": "custom.googleapis.com/OpenTelemetry/name", - "display_name": "name", - "description": "description", - "labels": [ - LabelDescriptor(key="label1", value_type="STRING") - ], - "metric_kind": "GAUGE", - "value_type": "INT64", - } - ), - ) - - # Getting a cached metric descriptor shouldn't use another call - cached_metric_descriptor = exporter._get_metric_descriptor(record) - self.assertEqual(client.create_metric_descriptor.call_count, 1) - self.assertEqual(metric_descriptor, cached_metric_descriptor) - - # Drop labels with values that aren't string, int or bool - exporter._get_metric_descriptor( - MetricRecord( - MockMetric(name="name2", value_type=float), - ( - ("label1", "value1"), - ("label2", dict()), - ("label3", 3), - ("label4", False), - ), - SumAggregator(), - ) - ) - client.create_metric_descriptor.assert_called_with( - self.project_name, - MetricDescriptor( - **{ - "name": None, - "type": "custom.googleapis.com/OpenTelemetry/name2", - "display_name": "name2", - "description": "description", - "labels": [ - LabelDescriptor(key="label1", value_type="STRING"), - LabelDescriptor(key="label3", value_type="INT64"), - LabelDescriptor(key="label4", value_type="BOOL"), - ], - "metric_kind": "GAUGE", - "value_type": "DOUBLE", - } - ), - ) - - def test_export(self): - client = mock.Mock() - exporter = CloudMonitoringMetricsExporter( - project_id=self.project_id, client=client - ) - exporter.project_name = self.project_name - - exporter.export( - [ - MetricRecord( - MockMetric(), - (("label1", "value1"),), - UnsupportedAggregator(), - ) - ] - ) - client.create_time_series.assert_not_called() - - client.create_metric_descriptor.return_value = MetricDescriptor( - **{ - "name": None, - "type": "custom.googleapis.com/OpenTelemetry/name", - "display_name": "name", - "description": "description", - "labels": [ - LabelDescriptor(key="label1", value_type="STRING"), - LabelDescriptor(key="label2", value_type="INT64"), - ], - "metric_kind": "GAUGE", - "value_type": "DOUBLE", - } - ) - - resource = Resource( - labels={ - "cloud.account.id": 123, - "host.id": "host", - "cloud.zone": "US", - "cloud.provider": "gcp", - "extra_info": "extra", - "gcp.resource_type": "gce_instance", - "not_gcp_resource": "value", - } - ) - - sum_agg_one = SumAggregator() - sum_agg_one.checkpoint = 1 - sum_agg_one.last_update_timestamp = (WRITE_INTERVAL + 1) * 1e9 - exporter.export( - [ - MetricRecord( - MockMetric(meter=MockMeter(resource=resource)), - (("label1", "value1"), ("label2", 1),), - sum_agg_one, - ), - MetricRecord( - MockMetric(meter=MockMeter(resource=resource)), - (("label1", "value2"), ("label2", 2),), - sum_agg_one, - ), - ] - ) - expected_resource = MonitoredResource( - type="gce_instance", - labels={"project_id": "123", "instance_id": "host", "zone": "US"}, - ) - - series1 = TimeSeries(resource=expected_resource) - series1.metric.type = "custom.googleapis.com/OpenTelemetry/name" - series1.metric.labels["label1"] = "value1" - series1.metric.labels["label2"] = "1" - point = series1.points.add() - point.value.int64_value = 1 - point.interval.end_time.seconds = WRITE_INTERVAL + 1 - point.interval.end_time.nanos = 0 - - series2 = TimeSeries(resource=expected_resource) - series2.metric.type = "custom.googleapis.com/OpenTelemetry/name" - series2.metric.labels["label1"] = "value2" - series2.metric.labels["label2"] = "2" - point = series2.points.add() - point.value.int64_value = 1 - point.interval.end_time.seconds = WRITE_INTERVAL + 1 - point.interval.end_time.nanos = 0 - client.create_time_series.assert_has_calls( - [mock.call(self.project_name, [series1, series2])] - ) - - # Attempting to export too soon after another export with the exact - # same labels leads to it being dropped - - sum_agg_two = SumAggregator() - sum_agg_two.checkpoint = 1 - sum_agg_two.last_update_timestamp = (WRITE_INTERVAL + 2) * 1e9 - exporter.export( - [ - MetricRecord( - MockMetric(), - (("label1", "value1"), ("label2", 1),), - sum_agg_two, - ), - MetricRecord( - MockMetric(), - (("label1", "value2"), ("label2", 2),), - sum_agg_two, - ), - ] - ) - self.assertEqual(client.create_time_series.call_count, 1) - - # But exporting with different labels is fine - sum_agg_two.checkpoint = 2 - exporter.export( - [ - MetricRecord( - MockMetric(), - (("label1", "changed_label"), ("label2", 2),), - sum_agg_two, - ), - ] - ) - series3 = TimeSeries() - series3.metric.type = "custom.googleapis.com/OpenTelemetry/name" - series3.metric.labels["label1"] = "changed_label" - series3.metric.labels["label2"] = "2" - point = series3.points.add() - point.value.int64_value = 2 - point.interval.end_time.seconds = WRITE_INTERVAL + 2 - point.interval.end_time.nanos = 0 - client.create_time_series.assert_has_calls( - [ - mock.call(self.project_name, [series1, series2]), - mock.call(self.project_name, [series3]), - ] - ) - - def test_unique_identifier(self): - client = mock.Mock() - exporter1 = CloudMonitoringMetricsExporter( - project_id=self.project_id, - client=client, - add_unique_identifier=True, - ) - exporter2 = CloudMonitoringMetricsExporter( - project_id=self.project_id, - client=client, - add_unique_identifier=True, - ) - exporter1.project_name = self.project_name - exporter2.project_name = self.project_name - - client.create_metric_descriptor.return_value = MetricDescriptor( - **{ - "name": None, - "type": "custom.googleapis.com/OpenTelemetry/name", - "display_name": "name", - "description": "description", - "labels": [ - LabelDescriptor( - key=UNIQUE_IDENTIFIER_KEY, value_type="STRING" - ), - ], - "metric_kind": "GAUGE", - "value_type": "DOUBLE", - } - ) - - sum_agg_one = SumAggregator() - sum_agg_one.update(1) - metric_record = MetricRecord(MockMetric(), (), sum_agg_one,) - exporter1.export([metric_record]) - exporter2.export([metric_record]) - - ( - first_call, - second_call, - ) = client.create_metric_descriptor.call_args_list - self.assertEqual(first_call[0][1].labels[0].key, UNIQUE_IDENTIFIER_KEY) - self.assertEqual( - second_call[0][1].labels[0].key, UNIQUE_IDENTIFIER_KEY - ) - - first_call, second_call = client.create_time_series.call_args_list - self.assertNotEqual( - first_call[0][1][0].metric.labels[UNIQUE_IDENTIFIER_KEY], - second_call[0][1][0].metric.labels[UNIQUE_IDENTIFIER_KEY], - ) - - def test_extract_resources(self): - exporter = CloudMonitoringMetricsExporter(project_id=self.project_id) - - self.assertIsNone( - exporter._get_monitored_resource(Resource.create_empty()) - ) - resource = Resource( - labels={ - "cloud.account.id": 123, - "host.id": "host", - "cloud.zone": "US", - "cloud.provider": "gcp", - "extra_info": "extra", - "gcp.resource_type": "gce_instance", - "not_gcp_resource": "value", - } - ) - expected_extract = MonitoredResource( - type="gce_instance", - labels={"project_id": "123", "instance_id": "host", "zone": "US"}, - ) - self.assertEqual( - exporter._get_monitored_resource(resource), expected_extract - ) - - resource = Resource( - labels={ - "cloud.account.id": "123", - "host.id": "host", - "extra_info": "extra", - "not_gcp_resource": "value", - "gcp.resource_type": "gce_instance", - "cloud.provider": "gcp", - } - ) - # Should throw when passed a malformed GCP resource dict - self.assertRaises(KeyError, exporter._get_monitored_resource, resource) - - resource = Resource( - labels={ - "cloud.account.id": "123", - "host.id": "host", - "extra_info": "extra", - "not_gcp_resource": "value", - "gcp.resource_type": "unsupported_gcp_resource", - "cloud.provider": "gcp", - } - ) - self.assertIsNone(exporter._get_monitored_resource(resource)) - - resource = Resource( - labels={ - "cloud.account.id": "123", - "host.id": "host", - "extra_info": "extra", - "not_gcp_resource": "value", - "cloud.provider": "aws", - } - ) - self.assertIsNone(exporter._get_monitored_resource(resource)) diff --git a/ext/opentelemetry-exporter-cloud-trace/CHANGELOG.md b/ext/opentelemetry-exporter-cloud-trace/CHANGELOG.md deleted file mode 100644 index 22eac3e4bf9..00000000000 --- a/ext/opentelemetry-exporter-cloud-trace/CHANGELOG.md +++ /dev/null @@ -1,25 +0,0 @@ -# Changelog - -## Unreleased - -- Add support for resources and resource detector - ([#853](https://github.com/open-telemetry/opentelemetry-python/pull/853)) - -## Version 0.10b0 - -Released 2020-06-23 - -- Add g.co/agent label for Google internal metrics tracking - ([#833](https://github.com/open-telemetry/opentelemetry-python/pull/833)) -- Adding trouble-shooting tips - ([#827](https://github.com/open-telemetry/opentelemetry-python/pull/827)) -- Added Cloud Trace context propagation - ([#819](https://github.com/open-telemetry/opentelemetry-python/pull/819)) -- Added tests to tox coverage files - ([#804](https://github.com/open-telemetry/opentelemetry-python/pull/804)) - -## 0.9b0 - -Released 2020-06-10 - -- Initial release diff --git a/ext/opentelemetry-exporter-cloud-trace/LICENSE b/ext/opentelemetry-exporter-cloud-trace/LICENSE deleted file mode 100644 index 261eeb9e9f8..00000000000 --- a/ext/opentelemetry-exporter-cloud-trace/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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 [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. - 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/ext/opentelemetry-exporter-cloud-trace/MANIFEST.in b/ext/opentelemetry-exporter-cloud-trace/MANIFEST.in deleted file mode 100644 index aed3e33273b..00000000000 --- a/ext/opentelemetry-exporter-cloud-trace/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -graft src -graft tests -global-exclude *.pyc -global-exclude *.pyo -global-exclude __pycache__/* -include CHANGELOG.md -include MANIFEST.in -include README.rst -include LICENSE diff --git a/ext/opentelemetry-exporter-cloud-trace/README.rst b/ext/opentelemetry-exporter-cloud-trace/README.rst deleted file mode 100644 index 001f163007e..00000000000 --- a/ext/opentelemetry-exporter-cloud-trace/README.rst +++ /dev/null @@ -1,43 +0,0 @@ -OpenTelemetry Cloud Trace Exporters -=================================== - -This library provides classes for exporting trace data to Google Cloud Trace. - -Installation ------------- - -:: - - pip install opentelemetry-exporter-cloud-trace - -Usage ------ - -.. code:: python - - from opentelemetry import trace - from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter - from opentelemetry.sdk.trace import TracerProvider - from opentelemetry.sdk.trace.export import ( - SimpleExportSpanProcessor, - ) - - trace.set_tracer_provider(TracerProvider()) - - cloud_trace_exporter = CloudTraceSpanExporter( - project_id='my-gcloud-project', - ) - trace.get_tracer_provider().add_span_processor( - SimpleExportSpanProcessor(cloud_trace_exporter) - ) - tracer = trace.get_tracer(__name__) - with tracer.start_as_current_span('foo'): - print('Hello world!') - - - -References ----------- - -* `Cloud Trace `_ -* `OpenTelemetry Project `_ diff --git a/ext/opentelemetry-exporter-cloud-trace/setup.cfg b/ext/opentelemetry-exporter-cloud-trace/setup.cfg deleted file mode 100644 index 4437f40fdd3..00000000000 --- a/ext/opentelemetry-exporter-cloud-trace/setup.cfg +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright 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. -# -[metadata] -name = opentelemetry-exporter-cloud-trace -description = Cloud Trace integration for OpenTelemetry -long_description = file: README.rst -long_description_content_type = text/x-rst -author = OpenTelemetry Authors -author_email = cncf-opentelemetry-contributors@lists.cncf.io -url = https://github.com/open-telemetry/opentelemetry-python/ext/opentelemetry-exporter-cloud-trace -platforms = any -license = Apache-2.0 -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.4 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - -[options] -python_requires = >=3.4 -package_dir= - =src -packages=find_namespace: -install_requires = - requests - opentelemetry-api - opentelemetry-sdk - google-cloud-trace - -[options.packages.find] -where = src - -[options.extras_require] -test = diff --git a/ext/opentelemetry-exporter-cloud-trace/setup.py b/ext/opentelemetry-exporter-cloud-trace/setup.py deleted file mode 100644 index 332cf41d01c..00000000000 --- a/ext/opentelemetry-exporter-cloud-trace/setup.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 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 os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, "src", "opentelemetry", "exporter", "cloud_trace", "version.py" -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/ext/opentelemetry-exporter-cloud-trace/src/opentelemetry/exporter/cloud_trace/__init__.py b/ext/opentelemetry-exporter-cloud-trace/src/opentelemetry/exporter/cloud_trace/__init__.py deleted file mode 100644 index ea36d227275..00000000000 --- a/ext/opentelemetry-exporter-cloud-trace/src/opentelemetry/exporter/cloud_trace/__init__.py +++ /dev/null @@ -1,382 +0,0 @@ -# Copyright 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. - -"""Cloud Trace Span Exporter for OpenTelemetry. Uses Cloud Trace Client's REST -API to export traces and spans for viewing in Cloud Trace. - -Usage ------ - -.. code-block:: python - - from opentelemetry import trace - from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter - from opentelemetry.sdk.trace import TracerProvider - from opentelemetry.sdk.trace.export import SimpleExportSpanProcessor - - trace.set_tracer_provider(TracerProvider()) - - cloud_trace_exporter = CloudTraceSpanExporter() - trace.get_tracer_provider().add_span_processor( - SimpleExportSpanProcessor(cloud_trace_exporter) - ) - tracer = trace.get_tracer(__name__) - with tracer.start_as_current_span("foo"): - print("Hello world!") - - -API ---- -""" - -import logging -from typing import Any, Dict, List, Optional, Sequence, Tuple - -import google.auth -import pkg_resources -from google.cloud.trace_v2 import TraceServiceClient -from google.cloud.trace_v2.proto.trace_pb2 import AttributeValue -from google.cloud.trace_v2.proto.trace_pb2 import Span as ProtoSpan -from google.cloud.trace_v2.proto.trace_pb2 import TruncatableString -from google.rpc.status_pb2 import Status - -import opentelemetry.trace as trace_api -from opentelemetry.exporter.cloud_trace.version import ( - __version__ as cloud_trace_version, -) -from opentelemetry.sdk.resources import Resource -from opentelemetry.sdk.trace import Event -from opentelemetry.sdk.trace.export import Span, SpanExporter, SpanExportResult -from opentelemetry.sdk.util import BoundedDict -from opentelemetry.trace.span import ( - get_hexadecimal_span_id, - get_hexadecimal_trace_id, -) -from opentelemetry.util import types - -logger = logging.getLogger(__name__) - -MAX_NUM_LINKS = 128 -MAX_NUM_EVENTS = 32 -MAX_EVENT_ATTRS = 4 -MAX_LINK_ATTRS = 32 -MAX_SPAN_ATTRS = 32 - - -class CloudTraceSpanExporter(SpanExporter): - """Cloud Trace span exporter for OpenTelemetry. - - Args: - project_id: ID of the cloud project that will receive the traces. - client: Cloud Trace client. If not given, will be taken from gcloud - default credentials - """ - - def __init__( - self, project_id=None, client=None, - ): - self.client = client or TraceServiceClient() - if not project_id: - _, self.project_id = google.auth.default() - else: - self.project_id = project_id - - def export(self, spans: Sequence[Span]) -> SpanExportResult: - """Export the spans to Cloud Trace. - - See: https://cloud.google.com/trace/docs/reference/v2/rest/v2/projects.traces/batchWrite - - Args: - spans: Tuple of spans to export - """ - cloud_trace_spans = [] - for span in self._translate_to_cloud_trace(spans): - try: - cloud_trace_spans.append(self.client.create_span(**span)) - # pylint: disable=broad-except - except Exception as ex: - logger.error("Error when creating span %s", span, exc_info=ex) - try: - self.client.batch_write_spans( - "projects/{}".format(self.project_id), cloud_trace_spans, - ) - # pylint: disable=broad-except - except Exception as ex: - logger.error("Error while writing to Cloud Trace", exc_info=ex) - return SpanExportResult.FAILURE - - return SpanExportResult.SUCCESS - - def _translate_to_cloud_trace( - self, spans: Sequence[Span] - ) -> List[Dict[str, Any]]: - """Translate the spans to Cloud Trace format. - - Args: - spans: Tuple of spans to convert - """ - - cloud_trace_spans = [] - - for span in spans: - ctx = span.get_context() - trace_id = get_hexadecimal_trace_id(ctx.trace_id) - span_id = get_hexadecimal_span_id(ctx.span_id) - span_name = "projects/{}/traces/{}/spans/{}".format( - self.project_id, trace_id, span_id - ) - - parent_id = None - if span.parent: - parent_id = get_hexadecimal_span_id(span.parent.span_id) - - start_time = _get_time_from_ns(span.start_time) - end_time = _get_time_from_ns(span.end_time) - - if len(span.attributes) > MAX_SPAN_ATTRS: - logger.warning( - "Span has more then %s attributes, some will be truncated", - MAX_SPAN_ATTRS, - ) - - # Span does not support a MonitoredResource object. We put the - # information into labels instead. - resources_and_attrs = _extract_resources(span.resource) - resources_and_attrs.update(span.attributes) - - cloud_trace_spans.append( - { - "name": span_name, - "span_id": span_id, - "display_name": _get_truncatable_str_object( - span.name, 128 - ), - "start_time": start_time, - "end_time": end_time, - "parent_span_id": parent_id, - "attributes": _extract_attributes( - resources_and_attrs, - MAX_SPAN_ATTRS, - add_agent_attr=True, - ), - "links": _extract_links(span.links), - "status": _extract_status(span.status), - "time_events": _extract_events(span.events), - } - ) - # TODO: Leverage more of the Cloud Trace API, e.g. - # same_process_as_parent_span and child_span_count - - return cloud_trace_spans - - def shutdown(self): - pass - - -def _get_time_from_ns(nanoseconds: int) -> Dict: - """Given epoch nanoseconds, split into epoch milliseconds and remaining - nanoseconds""" - if not nanoseconds: - return None - seconds, nanos = divmod(nanoseconds, 1e9) - return {"seconds": int(seconds), "nanos": int(nanos)} - - -def _get_truncatable_str_object(str_to_convert: str, max_length: int): - """Truncate the string if it exceeds the length limit and record the - truncated bytes count.""" - truncated, truncated_byte_count = _truncate_str(str_to_convert, max_length) - - return TruncatableString( - value=truncated, truncated_byte_count=truncated_byte_count - ) - - -def _truncate_str(str_to_check: str, limit: int) -> Tuple[str, int]: - """Check the length of a string. If exceeds limit, then truncate it.""" - encoded = str_to_check.encode("utf-8") - truncated_str = encoded[:limit].decode("utf-8", errors="ignore") - return truncated_str, len(encoded) - len(truncated_str.encode("utf-8")) - - -def _extract_status(status: trace_api.Status) -> Optional[Status]: - """Convert a Status object to protobuf object.""" - if not status: - return None - status_dict = {"details": None, "code": status.canonical_code.value} - - if status.description is not None: - status_dict["message"] = status.description - - return Status(**status_dict) - - -def _extract_links(links: Sequence[trace_api.Link]) -> ProtoSpan.Links: - """Convert span.links""" - if not links: - return None - extracted_links = [] - dropped_links = 0 - if len(links) > MAX_NUM_LINKS: - logger.warning( - "Exporting more then %s links, some will be truncated", - MAX_NUM_LINKS, - ) - dropped_links = len(links) - MAX_NUM_LINKS - links = links[:MAX_NUM_LINKS] - for link in links: - if len(link.attributes) > MAX_LINK_ATTRS: - logger.warning( - "Link has more then %s attributes, some will be truncated", - MAX_LINK_ATTRS, - ) - trace_id = get_hexadecimal_trace_id(link.context.trace_id) - span_id = get_hexadecimal_span_id(link.context.span_id) - extracted_links.append( - { - "trace_id": trace_id, - "span_id": span_id, - "type": "TYPE_UNSPECIFIED", - "attributes": _extract_attributes( - link.attributes, MAX_LINK_ATTRS - ), - } - ) - return ProtoSpan.Links( - link=extracted_links, dropped_links_count=dropped_links - ) - - -def _extract_events(events: Sequence[Event]) -> ProtoSpan.TimeEvents: - """Convert span.events to dict.""" - if not events: - return None - logs = [] - dropped_annontations = 0 - if len(events) > MAX_NUM_EVENTS: - logger.warning( - "Exporting more then %s annotations, some will be truncated", - MAX_NUM_EVENTS, - ) - dropped_annontations = len(events) - MAX_NUM_EVENTS - events = events[:MAX_NUM_EVENTS] - for event in events: - if len(event.attributes) > MAX_EVENT_ATTRS: - logger.warning( - "Event %s has more then %s attributes, some will be truncated", - event.name, - MAX_EVENT_ATTRS, - ) - logs.append( - { - "time": _get_time_from_ns(event.timestamp), - "annotation": { - "description": _get_truncatable_str_object( - event.name, 256 - ), - "attributes": _extract_attributes( - event.attributes, MAX_EVENT_ATTRS - ), - }, - } - ) - return ProtoSpan.TimeEvents( - time_event=logs, - dropped_annotations_count=dropped_annontations, - dropped_message_events_count=0, - ) - - -def _strip_characters(ot_version): - return "".join(filter(lambda x: x.isdigit() or x == ".", ot_version)) - - -OT_RESOURCE_LABEL_TO_GCP = { - "gce_instance": { - "cloud.account.id": "project_id", - "host.id": "instance_id", - "cloud.zone": "zone", - } -} - - -def _extract_resources(resource: Resource) -> Dict[str, str]: - if resource.labels.get("cloud.provider") != "gcp": - return {} - resource_type = resource.labels["gcp.resource_type"] - if resource_type not in OT_RESOURCE_LABEL_TO_GCP: - return {} - return { - "g.co/r/{}/{}".format(resource_type, gcp_resource_key,): str( - resource.labels[ot_resource_key] - ) - for ot_resource_key, gcp_resource_key in OT_RESOURCE_LABEL_TO_GCP[ - resource_type - ].items() - } - - -def _extract_attributes( - attrs: types.Attributes, - num_attrs_limit: int, - add_agent_attr: bool = False, -) -> ProtoSpan.Attributes: - """Convert span.attributes to dict.""" - attributes_dict = BoundedDict(num_attrs_limit) - invalid_value_dropped_count = 0 - for key, value in attrs.items(): - key = _truncate_str(key, 128)[0] - value = _format_attribute_value(value) - - if value: - attributes_dict[key] = value - else: - invalid_value_dropped_count += 1 - if add_agent_attr: - attributes_dict["g.co/agent"] = _format_attribute_value( - "opentelemetry-python {}; google-cloud-trace-exporter {}".format( - _strip_characters( - pkg_resources.get_distribution("opentelemetry-sdk").version - ), - _strip_characters(cloud_trace_version), - ) - ) - return ProtoSpan.Attributes( - attribute_map=attributes_dict, - dropped_attributes_count=attributes_dict.dropped - + invalid_value_dropped_count, - ) - - -def _format_attribute_value(value: types.AttributeValue) -> AttributeValue: - if isinstance(value, bool): - value_type = "bool_value" - elif isinstance(value, int): - value_type = "int_value" - elif isinstance(value, str): - value_type = "string_value" - value = _get_truncatable_str_object(value, 256) - elif isinstance(value, float): - value_type = "string_value" - value = _get_truncatable_str_object("{:0.4f}".format(value), 256) - else: - logger.warning( - "ignoring attribute value %s of type %s. Values type must be one " - "of bool, int, string or float", - value, - type(value), - ) - return None - - return AttributeValue(**{value_type: value}) diff --git a/ext/opentelemetry-exporter-cloud-trace/src/opentelemetry/exporter/cloud_trace/cloud_trace_propagator.py b/ext/opentelemetry-exporter-cloud-trace/src/opentelemetry/exporter/cloud_trace/cloud_trace_propagator.py deleted file mode 100644 index 3f00c66d690..00000000000 --- a/ext/opentelemetry-exporter-cloud-trace/src/opentelemetry/exporter/cloud_trace/cloud_trace_propagator.py +++ /dev/null @@ -1,89 +0,0 @@ -# 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 re -import typing - -import opentelemetry.trace as trace -from opentelemetry.context.context import Context -from opentelemetry.trace.propagation import httptextformat -from opentelemetry.trace.span import ( - SpanContext, - TraceFlags, - get_hexadecimal_trace_id, -) - -_TRACE_CONTEXT_HEADER_NAME = "X-Cloud-Trace-Context" -_TRACE_CONTEXT_HEADER_FORMAT = r"(?P[0-9a-f]{32})\/(?P[\d]{1,20});o=(?P\d+)" -_TRACE_CONTEXT_HEADER_RE = re.compile(_TRACE_CONTEXT_HEADER_FORMAT) - - -class CloudTraceFormatPropagator(httptextformat.HTTPTextFormat): - """This class is for injecting into a carrier the SpanContext in Google - Cloud format, or extracting the SpanContext from a carrier using Google - Cloud format. - """ - - def extract( - self, - get_from_carrier: httptextformat.Getter[ - httptextformat.HTTPTextFormatT - ], - carrier: httptextformat.HTTPTextFormatT, - context: typing.Optional[Context] = None, - ) -> Context: - header = get_from_carrier(carrier, _TRACE_CONTEXT_HEADER_NAME) - - if not header: - return trace.set_span_in_context(trace.INVALID_SPAN, context) - - match = re.fullmatch(_TRACE_CONTEXT_HEADER_RE, header[0]) - if match is None: - return trace.set_span_in_context(trace.INVALID_SPAN, context) - - trace_id = match.group("trace_id") - span_id = match.group("span_id") - trace_options = match.group("trace_flags") - - if trace_id == "0" * 32 or int(span_id) == 0: - return trace.set_span_in_context(trace.INVALID_SPAN, context) - - span_context = SpanContext( - trace_id=int(trace_id, 16), - span_id=int(span_id), - is_remote=True, - trace_flags=TraceFlags(trace_options), - ) - return trace.set_span_in_context( - trace.DefaultSpan(span_context), context - ) - - def inject( - self, - set_in_carrier: httptextformat.Setter[httptextformat.HTTPTextFormatT], - carrier: httptextformat.HTTPTextFormatT, - context: typing.Optional[Context] = None, - ) -> None: - span = trace.get_current_span(context) - span_context = span.get_context() - if span_context == trace.INVALID_SPAN_CONTEXT: - return - - header = "{}/{};o={}".format( - get_hexadecimal_trace_id(span_context.trace_id), - span_context.span_id, - int(span_context.trace_flags.sampled), - ) - set_in_carrier(carrier, _TRACE_CONTEXT_HEADER_NAME, header) diff --git a/ext/opentelemetry-exporter-cloud-trace/src/opentelemetry/exporter/cloud_trace/version.py b/ext/opentelemetry-exporter-cloud-trace/src/opentelemetry/exporter/cloud_trace/version.py deleted file mode 100644 index 8d3a82b3e54..00000000000 --- a/ext/opentelemetry-exporter-cloud-trace/src/opentelemetry/exporter/cloud_trace/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 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.11.dev0" diff --git a/ext/opentelemetry-exporter-cloud-trace/src/opentelemetry/tools/resource_detector.py b/ext/opentelemetry-exporter-cloud-trace/src/opentelemetry/tools/resource_detector.py deleted file mode 100644 index 5dbf7627f81..00000000000 --- a/ext/opentelemetry-exporter-cloud-trace/src/opentelemetry/tools/resource_detector.py +++ /dev/null @@ -1,47 +0,0 @@ -import requests - -from opentelemetry.context import attach, detach, set_value -from opentelemetry.sdk.resources import Resource, ResourceDetector - -_GCP_METADATA_URL = ( - "http://metadata.google.internal/computeMetadata/v1/?recursive=true" -) -_GCP_METADATA_URL_HEADER = {"Metadata-Flavor": "Google"} - - -def get_gce_resources(): - """ Resource finder for common GCE attributes - - See: https://cloud.google.com/compute/docs/storing-retrieving-metadata - """ - token = attach(set_value("suppress_instrumentation", True)) - all_metadata = requests.get( - _GCP_METADATA_URL, headers=_GCP_METADATA_URL_HEADER - ).json() - detach(token) - gce_resources = { - "host.id": all_metadata["instance"]["id"], - "cloud.account.id": all_metadata["project"]["projectId"], - "cloud.zone": all_metadata["instance"]["zone"].split("/")[-1], - "cloud.provider": "gcp", - "gcp.resource_type": "gce_instance", - } - return gce_resources - - -_RESOURCE_FINDERS = [get_gce_resources] - - -class GoogleCloudResourceDetector(ResourceDetector): - def __init__(self, raise_on_error=False): - super().__init__(raise_on_error) - self.cached = False - self.gcp_resources = {} - - def detect(self) -> "Resource": - if not self.cached: - self.cached = True - for resource_finder in _RESOURCE_FINDERS: - found_resources = resource_finder() - self.gcp_resources.update(found_resources) - return Resource(self.gcp_resources) diff --git a/ext/opentelemetry-exporter-cloud-trace/tests/__init__.py b/ext/opentelemetry-exporter-cloud-trace/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/ext/opentelemetry-exporter-cloud-trace/tests/test_cloud_trace_exporter.py b/ext/opentelemetry-exporter-cloud-trace/tests/test_cloud_trace_exporter.py deleted file mode 100644 index 7400207ff46..00000000000 --- a/ext/opentelemetry-exporter-cloud-trace/tests/test_cloud_trace_exporter.py +++ /dev/null @@ -1,508 +0,0 @@ -# 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 unittest import mock - -import pkg_resources -from google.cloud.trace_v2.proto.trace_pb2 import AttributeValue -from google.cloud.trace_v2.proto.trace_pb2 import Span as ProtoSpan -from google.cloud.trace_v2.proto.trace_pb2 import TruncatableString -from google.rpc.status_pb2 import Status - -from opentelemetry.exporter.cloud_trace import ( - MAX_EVENT_ATTRS, - MAX_LINK_ATTRS, - MAX_NUM_EVENTS, - MAX_NUM_LINKS, - CloudTraceSpanExporter, - _extract_attributes, - _extract_events, - _extract_links, - _extract_resources, - _extract_status, - _format_attribute_value, - _strip_characters, - _truncate_str, -) -from opentelemetry.exporter.cloud_trace.version import ( - __version__ as cloud_trace_version, -) -from opentelemetry.sdk.resources import Resource -from opentelemetry.sdk.trace import Event -from opentelemetry.sdk.trace.export import Span -from opentelemetry.trace import Link, SpanContext, SpanKind -from opentelemetry.trace.status import Status as SpanStatus -from opentelemetry.trace.status import StatusCanonicalCode - - -class TestCloudTraceSpanExporter(unittest.TestCase): - def setUp(self): - self.client_patcher = mock.patch( - "opentelemetry.exporter.cloud_trace.TraceServiceClient" - ) - self.client_patcher.start() - self.project_id = "PROJECT" - self.attributes_variety_pack = { - "str_key": "str_value", - "bool_key": False, - "double_key": 1.421, - "int_key": 123, - } - self.extracted_attributes_variety_pack = ProtoSpan.Attributes( - attribute_map={ - "str_key": AttributeValue( - string_value=TruncatableString( - value="str_value", truncated_byte_count=0 - ) - ), - "bool_key": AttributeValue(bool_value=False), - "double_key": AttributeValue( - string_value=TruncatableString( - value="1.4210", truncated_byte_count=0 - ) - ), - "int_key": AttributeValue(int_value=123), - } - ) - - def tearDown(self): - self.client_patcher.stop() - - def test_constructor_default(self): - exporter = CloudTraceSpanExporter(self.project_id) - self.assertEqual(exporter.project_id, self.project_id) - - def test_constructor_explicit(self): - client = mock.Mock() - exporter = CloudTraceSpanExporter(self.project_id, client=client) - - self.assertIs(exporter.client, client) - self.assertEqual(exporter.project_id, self.project_id) - - def test_export(self): - trace_id = "6e0c63257de34c92bf9efcd03927272e" - span_id = "95bb5edabd45950f" - resource_info = Resource( - { - "cloud.account.id": 123, - "host.id": "host", - "cloud.zone": "US", - "cloud.provider": "gcp", - "gcp.resource_type": "gce_instance", - } - ) - span_datas = [ - Span( - name="span_name", - context=SpanContext( - trace_id=int(trace_id, 16), - span_id=int(span_id, 16), - is_remote=False, - ), - parent=None, - kind=SpanKind.INTERNAL, - resource=resource_info, - attributes={"attr_key": "attr_value"}, - ) - ] - - cloud_trace_spans = { - "name": "projects/{}/traces/{}/spans/{}".format( - self.project_id, trace_id, span_id - ), - "span_id": span_id, - "parent_span_id": None, - "display_name": TruncatableString( - value="span_name", truncated_byte_count=0 - ), - "attributes": ProtoSpan.Attributes( - attribute_map={ - "g.co/r/gce_instance/zone": _format_attribute_value("US"), - "g.co/r/gce_instance/instance_id": _format_attribute_value( - "host" - ), - "g.co/r/gce_instance/project_id": _format_attribute_value( - "123" - ), - "g.co/agent": _format_attribute_value( - "opentelemetry-python {}; google-cloud-trace-exporter {}".format( - _strip_characters( - pkg_resources.get_distribution( - "opentelemetry-sdk" - ).version - ), - _strip_characters(cloud_trace_version), - ) - ), - "attr_key": _format_attribute_value("attr_value"), - } - ), - "links": None, - "status": None, - "time_events": None, - "start_time": None, - "end_time": None, - } - - client = mock.Mock() - - exporter = CloudTraceSpanExporter(self.project_id, client=client) - - exporter.export(span_datas) - - client.create_span.assert_called_with(**cloud_trace_spans) - self.assertTrue(client.create_span.called) - - def test_extract_status(self): - self.assertIsNone(_extract_status(None)) - self.assertEqual( - _extract_status(SpanStatus(canonical_code=StatusCanonicalCode.OK)), - Status(details=None, code=0), - ) - self.assertEqual( - _extract_status( - SpanStatus( - canonical_code=StatusCanonicalCode.UNKNOWN, - description="error_desc", - ) - ), - Status(details=None, code=2, message="error_desc"), - ) - - def test_extract_attributes(self): - self.assertEqual( - _extract_attributes({}, 4), ProtoSpan.Attributes(attribute_map={}) - ) - self.assertEqual( - _extract_attributes(self.attributes_variety_pack, 4), - self.extracted_attributes_variety_pack, - ) - # Test ignoring attributes with illegal value type - self.assertEqual( - _extract_attributes({"illegal_attribute_value": dict()}, 4), - ProtoSpan.Attributes(attribute_map={}, dropped_attributes_count=1), - ) - - too_many_attrs = {} - for attr_key in range(5): - too_many_attrs[str(attr_key)] = 0 - proto_attrs = _extract_attributes(too_many_attrs, 4) - self.assertEqual(proto_attrs.dropped_attributes_count, 1) - - def test_extract_events(self): - self.assertIsNone(_extract_events([])) - time_in_ns1 = 1589919268850900051 - time_in_ms_and_ns1 = {"seconds": 1589919268, "nanos": 850899968} - time_in_ns2 = 1589919438550020326 - time_in_ms_and_ns2 = {"seconds": 1589919438, "nanos": 550020352} - event1 = Event( - name="event1", - attributes=self.attributes_variety_pack, - timestamp=time_in_ns1, - ) - event2 = Event( - name="event2", - attributes={"illegal_attr_value": dict()}, - timestamp=time_in_ns2, - ) - self.assertEqual( - _extract_events([event1, event2]), - ProtoSpan.TimeEvents( - time_event=[ - { - "time": time_in_ms_and_ns1, - "annotation": { - "description": TruncatableString( - value="event1", truncated_byte_count=0 - ), - "attributes": self.extracted_attributes_variety_pack, - }, - }, - { - "time": time_in_ms_and_ns2, - "annotation": { - "description": TruncatableString( - value="event2", truncated_byte_count=0 - ), - "attributes": ProtoSpan.Attributes( - attribute_map={}, dropped_attributes_count=1 - ), - }, - }, - ] - ), - ) - - def test_extract_links(self): - self.assertIsNone(_extract_links([])) - trace_id = "6e0c63257de34c92bf9efcd03927272e" - span_id1 = "95bb5edabd45950f" - span_id2 = "b6b86ad2915c9ddc" - link1 = Link( - context=SpanContext( - trace_id=int(trace_id, 16), - span_id=int(span_id1, 16), - is_remote=False, - ), - attributes={}, - ) - link2 = Link( - context=SpanContext( - trace_id=int(trace_id, 16), - span_id=int(span_id1, 16), - is_remote=False, - ), - attributes=self.attributes_variety_pack, - ) - link3 = Link( - context=SpanContext( - trace_id=int(trace_id, 16), - span_id=int(span_id2, 16), - is_remote=False, - ), - attributes={"illegal_attr_value": dict(), "int_attr_value": 123}, - ) - self.assertEqual( - _extract_links([link1, link2, link3]), - ProtoSpan.Links( - link=[ - { - "trace_id": trace_id, - "span_id": span_id1, - "type": "TYPE_UNSPECIFIED", - "attributes": ProtoSpan.Attributes(attribute_map={}), - }, - { - "trace_id": trace_id, - "span_id": span_id1, - "type": "TYPE_UNSPECIFIED", - "attributes": self.extracted_attributes_variety_pack, - }, - { - "trace_id": trace_id, - "span_id": span_id2, - "type": "TYPE_UNSPECIFIED", - "attributes": { - "attribute_map": { - "int_attr_value": AttributeValue(int_value=123) - }, - "dropped_attributes_count": 1, - }, - }, - ] - ), - ) - - def test_extract_empty_resources(self): - self.assertEqual(_extract_resources(Resource.create_empty()), {}) - - def test_extract_well_formed_resources(self): - resource = Resource( - labels={ - "cloud.account.id": 123, - "host.id": "host", - "cloud.zone": "US", - "cloud.provider": "gcp", - "extra_info": "extra", - "gcp.resource_type": "gce_instance", - "not_gcp_resource": "value", - } - ) - expected_extract = { - "g.co/r/gce_instance/project_id": "123", - "g.co/r/gce_instance/instance_id": "host", - "g.co/r/gce_instance/zone": "US", - } - self.assertEqual(_extract_resources(resource), expected_extract) - - def test_extract_malformed_resources(self): - # This resource doesn't have all the fields required for a gce_instance - # Specifically its missing "host.id", "cloud.zone", "cloud.account.id" - resource = Resource( - labels={ - "gcp.resource_type": "gce_instance", - "cloud.provider": "gcp", - } - ) - # Should throw when passed a malformed GCP resource dict - self.assertRaises(KeyError, _extract_resources, resource) - - def test_extract_unsupported_gcp_resources(self): - resource = Resource( - labels={ - "cloud.account.id": "123", - "host.id": "host", - "extra_info": "extra", - "not_gcp_resource": "value", - "gcp.resource_type": "unsupported_gcp_resource", - "cloud.provider": "gcp", - } - ) - self.assertEqual(_extract_resources(resource), {}) - - def test_extract_unsupported_provider_resources(self): - # Resources with currently unsupported providers will be ignored - resource = Resource( - labels={ - "cloud.account.id": "123", - "host.id": "host", - "extra_info": "extra", - "not_gcp_resource": "value", - "cloud.provider": "aws", - } - ) - self.assertEqual(_extract_resources(resource), {}) - - # pylint:disable=too-many-locals - def test_truncate(self): - """Cloud Trace API imposes limits on the length of many things, - e.g. strings, number of events, number of attributes. We truncate - these things before sending it to the API as an optimization. - """ - str_300 = "a" * 300 - str_256 = "a" * 256 - str_128 = "a" * 128 - self.assertEqual(_truncate_str("aaaa", 1), ("a", 3)) - self.assertEqual(_truncate_str("aaaa", 5), ("aaaa", 0)) - self.assertEqual(_truncate_str("aaaa", 4), ("aaaa", 0)) - self.assertEqual(_truncate_str("中文翻译", 4), ("中", 9)) - - self.assertEqual( - _format_attribute_value(str_300), - AttributeValue( - string_value=TruncatableString( - value=str_256, truncated_byte_count=300 - 256 - ) - ), - ) - - self.assertEqual( - _extract_attributes({str_300: str_300}, 4), - ProtoSpan.Attributes( - attribute_map={ - str_128: AttributeValue( - string_value=TruncatableString( - value=str_256, truncated_byte_count=300 - 256 - ) - ) - } - ), - ) - - time_in_ns1 = 1589919268850900051 - time_in_ms_and_ns1 = {"seconds": 1589919268, "nanos": 850899968} - event1 = Event(name=str_300, attributes={}, timestamp=time_in_ns1) - self.assertEqual( - _extract_events([event1]), - ProtoSpan.TimeEvents( - time_event=[ - { - "time": time_in_ms_and_ns1, - "annotation": { - "description": TruncatableString( - value=str_256, truncated_byte_count=300 - 256 - ), - "attributes": {}, - }, - }, - ] - ), - ) - - trace_id = "6e0c63257de34c92bf9efcd03927272e" - span_id = "95bb5edabd45950f" - link = Link( - context=SpanContext( - trace_id=int(trace_id, 16), - span_id=int(span_id, 16), - is_remote=False, - ), - attributes={}, - ) - too_many_links = [link] * (MAX_NUM_LINKS + 1) - self.assertEqual( - _extract_links(too_many_links), - ProtoSpan.Links( - link=[ - { - "trace_id": trace_id, - "span_id": span_id, - "type": "TYPE_UNSPECIFIED", - "attributes": {}, - } - ] - * MAX_NUM_LINKS, - dropped_links_count=len(too_many_links) - MAX_NUM_LINKS, - ), - ) - - link_attrs = {} - for attr_key in range(MAX_LINK_ATTRS + 1): - link_attrs[str(attr_key)] = 0 - attr_link = Link( - context=SpanContext( - trace_id=int(trace_id, 16), - span_id=int(span_id, 16), - is_remote=False, - ), - attributes=link_attrs, - ) - - proto_link = _extract_links([attr_link]) - self.assertEqual( - len(proto_link.link[0].attributes.attribute_map), MAX_LINK_ATTRS - ) - - too_many_events = [event1] * (MAX_NUM_EVENTS + 1) - self.assertEqual( - _extract_events(too_many_events), - ProtoSpan.TimeEvents( - time_event=[ - { - "time": time_in_ms_and_ns1, - "annotation": { - "description": TruncatableString( - value=str_256, truncated_byte_count=300 - 256 - ), - "attributes": {}, - }, - }, - ] - * MAX_NUM_EVENTS, - dropped_annotations_count=len(too_many_events) - - MAX_NUM_EVENTS, - ), - ) - - time_in_ns1 = 1589919268850900051 - event_attrs = {} - for attr_key in range(MAX_EVENT_ATTRS + 1): - event_attrs[str(attr_key)] = 0 - proto_events = _extract_events( - [Event(name="a", attributes=event_attrs, timestamp=time_in_ns1)] - ) - self.assertEqual( - len( - proto_events.time_event[0].annotation.attributes.attribute_map - ), - MAX_EVENT_ATTRS, - ) - - def test_strip_characters(self): - self.assertEqual("0.10.0", _strip_characters("0.10.0b")) - self.assertEqual("1.20.5", _strip_characters("1.20.5")) - self.assertEqual("3.1.0", _strip_characters("3.1.0beta")) - self.assertEqual("4.2.0", _strip_characters("4b.2rc.0a")) - self.assertEqual("6.20.15", _strip_characters("b6.20.15")) diff --git a/ext/opentelemetry-exporter-cloud-trace/tests/test_cloud_trace_propagator.py b/ext/opentelemetry-exporter-cloud-trace/tests/test_cloud_trace_propagator.py deleted file mode 100644 index a94127b7b7d..00000000000 --- a/ext/opentelemetry-exporter-cloud-trace/tests/test_cloud_trace_propagator.py +++ /dev/null @@ -1,238 +0,0 @@ -# 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 typing -import unittest - -import opentelemetry.trace as trace -from opentelemetry.context import get_current -from opentelemetry.exporter.cloud_trace.cloud_trace_propagator import ( - _TRACE_CONTEXT_HEADER_NAME, - CloudTraceFormatPropagator, -) -from opentelemetry.trace.span import ( - INVALID_SPAN_ID, - INVALID_TRACE_ID, - SpanContext, - TraceFlags, - get_hexadecimal_trace_id, -) - - -def get_dict_value(dict_object: typing.Dict[str, str], key: str) -> str: - return dict_object.get(key, "") - - -class TestCloudTraceFormatPropagator(unittest.TestCase): - def setUp(self): - self.propagator = CloudTraceFormatPropagator() - self.valid_trace_id = 281017822499060589596062859815111849546 - self.valid_span_id = 17725314949316355921 - self.too_long_id = 111111111111111111111111111111111111111111111 - - def _extract(self, header_value): - """Test helper""" - header = {_TRACE_CONTEXT_HEADER_NAME: [header_value]} - new_context = self.propagator.extract(get_dict_value, header) - return trace.get_current_span(new_context).get_context() - - def _inject(self, span=None): - """Test helper""" - ctx = get_current() - if span is not None: - ctx = trace.set_span_in_context(span, ctx) - output = {} - self.propagator.inject(dict.__setitem__, output, context=ctx) - return output.get(_TRACE_CONTEXT_HEADER_NAME) - - def test_no_context_header(self): - header = {} - new_context = self.propagator.extract(get_dict_value, header) - self.assertEqual( - trace.get_current_span(new_context).get_context(), - trace.INVALID_SPAN.get_context(), - ) - - def test_empty_context_header(self): - header = "" - self.assertEqual( - self._extract(header), trace.INVALID_SPAN.get_context() - ) - - def test_valid_header(self): - header = "{}/{};o=1".format( - get_hexadecimal_trace_id(self.valid_trace_id), self.valid_span_id - ) - new_span_context = self._extract(header) - self.assertEqual(new_span_context.trace_id, self.valid_trace_id) - self.assertEqual(new_span_context.span_id, self.valid_span_id) - self.assertEqual(new_span_context.trace_flags, TraceFlags(1)) - self.assertTrue(new_span_context.is_remote) - - header = "{}/{};o=10".format( - get_hexadecimal_trace_id(self.valid_trace_id), self.valid_span_id - ) - new_span_context = self._extract(header) - self.assertEqual(new_span_context.trace_id, self.valid_trace_id) - self.assertEqual(new_span_context.span_id, self.valid_span_id) - self.assertEqual(new_span_context.trace_flags, TraceFlags(10)) - self.assertTrue(new_span_context.is_remote) - - header = "{}/{};o=0".format( - get_hexadecimal_trace_id(self.valid_trace_id), self.valid_span_id - ) - new_span_context = self._extract(header) - self.assertEqual(new_span_context.trace_id, self.valid_trace_id) - self.assertEqual(new_span_context.span_id, self.valid_span_id) - self.assertEqual(new_span_context.trace_flags, TraceFlags(0)) - self.assertTrue(new_span_context.is_remote) - - header = "{}/{};o=0".format( - get_hexadecimal_trace_id(self.valid_trace_id), 345 - ) - new_span_context = self._extract(header) - self.assertEqual(new_span_context.trace_id, self.valid_trace_id) - self.assertEqual(new_span_context.span_id, 345) - self.assertEqual(new_span_context.trace_flags, TraceFlags(0)) - self.assertTrue(new_span_context.is_remote) - - def test_invalid_header_format(self): - header = "invalid_header" - self.assertEqual( - self._extract(header), trace.INVALID_SPAN.get_context() - ) - - header = "{}/{};o=".format( - get_hexadecimal_trace_id(self.valid_trace_id), self.valid_span_id - ) - self.assertEqual( - self._extract(header), trace.INVALID_SPAN.get_context() - ) - - header = "extra_chars/{}/{};o=1".format( - get_hexadecimal_trace_id(self.valid_trace_id), self.valid_span_id - ) - self.assertEqual( - self._extract(header), trace.INVALID_SPAN.get_context() - ) - - header = "{}/{}extra_chars;o=1".format( - get_hexadecimal_trace_id(self.valid_trace_id), self.valid_span_id - ) - self.assertEqual( - self._extract(header), trace.INVALID_SPAN.get_context() - ) - - header = "{}/{};o=1extra_chars".format( - get_hexadecimal_trace_id(self.valid_trace_id), self.valid_span_id - ) - self.assertEqual( - self._extract(header), trace.INVALID_SPAN.get_context() - ) - - header = "{}/;o=1".format( - get_hexadecimal_trace_id(self.valid_trace_id) - ) - self.assertEqual( - self._extract(header), trace.INVALID_SPAN.get_context() - ) - - header = "/{};o=1".format(self.valid_span_id) - self.assertEqual( - self._extract(header), trace.INVALID_SPAN.get_context() - ) - - header = "{}/{};o={}".format("123", "34", "4") - self.assertEqual( - self._extract(header), trace.INVALID_SPAN.get_context() - ) - - def test_invalid_trace_id(self): - header = "{}/{};o={}".format(INVALID_TRACE_ID, self.valid_span_id, 1) - self.assertEqual( - self._extract(header), trace.INVALID_SPAN.get_context() - ) - header = "{}/{};o={}".format("0" * 32, self.valid_span_id, 1) - self.assertEqual( - self._extract(header), trace.INVALID_SPAN.get_context() - ) - - header = "0/{};o={}".format(self.valid_span_id, 1) - self.assertEqual( - self._extract(header), trace.INVALID_SPAN.get_context() - ) - - header = "234/{};o={}".format(self.valid_span_id, 1) - self.assertEqual( - self._extract(header), trace.INVALID_SPAN.get_context() - ) - - header = "{}/{};o={}".format(self.too_long_id, self.valid_span_id, 1) - self.assertEqual( - self._extract(header), trace.INVALID_SPAN.get_context() - ) - - def test_invalid_span_id(self): - header = "{}/{};o={}".format( - get_hexadecimal_trace_id(self.valid_trace_id), INVALID_SPAN_ID, 1 - ) - self.assertEqual( - self._extract(header), trace.INVALID_SPAN.get_context() - ) - - header = "{}/{};o={}".format( - get_hexadecimal_trace_id(self.valid_trace_id), "0" * 16, 1 - ) - self.assertEqual( - self._extract(header), trace.INVALID_SPAN.get_context() - ) - - header = "{}/{};o={}".format( - get_hexadecimal_trace_id(self.valid_trace_id), "0", 1 - ) - self.assertEqual( - self._extract(header), trace.INVALID_SPAN.get_context() - ) - - header = "{}/{};o={}".format( - get_hexadecimal_trace_id(self.valid_trace_id), self.too_long_id, 1 - ) - self.assertEqual( - self._extract(header), trace.INVALID_SPAN.get_context() - ) - - def test_inject_with_no_context(self): - output = self._inject() - self.assertIsNone(output) - - def test_inject_with_invalid_context(self): - output = self._inject(trace.INVALID_SPAN) - self.assertIsNone(output) - - def test_inject_with_valid_context(self): - span_context = SpanContext( - trace_id=self.valid_trace_id, - span_id=self.valid_span_id, - is_remote=True, - trace_flags=TraceFlags(1), - ) - output = self._inject(trace.DefaultSpan(span_context)) - self.assertEqual( - output, - "{}/{};o={}".format( - get_hexadecimal_trace_id(self.valid_trace_id), - self.valid_span_id, - 1, - ), - ) diff --git a/ext/opentelemetry-exporter-cloud-trace/tests/test_gcp_resource_detector.py b/ext/opentelemetry-exporter-cloud-trace/tests/test_gcp_resource_detector.py deleted file mode 100644 index de618acd610..00000000000 --- a/ext/opentelemetry-exporter-cloud-trace/tests/test_gcp_resource_detector.py +++ /dev/null @@ -1,84 +0,0 @@ -# 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 unittest import mock - -from opentelemetry.sdk.resources import Resource -from opentelemetry.tools.resource_detector import ( - _GCP_METADATA_URL, - GoogleCloudResourceDetector, - get_gce_resources, -) - -RESOURCES_JSON_STRING = { - "instance": {"id": "instance_id", "zone": "projects/123/zones/zone"}, - "project": {"projectId": "project_id"}, -} - - -class TestGCEResourceFinder(unittest.TestCase): - @mock.patch("opentelemetry.tools.resource_detector.requests.get") - def test_finding_gce_resources(self, getter): - getter.return_value.json.return_value = RESOURCES_JSON_STRING - found_resources = get_gce_resources() - self.assertEqual(getter.call_args_list[0][0][0], _GCP_METADATA_URL) - self.assertEqual( - found_resources, - { - "host.id": "instance_id", - "cloud.provider": "gcp", - "cloud.account.id": "project_id", - "cloud.zone": "zone", - "gcp.resource_type": "gce_instance", - }, - ) - - -class TestGoogleCloudResourceDetector(unittest.TestCase): - @mock.patch("opentelemetry.tools.resource_detector.requests.get") - def test_finding_resources(self, getter): - resource_finder = GoogleCloudResourceDetector() - getter.return_value.json.return_value = RESOURCES_JSON_STRING - found_resources = resource_finder.detect() - self.assertEqual(getter.call_args_list[0][0][0], _GCP_METADATA_URL) - self.assertEqual( - found_resources, - Resource( - labels={ - "host.id": "instance_id", - "cloud.provider": "gcp", - "cloud.account.id": "project_id", - "cloud.zone": "zone", - "gcp.resource_type": "gce_instance", - } - ), - ) - self.assertEqual(getter.call_count, 1) - - # Found resources should be cached and not require another network call - found_resources = resource_finder.detect() - self.assertEqual(getter.call_count, 1) - self.assertEqual( - found_resources, - Resource( - labels={ - "host.id": "instance_id", - "cloud.provider": "gcp", - "cloud.account.id": "project_id", - "cloud.zone": "zone", - "gcp.resource_type": "gce_instance", - } - ), - ) diff --git a/scripts/coverage.sh b/scripts/coverage.sh index a88aca74755..0b45fbf643b 100755 --- a/scripts/coverage.sh +++ b/scripts/coverage.sh @@ -36,8 +36,6 @@ cov ext/opentelemetry-ext-flask cov ext/opentelemetry-ext-requests cov ext/opentelemetry-ext-jaeger cov ext/opentelemetry-ext-opentracing-shim -cov ext/opentelemetry-exporter-cloud-trace -cov ext/opentelemetry-exporter-cloud-monitoring cov ext/opentelemetry-ext-wsgi cov ext/opentelemetry-ext-zipkin cov docs/examples/opentelemetry-example-app diff --git a/tox.ini b/tox.ini index 2c87f692467..ad7b8ad3e92 100644 --- a/tox.ini +++ b/tox.ini @@ -161,15 +161,6 @@ envlist = ; ext-system-metrics intentionally excluded from pypy3 ; known limitation: gc.get_count won't work under pypy - ; Coverage is temporarily disabled for pypy3 due to the pytest bug. - ; pypy3-coverage - - ; opentelemetry-exporter-cloud-monitoring - py3{4,5,6,7,8}-test-exporter-cloud-monitoring - - ; opentelemetry-exporter-cloud-trace - py3{4,5,6,7,8}-test-exporter-cloud-trace - lint py38-tracecontext py38-{mypy,mypyinstalled} @@ -235,8 +226,6 @@ changedir = test-exporter-opencensusexporter: ext/opentelemetry-ext-opencensusexporter/tests test-exporter-otlp: ext/opentelemetry-ext-otlp/tests test-exporter-prometheus: ext/opentelemetry-ext-prometheus/tests - test-exporter-cloud-trace: ext/opentelemetry-exporter-cloud-trace/tests - test-exporter-cloud-monitoring: ext/opentelemetry-exporter-cloud-monitoring/tests test-exporter-zipkin: ext/opentelemetry-ext-zipkin/tests commands_pre = @@ -319,8 +308,6 @@ commands_pre = system-metrics: pip install {toxinidir}/ext/opentelemetry-ext-system-metrics[test] - exporter-cloud-monitoring: pip install {toxinidir}/ext/opentelemetry-exporter-cloud-monitoring - exporter-cloud-trace: pip install {toxinidir}/ext/opentelemetry-exporter-cloud-trace elasticsearch{2,5,6,7}: pip install {toxinidir}/opentelemetry-instrumentation {toxinidir}/ext/opentelemetry-ext-elasticsearch[test] ; In order to get a healthy coverage report,