Skip to content

Commit

Permalink
v0.4.1 and stackdriver v0.2.1 release (#615)
Browse files Browse the repository at this point in the history
  • Loading branch information
c24t authored Apr 12, 2019
1 parent 11d1527 commit 1d30e38
Show file tree
Hide file tree
Showing 13 changed files with 133 additions and 29 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

## Unreleased

## 0.4.0
Released 2019-04-11

- Allow for metrics with empty label keys and values
([#611](https://github.com/census-instrumentation/opencensus-python/pull/611))
([#614](https://github.com/census-instrumentation/opencensus-python/pull/614))

## 0.4.0
Released 2019-04-08

Expand Down
5 changes: 5 additions & 0 deletions contrib/opencensus-ext-stackdriver/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## Unreleased

## 0.2.0
Released 2019-04-11
- Don't require exporter options, fall back to default GCP auth
([#610](https://github.com/census-instrumentation/opencensus-python/pull/610))

## 0.2.0
Released 2019-04-08

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

from google.api_core.gapic_v1 import client_info
from google.cloud import monitoring_v3
import google.auth

from opencensus.common import utils
from opencensus.common.monitored_resource import monitored_resource
Expand Down Expand Up @@ -365,12 +366,16 @@ def get_user_agent_slug():
return "opencensus-python/{}".format(__version__)


def new_stats_exporter(options, interval=None):
def new_stats_exporter(options=None, interval=None):
"""Get a stats exporter and running transport thread.
Create a new `StackdriverStatsExporter` with the given options and start
periodically exporting stats to stackdriver in the background.
Fall back to default auth if `options` is null. This will raise
`google.auth.exceptions.DefaultCredentialsError` if default credentials
aren't configured.
See `opencensus.metrics.transport.get_exporter_thread` for details on the
transport thread.
Expand All @@ -380,9 +385,12 @@ def new_stats_exporter(options, interval=None):
:type interval: int or float
:param interval: Seconds between export calls.
:rtype: :class:`StackdriverStatsExporter` and :class:`PeriodicTask`
:return: A tuple of the exporter and transport thread.
:rtype: :class:`StackdriverStatsExporter`
:return: The newly-created exporter.
"""
if options is None:
_, project_id = google.auth.default()
options = Options(project_id=project_id)
if str(options.project_id).strip() == "":
raise ValueError(ERROR_BLANK_PROJECT_ID)

Expand Down
27 changes: 27 additions & 0 deletions contrib/opencensus-ext-stackdriver/tests/test_stackdriver_stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import unittest

from google.cloud import monitoring_v3
import google.auth

from opencensus.common import utils
from opencensus.common.version import __version__
Expand Down Expand Up @@ -115,6 +116,32 @@ def test_constructor_param(self):
default_monitoring_labels=default_labels))
self.assertEqual(exporter.options.project_id, project_id)

def test_null_options(self):
# Check that we don't suppress auth errors
auth_error = google.auth.exceptions.DefaultCredentialsError
mock_auth_error = mock.Mock()
mock_auth_error.side_effect = auth_error
with mock.patch('opencensus.ext.stackdriver.stats_exporter'
'.google.auth.default', mock_auth_error):
with self.assertRaises(auth_error):
stackdriver.new_stats_exporter()

# Check that we get the default credentials' project ID
mock_auth_ok = mock.Mock()
mock_auth_ok.return_value = (None, 123)
with mock.patch('opencensus.ext.stackdriver.stats_exporter'
'.google.auth.default', mock_auth_ok):
sdse = stackdriver.new_stats_exporter()
self.assertEqual(sdse.options.project_id, 123)

# Check that we raise if auth works but the project is empty
mock_auth_no_project = mock.Mock()
mock_auth_no_project.return_value = (None, '')
with mock.patch('opencensus.ext.stackdriver.stats_exporter'
'.google.auth.default', mock_auth_no_project):
with self.assertRaises(ValueError):
stackdriver.new_stats_exporter()

def test_blank_project(self):
self.assertRaises(ValueError, stackdriver.new_stats_exporter,
stackdriver.Options(project_id=""))
Expand Down
2 changes: 1 addition & 1 deletion contrib/opencensus-ext-stackdriver/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.

__version__ = '0.2.0'
__version__ = '0.2.1'
2 changes: 1 addition & 1 deletion opencensus/common/version/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.

__version__ = '0.4.0'
__version__ = '0.4.1'
4 changes: 2 additions & 2 deletions opencensus/metrics/export/metric_descriptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@ def __init__(self, name, description, unit, type_, label_keys):
if type_ not in MetricDescriptorType:
raise ValueError("Invalid type")

if not label_keys:
raise ValueError("label_keys must not be empty or null")
if label_keys is None:
raise ValueError("label_keys must not be None")

if any(key is None for key in label_keys):
raise ValueError("label_keys must not contain null keys")
Expand Down
4 changes: 2 additions & 2 deletions opencensus/metrics/export/time_series.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ class TimeSeries(object):
""" # noqa

def __init__(self, label_values, points, start_timestamp):
if not label_values:
raise ValueError("label_values must not be null or empty")
if label_values is None:
raise ValueError("label_values must not be None")
if not points:
raise ValueError("points must not be null or empty")
self._label_values = label_values
Expand Down
8 changes: 4 additions & 4 deletions opencensus/trace/propagation/b3_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ def from_headers(self, headers):
sampled = headers.get(_SAMPLED_KEY)

if sampled is not None:
if len(sampled) != 1:
return SpanContext(from_header=False)

sampled = sampled in ('1', 'd')
# The specification encodes an enabled tracing decision as "1".
# In the wild pre-standard implementations might still send "true".
# "d" is set in the single header case when debugging is enabled.
sampled = sampled.lower() in ('1', 'd', 'true')
else:
# If there's no incoming sampling decision, it was deferred to us.
# Even though we set it to False here, we might still sample
Expand Down
5 changes: 5 additions & 0 deletions tests/unit/metrics/export/test_metric_descriptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ def test_null_label_keys(self):
NAME, DESCRIPTION, UNIT,
metric_descriptor.MetricDescriptorType.GAUGE_DOUBLE, None)

def test_empty_label_keys(self):
metric_descriptor.MetricDescriptor(
NAME, DESCRIPTION, UNIT,
metric_descriptor.MetricDescriptorType.GAUGE_DOUBLE, [])

def test_null_label_key_values(self):
with self.assertRaises(ValueError):
metric_descriptor.MetricDescriptor(
Expand Down
2 changes: 0 additions & 2 deletions tests/unit/metrics/export/test_time_series.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ def test_init_invalid(self):
time_series.TimeSeries(LABEL_VALUES, POINTS, None)
with self.assertRaises(ValueError):
time_series.TimeSeries(None, POINTS, START_TIMESTAMP)
with self.assertRaises(ValueError):
time_series.TimeSeries([], POINTS, START_TIMESTAMP)
with self.assertRaises(ValueError):
time_series.TimeSeries(LABEL_VALUES, None, START_TIMESTAMP)
with self.assertRaises(ValueError):
Expand Down
33 changes: 33 additions & 0 deletions tests/unit/stats/test_metric_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,36 @@ def test_view_data_to_metric(self):
]
for args in args_list:
self.do_test_view_data_to_metric(*args)

def test_convert_view_without_labels(self):
mock_measure = mock.Mock(spec=measure.MeasureFloat)
mock_aggregation = mock.Mock(spec=aggregation.DistributionAggregation)
mock_aggregation.aggregation_type = aggregation.Type.DISTRIBUTION

vd = mock.Mock(spec=view_data.ViewData)
vd.view = view.View(
name=mock.Mock(),
description=mock.Mock(),
columns=[],
measure=mock_measure,
aggregation=mock_aggregation)
vd.start_time = '2019-04-11T22:33:44.555555Z'

mock_point = mock.Mock(spec=point.Point)
mock_point.value = mock.Mock(spec=value.ValueDistribution)

mock_agg = mock.Mock(spec=aggregation_data.SumAggregationDataFloat)
mock_agg.to_point.return_value = mock_point

vd.tag_value_aggregation_data_map = {tuple(): mock_agg}

current_time = '2019-04-11T22:33:55.666666Z'
metric = metric_utils.view_data_to_metric(vd, current_time)

self.assertEqual(metric.descriptor.label_keys, [])
self.assertEqual(len(metric.time_series), 1)
[ts] = metric.time_series
self.assertEqual(ts.label_values, [])
self.assertEqual(len(ts.points), 1)
[pt] = ts.points
self.assertEqual(pt, mock_point)
49 changes: 35 additions & 14 deletions tests/unit/trace/propagation/test_b3_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,44 @@ def test_from_headers_no_headers(self):
def test_from_headers_keys_exist(self):
test_trace_id = '6e0c63257de34c92bf9efcd03927272e'
test_span_id = '00f067aa0ba902b7'
test_sampled = '1'

headers = {
b3_format._TRACE_ID_KEY: test_trace_id,
b3_format._SPAN_ID_KEY: test_span_id,
b3_format._SAMPLED_KEY: test_sampled,
}
for test_sampled in ['1', 'True', 'true', 'd']:
headers = {
b3_format._TRACE_ID_KEY: test_trace_id,
b3_format._SPAN_ID_KEY: test_span_id,
b3_format._SAMPLED_KEY: test_sampled,
}

propagator = b3_format.B3FormatPropagator()
span_context = propagator.from_headers(headers)
propagator = b3_format.B3FormatPropagator()
span_context = propagator.from_headers(headers)

self.assertEqual(span_context.trace_id, test_trace_id)
self.assertEqual(span_context.span_id, test_span_id)
self.assertEqual(
span_context.trace_options.enabled,
bool(test_sampled)
)
self.assertEqual(span_context.trace_id, test_trace_id)
self.assertEqual(span_context.span_id, test_span_id)
self.assertEqual(
span_context.trace_options.enabled,
True
)

def test_from_headers_keys_exist_disabled_sampling(self):
test_trace_id = '6e0c63257de34c92bf9efcd03927272e'
test_span_id = '00f067aa0ba902b7'

for test_sampled in ['0', 'False', 'false', None]:
headers = {
b3_format._TRACE_ID_KEY: test_trace_id,
b3_format._SPAN_ID_KEY: test_span_id,
b3_format._SAMPLED_KEY: test_sampled,
}

propagator = b3_format.B3FormatPropagator()
span_context = propagator.from_headers(headers)

self.assertEqual(span_context.trace_id, test_trace_id)
self.assertEqual(span_context.span_id, test_span_id)
self.assertEqual(
span_context.trace_options.enabled,
False
)

def test_from_headers_keys_not_exist(self):
propagator = b3_format.B3FormatPropagator()
Expand Down

0 comments on commit 1d30e38

Please sign in to comment.