3
import cPickle as pickle
4
from mock import patch, call, mock_open
5
from StringIO import StringIO
6
from mock import MagicMock
7
from testtools import TestCase
10
from charmhelpers.core import hookenv
12
CHARM_METADATA = """name: testmock
13
summary: test mock summary
14
description: test mock description
27
class SerializableTest(TestCase):
28
def test_serializes_object_to_json(self):
32
wrapped = hookenv.Serializable(foo)
33
self.assertEqual(wrapped.json(), json.dumps(foo))
35
def test_serializes_object_to_yaml(self):
39
wrapped = hookenv.Serializable(foo)
40
self.assertEqual(wrapped.yaml(), yaml.dump(foo))
42
def test_gets_attribute_from_inner_object_as_dict(self):
46
wrapped = hookenv.Serializable(foo)
48
self.assertEqual(wrapped.bar, 'baz')
50
def test_raises_error_from_inner_object_as_dict(self):
54
wrapped = hookenv.Serializable(foo)
56
self.assertRaises(AttributeError, getattr, wrapped, 'baz')
58
def test_dict_methods_from_inner_object(self):
62
wrapped = hookenv.Serializable(foo)
63
for meth in ('keys', 'values', 'items'):
64
self.assertEqual(getattr(wrapped, meth)(), getattr(foo, meth)())
66
for meth in ('iterkeys', 'itervalues', 'iteritems'):
67
self.assertEqual(list(getattr(wrapped, meth)()),
68
list(getattr(foo, meth)()))
69
self.assertEqual(wrapped.get('bar'), foo.get('bar'))
70
self.assertEqual(wrapped.get('baz', 42), foo.get('baz', 42))
71
self.assertIn('bar', wrapped)
73
def test_get_gets_from_inner_object(self):
77
wrapped = hookenv.Serializable(foo)
79
self.assertEqual(wrapped.get('foo'), None)
80
self.assertEqual(wrapped.get('bar'), 'baz')
81
self.assertEqual(wrapped.get('zoo', 'bla'), 'bla')
83
def test_gets_inner_object(self):
87
wrapped = hookenv.Serializable(foo)
89
self.assertIs(wrapped.data, foo)
91
def test_pickle(self):
93
wrapped = hookenv.Serializable(foo)
94
pickled = pickle.dumps(wrapped)
95
unpickled = pickle.loads(pickled)
97
self.assert_(isinstance(unpickled, hookenv.Serializable))
98
self.assertEqual(unpickled, foo)
100
def test_boolean(self):
101
true_dict = {'foo': 'bar'}
104
self.assertIs(bool(hookenv.Serializable(true_dict)), True)
105
self.assertIs(bool(hookenv.Serializable(false_dict)), False)
107
def test_equality(self):
110
wrapped_foo = hookenv.Serializable(foo)
112
self.assertEqual(wrapped_foo, foo)
113
self.assertEqual(wrapped_foo, wrapped_foo)
114
self.assertNotEqual(wrapped_foo, bar)
117
class HelpersTest(TestCase):
119
super(HelpersTest, self).setUp()
120
# Reset hookenv cache for each test
123
@patch('subprocess.call')
124
def test_logs_messages_to_juju_with_default_level(self, mock_call):
127
mock_call.assert_called_with(['juju-log', 'foo'])
129
@patch('subprocess.call')
130
def test_logs_messages_with_alternative_levels(self, mock_call):
131
alternative_levels = [
138
for level in alternative_levels:
139
hookenv.log('foo', level)
140
mock_call.assert_called_with(['juju-log', '-l', level, 'foo'])
142
@patch('subprocess.check_output')
143
def test_gets_charm_config_with_scope(self, check_output):
145
check_output.return_value = json.dumps(config_data)
147
result = hookenv.config(scope='baz')
149
self.assertEqual(result, 'bar')
150
check_output.assert_called_with(['config-get', 'baz', '--format=json'])
152
# The result can be used like a string
153
self.assertEqual(result[1], 'a')
155
# ... because the result is actually a string
156
self.assert_(isinstance(result, basestring))
158
@patch('subprocess.check_output')
159
def test_gets_missing_charm_config_with_scope(self, check_output):
160
check_output.return_value = ''
162
result = hookenv.config(scope='baz')
164
self.assertEqual(result, None)
165
check_output.assert_called_with(['config-get', 'baz', '--format=json'])
167
@patch('charmhelpers.core.hookenv.os')
168
def test_gets_the_local_unit(self, os_):
170
'JUJU_UNIT_NAME': 'foo',
173
self.assertEqual(hookenv.local_unit(), 'foo')
175
@patch('charmhelpers.core.hookenv.unit_get')
176
def test_gets_unit_private_ip(self, _unitget):
177
_unitget.return_value = 'foo'
178
self.assertEqual("foo", hookenv.unit_private_ip())
179
_unitget.assert_called_with('private-address')
181
@patch('charmhelpers.core.hookenv.os')
182
def test_checks_that_is_running_in_relation_hook(self, os_):
184
'JUJU_RELATION': 'foo',
187
self.assertTrue(hookenv.in_relation_hook())
189
@patch('charmhelpers.core.hookenv.os')
190
def test_checks_that_is_not_running_in_relation_hook(self, os_):
195
self.assertFalse(hookenv.in_relation_hook())
197
@patch('charmhelpers.core.hookenv.os')
198
def test_gets_the_relation_type(self, os_):
200
'JUJU_RELATION': 'foo',
203
self.assertEqual(hookenv.relation_type(), 'foo')
205
@patch('charmhelpers.core.hookenv.os')
206
def test_relation_type_none_if_not_in_environment(self, os_):
208
self.assertEqual(hookenv.relation_type(), None)
210
@patch('subprocess.check_output')
211
@patch('charmhelpers.core.hookenv.relation_type')
212
def test_gets_relation_ids(self, relation_type, check_output):
214
check_output.return_value = json.dumps(ids)
216
relation_type.return_value = reltype
218
result = hookenv.relation_ids()
220
self.assertEqual(result, ids)
221
check_output.assert_called_with(['relation-ids', '--format=json',
224
@patch('subprocess.check_output')
225
@patch('charmhelpers.core.hookenv.relation_type')
226
def test_gets_relation_ids_empty_array(self, relation_type, check_output):
228
check_output.return_value = json.dumps(None)
230
relation_type.return_value = reltype
232
result = hookenv.relation_ids()
234
self.assertEqual(result, ids)
235
check_output.assert_called_with(['relation-ids', '--format=json',
238
@patch('subprocess.check_output')
239
@patch('charmhelpers.core.hookenv.relation_type')
240
def test_relation_ids_no_relation_type(self, relation_type, check_output):
242
check_output.return_value = json.dumps(ids)
243
relation_type.return_value = None
245
result = hookenv.relation_ids()
247
self.assertEqual(result, [])
249
@patch('subprocess.check_output')
250
@patch('charmhelpers.core.hookenv.relation_type')
251
def test_gets_relation_ids_for_type(self, relation_type, check_output):
253
check_output.return_value = json.dumps(ids)
256
result = hookenv.relation_ids(reltype)
258
self.assertEqual(result, ids)
259
check_output.assert_called_with(['relation-ids', '--format=json',
261
self.assertFalse(relation_type.called)
263
@patch('subprocess.check_output')
264
@patch('charmhelpers.core.hookenv.relation_id')
265
def test_gets_related_units(self, relation_id, check_output):
267
units = ['foo', 'bar']
268
relation_id.return_value = relid
269
check_output.return_value = json.dumps(units)
271
result = hookenv.related_units()
273
self.assertEqual(result, units)
274
check_output.assert_called_with(['relation-list', '--format=json',
277
@patch('subprocess.check_output')
278
@patch('charmhelpers.core.hookenv.relation_id')
279
def test_gets_related_units_empty_array(self, relation_id, check_output):
282
relation_id.return_value = relid
283
check_output.return_value = json.dumps(None)
285
result = hookenv.related_units()
287
self.assertEqual(result, units)
288
check_output.assert_called_with(['relation-list', '--format=json',
291
@patch('subprocess.check_output')
292
@patch('charmhelpers.core.hookenv.relation_id')
293
def test_related_units_no_relation(self, relation_id, check_output):
294
units = ['foo', 'bar']
295
relation_id.return_value = None
296
check_output.return_value = json.dumps(units)
298
result = hookenv.related_units()
300
self.assertEqual(result, units)
301
check_output.assert_called_with(['relation-list', '--format=json'])
303
@patch('subprocess.check_output')
304
@patch('charmhelpers.core.hookenv.relation_id')
305
def test_gets_related_units_for_id(self, relation_id, check_output):
307
units = ['foo', 'bar']
308
check_output.return_value = json.dumps(units)
310
result = hookenv.related_units(relid)
312
self.assertEqual(result, units)
313
check_output.assert_called_with(['relation-list', '--format=json',
315
self.assertFalse(relation_id.called)
317
@patch('charmhelpers.core.hookenv.os')
318
def test_gets_the_remote_unit(self, os_):
320
'JUJU_REMOTE_UNIT': 'foo',
323
self.assertEqual(hookenv.remote_unit(), 'foo')
325
@patch('charmhelpers.core.hookenv.remote_unit')
326
@patch('charmhelpers.core.hookenv.relation_get')
327
def test_gets_relation_for_unit(self, relation_get, remote_unit):
332
remote_unit.return_value = unit
333
relation_get.return_value = raw_relation
335
result = hookenv.relation_for_unit()
337
self.assertEqual(result['__unit__'], unit)
338
self.assertEqual(result['foo'], 'bar')
339
relation_get.assert_called_with(unit=unit, rid=None)
341
@patch('charmhelpers.core.hookenv.remote_unit')
342
@patch('charmhelpers.core.hookenv.relation_get')
343
def test_gets_relation_for_unit_with_list(self, relation_get, remote_unit):
346
'foo-list': 'one two three',
348
remote_unit.return_value = unit
349
relation_get.return_value = raw_relation
351
result = hookenv.relation_for_unit()
353
self.assertEqual(result['__unit__'], unit)
354
self.assertEqual(result['foo-list'], ['one', 'two', 'three'])
355
relation_get.assert_called_with(unit=unit, rid=None)
357
@patch('charmhelpers.core.hookenv.remote_unit')
358
@patch('charmhelpers.core.hookenv.relation_get')
359
def test_gets_relation_for_specific_unit(self, relation_get, remote_unit):
364
relation_get.return_value = raw_relation
366
result = hookenv.relation_for_unit(unit)
368
self.assertEqual(result['__unit__'], unit)
369
self.assertEqual(result['foo'], 'bar')
370
relation_get.assert_called_with(unit=unit, rid=None)
371
self.assertFalse(remote_unit.called)
373
@patch('charmhelpers.core.hookenv.relation_ids')
374
@patch('charmhelpers.core.hookenv.related_units')
375
@patch('charmhelpers.core.hookenv.relation_for_unit')
376
def test_gets_relations_for_id(self, relation_for_unit, related_units,
379
units = ['foo', 'bar']
381
{'foo-item': 'bar-item'},
382
{'foo-item2': 'bar-item2'},
384
relation_ids.return_value = relid
385
related_units.return_value = units
386
relation_for_unit.side_effect = unit_data
388
result = hookenv.relations_for_id()
390
self.assertEqual(result[0]['__relid__'], relid)
391
self.assertEqual(result[0]['foo-item'], 'bar-item')
392
self.assertEqual(result[1]['__relid__'], relid)
393
self.assertEqual(result[1]['foo-item2'], 'bar-item2')
394
related_units.assert_called_with(relid)
395
self.assertEqual(relation_for_unit.mock_calls, [
400
@patch('charmhelpers.core.hookenv.relation_ids')
401
@patch('charmhelpers.core.hookenv.related_units')
402
@patch('charmhelpers.core.hookenv.relation_for_unit')
403
def test_gets_relations_for_specific_id(self, relation_for_unit,
404
related_units, relation_ids):
406
units = ['foo', 'bar']
408
{'foo-item': 'bar-item'},
409
{'foo-item2': 'bar-item2'},
411
related_units.return_value = units
412
relation_for_unit.side_effect = unit_data
414
result = hookenv.relations_for_id(relid)
416
self.assertEqual(result[0]['__relid__'], relid)
417
self.assertEqual(result[0]['foo-item'], 'bar-item')
418
self.assertEqual(result[1]['__relid__'], relid)
419
self.assertEqual(result[1]['foo-item2'], 'bar-item2')
420
related_units.assert_called_with(relid)
421
self.assertEqual(relation_for_unit.mock_calls, [
425
self.assertFalse(relation_ids.called)
427
@patch('charmhelpers.core.hookenv.in_relation_hook')
428
@patch('charmhelpers.core.hookenv.relation_type')
429
@patch('charmhelpers.core.hookenv.relation_ids')
430
@patch('charmhelpers.core.hookenv.relations_for_id')
431
def test_gets_relations_for_type(self, relations_for_id, relation_ids,
432
relation_type, in_relation_hook):
445
is_in_relation = True
447
relation_type.return_value = reltype
448
relation_ids.return_value = relids
449
relations_for_id.side_effect = relations
450
in_relation_hook.return_value = is_in_relation
452
result = hookenv.relations_of_type()
454
self.assertEqual(result[0]['__relid__'], 123)
455
self.assertEqual(result[0]['foo'], 'bar')
456
self.assertEqual(result[1]['__relid__'], 123)
457
self.assertEqual(result[1]['foo2'], 'bar2')
458
self.assertEqual(result[2]['__relid__'], 234)
459
self.assertEqual(result[2]['FOO'], 'BAR')
460
self.assertEqual(result[3]['__relid__'], 234)
461
self.assertEqual(result[3]['FOO2'], 'BAR2')
462
relation_ids.assert_called_with(reltype)
463
self.assertEqual(relations_for_id.mock_calls, [
468
@patch('charmhelpers.core.hookenv.local_unit')
469
@patch('charmhelpers.core.hookenv.relation_types')
470
@patch('charmhelpers.core.hookenv.relation_ids')
471
@patch('charmhelpers.core.hookenv.related_units')
472
@patch('charmhelpers.core.hookenv.relation_get')
473
def test_gets_relations(self, relation_get, related_units,
474
relation_ids, relation_types, local_unit):
475
local_unit.return_value = 'u0'
476
relation_types.return_value = ['t1', 't2']
477
relation_ids.return_value = ['i1']
478
related_units.return_value = ['u1', 'u2']
479
relation_get.return_value = {'key': 'val'}
481
result = hookenv.relations()
483
self.assertEqual(result, {
486
'u0': {'key': 'val'},
487
'u1': {'key': 'val'},
488
'u2': {'key': 'val'},
493
'u0': {'key': 'val'},
494
'u1': {'key': 'val'},
495
'u2': {'key': 'val'},
500
@patch('charmhelpers.core.hookenv.config')
501
@patch('charmhelpers.core.hookenv.relation_type')
502
@patch('charmhelpers.core.hookenv.local_unit')
503
@patch('charmhelpers.core.hookenv.relation_id')
504
@patch('charmhelpers.core.hookenv.relations')
505
@patch('charmhelpers.core.hookenv.relation_get')
506
@patch('charmhelpers.core.hookenv.os')
507
def test_gets_execution_environment(self, os_, relations_get,
508
relations, relation_id, local_unit,
509
relation_type, config):
510
config.return_value = 'some-config'
511
relation_type.return_value = 'some-type'
512
local_unit.return_value = 'some-unit'
513
relation_id.return_value = 'some-id'
514
relations.return_value = 'all-relations'
515
relations_get.return_value = 'some-relations'
516
os_.environ = 'some-environment'
518
result = hookenv.execution_environment()
520
self.assertEqual(result, {
521
'conf': 'some-config',
522
'reltype': 'some-type',
525
'rel': 'some-relations',
526
'rels': 'all-relations',
527
'env': 'some-environment',
530
@patch('charmhelpers.core.hookenv.config')
531
@patch('charmhelpers.core.hookenv.relation_type')
532
@patch('charmhelpers.core.hookenv.local_unit')
533
@patch('charmhelpers.core.hookenv.relation_id')
534
@patch('charmhelpers.core.hookenv.relations')
535
@patch('charmhelpers.core.hookenv.relation_get')
536
@patch('charmhelpers.core.hookenv.os')
537
def test_gets_execution_environment_no_relation(
538
self, os_, relations_get, relations, relation_id,
539
local_unit, relation_type, config):
540
config.return_value = 'some-config'
541
relation_type.return_value = 'some-type'
542
local_unit.return_value = 'some-unit'
543
relation_id.return_value = None
544
relations.return_value = 'all-relations'
545
relations_get.return_value = 'some-relations'
546
os_.environ = 'some-environment'
548
result = hookenv.execution_environment()
550
self.assertEqual(result, {
551
'conf': 'some-config',
553
'rels': 'all-relations',
554
'env': 'some-environment',
557
@patch('charmhelpers.core.hookenv.os')
558
def test_gets_the_relation_id(self, os_):
560
'JUJU_RELATION_ID': 'foo',
563
self.assertEqual(hookenv.relation_id(), 'foo')
565
@patch('charmhelpers.core.hookenv.os')
566
def test_relation_id_none_if_no_env(self, os_):
568
self.assertEqual(hookenv.relation_id(), None)
570
@patch('subprocess.check_output')
571
def test_gets_relation(self, check_output):
572
data = {"foo": "BAR"}
573
check_output.return_value = json.dumps(data)
574
result = hookenv.relation_get()
576
self.assertEqual(result['foo'], 'BAR')
577
check_output.assert_called_with(['relation-get', '--format=json', '-'])
579
@patch('charmhelpers.core.hookenv.subprocess')
580
def test_relation_get_none(self, mock_subprocess):
581
mock_subprocess.check_output.return_value = 'null'
583
result = hookenv.relation_get()
585
self.assertIsNone(result)
587
@patch('subprocess.check_output')
588
def test_gets_relation_with_scope(self, check_output):
589
check_output.return_value = json.dumps('bar')
591
result = hookenv.relation_get(attribute='baz-scope')
593
self.assertEqual(result, 'bar')
594
check_output.assert_called_with(['relation-get', '--format=json',
597
@patch('subprocess.check_output')
598
def test_gets_missing_relation_with_scope(self, check_output):
599
check_output.return_value = ""
601
result = hookenv.relation_get(attribute='baz-scope')
603
self.assertEqual(result, None)
604
check_output.assert_called_with(['relation-get', '--format=json',
607
@patch('subprocess.check_output')
608
def test_gets_relation_with_unit_name(self, check_output):
609
check_output.return_value = json.dumps('BAR')
611
result = hookenv.relation_get(attribute='baz-scope', unit='baz-unit')
613
self.assertEqual(result, 'BAR')
614
check_output.assert_called_with(['relation-get', '--format=json',
615
'baz-scope', 'baz-unit'])
617
@patch('charmhelpers.core.hookenv.local_unit')
618
@patch('subprocess.check_call')
619
@patch('subprocess.check_output')
620
def test_relation_set_flushes_local_unit_cache(self, check_output,
621
check_call, local_unit):
622
check_output.return_value = json.dumps('BAR')
623
local_unit.return_value = 'baz_unit'
624
hookenv.relation_get(attribute='baz_scope', unit='baz_unit')
625
hookenv.relation_get(attribute='bar_scope')
626
self.assertTrue(len(hookenv.cache) == 2)
627
hookenv.relation_set(baz_scope='hello')
628
# relation_set should flush any entries for local_unit
629
self.assertTrue(len(hookenv.cache) == 1)
631
@patch('subprocess.check_output')
632
def test_gets_relation_with_relation_id(self, check_output):
633
check_output.return_value = json.dumps('BAR')
635
result = hookenv.relation_get(attribute='baz-scope', unit='baz-unit',
638
self.assertEqual(result, 'BAR')
639
check_output.assert_called_with(['relation-get', '--format=json', '-r',
640
123, 'baz-scope', 'baz-unit'])
642
@patch('subprocess.check_call')
643
def test_sets_relation_with_kwargs(self, check_call_):
644
hookenv.relation_set(foo="bar")
645
check_call_.assert_called_with(['relation-set', 'foo=bar'])
647
@patch('subprocess.check_call')
648
def test_sets_relation_with_dict(self, check_call_):
649
hookenv.relation_set(relation_settings={"foo": "bar"})
650
check_call_.assert_called_with(['relation-set', 'foo=bar'])
652
@patch('subprocess.check_call')
653
def test_sets_relation_with_relation_id(self, check_call_):
654
hookenv.relation_set(relation_id="foo", bar="baz")
655
check_call_.assert_called_with(['relation-set', '-r', 'foo',
658
@patch('subprocess.check_call')
659
def test_sets_relation_with_missing_value(self, check_call_):
660
hookenv.relation_set(foo=None)
661
check_call_.assert_called_with(['relation-set', 'foo='])
663
def test_lists_relation_types(self):
665
open_.return_value = StringIO(CHARM_METADATA)
666
with patch('charmhelpers.core.hookenv.open', open_, create=True):
667
with patch.dict('os.environ', {'CHARM_DIR': '/var/empty'}):
668
reltypes = set(hookenv.relation_types())
669
open_.assert_called_once_with('/var/empty/metadata.yaml')
670
self.assertEqual(set(('testreqs', 'testprov', 'testpeer')), reltypes)
672
@patch('subprocess.check_call')
673
def test_opens_port(self, check_call_):
674
hookenv.open_port(443, "TCP")
675
hookenv.open_port(80)
676
hookenv.open_port(100, "UDP")
678
call(['open-port', '443/TCP']),
679
call(['open-port', '80/TCP']),
680
call(['open-port', '100/UDP']),
682
check_call_.assert_has_calls(calls)
684
@patch('subprocess.check_call')
685
def test_closes_port(self, check_call_):
686
hookenv.close_port(443, "TCP")
687
hookenv.close_port(80)
688
hookenv.close_port(100, "UDP")
690
call(['close-port', '443/TCP']),
691
call(['close-port', '80/TCP']),
692
call(['close-port', '100/UDP']),
694
check_call_.assert_has_calls(calls)
696
@patch('subprocess.check_output')
697
def test_gets_unit_attribute(self, check_output_):
698
check_output_.return_value = json.dumps('bar')
699
self.assertEqual(hookenv.unit_get('foo'), 'bar')
700
check_output_.assert_called_with(['unit-get', '--format=json', 'foo'])
702
@patch('subprocess.check_output')
703
def test_gets_missing_unit_attribute(self, check_output_):
704
check_output_.return_value = ""
705
self.assertEqual(hookenv.unit_get('foo'), None)
706
check_output_.assert_called_with(['unit-get', '--format=json', 'foo'])
708
def test_cached_decorator(self):
717
def cache_function(attribute):
718
calls.append(attribute)
719
return values[attribute]
721
self.assertEquals(cache_function('hello'), 'world')
722
self.assertEquals(cache_function('hello'), 'world')
723
self.assertEquals(cache_function('foo'), 'bar')
724
self.assertEquals(cache_function('baz'), None)
725
self.assertEquals(cache_function('baz'), None)
726
self.assertEquals(calls, ['hello', 'foo', 'baz'])
728
def test_gets_charm_dir(self):
729
with patch.dict('os.environ', {'CHARM_DIR': '/var/empty'}):
730
self.assertEqual(hookenv.charm_dir(), '/var/empty')
733
class HooksTest(TestCase):
734
def test_runs_a_registered_function(self):
736
hooks = hookenv.Hooks()
737
hooks.register('foo', foo)
739
hooks.execute(['foo', 'some', 'other', 'args'])
741
foo.assert_called_with()
743
def test_cannot_run_unregistered_function(self):
745
hooks = hookenv.Hooks()
746
hooks.register('foo', foo)
748
self.assertRaises(hookenv.UnregisteredHookError, hooks.execute,
751
def test_can_run_a_decorated_function_as_one_or_more_hooks(self):
753
hooks = hookenv.Hooks()
755
@hooks.hook('bar', 'baz')
759
hooks.execute(['bar'])
760
hooks.execute(['baz'])
761
self.assertRaises(hookenv.UnregisteredHookError, hooks.execute,
763
self.assertEqual(execs, [True, True])
765
def test_can_run_a_decorated_function_as_itself(self):
767
hooks = hookenv.Hooks()
773
hooks.execute(['func'])
774
self.assertRaises(hookenv.UnregisteredHookError, hooks.execute,
776
self.assertEqual(execs, [True])
778
def test_magic_underscores(self):
779
# Juju hook names use hypens as separators. Python functions use
780
# underscores. If explicit names have not been provided, hooks
781
# are registered with both the function name and the function
782
# name with underscores replaced with hypens for convenience.
784
hooks = hookenv.Hooks()
790
hooks.execute(['call-me-maybe'])
791
hooks.execute(['call_me_maybe'])
792
self.assertEqual(execs, [True, True])
794
@patch('charmhelpers.core.hookenv.local_unit')
795
def test_gets_service_name(self, _unit):
796
_unit.return_value = 'mysql/3'
797
self.assertEqual(hookenv.service_name(), 'mysql')