Skip to content

Commit 0c60c73

Browse files
ezhang6811syed-ahsan-ishtiaquewangzleijj22eeliustve
authored
0.14.2 patch commits (#578)
*Issue #, if available:* *Description of changes:* Ports the following commits from main into the 0.14.x release branch for pending 0.14.2 release: * #570 * #571 * #572 * #573 * #574 * #575 * #577 By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. --------- Co-authored-by: Syed Ahsan Ishtiaque <176968742+syed-ahsan-ishtiaque@users.noreply.github.com> Co-authored-by: Lei Wang <66336933+wangzlei@users.noreply.github.com> Co-authored-by: Jonathan Lee <107072447+jj22ee@users.noreply.github.com> Co-authored-by: Steve Liu <liustve@amazon.com> Co-authored-by: Mahad Janjua <134644284+majanjua-amzn@users.noreply.github.com>
1 parent e7dce11 commit 0c60c73

11 files changed

Lines changed: 176 additions & 141 deletions

File tree

.github/workflows/release-build.yml

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -124,21 +124,6 @@ jobs:
124124
- name: Checkout Repo @ SHA - ${{ github.sha }}
125125
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0
126126

127-
- name: Configure AWS credentials for PyPI secrets
128-
uses: aws-actions/configure-aws-credentials@a03048d87541d1d9fcf2ecf528a4a65ba9bd7838 #v5.0.0
129-
with:
130-
role-to-assume: ${{ secrets.AWS_ROLE_ARN_SECRETS_MANAGER }}
131-
aws-region: ${{ env.AWS_DEFAULT_REGION }}
132-
133-
- name: Get PyPI secrets
134-
uses: aws-actions/aws-secretsmanager-get-secrets@a9a7eb4e2f2871d30dc5b892576fde60a2ecc802 #v2.0.10
135-
id: pypi_secrets
136-
with:
137-
secret-ids: |
138-
PROD_PYPI_TOKEN,${{ secrets.PYPI_PROD_TOKEN_SECRET_ARN }}
139-
TEST_PYPI_TOKEN,${{ secrets.PYPI_TEST_TOKEN_SECRET_ARN }}
140-
parse-json-secrets: true
141-
142127
- name: Set up Docker Buildx
143128
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 #3.11.1
144129

@@ -163,36 +148,35 @@ jobs:
163148
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 #v3.5.0
164149
with:
165150
registry: public.ecr.aws
166-
167-
# The step below publishes to testpypi in order to catch any issues
168-
# with the package configuration that would cause a failure to upload to pypi.
169-
- name: Install twine
170-
run: pip install twine==5.1.1
171-
151+
172152
- name: Download SDK wheel artifact
173153
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 #v5.0.0
174154
with:
175155
name: ${{ env.WHEEL_ARTIFACT_NAME }}
156+
path: dist-pypi
176157

177158
- name: Download SDK source artifact
178159
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 #v5.0.0
179160
with:
180161
name: ${{ env.SOURCE_ARTIFACT_NAME }}
162+
path: dist-pypi
181163

164+
# The step below publishes to testpypi in order to catch any issues
182165
- name: Publish to TestPyPI
183-
env:
184-
TWINE_USERNAME: '__token__'
185-
TWINE_PASSWORD: ${{ env.TEST_PYPI_TOKEN_API_TOKEN }}
186-
run: |
187-
twine upload --repository testpypi --skip-existing --verbose ${{ env.WHEEL_ARTIFACT_NAME }} ${{ env.SOURCE_ARTIFACT_NAME }}
166+
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e
167+
with:
168+
repository-url: https://test.pypi.org/legacy/
169+
skip-existing: true
170+
verbose: true
171+
packages-dir: dist-pypi
188172

189173
# Publish to prod PyPI
190174
- name: Publish to PyPI
191-
env:
192-
TWINE_USERNAME: '__token__'
193-
TWINE_PASSWORD: ${{ env.PROD_PYPI_TOKEN_API_TOKEN }}
194-
run: |
195-
twine upload --skip-existing --verbose ${{ env.WHEEL_ARTIFACT_NAME }} ${{ env.SOURCE_ARTIFACT_NAME }}
175+
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e
176+
with:
177+
skip-existing: true
178+
verbose: true
179+
packages-dir: dist-pypi
196180

197181
# Publish to public ECR
198182
- name: Build and push public ECR image
@@ -256,10 +240,31 @@ jobs:
256240
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 #v5.0.0
257241
with:
258242
name: layer.zip
259-
- name: publish
243+
244+
- name: Upload to S3 and Sign
245+
continue-on-error: true
260246
run: |
261247
aws s3 mb s3://${{ env.BUCKET_NAME }}
262248
aws s3 cp aws-opentelemetry-python-layer.zip s3://${{ env.BUCKET_NAME }}
249+
250+
# Sign the layer
251+
PROFILE=$(aws signer list-signing-profiles --query "profiles[?profileName=='ADOTLambdaLayerSigningProfile'].arn" --output text 2>/dev/null)
252+
[ -z "$PROFILE" ] && exit 0
253+
254+
JOB_ID=$(aws signer start-signing-job \
255+
--source "s3={bucketName=${{ env.BUCKET_NAME }},key=aws-opentelemetry-python-layer.zip,version=null}" \
256+
--destination "s3={bucketName=${{ env.BUCKET_NAME }},prefix=signed-}" \
257+
--profile-name ADOTLambdaLayerSigningProfile \
258+
--query 'jobId' --output text 2>/dev/null) || exit 0
259+
[ -z "$JOB_ID" ] && exit 0
260+
261+
aws signer wait successful-signing-job --job-id "$JOB_ID" || exit 0
262+
263+
SIGNED=$(aws signer describe-signing-job --job-id "$JOB_ID" --query 'signedObject.s3.key' --output text 2>/dev/null)
264+
[ -n "$SIGNED" ] && aws s3 mv "s3://${{ env.BUCKET_NAME }}/$SIGNED" "s3://${{ env.BUCKET_NAME }}/aws-opentelemetry-python-layer.zip"
265+
266+
- name: Publish Layer Version
267+
run: |
263268
layerARN=$(
264269
aws lambda publish-layer-version \
265270
--layer-name ${{ env.LAYER_NAME }} \
@@ -276,6 +281,11 @@ jobs:
276281
mkdir ${{ env.LAYER_NAME }}
277282
echo $layerARN > ${{ env.LAYER_NAME }}/${{ matrix.aws_region }}
278283
cat ${{ env.LAYER_NAME }}/${{ matrix.aws_region }}
284+
285+
# Output SigningProfileVersionArn
286+
aws lambda get-layer-version-by-arn \
287+
--arn $layerARN \
288+
--output json | jq -r '.Content.SigningProfileVersionArn'
279289
- name: public layer
280290
run: |
281291
layerVersion=$(

CHANGELOG.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,19 @@ For any change that affects end users of this package, please add an entry under
1111
If your change does not need a CHANGELOG entry, add the "skip changelog" label to your PR.
1212

1313
## Unreleased
14+
- Fix: Support new fields in X-Ray API responses
15+
([#577](https://github.com/aws-observability/aws-otel-python-instrumentation/pull/577))
16+
- Fix CVE-2025-66471 and CVE-2026-21441. No associated PR since `urllib3` dependency will auto-bump to the latest `2.6.x` version upon release.
17+
- Add cloud.platform attribute to resource attributes in lambda
18+
([#561](https://github.com/aws-observability/aws-otel-python-instrumentation/pull/561))
19+
- Sign Lambda layer by AWS Signer
20+
([#573](https://github.com/aws-observability/aws-otel-python-instrumentation/pull/573))
21+
- Update opentelemetry-sdk-extension-aws to version 2.1.0, and remove unneeded Resource Detector patches
22+
([#572](https://github.com/aws-observability/aws-otel-python-instrumentation/pull/572))
23+
- Support PyPI Signature
24+
([#571](https://github.com/aws-observability/aws-otel-python-instrumentation/pull/571))
25+
- Remove redundant environment variable configuration in Lambda layer
26+
([#570](https://github.com/aws-observability/aws-otel-python-instrumentation/pull/570))
1427

1528
## v0.14.1 - 2025-12-15
1629
- Add custom ADOT UserAgent for OTLP Spans Exporter
@@ -38,4 +51,3 @@ If your change does not need a CHANGELOG entry, add the "skip changelog" label t
3851
([#522](https://github.com/aws-observability/aws-otel-python-instrumentation/pull/522))
3952
- Support credentials provider name for BedrockAgentCore Identity
4053
([#534](https://github.com/aws-observability/aws-otel-python-instrumentation/pull/534))
41-

aws-opentelemetry-distro/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ dependencies = [
3333
"opentelemetry-propagator-b3 == 1.33.1",
3434
"opentelemetry-propagator-jaeger == 1.33.1",
3535
"opentelemetry-exporter-otlp-proto-common == 1.33.1",
36-
"opentelemetry-sdk-extension-aws == 2.0.2",
36+
"opentelemetry-sdk-extension-aws == 2.1.0",
3737
"opentelemetry-propagator-aws-xray == 1.0.1",
3838
"opentelemetry-distro == 0.54b1",
3939
"opentelemetry-processor-baggage == 0.54b1",

aws-opentelemetry-distro/src/amazon/opentelemetry/distro/patches/_instrumentation_patch.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
from amazon.opentelemetry.distro._utils import is_installed
77
from amazon.opentelemetry.distro.aws_opentelemetry_configurator import is_enhanced_code_attributes
8-
from amazon.opentelemetry.distro.patches._resource_detector_patches import _apply_resource_detector_patches
98

109
_logger: Logger = getLogger(__name__)
1110

@@ -85,7 +84,3 @@ def apply_instrumentation_patches() -> None: # pylint: disable=too-many-branche
8584
from amazon.opentelemetry.distro.patches._aio_pika_patches import _apply_aio_pika_instrumentation_patches
8685

8786
_apply_aio_pika_instrumentation_patches()
88-
89-
# No need to check if library is installed as this patches opentelemetry.sdk,
90-
# which must be installed for the distro to work at all.
91-
_apply_resource_detector_patches()

aws-opentelemetry-distro/src/amazon/opentelemetry/distro/patches/_resource_detector_patches.py

Lines changed: 0 additions & 40 deletions
This file was deleted.

aws-opentelemetry-distro/src/amazon/opentelemetry/distro/sampler/_sampling_rule.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
# SPDX-License-Identifier: Apache-2.0
33

4+
from logging import getLogger
5+
6+
_logger = getLogger(__name__)
7+
48

59
# Disable snake_case naming style so this class can match the sampling rules response from X-Ray
610
# pylint: disable=invalid-name
@@ -20,6 +24,7 @@ def __init__(
2024
ServiceType: str = None,
2125
URLPath: str = None,
2226
Version: int = None,
27+
**kwargs,
2328
):
2429
self.Attributes = Attributes if Attributes is not None else {}
2530
self.FixedRate = FixedRate if FixedRate is not None else 0.0
@@ -36,6 +41,10 @@ def __init__(
3641
self.URLPath = URLPath if URLPath is not None else ""
3742
self.Version = Version if Version is not None else 0
3843

44+
# Log unknown fields for debugging/monitoring
45+
if kwargs:
46+
_logger.debug("Ignoring unknown fields in _SamplingRule: %s", list(kwargs.keys()))
47+
3948
def __lt__(self, other: "_SamplingRule") -> bool:
4049
if self.Priority == other.Priority:
4150
# String order priority example:

aws-opentelemetry-distro/src/amazon/opentelemetry/distro/sampler/_sampling_target.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
# SPDX-License-Identifier: Apache-2.0
33
from logging import getLogger
4+
from typing import List
45

56
_logger = getLogger(__name__)
67

@@ -15,47 +16,62 @@ def __init__(
1516
ReservoirQuota: int = None,
1617
ReservoirQuotaTTL: float = None,
1718
RuleName: str = None,
19+
**kwargs,
1820
):
1921
self.FixedRate = FixedRate if FixedRate is not None else 0.0
2022
self.Interval = Interval # can be None
2123
self.ReservoirQuota = ReservoirQuota # can be None
2224
self.ReservoirQuotaTTL = ReservoirQuotaTTL # can be None
2325
self.RuleName = RuleName if RuleName is not None else ""
2426

27+
# Log unknown fields for debugging/monitoring
28+
if kwargs:
29+
_logger.debug("Ignoring unknown fields in _SamplingTarget: %s", list(kwargs.keys()))
30+
2531

2632
class _UnprocessedStatistics:
2733
def __init__(
2834
self,
2935
ErrorCode: str = None,
3036
Message: str = None,
3137
RuleName: str = None,
38+
**kwargs,
3239
):
3340
self.ErrorCode = ErrorCode if ErrorCode is not None else ""
3441
self.Message = Message if ErrorCode is not None else ""
3542
self.RuleName = RuleName if ErrorCode is not None else ""
3643

44+
# Log unknown fields for debugging/monitoring
45+
if kwargs:
46+
_logger.debug("Ignoring unknown fields in _UnprocessedStatistics: %s", list(kwargs.keys()))
47+
3748

3849
class _SamplingTargetResponse:
3950
def __init__(
4051
self,
4152
LastRuleModification: float,
42-
SamplingTargetDocuments: [dict] = None,
43-
UnprocessedStatistics: [dict] = None,
53+
SamplingTargetDocuments: List[dict] = None,
54+
UnprocessedStatistics: List[dict] = None,
55+
**kwargs,
4456
):
4557
self.LastRuleModification: float = LastRuleModification if LastRuleModification is not None else 0.0
4658

47-
self.SamplingTargetDocuments: [_SamplingTarget] = []
59+
self.SamplingTargetDocuments: List[_SamplingTarget] = []
4860
if SamplingTargetDocuments is not None:
4961
for document in SamplingTargetDocuments:
5062
try:
5163
self.SamplingTargetDocuments.append(_SamplingTarget(**document))
5264
except TypeError as e:
5365
_logger.debug("TypeError occurred: %s", e)
5466

55-
self.UnprocessedStatistics: [_UnprocessedStatistics] = []
67+
self.UnprocessedStatistics: List[_UnprocessedStatistics] = []
5668
if UnprocessedStatistics is not None:
5769
for unprocessed in UnprocessedStatistics:
5870
try:
5971
self.UnprocessedStatistics.append(_UnprocessedStatistics(**unprocessed))
6072
except TypeError as e:
6173
_logger.debug("TypeError occurred: %s", e)
74+
75+
# Log unknown fields for debugging/monitoring
76+
if kwargs:
77+
_logger.debug("Ignoring unknown fields in _SamplingTargetResponse: %s", list(kwargs.keys()))

aws-opentelemetry-distro/tests/amazon/opentelemetry/distro/patches/test_instrumentation_patch.py

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
from unittest import TestCase
66
from unittest.mock import MagicMock, patch
77

8-
import opentelemetry.sdk.extension.aws.resource.ec2 as ec2_resource
9-
import opentelemetry.sdk.extension.aws.resource.eks as eks_resource
108
from amazon.opentelemetry.distro._aws_attribute_keys import (
119
AWS_AUTH_CREDENTIAL_PROVIDER,
1210
AWS_BEDROCK_AGENTCORE_BROWSER_ARN,
@@ -141,8 +139,6 @@ def _run_patch_mechanism_tests(self):
141139
"""
142140
self._test_botocore_installed_flag()
143141
self._reset_mocks()
144-
self._test_resource_detector_patches()
145-
self._reset_mocks()
146142
self._test_starlette_installed_flag()
147143
self._reset_mocks()
148144
self._test_enhanced_code_attributes_patches()
@@ -789,53 +785,6 @@ def _test_patched_bedrock_agent_instrumentation(self):
789785
self.assertEqual(len(bedrock_agent_success_attributes), 1)
790786
self.assertEqual(bedrock_agent_success_attributes[attribute_tuple[0]], attribute_tuple[1])
791787

792-
def _test_resource_detector_patches(self):
793-
"""Test that resource detector patches are applied and work correctly"""
794-
# Test that the functions were patched
795-
self.assertIsNotNone(ec2_resource._aws_http_request)
796-
self.assertIsNotNone(eks_resource._aws_http_request)
797-
798-
# Test EC2 patched function
799-
with patch("amazon.opentelemetry.distro.patches._resource_detector_patches.urlopen") as mock_urlopen:
800-
mock_response = MagicMock()
801-
mock_response.read.return_value = b'{"test": "ec2-data"}'
802-
mock_urlopen.return_value.__enter__.return_value = mock_response
803-
804-
result = ec2_resource._aws_http_request("GET", "/test/path", {"X-Test": "header"})
805-
self.assertEqual(result, '{"test": "ec2-data"}')
806-
807-
# Verify the request was made correctly
808-
args, kwargs = mock_urlopen.call_args
809-
request = args[0]
810-
self.assertEqual(request.full_url, "http://169.254.169.254/test/path")
811-
self.assertEqual(request.headers, {"X-test": "header"})
812-
self.assertEqual(kwargs["timeout"], 5)
813-
814-
# Test EKS patched function
815-
with patch("amazon.opentelemetry.distro.patches._resource_detector_patches.urlopen") as mock_urlopen, patch(
816-
"amazon.opentelemetry.distro.patches._resource_detector_patches.ssl.create_default_context"
817-
) as mock_ssl:
818-
mock_response = MagicMock()
819-
mock_response.read.return_value = b'{"test": "eks-data"}'
820-
mock_urlopen.return_value.__enter__.return_value = mock_response
821-
822-
mock_context = MagicMock()
823-
mock_ssl.return_value = mock_context
824-
825-
result = eks_resource._aws_http_request("GET", "/api/v1/test", "Bearer token123")
826-
self.assertEqual(result, '{"test": "eks-data"}')
827-
828-
# Verify the request was made correctly
829-
args, kwargs = mock_urlopen.call_args
830-
request = args[0]
831-
self.assertEqual(request.full_url, "https://kubernetes.default.svc/api/v1/test")
832-
self.assertEqual(request.headers, {"Authorization": "Bearer token123"})
833-
self.assertEqual(kwargs["timeout"], 5)
834-
self.assertEqual(kwargs["context"], mock_context)
835-
836-
# Verify SSL context was created with correct CA file
837-
mock_ssl.assert_called_once_with(cafile="/var/run/secrets/kubernetes.io/serviceaccount/ca.crt")
838-
839788
def _test_unpatched_botocore_propagator(self):
840789
"""Test that BotocoreInstrumentor uses its own propagator by default."""
841790
# Create a fresh instrumentor to test its initial state

0 commit comments

Comments
 (0)