From d30d64ce55701c75a6761484668380d91e22edb3 Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Wed, 15 May 2019 16:18:16 -0700 Subject: [PATCH 1/4] Update gke/k8s labels in stackdriver exporter --- .../stackdriver/stats_exporter/__init__.py | 104 +++++++++--------- .../stackdriver/trace_exporter/__init__.py | 85 ++++++-------- .../tests/test_stackdriver_exporter.py | 10 +- .../tests/test_stackdriver_stats.py | 18 +-- .../test_monitored_resource.py | 8 +- 5 files changed, 106 insertions(+), 119 deletions(-) diff --git a/contrib/opencensus-ext-stackdriver/opencensus/ext/stackdriver/stats_exporter/__init__.py b/contrib/opencensus-ext-stackdriver/opencensus/ext/stackdriver/stats_exporter/__init__.py index 271935190..2b87f3514 100644 --- a/contrib/opencensus-ext-stackdriver/opencensus/ext/stackdriver/stats_exporter/__init__.py +++ b/contrib/opencensus-ext-stackdriver/opencensus/ext/stackdriver/stats_exporter/__init__.py @@ -25,6 +25,8 @@ import google.auth from opencensus.common import utils +from opencensus.common.monitored_resource import gcp_metadata_config +from opencensus.common.monitored_resource import k8s_utils from opencensus.common.monitored_resource import monitored_resource from opencensus.common.version import __version__ from opencensus.metrics import label_key @@ -300,62 +302,62 @@ def register_metric_descriptor(self, oc_md): def set_monitored_resource(series, option_resource_type): - """Set a resource(type and labels) that can be used for monitoring. + """Set this series' monitored resource and labels. + :param series: TimeSeries object based on view data :param option_resource_type: Resource is an optional field that represents the Stackdriver MonitoredResource type. """ - resource_type = GLOBAL_RESOURCE_TYPE - - if option_resource_type == "": - resource = monitored_resource.get_instance() - if resource is not None: - resource_labels = resource.get_labels() - - if resource.get_type() == 'gke_container': - resource_type = 'k8s_container' - set_attribute_label(series, resource_labels, 'project_id') - set_attribute_label(series, resource_labels, 'cluster_name') - set_attribute_label(series, resource_labels, 'container_name') - set_attribute_label(series, resource_labels, 'namespace_id', - 'namespace_name') - set_attribute_label(series, resource_labels, 'pod_id', - 'pod_name') - set_attribute_label(series, resource_labels, 'zone', - 'location') - - elif resource.get_type() == 'gce_instance': - resource_type = 'gce_instance' - set_attribute_label(series, resource_labels, 'project_id') - set_attribute_label(series, resource_labels, 'instance_id') - set_attribute_label(series, resource_labels, 'zone') - - elif resource.get_type() == 'aws_ec2_instance': - resource_type = 'aws_ec2_instance' - set_attribute_label(series, resource_labels, 'aws_account') - set_attribute_label(series, resource_labels, 'instance_id') - set_attribute_label(series, resource_labels, 'region', - label_value_prefix='aws:') - else: - resource_type = option_resource_type - series.resource.type = resource_type + if option_resource_type != "": + series.resource.type = option_resource_type + return + + resource = monitored_resource.get_instance() + if resource is None: + series.resource.type = GLOBAL_RESOURCE_TYPE + return + + resource_type = resource.get_type() + resource_labels = resource.get_labels() + + def set_attribute_label(attribute_key, label_key, label_value_prefix=''): + """Set a label to timeseries that can be used for monitoring. + + :param series: TimeSeries object based on view data + :param resource_labels: collection of labels + :param attribute_key: actual label key + :param label_key: exporter specific label key, Optional + :param label_value_prefix: optional exporter-specific prefix + """ + if attribute_key not in resource_labels: + return + + series.resource.labels[label_key] = (label_value_prefix + + resource_labels[attribute_key]) + + if resource_type == 'k8s_container': + series.resource.type = 'k8s_container' + set_attribute_label(gcp_metadata_config.PROJECT_ID_KEY, 'project_id') + set_attribute_label(k8s_utils.CLUSTER_NAME_KEY, 'cluster_name') + set_attribute_label(k8s_utils.CONTAINER_NAME_KEY, 'container_name') + set_attribute_label(k8s_utils.NAMESPACE_NAME_KEY, 'namespace_name') + set_attribute_label(k8s_utils.POD_NAME_KEY, 'pod_name') + set_attribute_label(gcp_metadata_config.ZONE_KEY, 'location') + + elif resource_type == 'gce_instance': + series.resource.type = 'gce_instance' + set_attribute_label(gcp_metadata_config.PROJECT_ID_KEY, 'project_id') + set_attribute_label(gcp_metadata_config.INSTANCE_ID_KEY, 'instance_id') + set_attribute_label(gcp_metadata_config.ZONE_KEY, 'zone') + + elif resource_type == 'aws_ec2_instance': + series.resource.type = 'aws_ec2_instance' + set_attribute_label('aws_account', 'aws_account') + set_attribute_label('instance_id', 'instance_id') + set_attribute_label('region', 'region', label_value_prefix='aws:') - -def set_attribute_label(series, resource_labels, attribute_key, - canonical_key=None, label_value_prefix=''): - """Set a label to timeseries that can be used for monitoring - :param series: TimeSeries object based on view data - :param resource_labels: collection of labels - :param attribute_key: actual label key - :param canonical_key: exporter specific label key, Optional - :param label_value_prefix: exporter specific label value prefix, Optional - """ - if attribute_key in resource_labels: - if canonical_key is None: - canonical_key = attribute_key - - series.resource.labels[canonical_key] = \ - label_value_prefix + resource_labels[attribute_key] + else: + series.resource.type = GLOBAL_RESOURCE_TYPE def get_user_agent_slug(): diff --git a/contrib/opencensus-ext-stackdriver/opencensus/ext/stackdriver/trace_exporter/__init__.py b/contrib/opencensus-ext-stackdriver/opencensus/ext/stackdriver/trace_exporter/__init__.py index 3788e2366..2d8bf15f3 100644 --- a/contrib/opencensus-ext-stackdriver/opencensus/ext/stackdriver/trace_exporter/__init__.py +++ b/contrib/opencensus-ext-stackdriver/opencensus/ext/stackdriver/trace_exporter/__init__.py @@ -17,6 +17,8 @@ from google.cloud.trace.client import Client +from opencensus.common.monitored_resource import gcp_metadata_config +from opencensus.common.monitored_resource import k8s_utils from opencensus.common.monitored_resource import monitored_resource from opencensus.common.transports import sync from opencensus.common.version import __version__ @@ -79,58 +81,23 @@ def set_monitored_resource_attributes(span): :param span: Span object """ resource = monitored_resource.get_instance() - if resource is not None: - resource_type = resource.get_type() - resource_labels = resource.get_labels() - - if resource_type == 'gke_container': - resource_type = 'k8s_container' - set_attribute_label(span, resource_type, resource_labels, - 'project_id') - set_attribute_label(span, resource_type, resource_labels, - 'cluster_name') - set_attribute_label(span, resource_type, resource_labels, - 'container_name') - set_attribute_label(span, resource_type, resource_labels, - 'namespace_id', 'namespace_name') - set_attribute_label(span, resource_type, resource_labels, - 'pod_id', 'pod_name') - set_attribute_label(span, resource_type, resource_labels, - 'zone', 'location') - - elif resource_type == 'gce_instance': - set_attribute_label(span, resource_type, resource_labels, - 'project_id') - set_attribute_label(span, resource_type, resource_labels, - 'instance_id') - set_attribute_label(span, resource_type, resource_labels, - 'zone') - - elif resource_type == 'aws_ec2_instance': - set_attribute_label(span, resource_type, resource_labels, - 'aws_account') - set_attribute_label(span, resource_type, resource_labels, - 'instance_id') - set_attribute_label(span, resource_type, resource_labels, - 'region', label_value_prefix='aws:') - - -def set_attribute_label(span, resource_type, resource_labels, attribute_key, - canonical_key=None, label_value_prefix=''): - """Set a label to span that can be used for tracing. - :param span: Span object - :param resource_type: resource type - :param resource_labels: collection of labels - :param attribute_key: actual label key - :param canonical_key: exporter specific label key, Optional - :param label_value_prefix: exporter specific label value prefix, Optional - """ + if resource is None: + return + + resource_type = resource.get_type() + resource_labels = resource.get_labels() - if attribute_key in resource_labels: - if canonical_key is None: - canonical_key = attribute_key + def set_attribute_label(attribute_key, label_key, + label_value_prefix=''): + """Add the attribute to the span attribute map. - pair = {RESOURCE_LABEL % (resource_type, canonical_key): + Update the span attribute map (`span['attributes']['attributeMap']`) to + include a given resource label. + """ + if attribute_key not in resource_labels: + return + + pair = {RESOURCE_LABEL % (resource_type, label_key): label_value_prefix + resource_labels[attribute_key] } pair_attrs = Attributes(pair).format_attributes_json()\ @@ -138,6 +105,24 @@ def set_attribute_label(span, resource_type, resource_labels, attribute_key, _update_attr_map(span, pair_attrs) + if resource_type == 'k8s_container': + set_attribute_label(gcp_metadata_config.PROJECT_ID_KEY, 'project_id') + set_attribute_label(k8s_utils.CLUSTER_NAME_KEY, 'cluster_name') + set_attribute_label(k8s_utils.CONTAINER_NAME_KEY, 'container_name') + set_attribute_label(k8s_utils.NAMESPACE_NAME_KEY, 'namespace_name') + set_attribute_label(k8s_utils.POD_NAME_KEY, 'pod_name') + set_attribute_label(gcp_metadata_config.ZONE_KEY, 'location') + + elif resource_type == 'gce_instance': + set_attribute_label(gcp_metadata_config.PROJECT_ID_KEY, 'project_id') + set_attribute_label(gcp_metadata_config.INSTANCE_ID_KEY, 'instance_id') + set_attribute_label(gcp_metadata_config.ZONE_KEY, 'zone') + + elif resource_type == 'aws_ec2_instance': + set_attribute_label('aws_account', 'aws_account') + set_attribute_label('instance_id', 'instance_id') + set_attribute_label('region', 'region', label_value_prefix='aws:') + def set_common_attributes(span): """Set the common attributes.""" diff --git a/contrib/opencensus-ext-stackdriver/tests/test_stackdriver_exporter.py b/contrib/opencensus-ext-stackdriver/tests/test_stackdriver_exporter.py index 925d1e415..0a45b8ee7 100644 --- a/contrib/opencensus-ext-stackdriver/tests/test_stackdriver_exporter.py +++ b/contrib/opencensus-ext-stackdriver/tests/test_stackdriver_exporter.py @@ -677,12 +677,12 @@ def test_monitored_resource_attributes_gke(self, gmr_mock): } mock_resource = mock.Mock() - mock_resource.get_type.return_value = 'gke_container' + mock_resource.get_type.return_value = 'k8s_container' mock_resource.get_labels.return_value = { - 'pod_id': 'pod', - 'cluster_name': 'cluster', - 'namespace_id': 'namespace', - 'container_name': 'c1', + 'k8s.io/pod/name': 'pod', + 'k8s.io/cluster/name': 'cluster', + 'k8s.io/namespace/name': 'namespace', + 'k8s.io/container/name': 'c1', 'project_id': 'my_project', 'instance_id': 'instance', 'zone': 'zone1' diff --git a/contrib/opencensus-ext-stackdriver/tests/test_stackdriver_stats.py b/contrib/opencensus-ext-stackdriver/tests/test_stackdriver_stats.py index f176283cf..f57532acf 100644 --- a/contrib/opencensus-ext-stackdriver/tests/test_stackdriver_stats.py +++ b/contrib/opencensus-ext-stackdriver/tests/test_stackdriver_stats.py @@ -93,8 +93,8 @@ def test_options_parameters(self): def test_default_monitoring_labels(self): options = stackdriver.Options(default_monitoring_labels={ - label_key.LabelKey('lk_key', 'lk_desc'): - label_value.LabelValue('lk_value') + label_key.LabelKey('lk_key', 'lk_desc'): + label_value.LabelValue('lk_value') }) self.assertEqual(len(options.default_monitoring_labels), 1) @@ -876,8 +876,8 @@ def test_create_timeseries_with_resource(self, monitor_resource_mock): 'instance_id': 'my-instance', 'project_id': 'my-project', 'zone': 'us-east1', - 'pod_id': 'localhost', - 'namespace_id': 'namespace' + 'k8s.io/pod/name': 'localhost', + 'k8s.io/namespace/name': 'namespace', } mock_resource = mock.Mock() @@ -908,18 +908,18 @@ def test_create_timeseries_with_resource(self, monitor_resource_mock): time_series.metric.type, "custom.googleapis.com/opencensus/my.org/views/video_size_test2") - # check for gke_container monitored resource + # check for k8s_container monitored resource mocked_labels = { 'instance_id': 'my-instance', 'project_id': 'my-project', 'zone': 'us-east1', - 'pod_id': 'localhost', - 'cluster_name': 'cluster', - 'namespace_id': 'namespace' + 'k8s.io/pod/name': 'localhost', + 'k8s.io/cluster/name': 'cluster', + 'k8s.io/namespace/name': 'namespace', } mock_resource = mock.Mock() - mock_resource.get_type.return_value = 'gke_container' + mock_resource.get_type.return_value = 'k8s_container' mock_resource.get_labels.return_value = mocked_labels monitor_resource_mock.return_value = mock_resource diff --git a/tests/unit/common/monitored_resource_util/test_monitored_resource.py b/tests/unit/common/monitored_resource_util/test_monitored_resource.py index 7274dd5ba..4cbe537d2 100644 --- a/tests/unit/common/monitored_resource_util/test_monitored_resource.py +++ b/tests/unit/common/monitored_resource_util/test_monitored_resource.py @@ -112,12 +112,12 @@ def test_gcp_k8s_monitored_resource(self, gcp_md_mock): mocked_labels = { 'instance_id': 'my-instance', - 'cluster_name': 'cluster', 'project_id': 'my-project', 'zone': 'us-east1', - 'pod_id': 'localhost', - 'namespace_id': 'namespace', - 'container_name': 'container' + 'k8s.io/cluster/name': 'cluster', + 'k8s.io/pod/name': 'localhost', + 'k8s.io/namespace/name': 'namespace', + 'k8s.io/container/name': 'container' } cluster_name_key = 'instance/attributes/cluster-name' cluster_name_val = 'cluster' From 1ed619accc4a26938d7f25484fc7ee92d3fe4e2c Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Wed, 15 May 2019 16:33:11 -0700 Subject: [PATCH 2/4] Make aws label keys constant --- .../ext/stackdriver/stats_exporter/__init__.py | 10 +++++++--- .../ext/stackdriver/trace_exporter/__init__.py | 10 +++++++--- .../monitored_resource/aws_identity_doc_utils.py | 10 +++++++--- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/contrib/opencensus-ext-stackdriver/opencensus/ext/stackdriver/stats_exporter/__init__.py b/contrib/opencensus-ext-stackdriver/opencensus/ext/stackdriver/stats_exporter/__init__.py index 2b87f3514..6556eb661 100644 --- a/contrib/opencensus-ext-stackdriver/opencensus/ext/stackdriver/stats_exporter/__init__.py +++ b/contrib/opencensus-ext-stackdriver/opencensus/ext/stackdriver/stats_exporter/__init__.py @@ -25,6 +25,7 @@ import google.auth from opencensus.common import utils +from opencensus.common.monitored_resource import aws_identity_doc_utils from opencensus.common.monitored_resource import gcp_metadata_config from opencensus.common.monitored_resource import k8s_utils from opencensus.common.monitored_resource import monitored_resource @@ -352,9 +353,12 @@ def set_attribute_label(attribute_key, label_key, label_value_prefix=''): elif resource_type == 'aws_ec2_instance': series.resource.type = 'aws_ec2_instance' - set_attribute_label('aws_account', 'aws_account') - set_attribute_label('instance_id', 'instance_id') - set_attribute_label('region', 'region', label_value_prefix='aws:') + set_attribute_label(aws_identity_doc_utils.ACCOUNT_ID_KEY, + 'aws_account') + set_attribute_label(aws_identity_doc_utils.INSTANCE_ID_KEY, + 'instance_id') + set_attribute_label(aws_identity_doc_utils.REGION_KEY, 'region', + label_value_prefix='aws:') else: series.resource.type = GLOBAL_RESOURCE_TYPE diff --git a/contrib/opencensus-ext-stackdriver/opencensus/ext/stackdriver/trace_exporter/__init__.py b/contrib/opencensus-ext-stackdriver/opencensus/ext/stackdriver/trace_exporter/__init__.py index 2d8bf15f3..d83d63ba4 100644 --- a/contrib/opencensus-ext-stackdriver/opencensus/ext/stackdriver/trace_exporter/__init__.py +++ b/contrib/opencensus-ext-stackdriver/opencensus/ext/stackdriver/trace_exporter/__init__.py @@ -17,6 +17,7 @@ from google.cloud.trace.client import Client +from opencensus.common.monitored_resource import aws_identity_doc_utils from opencensus.common.monitored_resource import gcp_metadata_config from opencensus.common.monitored_resource import k8s_utils from opencensus.common.monitored_resource import monitored_resource @@ -119,9 +120,12 @@ def set_attribute_label(attribute_key, label_key, set_attribute_label(gcp_metadata_config.ZONE_KEY, 'zone') elif resource_type == 'aws_ec2_instance': - set_attribute_label('aws_account', 'aws_account') - set_attribute_label('instance_id', 'instance_id') - set_attribute_label('region', 'region', label_value_prefix='aws:') + set_attribute_label(aws_identity_doc_utils.ACCOUNT_ID_KEY, + 'aws_account') + set_attribute_label(aws_identity_doc_utils.INSTANCE_ID_KEY, + 'instance_id') + set_attribute_label(aws_identity_doc_utils.REGION_KEY, 'region', + label_value_prefix='aws:') def set_common_attributes(span): diff --git a/opencensus/common/monitored_resource/aws_identity_doc_utils.py b/opencensus/common/monitored_resource/aws_identity_doc_utils.py index 5049d4bd1..0b2b7ab98 100644 --- a/opencensus/common/monitored_resource/aws_identity_doc_utils.py +++ b/opencensus/common/monitored_resource/aws_identity_doc_utils.py @@ -15,6 +15,10 @@ from opencensus.common.http_handler import get_request import json +REGION_KEY = 'region' +ACCOUNT_ID_KEY = 'aws_account' +INSTANCE_ID_KEY = 'instance_id' + # AWS provides Instance Metadata via below url _AWS_INSTANCE_IDENTITY_DOCUMENT_URI = \ "http://169.254.169.254/latest/dynamic/instance-identity/document" @@ -23,13 +27,13 @@ # Region is the AWS region for the VM. The format of this field is # "aws:{region}", where supported values for {region} are listed at # http://docs.aws.amazon.com/general/latest/gr/rande.html. - 'region': 'region', + 'region': REGION_KEY, # accountId is the AWS account number for the VM. - 'accountId': 'aws_account', + 'accountId': ACCOUNT_ID_KEY, # instanceId is the instance id of the instance. - 'instanceId': 'instance_id' + 'instanceId': INSTANCE_ID_KEY } # inited is used to make sure AWS initialize executes only once. From 42be64a46e0c28b6812474cd705b42151445fba1 Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Thu, 16 May 2019 12:42:17 -0700 Subject: [PATCH 3/4] Remove instance_id from k8s resource in test --- .../tests/test_stackdriver_exporter.py | 1 - 1 file changed, 1 deletion(-) diff --git a/contrib/opencensus-ext-stackdriver/tests/test_stackdriver_exporter.py b/contrib/opencensus-ext-stackdriver/tests/test_stackdriver_exporter.py index f6aca2ade..c64806825 100644 --- a/contrib/opencensus-ext-stackdriver/tests/test_stackdriver_exporter.py +++ b/contrib/opencensus-ext-stackdriver/tests/test_stackdriver_exporter.py @@ -685,7 +685,6 @@ def test_monitored_resource_attributes_gke(self, gmr_mock): 'k8s.io/namespace/name': 'namespace', 'k8s.io/container/name': 'c1', 'project_id': 'my_project', - 'instance_id': 'instance', 'zone': 'zone1' } gmr_mock.return_value = mock_resource From ea49ac378efd185c6c050886799d4bd8e0733810 Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Thu, 16 May 2019 12:43:11 -0700 Subject: [PATCH 4/4] Change docstring wording --- .../opencensus/ext/stackdriver/stats_exporter/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/opencensus-ext-stackdriver/opencensus/ext/stackdriver/stats_exporter/__init__.py b/contrib/opencensus-ext-stackdriver/opencensus/ext/stackdriver/stats_exporter/__init__.py index 6556eb661..637202d85 100644 --- a/contrib/opencensus-ext-stackdriver/opencensus/ext/stackdriver/stats_exporter/__init__.py +++ b/contrib/opencensus-ext-stackdriver/opencensus/ext/stackdriver/stats_exporter/__init__.py @@ -327,7 +327,7 @@ def set_attribute_label(attribute_key, label_key, label_value_prefix=''): :param series: TimeSeries object based on view data :param resource_labels: collection of labels :param attribute_key: actual label key - :param label_key: exporter specific label key, Optional + :param label_key: optional exporter-specific label key :param label_value_prefix: optional exporter-specific prefix """ if attribute_key not in resource_labels: