Skip to content

Commit 4eb2f79

Browse files
authored
Replace MonitoredResource with Resource (census-instrumentation#508)
Add classes for the Resource API and remove MonitoredResource classes. Replace MonitoredResources with static Resources with labels fixed on creation.
1 parent 0251b4f commit 4eb2f79

File tree

10 files changed

+495
-272
lines changed

10 files changed

+495
-272
lines changed

opencensus/common/monitored_resource_util/monitored_resource.py

Lines changed: 47 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -12,75 +12,70 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import os
1516

16-
from opencensus.common.monitored_resource_util.gcp_metadata_config \
17-
import GcpMetadataConfig
18-
from opencensus.common.monitored_resource_util.aws_identity_doc_utils \
19-
import AwsIdentityDocumentUtils
17+
from opencensus.common import resource
18+
from opencensus.common.monitored_resource_util import aws_identity_doc_utils
19+
from opencensus.common.monitored_resource_util import gcp_metadata_config
2020

21-
# supported environments (resource types)
21+
22+
# Supported environments (resource types)
2223
_GCE_INSTANCE = "gce_instance"
2324
_GKE_CONTAINER = "gke_container"
2425
_AWS_EC2_INSTANCE = "aws_ec2_instance"
2526

27+
# Kubenertes environment variables
28+
_KUBERNETES_SERVICE_HOST = 'KUBERNETES_SERVICE_HOST'
2629

27-
class MonitoredResource(object):
28-
"""MonitoredResource returns the resource type and resource labels.
29-
"""
3030

31-
@property
32-
def resource_type(self):
33-
"""Returns the resource type this MonitoredResource.
34-
:return:
35-
"""
36-
raise NotImplementedError # pragma: NO COVER
31+
def is_gke_environment():
32+
"""Whether the environment is a GKE container instance.
3733
38-
def get_resource_labels(self):
39-
"""Returns the resource labels for this MonitoredResource.
40-
:return:
41-
"""
42-
raise NotImplementedError # pragma: NO COVER
34+
The KUBERNETES_SERVICE_HOST environment variable must be set.
35+
"""
36+
return _KUBERNETES_SERVICE_HOST in os.environ
4337

4438

45-
class GcpGceMonitoredResource(MonitoredResource):
46-
"""GceMonitoredResource represents gce_instance type monitored resource.
47-
For definition refer to
48-
https://cloud.google.com/monitoring/api/resources#tag_gce_instance
49-
"""
39+
def is_gce_environment():
40+
"""Whether the environment is a virtual machine on GCE."""
41+
return gcp_metadata_config.GcpMetadataConfig.is_running_on_gcp()
5042

51-
@property
52-
def resource_type(self):
53-
return _GCE_INSTANCE
5443

55-
def get_resource_labels(self):
56-
gcp_config = GcpMetadataConfig()
57-
return gcp_config.get_gce_metadata()
44+
def is_aws_environment():
45+
"""Whether the environment is a virtual machine instance on EC2."""
46+
return aws_identity_doc_utils.AwsIdentityDocumentUtils.is_running_on_aws()
5847

5948

60-
class GcpGkeMonitoredResource(MonitoredResource):
61-
"""GkeMonitoredResource represents gke_container type monitored resource.
62-
For definition refer to
63-
https://cloud.google.com/monitoring/api/resources#tag_gke_container
64-
"""
49+
def get_instance():
50+
"""Get a resource based on the application environment.
6551
66-
@property
67-
def resource_type(self):
68-
return _GKE_CONTAINER
52+
Returns a `Resource` configured for the current environment, or None if the
53+
environment is unknown or unsupported.
6954
70-
def get_resource_labels(self):
71-
gcp_config = GcpMetadataConfig()
72-
return gcp_config.get_gke_metadata()
55+
Supported environments include:
7356
57+
1. 'gke_container'
58+
- https://cloud.google.com/monitoring/api/resources#tag_gke_container
59+
2. 'gce_instance'
60+
- https://cloud.google.com/monitoring/api/resources#tag_gce_instance
61+
3. 'aws_ec2_instance'
62+
- https://cloud.google.com/monitoring/api/resources#tag_aws_ec2_instance
7463
75-
class AwsMonitoredResource(MonitoredResource):
76-
"""AwsMonitoredResource represents aws_ec2_instance type monitored resource.
77-
For definition refer to
78-
https://cloud.google.com/monitoring/api/resources#tag_aws_ec2_instance
64+
:rtype: :class:`opencensus.common.resource.Resource` or None
65+
:return: A `Resource` configured for the current environment.
7966
"""
80-
@property
81-
def resource_type(self):
82-
return _AWS_EC2_INSTANCE
83-
84-
def get_resource_labels(self):
85-
aws_util = AwsIdentityDocumentUtils()
86-
return aws_util.get_aws_metadata()
67+
if is_gke_environment():
68+
return resource.Resource(
69+
_GKE_CONTAINER,
70+
gcp_metadata_config.GcpMetadataConfig().get_gke_metadata())
71+
if is_gce_environment():
72+
return resource.Resource(
73+
_GCE_INSTANCE,
74+
gcp_metadata_config.GcpMetadataConfig().get_gce_metadata())
75+
if is_aws_environment():
76+
return resource.Resource(
77+
_AWS_EC2_INSTANCE,
78+
(aws_identity_doc_utils.AwsIdentityDocumentUtils()
79+
.get_aws_metadata()))
80+
81+
return None

opencensus/common/monitored_resource_util/monitored_resource_util.py

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

opencensus/common/resource.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# Copyright 2019 Google Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
from copy import copy
17+
import re
18+
19+
20+
# Matches anything outside ASCII 32-126 inclusive
21+
NON_PRINTABLE_ASCII = re.compile(
22+
r'[^ !"#$%&\'()*+,\-./:;<=>?@\[\\\]^_`{|}~0-9a-zA-Z]')
23+
24+
25+
def merge_resources(r1, r2):
26+
"""Merge two resources to get a new resource.
27+
28+
:type r1: :class:`Resource`
29+
:param r1: The first resource to merge, takes priority in conflicts.
30+
31+
:type r2: :class:`Resource`
32+
:param r2: The second resource to merge.
33+
34+
:rtype: :class:`Resource`
35+
:return: The new combined resource.
36+
"""
37+
type_ = r1.type or r2.type
38+
labels = copy(r2.labels)
39+
labels.update(r1.labels)
40+
return Resource(type_, labels)
41+
42+
43+
def check_ascii_256(string):
44+
"""Check that `string` is printable ASCII and at most 256 chars.
45+
46+
Raise a `ValueError` if this check fails. Note that `string` itself doesn't
47+
have to be ASCII-encoded.
48+
49+
:type string: str
50+
:param string: The string to check.
51+
"""
52+
if string is None:
53+
return
54+
if len(string) > 256:
55+
raise ValueError("Value is longer than 256 characters")
56+
bad_char = NON_PRINTABLE_ASCII.search(string)
57+
if bad_char:
58+
raise ValueError(u'Character "{}" at position {} is not printable '
59+
'ASCII'
60+
.format(
61+
string[bad_char.start():bad_char.end()],
62+
bad_char.start()))
63+
64+
65+
class Resource(object):
66+
"""A description of the entity for which signals are reported.
67+
68+
`type_` and `labels`' keys and values should contain only printable ASCII
69+
and should be at most 256 characters.
70+
71+
See:
72+
https://github.com/census-instrumentation/opencensus-specs/blob/master/resource/Resource.md
73+
74+
:type type_: str
75+
:param type_: The resource type identifier.
76+
77+
:type labels: dict
78+
:param labels: Key-value pairs that describe the entity.
79+
""" # noqa
80+
81+
def __init__(self, type_=None, labels=None):
82+
if type_ is not None and not type_:
83+
raise ValueError("Resource type must not be empty")
84+
check_ascii_256(type_)
85+
if labels is None:
86+
labels = {}
87+
for key, value in labels.items():
88+
if not key:
89+
raise ValueError("Resource key must not be null or empty")
90+
if value is None:
91+
raise ValueError("Resource value must not be null")
92+
check_ascii_256(key)
93+
check_ascii_256(value)
94+
95+
self.type = type_
96+
self.labels = copy(labels)
97+
98+
def get_type(self):
99+
"""Get this resource's type.
100+
101+
:rtype: str
102+
:return: The resource's type.
103+
"""
104+
return self.type
105+
106+
def get_labels(self):
107+
"""Get this resource's labels.
108+
109+
:rtype: dict
110+
:return: The resource's label dict.
111+
"""
112+
return copy(self.labels)
113+
114+
def merge(self, other):
115+
"""Get a copy of this resource combined with another resource.
116+
117+
The combined resource will have the union of both resources' labels,
118+
keeping this resource's label values if they conflict.
119+
120+
:type other: :class:`Resource`
121+
:param other: The other resource to merge.
122+
123+
:rtype: :class:`Resource`
124+
:return: The new combined resource.
125+
"""
126+
return merge_resources(self, other)

opencensus/stats/exporters/stackdriver_exporter.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@
2424
from google.cloud import monitoring_v3
2525

2626
from opencensus.common import utils
27-
from opencensus.common.monitored_resource_util.monitored_resource_util \
28-
import MonitoredResourceUtil
27+
from opencensus.common.monitored_resource_util import monitored_resource
2928
from opencensus.common.transports import async_
3029
from opencensus.common.version import __version__
3130
from opencensus.stats import aggregation
@@ -345,11 +344,11 @@ def set_monitored_resource(series, option_resource_type):
345344
resource_type = GLOBAL_RESOURCE_TYPE
346345

347346
if option_resource_type == "":
348-
monitored_resource = MonitoredResourceUtil.get_instance()
349-
if monitored_resource is not None:
350-
resource_labels = monitored_resource.get_resource_labels()
347+
resource = monitored_resource.get_instance()
348+
if resource is not None:
349+
resource_labels = resource.get_labels()
351350

352-
if monitored_resource.resource_type == 'gke_container':
351+
if resource.get_type() == 'gke_container':
353352
resource_type = 'k8s_container'
354353
set_attribute_label(series, resource_labels, 'project_id')
355354
set_attribute_label(series, resource_labels, 'cluster_name')
@@ -361,14 +360,14 @@ def set_monitored_resource(series, option_resource_type):
361360
set_attribute_label(series, resource_labels, 'zone',
362361
'location')
363362

364-
elif monitored_resource.resource_type == 'gce_instance':
365-
resource_type = monitored_resource.resource_type
363+
elif resource.get_type() == 'gce_instance':
364+
resource_type = 'gce_instance'
366365
set_attribute_label(series, resource_labels, 'project_id')
367366
set_attribute_label(series, resource_labels, 'instance_id')
368367
set_attribute_label(series, resource_labels, 'zone')
369368

370-
elif monitored_resource.resource_type == 'aws_ec2_instance':
371-
resource_type = monitored_resource.resource_type
369+
elif resource.get_type() == 'aws_ec2_instance':
370+
resource_type = 'aws_ec2_instance'
372371
set_attribute_label(series, resource_labels, 'aws_account')
373372
set_attribute_label(series, resource_labels, 'instance_id')
374373
set_attribute_label(series, resource_labels, 'region',

opencensus/trace/exporters/stackdriver_exporter.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@
1717

1818
from google.cloud.trace.client import Client
1919

20-
from opencensus.common.monitored_resource_util.monitored_resource_util \
21-
import MonitoredResourceUtil
20+
from opencensus.common.monitored_resource_util import monitored_resource
2221
from opencensus.common.transports import sync
2322
from opencensus.common.version import __version__
2423
from opencensus.trace import attributes_helper
@@ -79,10 +78,10 @@ def set_monitored_resource_attributes(span):
7978
"""Set labels to span that can be used for tracing.
8079
:param span: Span object
8180
"""
82-
monitored_resource = MonitoredResourceUtil.get_instance()
83-
if monitored_resource is not None:
84-
resource_type = monitored_resource.resource_type
85-
resource_labels = monitored_resource.get_resource_labels()
81+
resource = monitored_resource.get_instance()
82+
if resource is not None:
83+
resource_type = resource.get_type()
84+
resource_labels = resource.get_labels()
8685

8786
if resource_type == 'gke_container':
8887
resource_type = 'k8s_container'

0 commit comments

Comments
 (0)