113
69
self.assertEqual('bar is in state baz', str(e))
116
class ClientTest(FakeHomeTestCase):
72
class TestEnvJujuClient(TestCase):
119
super(ClientTest, self).setUp()
120
75
patcher = patch('jujupy.pause')
121
76
self.addCleanup(patcher.stop)
122
77
self.pause_mock = patcher.start()
125
class CloudSigmaTest:
127
def test__shell_environ_no_flags(self):
128
client = self.client_class(
129
SimpleEnvironment('baz', {'type': 'ec2'}), '1.25-foobar', 'path')
130
env = client._shell_environ()
131
self.assertEqual(env.get(JUJU_DEV_FEATURE_FLAGS, ''), '')
133
def test__shell_environ_cloudsigma(self):
134
client = self.client_class(
135
SimpleEnvironment('baz', {'type': 'cloudsigma'}),
136
'1.25-foobar', 'path')
137
env = client._shell_environ()
138
self.assertTrue('cloudsigma' in env[JUJU_DEV_FEATURE_FLAGS].split(","))
140
def test__shell_environ_juju_home(self):
141
client = self.client_class(
142
SimpleEnvironment('baz', {'type': 'ec2'}), '1.25-foobar', 'path',
144
env = client._shell_environ()
145
self.assertEqual(env['JUJU_HOME'], 'asdf')
148
class TestTempYamlFile(TestCase):
150
def test_temp_yaml_file(self):
151
with temp_yaml_file({'foo': 'bar'}) as yaml_file:
152
with open(yaml_file) as f:
153
self.assertEqual({'foo': 'bar'}, yaml.safe_load(f))
156
class TestJuju2Backend(TestCase):
158
test_environ = {'PATH': 'foo:bar'}
160
def test_juju2_backend(self):
161
backend = Juju2Backend('/bin/path', '2.0', set(), False)
162
self.assertEqual('/bin/path', backend.full_path)
163
self.assertEqual('2.0', backend.version)
165
def test_clone_retains_soft_deadline(self):
166
soft_deadline = object()
167
backend = Juju2Backend('/bin/path', '2.0', feature_flags=set(),
168
debug=False, soft_deadline=soft_deadline)
169
cloned = backend.clone(full_path=None, version=None, debug=None,
171
self.assertIsNot(cloned, backend)
172
self.assertIs(soft_deadline, cloned.soft_deadline)
174
def test__check_timeouts(self):
175
backend = Juju2Backend('/bin/path', '2.0', set(), debug=False,
176
soft_deadline=datetime(2015, 1, 2, 3, 4, 5))
177
with patch('jujupy.Juju2Backend._now',
178
return_value=backend.soft_deadline):
179
with backend._check_timeouts():
181
now = backend.soft_deadline + timedelta(seconds=1)
182
with patch('jujupy.Juju2Backend._now', return_value=now):
183
with self.assertRaisesRegexp(SoftDeadlineExceeded,
184
'Operation exceeded deadline.'):
185
with backend._check_timeouts():
188
def test__check_timeouts_no_deadline(self):
189
backend = Juju2Backend('/bin/path', '2.0', set(), debug=False,
191
now = datetime(2015, 1, 2, 3, 4, 6)
192
with patch('jujupy.Juju2Backend._now', return_value=now):
193
with backend._check_timeouts():
196
def test_ignore_soft_deadline_check_timeouts(self):
197
backend = Juju2Backend('/bin/path', '2.0', set(), debug=False,
198
soft_deadline=datetime(2015, 1, 2, 3, 4, 5))
199
now = backend.soft_deadline + timedelta(seconds=1)
200
with patch('jujupy.Juju2Backend._now', return_value=now):
201
with backend.ignore_soft_deadline():
202
with backend._check_timeouts():
204
with self.assertRaisesRegexp(SoftDeadlineExceeded,
205
'Operation exceeded deadline.'):
206
with backend._check_timeouts():
209
def test_juju_checks_timeouts(self):
210
backend = Juju2Backend('/bin/path', '2.0', set(), debug=False,
211
soft_deadline=datetime(2015, 1, 2, 3, 4, 5))
212
with patch('subprocess.check_call'):
213
with patch('jujupy.Juju2Backend._now',
214
return_value=backend.soft_deadline):
215
backend.juju('cmd', ('args',), [], 'home')
216
now = backend.soft_deadline + timedelta(seconds=1)
217
with patch('jujupy.Juju2Backend._now', return_value=now):
218
with self.assertRaisesRegexp(SoftDeadlineExceeded,
219
'Operation exceeded deadline.'):
220
backend.juju('cmd', ('args',), [], 'home')
222
def test_juju_async_checks_timeouts(self):
223
backend = Juju2Backend('/bin/path', '2.0', set(), debug=False,
224
soft_deadline=datetime(2015, 1, 2, 3, 4, 5))
225
with patch('subprocess.Popen') as mock_popen:
226
mock_popen.return_value.wait.return_value = 0
227
with patch('jujupy.Juju2Backend._now',
228
return_value=backend.soft_deadline):
229
with backend.juju_async('cmd', ('args',), [], 'home'):
231
now = backend.soft_deadline + timedelta(seconds=1)
232
with patch('jujupy.Juju2Backend._now', return_value=now):
233
with self.assertRaisesRegexp(SoftDeadlineExceeded,
234
'Operation exceeded deadline.'):
235
with backend.juju_async('cmd', ('args',), [], 'home'):
238
def test_get_juju_output_checks_timeouts(self):
239
backend = Juju2Backend('/bin/path', '2.0', set(), debug=False,
240
soft_deadline=datetime(2015, 1, 2, 3, 4, 5))
241
with patch('subprocess.Popen') as mock_popen:
242
mock_popen.return_value.returncode = 0
243
mock_popen.return_value.communicate.return_value = ('', '')
244
with patch('jujupy.Juju2Backend._now',
245
return_value=backend.soft_deadline):
246
backend.get_juju_output('cmd', ('args',), [], 'home')
247
now = backend.soft_deadline + timedelta(seconds=1)
248
with patch('jujupy.Juju2Backend._now', return_value=now):
249
with self.assertRaisesRegexp(SoftDeadlineExceeded,
250
'Operation exceeded deadline.'):
251
backend.get_juju_output('cmd', ('args',), [], 'home')
254
class TestEnvJujuClient26(ClientTest, CloudSigmaTest):
256
client_class = EnvJujuClient26
258
def test_enable_jes_already_supported(self):
259
client = self.client_class(
260
SimpleEnvironment('baz', {}),
261
'1.26-foobar', 'path')
262
fake_popen = FakePopen(CONTROLLER, '', 0)
263
with patch('subprocess.Popen', autospec=True,
264
return_value=fake_popen) as po_mock:
265
with self.assertRaises(JESByDefault):
267
self.assertNotIn('jes', client.feature_flags)
269
self, po_mock, client, ('path', '--show-log', 'help', 'commands'))
271
def test_enable_jes_unsupported(self):
272
client = self.client_class(
273
SimpleEnvironment('baz', {}),
274
'1.24-foobar', 'path')
275
with patch('subprocess.Popen', autospec=True,
276
return_value=FakePopen('', '', 0)) as po_mock:
277
with self.assertRaises(JESNotSupported):
279
self.assertNotIn('jes', client.feature_flags)
281
self, po_mock, client, ('path', '--show-log', 'help', 'commands'),
284
self, po_mock, client, ('path', '--show-log', 'help', 'commands'),
286
self.assertEqual(po_mock.call_count, 2)
288
def test_enable_jes_requires_flag(self):
289
client = self.client_class(
290
SimpleEnvironment('baz', {}),
291
'1.25-foobar', 'path')
292
# The help output will change when the jes feature flag is set.
293
with patch('subprocess.Popen', autospec=True, side_effect=[
294
FakePopen('', '', 0),
295
FakePopen(SYSTEM, '', 0)]) as po_mock:
297
self.assertIn('jes', client.feature_flags)
299
self, po_mock, client, ('path', '--show-log', 'help', 'commands'),
301
# GZ 2015-10-26: Should assert that env has feature flag at call time.
303
self, po_mock, client, ('path', '--show-log', 'help', 'commands'),
305
self.assertEqual(po_mock.call_count, 2)
307
def test_disable_jes(self):
308
client = self.client_class(
309
SimpleEnvironment('baz', {}),
310
'1.25-foobar', 'path')
311
client.feature_flags.add('jes')
313
self.assertNotIn('jes', client.feature_flags)
315
def test__shell_environ_jes(self):
316
client = self.client_class(
317
SimpleEnvironment('baz', {}),
318
'1.25-foobar', 'path')
319
client.feature_flags.add('jes')
320
env = client._shell_environ()
321
self.assertIn('jes', env[JUJU_DEV_FEATURE_FLAGS].split(","))
323
def test__shell_environ_jes_cloudsigma(self):
324
client = self.client_class(
325
SimpleEnvironment('baz', {'type': 'cloudsigma'}),
326
'1.25-foobar', 'path')
327
client.feature_flags.add('jes')
328
env = client._shell_environ()
329
flags = env[JUJU_DEV_FEATURE_FLAGS].split(",")
330
self.assertItemsEqual(['cloudsigma', 'jes'], flags)
332
def test_clone_unchanged(self):
333
client1 = self.client_class(
334
SimpleEnvironment('foo'), '1.27', 'full/path', debug=True)
335
client2 = client1.clone()
336
self.assertIsNot(client1, client2)
337
self.assertIs(type(client1), type(client2))
338
self.assertIs(client1.env, client2.env)
339
self.assertEqual(client1.version, client2.version)
340
self.assertEqual(client1.full_path, client2.full_path)
341
self.assertIs(client1.debug, client2.debug)
342
self.assertEqual(client1._backend, client2._backend)
344
def test_clone_changed(self):
345
client1 = self.client_class(
346
SimpleEnvironment('foo'), '1.27', 'full/path', debug=True)
347
env2 = SimpleEnvironment('bar')
348
client2 = client1.clone(env2, '1.28', 'other/path', debug=False,
350
self.assertIs(EnvJujuClient1X, type(client2))
351
self.assertIs(env2, client2.env)
352
self.assertEqual('1.28', client2.version)
353
self.assertEqual('other/path', client2.full_path)
354
self.assertIs(False, client2.debug)
356
def test_clone_defaults(self):
357
client1 = self.client_class(
358
SimpleEnvironment('foo'), '1.27', 'full/path', debug=True)
359
client2 = client1.clone()
360
self.assertIsNot(client1, client2)
361
self.assertIs(self.client_class, type(client2))
362
self.assertEqual(set(), client2.feature_flags)
364
def test_clone_enabled(self):
365
client1 = self.client_class(
366
SimpleEnvironment('foo'), '1.27', 'full/path', debug=True)
367
client1.enable_feature('jes')
368
client1.enable_feature('address-allocation')
369
client2 = client1.clone()
370
self.assertIsNot(client1, client2)
371
self.assertIs(self.client_class, type(client2))
373
set(['jes', 'address-allocation']),
374
client2.feature_flags)
376
def test_clone_old_feature(self):
377
client1 = self.client_class(
378
SimpleEnvironment('foo'), '1.27', 'full/path', debug=True)
379
client1.enable_feature('actions')
380
client1.enable_feature('address-allocation')
381
client2 = client1.clone()
382
self.assertIsNot(client1, client2)
383
self.assertIs(self.client_class, type(client2))
384
self.assertEqual(set(['address-allocation']), client2.feature_flags)
387
class TestEnvJujuClient25(TestEnvJujuClient26):
389
client_class = EnvJujuClient25
392
class TestEnvJujuClient22(ClientTest):
394
client_class = EnvJujuClient22
396
def test__shell_environ(self):
397
client = self.client_class(
398
SimpleEnvironment('baz', {'type': 'ec2'}), '1.22-foobar', 'path')
399
env = client._shell_environ()
400
self.assertEqual(env.get(JUJU_DEV_FEATURE_FLAGS), 'actions')
402
def test__shell_environ_juju_home(self):
403
client = self.client_class(
404
SimpleEnvironment('baz', {'type': 'ec2'}), '1.22-foobar', 'path',
406
env = client._shell_environ()
407
self.assertEqual(env['JUJU_HOME'], 'asdf')
410
class TestEnvJujuClient24(ClientTest, CloudSigmaTest):
412
client_class = EnvJujuClient24
414
def test_no_jes(self):
415
client = self.client_class(
416
SimpleEnvironment('baz', {'type': 'cloudsigma'}),
417
'1.25-foobar', 'path')
418
with self.assertRaises(JESNotSupported):
420
client._use_jes = True
421
env = client._shell_environ()
422
self.assertNotIn('jes', env[JUJU_DEV_FEATURE_FLAGS].split(","))
424
def test_add_ssh_machines(self):
425
client = self.client_class(SimpleEnvironment('foo', {}), None, 'juju')
426
with patch('subprocess.check_call', autospec=True) as cc_mock:
427
client.add_ssh_machines(['m-foo', 'm-bar', 'm-baz'])
428
assert_juju_call(self, cc_mock, client, (
429
'juju', '--show-log', 'add-machine', '-e', 'foo', 'ssh:m-foo'), 0)
430
assert_juju_call(self, cc_mock, client, (
431
'juju', '--show-log', 'add-machine', '-e', 'foo', 'ssh:m-bar'), 1)
432
assert_juju_call(self, cc_mock, client, (
433
'juju', '--show-log', 'add-machine', '-e', 'foo', 'ssh:m-baz'), 2)
434
self.assertEqual(cc_mock.call_count, 3)
436
def test_add_ssh_machines_no_retry(self):
437
client = self.client_class(SimpleEnvironment('foo', {}), None, 'juju')
438
with patch('subprocess.check_call', autospec=True,
439
side_effect=[subprocess.CalledProcessError(None, None),
440
None, None, None]) as cc_mock:
441
with self.assertRaises(subprocess.CalledProcessError):
442
client.add_ssh_machines(['m-foo', 'm-bar', 'm-baz'])
443
assert_juju_call(self, cc_mock, client, (
444
'juju', '--show-log', 'add-machine', '-e', 'foo', 'ssh:m-foo'))
447
class TestTearDown(TestCase):
449
def test_tear_down_no_jes(self):
451
client.destroy_environment.return_value = 0
452
tear_down(client, False)
453
client.destroy_environment.assert_called_once_with(force=False)
454
self.assertEqual(0, client.kill_controller.call_count)
455
self.assertEqual(0, client.disable_jes.call_count)
457
def test_tear_down_no_jes_exception(self):
459
client.destroy_environment.side_effect = [1, 0]
460
tear_down(client, False)
462
client.destroy_environment.mock_calls,
463
[call(force=False), call(force=True)])
464
self.assertEqual(0, client.kill_controller.call_count)
465
self.assertEqual(0, client.disable_jes.call_count)
467
def test_tear_down_jes(self):
469
tear_down(client, True)
470
client.kill_controller.assert_called_once_with()
471
self.assertEqual(0, client.destroy_environment.call_count)
472
self.assertEqual(0, client.enable_jes.call_count)
473
self.assertEqual(0, client.disable_jes.call_count)
475
def test_tear_down_try_jes(self):
478
client.enable_jes.assert_called_once_with()
479
self.assertEqual(0, client.disable_jes.call_count)
482
client.kill_controller.side_effect = check_jes
484
tear_down(client, jes_enabled=False, try_jes=True)
485
client.kill_controller.assert_called_once_with()
486
client.disable_jes.assert_called_once_with()
488
def test_tear_down_jes_try_jes(self):
490
tear_down(client, jes_enabled=True, try_jes=True)
491
client.kill_controller.assert_called_once_with()
492
self.assertEqual(0, client.destroy_environment.call_count)
493
self.assertEqual(0, client.enable_jes.call_count)
494
self.assertEqual(0, client.disable_jes.call_count)
496
def test_tear_down_try_jes_not_supported(self):
498
def check_jes(force=True):
499
client.enable_jes.assert_called_once_with()
503
client.enable_jes.side_effect = JESNotSupported
504
client.destroy_environment.side_effect = check_jes
506
tear_down(client, jes_enabled=False, try_jes=True)
507
client.destroy_environment.assert_called_once_with(force=False)
508
self.assertEqual(0, client.disable_jes.call_count)
511
class FakePopen(object):
513
def __init__(self, out, err, returncode):
516
self._code = returncode
518
def communicate(self):
519
self.returncode = self._code
520
return self._out, self._err
527
def observable_temp_file():
528
temporary_file = NamedTemporaryFile(delete=False)
530
with temporary_file as temp_file:
531
with patch('jujupy.NamedTemporaryFile',
532
return_value=temp_file):
533
with patch.object(temp_file, '__exit__'):
537
os.unlink(temporary_file.name)
539
# File may have already been deleted, e.g. by temp_yaml_file.
540
if e.errno != errno.ENOENT:
544
class TestClientFromConfig(ClientTest):
546
@patch.object(JujuData, 'from_config', return_value=JujuData('', {}))
547
@patch.object(SimpleEnvironment, 'from_config',
548
return_value=SimpleEnvironment('', {}))
549
@patch.object(EnvJujuClient, 'get_full_path', return_value='fake-path')
550
def test_from_config(self, gfp_mock, se_fc_mock, jd_fc_mock):
79
def test_get_version(self):
81
with patch('subprocess.check_output', return_value=value) as vsn:
82
version = EnvJujuClient.get_version()
83
self.assertEqual('5.6', version)
84
vsn.assert_called_with(('juju', '--version'))
86
def test_get_version_path(self):
87
with patch('subprocess.check_output', return_value=' 4.3') as vsn:
88
EnvJujuClient.get_version('foo/bar/baz')
89
vsn.assert_called_once_with(('foo/bar/baz', '--version'))
91
def test_get_matching_agent_version(self):
92
client = EnvJujuClient(SimpleEnvironment(None, {'type': 'local'}),
93
'1.23-series-arch', None)
94
self.assertEqual('1.23.1', client.get_matching_agent_version())
95
self.assertEqual('1.23', client.get_matching_agent_version(
97
client.version = '1.20-beta1-series-arch'
98
self.assertEqual('1.20-beta1.1', client.get_matching_agent_version())
100
def test_upgrade_juju_nonlocal(self):
101
client = EnvJujuClient(
102
SimpleEnvironment('foo', {'type': 'nonlocal'}), '1.234-76', None)
103
with patch.object(client, 'juju') as juju_mock:
104
client.upgrade_juju()
105
juju_mock.assert_called_with(
106
'upgrade-juju', ('--version', '1.234'))
108
def test_upgrade_juju_local(self):
109
client = EnvJujuClient(
110
SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None)
111
with patch.object(client, 'juju') as juju_mock:
112
client.upgrade_juju()
113
juju_mock.assert_called_with(
114
'upgrade-juju', ('--version', '1.234', '--upload-tools',))
116
def test_upgrade_juju_no_force_version(self):
117
client = EnvJujuClient(
118
SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None)
119
with patch.object(client, 'juju') as juju_mock:
120
client.upgrade_juju(force_version=False)
121
juju_mock.assert_called_with(
122
'upgrade-juju', ('--upload-tools',))
124
def test_by_version(self):
551
125
def juju_cmd_iterator():
582
131
context = patch.object(
583
132
EnvJujuClient, 'get_version',
584
133
side_effect=juju_cmd_iterator().send)
586
self.assertIs(EnvJujuClient1X,
587
type(client_from_config('foo', None)))
135
self.assertIs(EnvJujuClient,
136
type(EnvJujuClient.by_version(None)))
588
137
with self.assertRaisesRegexp(Exception, 'Unsupported juju: 1.16'):
589
client_from_config('foo', None)
138
EnvJujuClient.by_version(None)
590
139
with self.assertRaisesRegexp(Exception,
591
140
'Unsupported juju: 1.16.1'):
592
client_from_config('foo', None)
594
def test_fc(version, cls):
595
client = client_from_config('foo', None)
596
if isinstance(client, EnvJujuClient2A2):
597
self.assertEqual(se_fc_mock.return_value, client.env)
599
self.assertEqual(jd_fc_mock.return_value, client.env)
600
self.assertIs(cls, type(client))
601
self.assertEqual(version, client.version)
603
test_fc('1.15', EnvJujuClient1X)
604
test_fc('1.22.1', EnvJujuClient22)
605
test_fc('1.24-alpha1', EnvJujuClient24)
606
test_fc('1.24.7', EnvJujuClient24)
607
test_fc('1.25.1', EnvJujuClient25)
608
test_fc('1.26.1', EnvJujuClient26)
609
test_fc('1.27.1', EnvJujuClient1X)
610
test_fc('2.0-alpha1', EnvJujuClient2A1)
611
test_fc('2.0-alpha2', EnvJujuClient2A2)
612
test_fc('2.0-alpha3', EnvJujuClient2B2)
613
test_fc('2.0-beta1', EnvJujuClient2B2)
614
test_fc('2.0-beta2', EnvJujuClient2B2)
615
test_fc('2.0-beta3', EnvJujuClient2B3)
616
test_fc('2.0-beta4', EnvJujuClient2B3)
617
test_fc('2.0-beta5', EnvJujuClient2B3)
618
test_fc('2.0-beta6', EnvJujuClient2B3)
619
test_fc('2.0-beta7', EnvJujuClient2B7)
620
test_fc('2.0-beta8', EnvJujuClient2B8)
621
test_fc('2.0-beta9', EnvJujuClient2B9)
622
test_fc('2.0-beta10', EnvJujuClient2B9)
623
test_fc('2.0-beta11', EnvJujuClient2B9)
624
test_fc('2.0-beta12', EnvJujuClient2B9)
625
test_fc('2.0-beta13', EnvJujuClient2B9)
626
test_fc('2.0-beta14', EnvJujuClient2B9)
627
test_fc('2.0-beta15', EnvJujuClient)
628
test_fc('2.0-delta1', EnvJujuClient)
629
with self.assertRaises(StopIteration):
630
client_from_config('foo', None)
632
def test_client_from_config_path(self):
141
EnvJujuClient.by_version(None)
142
client = EnvJujuClient.by_version(None)
143
self.assertIs(EnvJujuClient, type(client))
144
self.assertEqual('1.15', client.version)
146
def test_by_version_path(self):
633
147
with patch('subprocess.check_output', return_value=' 4.3') as vsn:
634
with patch.object(JujuData, 'from_config'):
635
client = client_from_config('foo', 'foo/bar/qux')
148
client = EnvJujuClient.by_version(None, 'foo/bar/qux')
636
149
vsn.assert_called_once_with(('foo/bar/qux', '--version'))
637
150
self.assertNotEqual(client.full_path, 'foo/bar/qux')
638
151
self.assertEqual(client.full_path, os.path.abspath('foo/bar/qux'))
640
def test_client_from_config_keep_home(self):
641
env = JujuData({}, juju_home='/foo/bar')
642
with patch('subprocess.check_output', return_value='2.0-alpha3-a-b'):
643
with patch.object(JujuData, 'from_config',
644
side_effect=lambda x: JujuData(x, {})):
645
client_from_config('foo', 'foo/bar/qux')
646
self.assertEqual('/foo/bar', env.juju_home)
648
def test_client_from_config_deadline(self):
649
deadline = datetime(2012, 11, 10, 9, 8, 7)
650
with patch('subprocess.check_output', return_value='2.0-alpha3-a-b'):
651
with patch.object(JujuData, 'from_config',
652
side_effect=lambda x: JujuData(x, {})):
653
client = client_from_config(
654
'foo', 'foo/bar/qux', soft_deadline=deadline)
655
self.assertEqual(client._backend.soft_deadline, deadline)
659
def client_past_deadline():
660
client = EnvJujuClient(JujuData('local', juju_home=''), None, None)
661
soft_deadline = datetime(2015, 1, 2, 3, 4, 6)
662
now = soft_deadline + timedelta(seconds=1)
663
client._backend.soft_deadline = soft_deadline
664
with patch.object(client._backend, '_now', return_value=now,
669
class TestEnvJujuClient(ClientTest):
671
def test_no_duplicate_env(self):
672
env = JujuData('foo', {})
673
client = EnvJujuClient(env, '1.25', 'full_path')
674
self.assertIs(env, client.env)
676
def test_convert_to_juju_data(self):
677
env = SimpleEnvironment('foo', {'type': 'bar'}, 'baz')
678
with patch.object(JujuData, 'load_yaml'):
679
client = EnvJujuClient(env, '1.25', 'full_path')
680
client.env.load_yaml.assert_called_once_with()
681
self.assertIsInstance(client.env, JujuData)
682
self.assertEqual(client.env.environment, 'foo')
683
self.assertEqual(client.env.config, {'type': 'bar'})
684
self.assertEqual(client.env.juju_home, 'baz')
686
def test_get_version(self):
688
with patch('subprocess.check_output', return_value=value) as vsn:
689
version = EnvJujuClient.get_version()
690
self.assertEqual('5.6', version)
691
vsn.assert_called_with(('juju', '--version'))
693
def test_get_version_path(self):
694
with patch('subprocess.check_output', return_value=' 4.3') as vsn:
695
EnvJujuClient.get_version('foo/bar/baz')
696
vsn.assert_called_once_with(('foo/bar/baz', '--version'))
698
def test_get_matching_agent_version(self):
699
client = EnvJujuClient(
700
JujuData(None, {'type': 'local'}, juju_home='foo'),
701
'1.23-series-arch', None)
702
self.assertEqual('1.23.1', client.get_matching_agent_version())
703
self.assertEqual('1.23', client.get_matching_agent_version(
705
client = client.clone(version='1.20-beta1-series-arch')
706
self.assertEqual('1.20-beta1.1', client.get_matching_agent_version())
708
def test_upgrade_juju_nonlocal(self):
709
client = EnvJujuClient(
710
JujuData('foo', {'type': 'nonlocal'}), '2.0-betaX', None)
711
with patch.object(client, '_upgrade_juju') as juju_mock:
712
client.upgrade_juju()
713
juju_mock.assert_called_with(('--agent-version', '2.0'))
715
def test_upgrade_juju_local(self):
716
client = EnvJujuClient(
717
JujuData('foo', {'type': 'local'}), '2.0-betaX', None)
718
with patch.object(client, '_upgrade_juju') as juju_mock:
719
client.upgrade_juju()
720
juju_mock.assert_called_with(('--agent-version', '2.0',))
722
def test_upgrade_juju_no_force_version(self):
723
client = EnvJujuClient(
724
JujuData('foo', {'type': 'local'}), '2.0-betaX', None)
725
with patch.object(client, '_upgrade_juju') as juju_mock:
726
client.upgrade_juju(force_version=False)
727
juju_mock.assert_called_with(())
729
def test_clone_unchanged(self):
730
client1 = EnvJujuClient(JujuData('foo'), '1.27', 'full/path',
732
client2 = client1.clone()
733
self.assertIsNot(client1, client2)
734
self.assertIs(type(client1), type(client2))
735
self.assertIs(client1.env, client2.env)
736
self.assertEqual(client1.version, client2.version)
737
self.assertEqual(client1.full_path, client2.full_path)
738
self.assertIs(client1.debug, client2.debug)
739
self.assertEqual(client1.feature_flags, client2.feature_flags)
740
self.assertEqual(client1._backend, client2._backend)
742
def test_clone_changed(self):
743
client1 = EnvJujuClient(JujuData('foo'), '1.27', 'full/path',
745
env2 = SimpleEnvironment('bar')
746
client2 = client1.clone(env2, '1.28', 'other/path', debug=False,
748
self.assertIs(EnvJujuClient1X, type(client2))
749
self.assertIs(env2, client2.env)
750
self.assertEqual('1.28', client2.version)
751
self.assertEqual('other/path', client2.full_path)
752
self.assertIs(False, client2.debug)
753
self.assertEqual(client1.feature_flags, client2.feature_flags)
755
def test_get_cache_path(self):
756
client = EnvJujuClient(JujuData('foo', juju_home='/foo/'),
757
'1.27', 'full/path', debug=True)
758
self.assertEqual('/foo/models/cache.yaml',
759
client.get_cache_path())
761
def test_full_args(self):
762
env = JujuData('foo')
763
client = EnvJujuClient(env, None, 'my/juju/bin')
764
full = client._full_args('bar', False, ('baz', 'qux'))
765
self.assertEqual(('bin', '--show-log', 'bar', '-m', 'foo:foo', 'baz',
767
full = client._full_args('bar', True, ('baz', 'qux'))
769
'bin', '--show-log', 'bar', '-m', 'foo:foo', 'baz', 'qux'), full)
770
full = client._full_args('bar', True, ('baz', 'qux'), controller=True)
772
('bin', '--show-log', 'bar', '-m', 'foo:controller', 'baz', 'qux'),
775
full = client._full_args('bar', False, ('baz', 'qux'))
776
self.assertEqual(('bin', '--show-log', 'bar', 'baz', 'qux'), full)
778
def test_full_args_debug(self):
779
env = JujuData('foo')
780
client = EnvJujuClient(env, None, 'my/juju/bin', debug=True)
781
full = client._full_args('bar', False, ('baz', 'qux'))
783
'bin', '--debug', 'bar', '-m', 'foo:foo', 'baz', 'qux'), full)
785
def test_full_args_action(self):
786
env = JujuData('foo')
787
client = EnvJujuClient(env, None, 'my/juju/bin')
788
full = client._full_args('action bar', False, ('baz', 'qux'))
790
('bin', '--show-log', 'action', 'bar', '-m', 'foo:foo',
794
def test_full_args_controller(self):
795
env = JujuData('foo')
796
client = EnvJujuClient(env, None, 'my/juju/bin')
797
with patch.object(client, 'get_controller_model_name',
798
return_value='controller') as gamn_mock:
799
full = client._full_args('bar', False, ('baz', 'qux'),
802
'bin', '--show-log', 'bar', '-m', 'foo:controller', 'baz', 'qux'),
804
gamn_mock.assert_called_once_with()
806
def test_make_model_config_prefers_agent_metadata_url(self):
807
env = JujuData('qux', {
808
'agent-metadata-url': 'foo',
809
'tools-metadata-url': 'bar',
812
client = EnvJujuClient(env, None, 'my/juju/bin')
814
'agent-metadata-url': 'foo',
816
}, client.make_model_config())
818
def test__bootstrap_config(self):
819
env = JujuData('foo', {
821
'admin-secret': 'foo',
822
'agent-stream': 'foo',
823
'application-id': 'foo',
824
'application-password': 'foo',
826
'authorized-keys': 'foo',
827
'availability-sets-enabled': 'foo',
828
'bootstrap-host': 'foo',
829
'bootstrap-timeout': 'foo',
830
'bootstrap-user': 'foo',
831
'client-email': 'foo',
834
'control-bucket': 'foo',
835
'default-series': 'foo',
836
'development': False,
837
'enable-os-upgrade': 'foo',
838
'image-metadata-url': 'foo',
841
'maas-server': 'foo',
842
'manta-key-id': 'foo',
844
'management-subscription-id': 'foo',
845
'management-certificate': 'foo',
848
'prefer-ipv6': 'foo',
849
'private-key': 'foo',
855
'storage-account-name': 'foo',
856
'subscription-id': 'foo',
858
'tenant-name': 'foo',
860
'tools-metadata-url': 'steve',
864
client = EnvJujuClient(env, None, 'my/juju/bin')
865
with client._bootstrap_config() as config_filename:
866
with open(config_filename) as f:
868
'agent-metadata-url': 'steve',
869
'agent-stream': 'foo',
870
'authorized-keys': 'foo',
871
'availability-sets-enabled': 'foo',
872
'bootstrap-timeout': 'foo',
873
'bootstrap-user': 'foo',
875
'default-series': 'foo',
876
'development': False,
877
'enable-os-upgrade': 'foo',
878
'image-metadata-url': 'foo',
879
'prefer-ipv6': 'foo',
881
}, yaml.safe_load(f))
883
def test_get_cloud_region(self):
885
'foo/bar', EnvJujuClient.get_cloud_region('foo', 'bar'))
887
'foo', EnvJujuClient.get_cloud_region('foo', None))
889
def test_bootstrap_maas(self):
890
env = JujuData('maas', {'type': 'foo', 'region': 'asdf'})
891
with patch.object(EnvJujuClient, 'juju') as mock:
892
client = EnvJujuClient(env, '2.0-zeta1', None)
893
with patch.object(client.env, 'maas', lambda: True):
894
with observable_temp_file() as config_file:
896
mock.assert_called_with(
898
'--constraints', 'mem=2G', 'maas', 'foo/asdf',
899
'--config', config_file.name, '--default-model', 'maas',
900
'--agent-version', '2.0'),
903
def test_bootstrap_joyent(self):
904
env = JujuData('joyent', {
905
'type': 'joyent', 'sdc-url': 'https://foo.api.joyentcloud.com'})
906
with patch.object(EnvJujuClient, 'juju', autospec=True) as mock:
907
client = EnvJujuClient(env, '2.0-zeta1', None)
908
with patch.object(client.env, 'joyent', lambda: True):
909
with observable_temp_file() as config_file:
911
mock.assert_called_once_with(
912
client, 'bootstrap', (
913
'--constraints', 'mem=2G cpu-cores=1', 'joyent',
914
'joyent/foo', '--config', config_file.name,
915
'--default-model', 'joyent', '--agent-version', '2.0',
918
def test_bootstrap(self):
919
env = JujuData('foo', {'type': 'bar', 'region': 'baz'})
920
with observable_temp_file() as config_file:
921
with patch.object(EnvJujuClient, 'juju') as mock:
922
client = EnvJujuClient(env, '2.0-zeta1', None)
924
mock.assert_called_with(
925
'bootstrap', ('--constraints', 'mem=2G',
927
'--config', config_file.name,
928
'--default-model', 'foo',
929
'--agent-version', '2.0'), include_e=False)
931
config = yaml.safe_load(config_file)
932
self.assertEqual({'test-mode': True}, config)
934
def test_bootstrap_upload_tools(self):
935
env = JujuData('foo', {'type': 'foo', 'region': 'baz'})
936
client = EnvJujuClient(env, '2.0-zeta1', None)
937
with patch.object(client.env, 'needs_sudo', lambda: True):
938
with observable_temp_file() as config_file:
939
with patch.object(client, 'juju') as mock:
940
client.bootstrap(upload_tools=True)
941
mock.assert_called_with(
943
'--upload-tools', '--constraints', 'mem=2G', 'foo',
944
'foo/baz', '--config', config_file.name,
945
'--default-model', 'foo'), include_e=False)
947
def test_bootstrap_credential(self):
948
env = JujuData('foo', {'type': 'foo', 'region': 'baz'})
949
client = EnvJujuClient(env, '2.0-zeta1', None)
950
with observable_temp_file() as config_file:
951
with patch.object(client, 'juju') as mock:
952
client.bootstrap(credential='credential_name')
953
mock.assert_called_with(
955
'--constraints', 'mem=2G', 'foo',
956
'foo/baz', '--config', config_file.name,
957
'--default-model', 'foo', '--agent-version', '2.0',
958
'--credential', 'credential_name'), include_e=False)
960
def test_bootstrap_bootstrap_series(self):
961
env = JujuData('foo', {'type': 'bar', 'region': 'baz'})
962
client = EnvJujuClient(env, '2.0-zeta1', None)
963
with patch.object(client, 'juju') as mock:
964
with observable_temp_file() as config_file:
965
client.bootstrap(bootstrap_series='angsty')
966
mock.assert_called_with(
968
'--constraints', 'mem=2G', 'foo', 'bar/baz',
969
'--config', config_file.name, '--default-model', 'foo',
970
'--agent-version', '2.0',
971
'--bootstrap-series', 'angsty'), include_e=False)
973
def test_bootstrap_auto_upgade(self):
974
env = JujuData('foo', {'type': 'bar', 'region': 'baz'})
975
client = EnvJujuClient(env, '2.0-zeta1', None)
976
with patch.object(client, 'juju') as mock:
977
with observable_temp_file() as config_file:
978
client.bootstrap(auto_upgrade=True)
979
mock.assert_called_with(
981
'--constraints', 'mem=2G', 'foo', 'bar/baz',
982
'--config', config_file.name, '--default-model', 'foo',
983
'--agent-version', '2.0', '--auto-upgrade'), include_e=False)
985
def test_bootstrap_metadata(self):
986
env = JujuData('foo', {'type': 'bar', 'region': 'baz'})
987
client = EnvJujuClient(env, '2.0-zeta1', None)
988
with patch.object(client, 'juju') as mock:
989
with observable_temp_file() as config_file:
990
client.bootstrap(metadata_source='/var/test-source')
991
mock.assert_called_with(
993
'--constraints', 'mem=2G', 'foo', 'bar/baz',
994
'--config', config_file.name, '--default-model', 'foo',
995
'--agent-version', '2.0',
996
'--metadata-source', '/var/test-source'), include_e=False)
998
def test_bootstrap_to(self):
999
env = JujuData('foo', {'type': 'bar', 'region': 'baz'})
1000
client = EnvJujuClient(env, '2.0-zeta1', None)
1001
with patch.object(client, 'juju') as mock:
1002
with observable_temp_file() as config_file:
1003
client.bootstrap(to='target')
1004
mock.assert_called_with(
1006
'--constraints', 'mem=2G', 'foo', 'bar/baz',
1007
'--config', config_file.name, '--default-model', 'foo',
1008
'--agent-version', '2.0', '--to', 'target'), include_e=False)
1010
def test_bootstrap_async(self):
1011
env = JujuData('foo', {'type': 'bar', 'region': 'baz'})
1012
with patch.object(EnvJujuClient, 'juju_async', autospec=True) as mock:
1013
client = EnvJujuClient(env, '2.0-zeta1', None)
1014
client.env.juju_home = 'foo'
1015
with observable_temp_file() as config_file:
1016
with client.bootstrap_async():
1017
mock.assert_called_once_with(
1018
client, 'bootstrap', (
1019
'--constraints', 'mem=2G', 'foo', 'bar/baz',
1020
'--config', config_file.name,
1021
'--default-model', 'foo',
1022
'--agent-version', '2.0'), include_e=False)
1024
def test_bootstrap_async_upload_tools(self):
1025
env = JujuData('foo', {'type': 'bar', 'region': 'baz'})
1026
with patch.object(EnvJujuClient, 'juju_async', autospec=True) as mock:
1027
client = EnvJujuClient(env, '2.0-zeta1', None)
1028
with observable_temp_file() as config_file:
1029
with client.bootstrap_async(upload_tools=True):
1030
mock.assert_called_with(
1031
client, 'bootstrap', (
1032
'--upload-tools', '--constraints', 'mem=2G',
1033
'foo', 'bar/baz', '--config', config_file.name,
1034
'--default-model', 'foo',
1038
def test_get_bootstrap_args_bootstrap_series(self):
1039
env = JujuData('foo', {'type': 'bar', 'region': 'baz'})
1040
client = EnvJujuClient(env, '2.0-zeta1', None)
1041
args = client.get_bootstrap_args(upload_tools=True,
1042
config_filename='config',
1043
bootstrap_series='angsty')
1044
self.assertEqual(args, (
1045
'--upload-tools', '--constraints', 'mem=2G', 'foo', 'bar/baz',
1046
'--config', 'config', '--default-model', 'foo',
1047
'--bootstrap-series', 'angsty'))
1049
def test_get_bootstrap_args_agent_version(self):
1050
env = JujuData('foo', {'type': 'bar', 'region': 'baz'})
1051
client = EnvJujuClient(env, '2.0-zeta1', None)
1052
args = client.get_bootstrap_args(upload_tools=False,
1053
config_filename='config',
1054
agent_version='2.0-lambda1')
1055
self.assertEqual(('--constraints', 'mem=2G', 'foo', 'bar/baz',
1056
'--config', 'config', '--default-model', 'foo',
1057
'--agent-version', '2.0-lambda1'), args)
1059
def test_get_bootstrap_args_upload_tools_and_agent_version(self):
1060
env = JujuData('foo', {'type': 'bar', 'region': 'baz'})
1061
client = EnvJujuClient(env, '2.0-zeta1', None)
1062
with self.assertRaises(ValueError):
1063
client.get_bootstrap_args(upload_tools=True,
1064
config_filename='config',
1065
agent_version='2.0-lambda1')
1067
def test_add_model_hypenated_controller(self):
1069
'kill-controller', 'add-model', ('-c', 'foo'))
1071
def do_add_model(self, jes_command, create_cmd, controller_option):
1072
controller_client = EnvJujuClient(JujuData('foo'), None, None)
1073
model_data = JujuData('bar', {'type': 'foo'})
1074
client = EnvJujuClient(model_data, None, None)
1075
with patch.object(client, 'get_jes_command',
1076
return_value=jes_command):
1077
with patch.object(controller_client, 'juju') as ccj_mock:
1078
with observable_temp_file() as config_file:
1079
controller_client.add_model(model_data)
1080
ccj_mock.assert_called_once_with(
1081
create_cmd, controller_option + (
1082
'bar', '--config', config_file.name), include_e=False)
1084
def test_destroy_environment(self):
1085
env = JujuData('foo')
1086
client = EnvJujuClient(env, None, None)
1087
self.assertIs(False, hasattr(client, 'destroy_environment'))
1089
def test_destroy_model(self):
1090
env = JujuData('foo', {'type': 'gce'})
1091
client = EnvJujuClient(env, None, None)
1092
with patch.object(client, 'juju') as mock:
1093
client.destroy_model()
1094
mock.assert_called_with(
1095
'destroy-model', ('foo', '-y'),
1096
include_e=False, timeout=600)
1098
def test_destroy_model_azure(self):
1099
env = JujuData('foo', {'type': 'azure'})
1100
client = EnvJujuClient(env, None, None)
1101
with patch.object(client, 'juju') as mock:
1102
client.destroy_model()
1103
mock.assert_called_with(
1104
'destroy-model', ('foo', '-y'),
1105
include_e=False, timeout=1800)
1107
def test_kill_controller_system(self):
1108
self.do_kill_controller('system', 'system kill')
1110
def test_kill_controller_controller(self):
1111
self.do_kill_controller('controller', 'controller kill')
1113
def test_kill_controller_hyphenated(self):
1114
self.do_kill_controller('kill-controller', 'kill-controller')
1116
def do_kill_controller(self, jes_command, kill_command):
1117
client = EnvJujuClient(JujuData('foo', {'type': 'gce'}), None, None)
1118
with patch.object(client, 'get_jes_command',
1119
return_value=jes_command):
1120
with patch.object(client, 'juju') as juju_mock:
1121
client.kill_controller()
1122
juju_mock.assert_called_once_with(
1123
kill_command, ('foo', '-y'), check=False, include_e=False,
1126
def do_kill_controller_azure(self, jes_command, kill_command):
1127
client = EnvJujuClient(JujuData('foo', {'type': 'azure'}), None, None)
1128
with patch.object(client, 'get_jes_command',
1129
return_value=jes_command):
1130
with patch.object(client, 'juju') as juju_mock:
1131
client.kill_controller()
1132
juju_mock.assert_called_once_with(
1133
kill_command, ('foo', '-y'), check=False, include_e=False,
1136
def test_get_juju_output(self):
1137
env = JujuData('foo')
1138
client = EnvJujuClient(env, None, 'juju')
1139
fake_popen = FakePopen('asdf', None, 0)
1140
with patch('subprocess.Popen', return_value=fake_popen) as mock:
1141
result = client.get_juju_output('bar')
1142
self.assertEqual('asdf', result)
1143
self.assertEqual((('juju', '--show-log', 'bar', '-m', 'foo:foo'),),
1146
def test_get_juju_output_accepts_varargs(self):
1147
env = JujuData('foo')
1148
fake_popen = FakePopen('asdf', None, 0)
1149
client = EnvJujuClient(env, None, 'juju')
1150
with patch('subprocess.Popen', return_value=fake_popen) as mock:
1151
result = client.get_juju_output('bar', 'baz', '--qux')
1152
self.assertEqual('asdf', result)
1153
self.assertEqual((('juju', '--show-log', 'bar', '-m', 'foo:foo', 'baz',
1154
'--qux'),), mock.call_args[0])
1156
def test_get_juju_output_stderr(self):
1157
env = JujuData('foo')
1158
fake_popen = FakePopen(None, 'Hello!', 1)
1159
client = EnvJujuClient(env, None, 'juju')
1160
with self.assertRaises(subprocess.CalledProcessError) as exc:
1161
with patch('subprocess.Popen', return_value=fake_popen):
1162
client.get_juju_output('bar')
1163
self.assertEqual(exc.exception.stderr, 'Hello!')
1165
def test_get_juju_output_merge_stderr(self):
1166
env = JujuData('foo')
1167
fake_popen = FakePopen('Err on out', None, 0)
1168
client = EnvJujuClient(env, None, 'juju')
1169
with patch('subprocess.Popen', return_value=fake_popen) as mock_popen:
1170
result = client.get_juju_output('bar', merge_stderr=True)
1171
self.assertEqual(result, 'Err on out')
1172
mock_popen.assert_called_once_with(
1173
('juju', '--show-log', 'bar', '-m', 'foo:foo'),
1174
stdin=subprocess.PIPE, stderr=subprocess.STDOUT,
1175
stdout=subprocess.PIPE)
1177
def test_get_juju_output_full_cmd(self):
1178
env = JujuData('foo')
1179
fake_popen = FakePopen(None, 'Hello!', 1)
1180
client = EnvJujuClient(env, None, 'juju')
1181
with self.assertRaises(subprocess.CalledProcessError) as exc:
1182
with patch('subprocess.Popen', return_value=fake_popen):
1183
client.get_juju_output('bar', '--baz', 'qux')
1185
('juju', '--show-log', 'bar', '-m', 'foo:foo', '--baz', 'qux'),
1188
def test_get_juju_output_accepts_timeout(self):
1189
env = JujuData('foo')
1190
fake_popen = FakePopen('asdf', None, 0)
1191
client = EnvJujuClient(env, None, 'juju')
1192
with patch('subprocess.Popen', return_value=fake_popen) as po_mock:
1193
client.get_juju_output('bar', timeout=5)
1195
po_mock.call_args[0][0],
1196
(sys.executable, get_timeout_path(), '5.00', '--', 'juju',
1197
'--show-log', 'bar', '-m', 'foo:foo'))
1199
def test__shell_environ_juju_data(self):
1200
client = EnvJujuClient(
1201
JujuData('baz', {'type': 'ec2'}), '1.25-foobar', 'path', 'asdf')
1202
env = client._shell_environ()
1203
self.assertEqual(env['JUJU_DATA'], 'asdf')
1204
self.assertNotIn('JUJU_HOME', env)
1206
def test__shell_environ_cloudsigma(self):
1207
client = EnvJujuClient(
1208
JujuData('baz', {'type': 'cloudsigma'}), '1.24-foobar', 'path')
1209
env = client._shell_environ()
1210
self.assertEqual(env.get(JUJU_DEV_FEATURE_FLAGS, ''), '')
1212
def test_juju_output_supplies_path(self):
1213
env = JujuData('foo')
1214
client = EnvJujuClient(env, None, '/foobar/bar')
1216
def check_path(*args, **kwargs):
1217
self.assertRegexpMatches(os.environ['PATH'], r'/foobar\:')
1218
return FakePopen(None, None, 0)
1219
with patch('subprocess.Popen', autospec=True,
1220
side_effect=check_path):
1221
client.get_juju_output('cmd', 'baz')
1223
def test_get_status(self):
1224
output_text = dedent("""\
1229
env = JujuData('foo')
1230
client = EnvJujuClient(env, None, None)
1231
with patch.object(client, 'get_juju_output',
1232
return_value=output_text) as gjo_mock:
1233
result = client.get_status()
1234
gjo_mock.assert_called_once_with(
1235
'show-status', '--format', 'yaml', controller=False)
1236
self.assertEqual(Status, type(result))
1237
self.assertEqual(['a', 'b', 'c'], result.status)
1239
def test_get_status_retries_on_error(self):
1240
env = JujuData('foo')
1241
client = EnvJujuClient(env, None, None)
1244
def get_juju_output(command, *args, **kwargs):
1245
if client.attempt == 1:
1248
raise subprocess.CalledProcessError(1, command)
1250
with patch.object(client, 'get_juju_output', get_juju_output):
1253
def test_get_status_raises_on_timeout_1(self):
1254
env = JujuData('foo')
1255
client = EnvJujuClient(env, None, None)
1257
def get_juju_output(command, *args, **kwargs):
1258
raise subprocess.CalledProcessError(1, command)
1260
with patch.object(client, 'get_juju_output',
1261
side_effect=get_juju_output):
1262
with patch('jujupy.until_timeout', lambda x: iter([None, None])):
1263
with self.assertRaisesRegexp(
1264
Exception, 'Timed out waiting for juju status'):
1267
def test_get_status_raises_on_timeout_2(self):
1268
env = JujuData('foo')
1269
client = EnvJujuClient(env, None, None)
1270
with patch('jujupy.until_timeout', return_value=iter([1])) as mock_ut:
1271
with patch.object(client, 'get_juju_output',
1272
side_effect=StopIteration):
1273
with self.assertRaises(StopIteration):
1274
client.get_status(500)
1275
mock_ut.assert_called_with(500)
1278
def make_status_yaml(key, machine_value, unit_value):
1288
""".format(key, machine_value, unit_value))
1290
def test_deploy_non_joyent(self):
1291
env = EnvJujuClient(
1292
JujuData('foo', {'type': 'local'}), '1.234-76', None)
1293
with patch.object(env, 'juju') as mock_juju:
1294
env.deploy('mondogb')
1295
mock_juju.assert_called_with('deploy', ('mondogb',))
1297
def test_deploy_joyent(self):
1298
env = EnvJujuClient(
1299
JujuData('foo', {'type': 'local'}), '1.234-76', None)
1300
with patch.object(env, 'juju') as mock_juju:
1301
env.deploy('mondogb')
1302
mock_juju.assert_called_with('deploy', ('mondogb',))
1304
def test_deploy_repository(self):
1305
env = EnvJujuClient(
1306
JujuData('foo', {'type': 'local'}), '1.234-76', None)
1307
with patch.object(env, 'juju') as mock_juju:
1308
env.deploy('/home/jrandom/repo/mongodb')
1309
mock_juju.assert_called_with(
1310
'deploy', ('/home/jrandom/repo/mongodb',))
1312
def test_deploy_to(self):
1313
env = EnvJujuClient(
1314
JujuData('foo', {'type': 'local'}), '1.234-76', None)
1315
with patch.object(env, 'juju') as mock_juju:
1316
env.deploy('mondogb', to='0')
1317
mock_juju.assert_called_with(
1318
'deploy', ('mondogb', '--to', '0'))
1320
def test_deploy_service(self):
1321
env = EnvJujuClient(
1322
JujuData('foo', {'type': 'local'}), '1.234-76', None)
1323
with patch.object(env, 'juju') as mock_juju:
1324
env.deploy('local:mondogb', service='my-mondogb')
1325
mock_juju.assert_called_with(
1326
'deploy', ('local:mondogb', 'my-mondogb',))
1328
def test_deploy_force(self):
1329
env = EnvJujuClient(
1330
JujuData('foo', {'type': 'local'}), '1.234-76', None)
1331
with patch.object(env, 'juju') as mock_juju:
1332
env.deploy('local:mondogb', force=True)
1333
mock_juju.assert_called_with('deploy', ('local:mondogb', '--force',))
1335
def test_deploy_series(self):
1336
env = EnvJujuClient(
1337
JujuData('foo', {'type': 'local'}), '1.234-76', None)
1338
with patch.object(env, 'juju') as mock_juju:
1339
env.deploy('local:blah', series='xenial')
1340
mock_juju.assert_called_with(
1341
'deploy', ('local:blah', '--series', 'xenial'))
1343
def test_deploy_resource(self):
1344
env = EnvJujuClient(JujuData('foo', {'type': 'local'}), None, None)
1345
with patch.object(env, 'juju') as mock_juju:
1346
env.deploy('local:blah', resource='foo=/path/dir')
1347
mock_juju.assert_called_with(
1348
'deploy', ('local:blah', '--resource', 'foo=/path/dir'))
1350
def test_deploy_storage(self):
1351
env = EnvJujuClient1X(
1352
SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None)
1353
with patch.object(env, 'juju') as mock_juju:
1354
env.deploy('mondogb', storage='rootfs,1G')
1355
mock_juju.assert_called_with(
1356
'deploy', ('mondogb', '--storage', 'rootfs,1G'))
1358
def test_deploy_constraints(self):
1359
env = EnvJujuClient1X(
1360
SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None)
1361
with patch.object(env, 'juju') as mock_juju:
1362
env.deploy('mondogb', constraints='virt-type=kvm')
1363
mock_juju.assert_called_with(
1364
'deploy', ('mondogb', '--constraints', 'virt-type=kvm'))
1366
def test_attach(self):
1367
env = EnvJujuClient(JujuData('foo', {'type': 'local'}), None, None)
1368
with patch.object(env, 'juju') as mock_juju:
1369
env.attach('foo', resource='foo=/path/dir')
1370
mock_juju.assert_called_with('attach', ('foo', 'foo=/path/dir'))
1372
def test_list_resources(self):
1373
data = 'resourceid: resource/foo'
1374
client = EnvJujuClient(JujuData('local'), None, None)
1376
client, 'get_juju_output', return_value=data) as mock_gjo:
1377
status = client.list_resources('foo')
1378
self.assertEqual(status, yaml.safe_load(data))
1379
mock_gjo.assert_called_with(
1380
'list-resources', '--format', 'yaml', 'foo', '--details')
1382
def test_wait_for_resource(self):
1383
client = EnvJujuClient(JujuData('local'), None, None)
1385
client, 'list_resources',
1386
return_value=make_resource_list()) as mock_lr:
1387
client.wait_for_resource('dummy-resource/foo', 'foo')
1388
mock_lr.assert_called_once_with('foo')
1390
def test_wait_for_resource_timeout(self):
1391
client = EnvJujuClient(JujuData('local'), None, None)
1392
resource_list = make_resource_list()
1393
resource_list['resources'][0]['expected']['resourceid'] = 'bad_id'
1395
client, 'list_resources',
1396
return_value=resource_list) as mock_lr:
1397
with patch('jujupy.until_timeout', autospec=True,
1398
return_value=[0, 1]) as mock_ju:
1399
with patch('time.sleep', autospec=True) as mock_ts:
1400
with self.assertRaisesRegexp(
1401
JujuResourceTimeout,
1402
'Timeout waiting for a resource to be downloaded'):
1403
client.wait_for_resource('dummy-resource/foo', 'foo')
1404
calls = [call('foo'), call('foo')]
1405
self.assertEqual(mock_lr.mock_calls, calls)
1406
self.assertEqual(mock_ts.mock_calls, [call(.1), call(.1)])
1407
self.assertEqual(mock_ju.mock_calls, [call(60)])
1409
def test_wait_for_resource_suppresses_deadline(self):
1410
with client_past_deadline() as client:
1411
real_check_timeouts = client.check_timeouts
1413
def list_resources(service_or_unit):
1414
with real_check_timeouts():
1415
return make_resource_list()
1417
with patch.object(client, 'check_timeouts', autospec=True):
1418
with patch.object(client, 'list_resources', autospec=True,
1419
side_effect=list_resources):
1420
client.wait_for_resource('dummy-resource/foo',
1423
def test_wait_for_resource_checks_deadline(self):
1424
resource_list = make_resource_list()
1425
with client_past_deadline() as client:
1426
with patch.object(client, 'list_resources', autospec=True,
1427
return_value=resource_list):
1428
with self.assertRaises(SoftDeadlineExceeded):
1429
client.wait_for_resource('dummy-resource/foo', 'app_unit')
1431
def test_deploy_bundle_2x(self):
1432
client = EnvJujuClient(JujuData('an_env', None),
1433
'1.23-series-arch', None)
1434
with patch.object(client, 'juju') as mock_juju:
1435
client.deploy_bundle('bundle:~juju-qa/some-bundle')
1436
mock_juju.assert_called_with(
1437
'deploy', ('bundle:~juju-qa/some-bundle'), timeout=3600)
1439
def test_deploy_bundle_template(self):
1440
client = EnvJujuClient(JujuData('an_env', None),
1441
'1.23-series-arch', None)
1442
with patch.object(client, 'juju') as mock_juju:
1443
client.deploy_bundle('bundle:~juju-qa/some-{container}-bundle')
1444
mock_juju.assert_called_with(
1445
'deploy', ('bundle:~juju-qa/some-lxd-bundle'), timeout=3600)
1447
def test_upgrade_charm(self):
1448
env = EnvJujuClient(
1449
JujuData('foo', {'type': 'local'}), '2.34-74', None)
1450
with patch.object(env, 'juju') as mock_juju:
1451
env.upgrade_charm('foo-service',
1452
'/bar/repository/angsty/mongodb')
1453
mock_juju.assert_called_once_with(
1454
'upgrade-charm', ('foo-service', '--path',
1455
'/bar/repository/angsty/mongodb',))
1457
def test_remove_service(self):
1458
env = EnvJujuClient(
1459
JujuData('foo', {'type': 'local'}), '1.234-76', None)
1460
with patch.object(env, 'juju') as mock_juju:
1461
env.remove_service('mondogb')
1462
mock_juju.assert_called_with('remove-application', ('mondogb',))
1464
def test_status_until_always_runs_once(self):
1465
client = EnvJujuClient(
1466
JujuData('foo', {'type': 'local'}), '1.234-76', None)
1467
status_txt = self.make_status_yaml('agent-state', 'started', 'started')
1468
with patch.object(client, 'get_juju_output', return_value=status_txt):
1469
result = list(client.status_until(-1))
1471
[r.status for r in result], [Status.from_text(status_txt).status])
1473
def test_status_until_timeout(self):
1474
client = EnvJujuClient(
1475
JujuData('foo', {'type': 'local'}), '1.234-76', None)
1476
status_txt = self.make_status_yaml('agent-state', 'started', 'started')
1477
status_yaml = yaml.safe_load(status_txt)
1479
def until_timeout_stub(timeout, start=None):
1480
return iter([None, None])
1482
with patch.object(client, 'get_juju_output', return_value=status_txt):
1483
with patch('jujupy.until_timeout',
1484
side_effect=until_timeout_stub) as ut_mock:
1485
result = list(client.status_until(30, 70))
1487
[r.status for r in result], [status_yaml] * 3)
1488
# until_timeout is called by status as well as status_until.
1489
self.assertEqual(ut_mock.mock_calls,
1490
[call(60), call(30, start=70), call(60), call(60)])
1492
def test_status_until_suppresses_deadline(self):
1493
with self.only_status_checks() as client:
1494
list(client.status_until(0))
1496
def test_status_until_checks_deadline(self):
1497
with self.status_does_not_check() as client:
1498
with self.assertRaises(SoftDeadlineExceeded):
1499
list(client.status_until(0))
1501
def test_add_ssh_machines(self):
1502
client = EnvJujuClient(JujuData('foo'), None, 'juju')
1503
with patch('subprocess.check_call', autospec=True) as cc_mock:
1504
client.add_ssh_machines(['m-foo', 'm-bar', 'm-baz'])
1509
('juju', '--show-log', 'add-machine',
1510
'-m', 'foo:foo', 'ssh:m-foo'),
1516
('juju', '--show-log', 'add-machine',
1517
'-m', 'foo:foo', 'ssh:m-bar'),
1523
('juju', '--show-log', 'add-machine',
1524
'-m', 'foo:foo', 'ssh:m-baz'),
1526
self.assertEqual(cc_mock.call_count, 3)
1528
def test_add_ssh_machines_retry(self):
1529
client = EnvJujuClient(JujuData('foo'), None, 'juju')
1530
with patch('subprocess.check_call', autospec=True,
1531
side_effect=[subprocess.CalledProcessError(None, None),
1532
None, None, None]) as cc_mock:
1533
client.add_ssh_machines(['m-foo', 'm-bar', 'm-baz'])
1538
('juju', '--show-log', 'add-machine',
1539
'-m', 'foo:foo', 'ssh:m-foo'),
1541
self.pause_mock.assert_called_once_with(30)
1546
('juju', '--show-log', 'add-machine',
1547
'-m', 'foo:foo', 'ssh:m-foo'),
1553
('juju', '--show-log', 'add-machine',
1554
'-m', 'foo:foo', 'ssh:m-bar'),
1560
('juju', '--show-log', 'add-machine',
1561
'-m', 'foo:foo', 'ssh:m-baz'),
1563
self.assertEqual(cc_mock.call_count, 4)
1565
def test_add_ssh_machines_fail_on_second_machine(self):
1566
client = EnvJujuClient(JujuData('foo'), None, 'juju')
1567
with patch('subprocess.check_call', autospec=True, side_effect=[
1568
None, subprocess.CalledProcessError(None, None), None, None
1570
with self.assertRaises(subprocess.CalledProcessError):
1571
client.add_ssh_machines(['m-foo', 'm-bar', 'm-baz'])
1576
('juju', '--show-log', 'add-machine',
1577
'-m', 'foo:foo', 'ssh:m-foo'),
1583
('juju', '--show-log', 'add-machine',
1584
'-m', 'foo:foo', 'ssh:m-bar'),
1586
self.assertEqual(cc_mock.call_count, 2)
1588
def test_add_ssh_machines_fail_on_second_attempt(self):
1589
client = EnvJujuClient(JujuData('foo'), None, 'juju')
1590
with patch('subprocess.check_call', autospec=True, side_effect=[
1591
subprocess.CalledProcessError(None, None),
1592
subprocess.CalledProcessError(None, None)]) as cc_mock:
1593
with self.assertRaises(subprocess.CalledProcessError):
1594
client.add_ssh_machines(['m-foo', 'm-bar', 'm-baz'])
1599
('juju', '--show-log', 'add-machine',
1600
'-m', 'foo:foo', 'ssh:m-foo'),
1606
('juju', '--show-log', 'add-machine',
1607
'-m', 'foo:foo', 'ssh:m-foo'),
1609
self.assertEqual(cc_mock.call_count, 2)
1611
def test_wait_for_started(self):
1612
value = self.make_status_yaml('agent-state', 'started', 'started')
1613
client = EnvJujuClient(JujuData('local'), None, None)
1614
with patch.object(client, 'get_juju_output', return_value=value):
1615
client.wait_for_started()
1617
def test_wait_for_started_timeout(self):
1618
value = self.make_status_yaml('agent-state', 'pending', 'started')
1619
client = EnvJujuClient(JujuData('local'), None, None)
1620
with patch('jujupy.until_timeout', lambda x, start=None: range(1)):
1621
with patch.object(client, 'get_juju_output', return_value=value):
1623
with patch.object(GroupReporter, '_write', autospec=True,
1624
side_effect=lambda _, s: writes.append(s)):
1625
with self.assertRaisesRegexp(
1627
'Timed out waiting for agents to start in local'):
1628
client.wait_for_started()
1629
self.assertEqual(writes, ['pending: 0', ' .', '\n'])
1631
def test_wait_for_started_start(self):
1632
value = self.make_status_yaml('agent-state', 'started', 'pending')
1633
client = EnvJujuClient(JujuData('local'), None, None)
1634
now = datetime.now() + timedelta(days=1)
1635
with patch('utility.until_timeout.now', return_value=now):
1636
with patch.object(client, 'get_juju_output', return_value=value):
1638
with patch.object(GroupReporter, '_write', autospec=True,
1639
side_effect=lambda _, s: writes.append(s)):
1640
with self.assertRaisesRegexp(
1642
'Timed out waiting for agents to start in local'):
1643
client.wait_for_started(start=now - timedelta(1200))
1644
self.assertEqual(writes, ['pending: jenkins/0', '\n'])
1646
def make_ha_status(self):
1647
return {'machines': {
1648
'0': {'controller-member-status': 'has-vote'},
1649
'1': {'controller-member-status': 'has-vote'},
1650
'2': {'controller-member-status': 'has-vote'},
1654
def only_status_checks(self, status=None):
1655
"""This context manager ensure only get_status calls check_timeouts.
1657
Everything else will get a mock object.
1659
Also, the client is patched so that the soft_deadline has been hit.
1661
with client_past_deadline() as client:
1662
# This will work even after we patch check_timeouts below.
1663
real_check_timeouts = client.check_timeouts
1665
def check(timeout=60, controller=False):
1666
with real_check_timeouts():
1667
return client.status_class(status, '')
1669
with patch.object(client, 'get_status', autospec=True,
1671
with patch.object(client, 'check_timeouts', autospec=True):
1674
def test__wait_for_status_suppresses_deadline(self):
1679
with self.only_status_checks() as client:
1680
client._wait_for_status(Mock(), translate)
1683
def status_does_not_check(self, status=None):
1684
"""This context manager ensure get_status never calls check_timeouts.
1686
Also, the client is patched so that the soft_deadline has been hit.
1688
with client_past_deadline() as client:
1689
status_obj = client.status_class(status, '')
1690
with patch.object(client, 'get_status', autospec=True,
1691
return_value=status_obj):
1694
def test__wait_for_status_checks_deadline(self):
1699
with self.status_does_not_check() as client:
1700
with self.assertRaises(SoftDeadlineExceeded):
1701
client._wait_for_status(Mock(), translate)
1703
def test_wait_for_started_logs_status(self):
1704
value = self.make_status_yaml('agent-state', 'pending', 'started')
1705
client = EnvJujuClient(JujuData('local'), None, None)
1706
with patch.object(client, 'get_juju_output', return_value=value):
1708
with patch.object(GroupReporter, '_write', autospec=True,
1709
side_effect=lambda _, s: writes.append(s)):
1710
with self.assertRaisesRegexp(
1712
'Timed out waiting for agents to start in local'):
1713
client.wait_for_started(0)
1714
self.assertEqual(writes, ['pending: 0', '\n'])
1715
self.assertEqual(self.log_stream.getvalue(), 'ERROR %s\n' % value)
1717
def test_wait_for_subordinate_units(self):
1721
agent-state: started
1728
agent-state: started
1734
agent-state: started
1736
agent-state: started
1738
client = EnvJujuClient(JujuData('local'), None, None)
1739
now = datetime.now() + timedelta(days=1)
1740
with patch('utility.until_timeout.now', return_value=now):
1741
with patch.object(client, 'get_juju_output', return_value=value):
1742
with patch('jujupy.GroupReporter.update') as update_mock:
1743
with patch('jujupy.GroupReporter.finish') as finish_mock:
1744
client.wait_for_subordinate_units(
1745
'jenkins', 'sub1', start=now - timedelta(1200))
1746
self.assertEqual([], update_mock.call_args_list)
1747
finish_mock.assert_called_once_with()
1749
def test_wait_for_subordinate_units_with_agent_status(self):
1753
agent-state: started
1773
client = EnvJujuClient(JujuData('local'), None, None)
1774
now = datetime.now() + timedelta(days=1)
1775
with patch('utility.until_timeout.now', return_value=now):
1776
with patch.object(client, 'get_juju_output', return_value=value):
1777
with patch('jujupy.GroupReporter.update') as update_mock:
1778
with patch('jujupy.GroupReporter.finish') as finish_mock:
1779
client.wait_for_subordinate_units(
1780
'jenkins', 'sub1', start=now - timedelta(1200))
1781
self.assertEqual([], update_mock.call_args_list)
1782
finish_mock.assert_called_once_with()
1784
def test_wait_for_multiple_subordinate_units(self):
1788
agent-state: started
1795
agent-state: started
1799
agent-state: started
1801
client = EnvJujuClient(JujuData('local'), None, None)
1802
now = datetime.now() + timedelta(days=1)
1803
with patch('utility.until_timeout.now', return_value=now):
1804
with patch.object(client, 'get_juju_output', return_value=value):
1805
with patch('jujupy.GroupReporter.update') as update_mock:
1806
with patch('jujupy.GroupReporter.finish') as finish_mock:
1807
client.wait_for_subordinate_units(
1808
'ubuntu', 'sub', start=now - timedelta(1200))
1809
self.assertEqual([], update_mock.call_args_list)
1810
finish_mock.assert_called_once_with()
1812
def test_wait_for_subordinate_units_checks_slash_in_unit_name(self):
1816
agent-state: started
1823
agent-state: started
1825
client = EnvJujuClient(JujuData('local'), None, None)
1826
now = datetime.now() + timedelta(days=1)
1827
with patch('utility.until_timeout.now', return_value=now):
1828
with patch.object(client, 'get_juju_output', return_value=value):
1829
with self.assertRaisesRegexp(
1831
'Timed out waiting for agents to start in local'):
1832
client.wait_for_subordinate_units(
1833
'jenkins', 'sub1', start=now - timedelta(1200))
1835
def test_wait_for_subordinate_units_no_subordinate(self):
1839
agent-state: started
1844
agent-state: started
1846
client = EnvJujuClient(JujuData('local'), None, None)
1847
now = datetime.now() + timedelta(days=1)
1848
with patch('utility.until_timeout.now', return_value=now):
1849
with patch.object(client, 'get_juju_output', return_value=value):
1850
with self.assertRaisesRegexp(
1852
'Timed out waiting for agents to start in local'):
1853
client.wait_for_subordinate_units(
1854
'jenkins', 'sub1', start=now - timedelta(1200))
1856
def test_wait_for_workload(self):
1857
initial_status = Status.from_text("""\
1869
final_status = Status(copy.deepcopy(initial_status.status), None)
1870
final_status.status['applications']['jenkins']['units']['jenkins/0'][
1871
'workload-status']['current'] = 'active'
1872
client = EnvJujuClient(JujuData('local'), None, None)
1874
with patch('utility.until_timeout', autospec=True, return_value=[1]):
1875
with patch.object(client, 'get_status', autospec=True,
1876
side_effect=[initial_status, final_status]):
1877
with patch.object(GroupReporter, '_write', autospec=True,
1878
side_effect=lambda _, s: writes.append(s)):
1879
client.wait_for_workloads()
1880
self.assertEqual(writes, ['waiting: jenkins/0', '\n'])
1882
def test_wait_for_workload_all_unknown(self):
1883
status = Status.from_text("""\
1895
client = EnvJujuClient(JujuData('local'), None, None)
1897
with patch('utility.until_timeout', autospec=True, return_value=[]):
1898
with patch.object(client, 'get_status', autospec=True,
1899
return_value=status):
1900
with patch.object(GroupReporter, '_write', autospec=True,
1901
side_effect=lambda _, s: writes.append(s)):
1902
client.wait_for_workloads(timeout=1)
1903
self.assertEqual(writes, [])
1905
def test_wait_for_workload_no_workload_status(self):
1906
status = Status.from_text("""\
1913
client = EnvJujuClient(JujuData('local'), None, None)
1915
with patch('utility.until_timeout', autospec=True, return_value=[]):
1916
with patch.object(client, 'get_status', autospec=True,
1917
return_value=status):
1918
with patch.object(GroupReporter, '_write', autospec=True,
1919
side_effect=lambda _, s: writes.append(s)):
1920
client.wait_for_workloads(timeout=1)
1921
self.assertEqual(writes, [])
1923
def test_list_models(self):
1924
client = EnvJujuClient(JujuData('foo'), None, None)
1925
with patch.object(client, 'juju') as j_mock:
1926
client.list_models()
1927
j_mock.assert_called_once_with(
1928
'list-models', ('-c', 'foo'), include_e=False)
1930
def test_get_models(self):
1941
client = EnvJujuClient(JujuData('baz'), None, None)
1942
with patch.object(client, 'get_juju_output',
1943
return_value=data) as gjo_mock:
1944
models = client.get_models()
1945
gjo_mock.assert_called_once_with(
1946
'list-models', '-c', 'baz', '--format', 'yaml',
1947
include_e=False, timeout=120)
1950
{'name': 'foo', 'model-uuid': 'aaaa', 'owner': 'admin@local'},
1951
{'name': 'bar', 'model-uuid': 'bbbb', 'owner': 'admin@local'}],
1952
'current-model': 'foo'
1954
self.assertEqual(expected_models, models)
1956
def test_iter_model_clients(self):
1967
client = EnvJujuClient(JujuData('foo', {}), None, None)
1968
with patch.object(client, 'get_juju_output', return_value=data):
1969
model_clients = list(client.iter_model_clients())
1970
self.assertEqual(2, len(model_clients))
1971
self.assertIs(client, model_clients[0])
1972
self.assertEqual('bar', model_clients[1].env.environment)
1974
def test_get_controller_model_name(self):
1977
{'name': 'controller', 'model-uuid': 'aaaa'},
1978
{'name': 'bar', 'model-uuid': 'bbbb'}],
1979
'current-model': 'bar'
1981
client = EnvJujuClient(JujuData('foo'), None, None)
1982
with patch.object(client, 'get_models',
1983
return_value=models) as gm_mock:
1984
controller_name = client.get_controller_model_name()
1985
self.assertEqual(0, gm_mock.call_count)
1986
self.assertEqual('controller', controller_name)
1988
def test_get_controller_model_name_without_controller(self):
1991
{'name': 'bar', 'model-uuid': 'aaaa'},
1992
{'name': 'baz', 'model-uuid': 'bbbb'}],
1993
'current-model': 'bar'
1995
client = EnvJujuClient(JujuData('foo'), None, None)
1996
with patch.object(client, 'get_models', return_value=models):
1997
controller_name = client.get_controller_model_name()
1998
self.assertEqual('controller', controller_name)
2000
def test_get_controller_model_name_no_models(self):
2001
client = EnvJujuClient(JujuData('foo'), None, None)
2002
with patch.object(client, 'get_models', return_value={}):
2003
controller_name = client.get_controller_model_name()
2004
self.assertEqual('controller', controller_name)
2006
def test_get_model_uuid_returns_uuid(self):
2007
model_uuid = '9ed1bde9-45c6-4d41-851d-33fdba7fa194'
2008
yaml_string = dedent("""\
2012
controller-uuid: eb67e1eb-6c54-45f5-8b6a-b6243be97202
2025
last-connection: just now
2026
""".format(uuid=model_uuid))
2027
client = EnvJujuClient(JujuData('foo'), None, None)
2028
with patch.object(client, 'get_juju_output') as m_get_juju_output:
2029
m_get_juju_output.return_value = yaml_string
2031
client.get_model_uuid(),
2034
m_get_juju_output.assert_called_once_with(
2035
'show-model', '--format', 'yaml', 'foo:foo', include_e=False)
2037
def test_get_controller_model_uuid_returns_uuid(self):
2038
controller_uuid = 'eb67e1eb-6c54-45f5-8b6a-b6243be97202'
2039
controller_model_uuid = '1c908e10-4f07-459a-8419-bb61553a4660'
2040
yaml_string = dedent("""\
2044
controller-uuid: {controller}
2045
controller-name: localtempveebers
2053
since: 59 seconds ago
2058
last-connection: just now""".format(model=controller_model_uuid,
2059
controller=controller_uuid))
2060
client = EnvJujuClient(JujuData('foo'), None, None)
2061
with patch.object(client, 'get_juju_output') as m_get_juju_output:
2062
m_get_juju_output.return_value = yaml_string
2064
client.get_controller_model_uuid(),
2065
controller_model_uuid
2067
m_get_juju_output.assert_called_once_with(
2068
'show-model', 'controller',
2069
'--format', 'yaml', include_e=False)
2071
def test_get_controller_uuid_returns_uuid(self):
2072
controller_uuid = 'eb67e1eb-6c54-45f5-8b6a-b6243be97202'
2073
yaml_string = dedent("""\
2077
api-endpoints: ['10.194.140.213:17070']
2084
uuid: 772cdd39-b454-4bd5-8704-dc9aa9ff1750
2085
current-model: default
2092
region: localhost""".format(uuid=controller_uuid))
2093
client = EnvJujuClient(JujuData('foo'), None, None)
2094
with patch.object(client, 'get_juju_output') as m_get_juju_output:
2095
m_get_juju_output.return_value = yaml_string
2097
client.get_controller_uuid(),
2100
m_get_juju_output.assert_called_once_with(
2101
'show-controller', '--format', 'yaml', include_e=False)
2103
def test_get_controller_client(self):
2104
client = EnvJujuClient(
2105
JujuData('foo', {'bar': 'baz'}, 'myhome'), None, None)
2106
controller_client = client.get_controller_client()
2107
controller_env = controller_client.env
2108
self.assertEqual('controller', controller_env.environment)
2110
{'bar': 'baz', 'name': 'controller'}, controller_env.config)
2112
def test_list_controllers(self):
2113
client = EnvJujuClient(JujuData('foo'), None, None)
2114
with patch.object(client, 'juju') as j_mock:
2115
client.list_controllers()
2116
j_mock.assert_called_once_with('list-controllers', (), include_e=False)
2118
def test_get_controller_endpoint_ipv4(self):
2122
api-endpoints: ['10.0.0.1:17070', '10.0.0.2:17070']
2124
client = EnvJujuClient(JujuData('foo'), None, None)
2125
with patch.object(client, 'get_juju_output',
2126
return_value=data) as gjo_mock:
2127
endpoint = client.get_controller_endpoint()
2128
self.assertEqual('10.0.0.1', endpoint)
2129
gjo_mock.assert_called_once_with(
2130
'show-controller', 'foo', include_e=False)
2132
def test_get_controller_endpoint_ipv6(self):
2136
api-endpoints: ['[::1]:17070', '[fe80::216:3eff:0:9dc7]:17070']
2138
client = EnvJujuClient(JujuData('foo'), None, None)
2139
with patch.object(client, 'get_juju_output',
2140
return_value=data) as gjo_mock:
2141
endpoint = client.get_controller_endpoint()
2142
self.assertEqual('::1', endpoint)
2143
gjo_mock.assert_called_once_with(
2144
'show-controller', 'foo', include_e=False)
2146
def test_get_controller_controller_name(self):
2150
api-endpoints: ['[::1]:17070', '[fe80::216:3eff:0:9dc7]:17070']
2152
client = EnvJujuClient(JujuData('foo', {}), None, None)
2153
controller_client = client.get_controller_client()
2154
client.env.controller.name = 'bar'
2155
with patch.object(controller_client, 'get_juju_output',
2156
return_value=data) as gjo:
2157
endpoint = controller_client.get_controller_endpoint()
2158
gjo.assert_called_once_with('show-controller', 'bar',
2160
self.assertEqual('::1', endpoint)
2162
def test_get_controller_members(self):
2163
status = Status.from_text("""\
2168
instance-id: juju-aaaa-machine-0
2169
controller-member-status: has-vote
2172
instance-id: juju-bbbb-machine-1
2175
instance-id: juju-cccc-machine-2
2176
controller-member-status: has-vote
2179
instance-id: juju-dddd-machine-3
2180
controller-member-status: has-vote
2182
client = EnvJujuClient(JujuData('foo'), None, None)
2183
with patch.object(client, 'get_status', autospec=True,
2184
return_value=status):
2185
with patch.object(client, 'get_controller_endpoint', autospec=True,
2186
return_value='10.0.0.3') as gce_mock:
2187
with patch.object(client, 'get_controller_member_status',
2188
wraps=client.get_controller_member_status,
2190
members = client.get_controller_members()
2191
# Machine 1 was ignored. Machine 3 is the leader, thus first.
2194
'dns-name': '10.0.0.3',
2195
'instance-id': 'juju-dddd-machine-3',
2196
'controller-member-status': 'has-vote'}),
2198
'dns-name': '10.0.0.0',
2199
'instance-id': 'juju-aaaa-machine-0',
2200
'controller-member-status': 'has-vote'}),
2202
'dns-name': '10.0.0.2',
2203
'instance-id': 'juju-cccc-machine-2',
2204
'controller-member-status': 'has-vote'}),
2206
self.assertEqual(expected, members)
2207
gce_mock.assert_called_once_with()
2208
# get_controller_member_status must be called to ensure compatibility
2209
# with all version of Juju.
2210
self.assertEqual(4, gcms_mock.call_count)
2212
def test_get_controller_members_one(self):
2213
status = Status.from_text("""\
2218
instance-id: juju-aaaa-machine-0
2219
controller-member-status: has-vote
2221
client = EnvJujuClient(JujuData('foo'), None, None)
2222
with patch.object(client, 'get_status', autospec=True,
2223
return_value=status):
2224
with patch.object(client, 'get_controller_endpoint') as gce_mock:
2225
members = client.get_controller_members()
2226
# Machine 0 was the only choice, no need to find the leader.
2229
'dns-name': '10.0.0.0',
2230
'instance-id': 'juju-aaaa-machine-0',
2231
'controller-member-status': 'has-vote'}),
2233
self.assertEqual(expected, members)
2234
self.assertEqual(0, gce_mock.call_count)
2236
def test_get_controller_leader(self):
2242
client = EnvJujuClient(JujuData('foo'), None, None)
2243
with patch.object(client, 'get_controller_members', autospec=True,
2244
return_value=members):
2245
leader = client.get_controller_leader()
2246
self.assertEqual(Machine('3', {}), leader)
2248
def test_wait_for_ha(self):
2249
value = yaml.safe_dump({
2251
'0': {'controller-member-status': 'has-vote'},
2252
'1': {'controller-member-status': 'has-vote'},
2253
'2': {'controller-member-status': 'has-vote'},
2257
client = EnvJujuClient(JujuData('local'), None, None)
2258
with patch.object(client, 'get_juju_output',
2259
return_value=value) as gjo_mock:
2260
client.wait_for_ha()
2261
gjo_mock.assert_called_once_with(
2262
'show-status', '--format', 'yaml', controller=True)
2264
def test_wait_for_ha_no_has_vote(self):
2265
value = yaml.safe_dump({
2267
'0': {'controller-member-status': 'no-vote'},
2268
'1': {'controller-member-status': 'no-vote'},
2269
'2': {'controller-member-status': 'no-vote'},
2273
client = EnvJujuClient(JujuData('local'), None, None)
2274
with patch.object(client, 'get_juju_output', return_value=value):
2276
with patch('jujupy.until_timeout', autospec=True,
2277
return_value=[2, 1]):
2278
with patch.object(GroupReporter, '_write', autospec=True,
2279
side_effect=lambda _, s: writes.append(s)):
2280
with self.assertRaisesRegexp(
2282
'Timed out waiting for voting to be enabled.'):
2283
client.wait_for_ha()
2284
self.assertEqual(writes[:2], ['no-vote: 0, 1, 2', ' .'])
2285
self.assertEqual(writes[2:-1], ['.'] * (len(writes) - 3))
2286
self.assertEqual(writes[-1:], ['\n'])
2288
def test_wait_for_ha_timeout(self):
2289
value = yaml.safe_dump({
2291
'0': {'controller-member-status': 'has-vote'},
2292
'1': {'controller-member-status': 'has-vote'},
2296
client = EnvJujuClient(JujuData('local'), None, None)
2297
with patch('jujupy.until_timeout', lambda x: range(0)):
2298
with patch.object(client, 'get_juju_output', return_value=value):
2299
with self.assertRaisesRegexp(
2301
'Timed out waiting for voting to be enabled.'):
2302
client.wait_for_ha()
2304
def test_wait_for_ha_timeout_with_status_error(self):
2305
value = yaml.safe_dump({
2307
'0': {'agent-state-info': 'running'},
2308
'1': {'agent-state-info': 'error: foo'},
2312
client = EnvJujuClient(JujuData('local'), None, None)
2313
with patch('jujupy.until_timeout', autospec=True, return_value=[2, 1]):
2314
with patch.object(client, 'get_juju_output', return_value=value):
2315
with self.assertRaisesRegexp(
2316
ErroredUnit, '1 is in state error: foo'):
2317
client.wait_for_ha()
2319
def test_wait_for_ha_suppresses_deadline(self):
2320
with self.only_status_checks(self.make_ha_status()) as client:
2321
client.wait_for_ha()
2323
def test_wait_for_ha_checks_deadline(self):
2324
with self.status_does_not_check(self.make_ha_status()) as client:
2325
with self.assertRaises(SoftDeadlineExceeded):
2326
client.wait_for_ha()
2328
def test_wait_for_deploy_started(self):
2329
value = yaml.safe_dump({
2331
'0': {'agent-state': 'started'},
2336
'jenkins/1': {'baz': 'qux'}
2341
client = EnvJujuClient(JujuData('local'), None, None)
2342
with patch.object(client, 'get_juju_output', return_value=value):
2343
client.wait_for_deploy_started()
2345
def test_wait_for_deploy_started_timeout(self):
2346
value = yaml.safe_dump({
2348
'0': {'agent-state': 'started'},
2352
client = EnvJujuClient(JujuData('local'), None, None)
2353
with patch('jujupy.until_timeout', lambda x: range(0)):
2354
with patch.object(client, 'get_juju_output', return_value=value):
2355
with self.assertRaisesRegexp(
2357
'Timed out waiting for services to start.'):
2358
client.wait_for_deploy_started()
2360
def make_deployed_status(self):
2363
'0': {'agent-state': 'started'},
2368
'jenkins/1': {'baz': 'qux'}
2374
def test_wait_for_deploy_started_suppresses_deadline(self):
2375
with self.only_status_checks(self.make_deployed_status()) as client:
2376
client.wait_for_deploy_started()
2378
def test_wait_for_deploy_started_checks_deadline(self):
2379
with self.status_does_not_check(self.make_deployed_status()) as client:
2380
with self.assertRaises(SoftDeadlineExceeded):
2381
client.wait_for_deploy_started()
2383
def test_wait_for_version(self):
2384
value = self.make_status_yaml('agent-version', '1.17.2', '1.17.2')
2385
client = EnvJujuClient(JujuData('local'), None, None)
2386
with patch.object(client, 'get_juju_output', return_value=value):
2387
client.wait_for_version('1.17.2')
2389
def test_wait_for_version_timeout(self):
2390
value = self.make_status_yaml('agent-version', '1.17.2', '1.17.1')
2391
client = EnvJujuClient(JujuData('local'), None, None)
2393
with patch('jujupy.until_timeout', lambda x, start=None: [x]):
2394
with patch.object(client, 'get_juju_output', return_value=value):
2395
with patch.object(GroupReporter, '_write', autospec=True,
2396
side_effect=lambda _, s: writes.append(s)):
2397
with self.assertRaisesRegexp(
2398
Exception, 'Some versions did not update'):
2399
client.wait_for_version('1.17.2')
2400
self.assertEqual(writes, ['1.17.1: jenkins/0', ' .', '\n'])
2402
def test_wait_for_version_handles_connection_error(self):
2403
err = subprocess.CalledProcessError(2, 'foo')
2404
err.stderr = 'Unable to connect to environment'
2405
err = CannotConnectEnv(err)
2406
status = self.make_status_yaml('agent-version', '1.17.2', '1.17.2')
2407
actions = [err, status]
2409
def get_juju_output_fake(*args, **kwargs):
2410
action = actions.pop(0)
2411
if isinstance(action, Exception):
2416
client = EnvJujuClient(JujuData('local'), None, None)
2417
with patch.object(client, 'get_juju_output', get_juju_output_fake):
2418
client.wait_for_version('1.17.2')
2420
def test_wait_for_version_raises_non_connection_error(self):
2421
err = Exception('foo')
2422
status = self.make_status_yaml('agent-version', '1.17.2', '1.17.2')
2423
actions = [err, status]
2425
def get_juju_output_fake(*args, **kwargs):
2426
action = actions.pop(0)
2427
if isinstance(action, Exception):
2432
client = EnvJujuClient(JujuData('local'), None, None)
2433
with patch.object(client, 'get_juju_output', get_juju_output_fake):
2434
with self.assertRaisesRegexp(Exception, 'foo'):
2435
client.wait_for_version('1.17.2')
2437
def test_wait_for_just_machine_0(self):
2438
value = yaml.safe_dump({
2440
'0': {'agent-state': 'started'},
2443
client = EnvJujuClient(JujuData('local'), None, None)
2444
with patch.object(client, 'get_juju_output', return_value=value):
2445
client.wait_for('machines-not-0', 'none')
2447
def test_wait_for_just_machine_0_timeout(self):
2448
value = yaml.safe_dump({
2450
'0': {'agent-state': 'started'},
2451
'1': {'agent-state': 'started'},
2454
client = EnvJujuClient(JujuData('local'), None, None)
2455
with patch.object(client, 'get_juju_output', return_value=value), \
2456
patch('jujupy.until_timeout', lambda x: range(0)), \
2457
self.assertRaisesRegexp(
2459
'Timed out waiting for machines-not-0'):
2460
client.wait_for('machines-not-0', 'none')
2462
def test_set_model_constraints(self):
2463
client = EnvJujuClient(JujuData('bar', {}), None, '/foo')
2464
with patch.object(client, 'juju') as juju_mock:
2465
client.set_model_constraints({'bar': 'baz'})
2466
juju_mock.assert_called_once_with('set-model-constraints',
2469
def test_get_model_config(self):
2470
env = JujuData('foo', None)
2471
fake_popen = FakePopen(yaml.safe_dump({'bar': 'baz'}), None, 0)
2472
client = EnvJujuClient(env, None, 'juju')
2473
with patch('subprocess.Popen', return_value=fake_popen) as po_mock:
2474
result = client.get_model_config()
2476
self, po_mock, client, (
2477
'juju', '--show-log',
2478
'model-config', '-m', 'foo:foo', '--format', 'yaml'))
2479
self.assertEqual({'bar': 'baz'}, result)
2481
def test_get_env_option(self):
2482
env = JujuData('foo', None)
2483
fake_popen = FakePopen('https://example.org/juju/tools', None, 0)
2484
client = EnvJujuClient(env, None, 'juju')
2485
with patch('subprocess.Popen', return_value=fake_popen) as mock:
2486
result = client.get_env_option('tools-metadata-url')
2488
mock.call_args[0][0],
2489
('juju', '--show-log', 'model-config', '-m', 'foo:foo',
2490
'tools-metadata-url'))
2491
self.assertEqual('https://example.org/juju/tools', result)
2493
def test_set_env_option(self):
2494
env = JujuData('foo')
2495
client = EnvJujuClient(env, None, 'juju')
2496
with patch('subprocess.check_call') as mock:
2497
client.set_env_option(
2498
'tools-metadata-url', 'https://example.org/juju/tools')
2499
environ = dict(os.environ)
2500
environ['JUJU_HOME'] = client.env.juju_home
2501
mock.assert_called_with(
2502
('juju', '--show-log', 'model-config', '-m', 'foo:foo',
2503
'tools-metadata-url=https://example.org/juju/tools'))
2505
def test_unset_env_option(self):
2506
env = JujuData('foo')
2507
client = EnvJujuClient(env, None, 'juju')
2508
with patch('subprocess.check_call') as mock:
2509
client.unset_env_option('tools-metadata-url')
2510
environ = dict(os.environ)
2511
environ['JUJU_HOME'] = client.env.juju_home
2512
mock.assert_called_with(
2513
('juju', '--show-log', 'model-config', '-m', 'foo:foo',
2514
'--reset', 'tools-metadata-url'))
2516
def test_set_testing_agent_metadata_url(self):
2517
env = JujuData(None, {'type': 'foo'})
2518
client = EnvJujuClient(env, None, None)
2519
with patch.object(client, 'get_env_option') as mock_get:
2520
mock_get.return_value = 'https://example.org/juju/tools'
2521
with patch.object(client, 'set_env_option') as mock_set:
2522
client.set_testing_agent_metadata_url()
2523
mock_get.assert_called_with('agent-metadata-url')
2524
mock_set.assert_called_with(
2525
'agent-metadata-url',
2526
'https://example.org/juju/testing/tools')
2528
def test_set_testing_agent_metadata_url_noop(self):
2529
env = JujuData(None, {'type': 'foo'})
2530
client = EnvJujuClient(env, None, None)
2531
with patch.object(client, 'get_env_option') as mock_get:
2532
mock_get.return_value = 'https://example.org/juju/testing/tools'
2533
with patch.object(client, 'set_env_option') as mock_set:
2534
client.set_testing_agent_metadata_url()
2535
mock_get.assert_called_with('agent-metadata-url',)
2536
self.assertEqual(0, mock_set.call_count)
2538
def test_juju(self):
2539
env = JujuData('qux')
2540
client = EnvJujuClient(env, None, 'juju')
2541
with patch('subprocess.check_call') as mock:
2542
client.juju('foo', ('bar', 'baz'))
2543
environ = dict(os.environ)
2544
environ['JUJU_HOME'] = client.env.juju_home
2545
mock.assert_called_with(('juju', '--show-log', 'foo', '-m', 'qux:qux',
2548
def test_expect_returns_pexpect_spawn_object(self):
2549
env = JujuData('qux')
2550
client = EnvJujuClient(env, None, 'juju')
2551
with patch('pexpect.spawn') as mock:
2552
process = client.expect('foo', ('bar', 'baz'))
2554
self.assertIs(process, mock.return_value)
2555
mock.assert_called_once_with('juju --show-log foo -m qux:qux bar baz')
2557
def test_expect_uses_provided_envvar_path(self):
2558
from pexpect import ExceptionPexpect
2559
env = JujuData('qux')
2560
client = EnvJujuClient(env, None, 'juju')
2562
with temp_dir() as empty_path:
2563
broken_envvars = dict(PATH=empty_path)
2567
'ls', (), extra_env=broken_envvars,
2570
def test_juju_env(self):
2571
env = JujuData('qux')
2572
client = EnvJujuClient(env, None, '/foobar/baz')
2574
def check_path(*args, **kwargs):
2575
self.assertRegexpMatches(os.environ['PATH'], r'/foobar\:')
2576
with patch('subprocess.check_call', side_effect=check_path):
2577
client.juju('foo', ('bar', 'baz'))
2579
def test_juju_no_check(self):
2580
env = JujuData('qux')
2581
client = EnvJujuClient(env, None, 'juju')
2582
environ = dict(os.environ)
2583
environ['JUJU_HOME'] = client.env.juju_home
2584
with patch('subprocess.call') as mock:
2585
client.juju('foo', ('bar', 'baz'), check=False)
2586
mock.assert_called_with(('juju', '--show-log', 'foo', '-m', 'qux:qux',
2589
def test_juju_no_check_env(self):
2590
env = JujuData('qux')
2591
client = EnvJujuClient(env, None, '/foobar/baz')
2593
def check_path(*args, **kwargs):
2594
self.assertRegexpMatches(os.environ['PATH'], r'/foobar\:')
2595
with patch('subprocess.call', side_effect=check_path):
2596
client.juju('foo', ('bar', 'baz'), check=False)
2598
def test_juju_timeout(self):
2599
env = JujuData('qux')
2600
client = EnvJujuClient(env, None, '/foobar/baz')
2601
with patch('subprocess.check_call') as cc_mock:
2602
client.juju('foo', ('bar', 'baz'), timeout=58)
2603
self.assertEqual(cc_mock.call_args[0][0], (
2604
sys.executable, get_timeout_path(), '58.00', '--', 'baz',
2605
'--show-log', 'foo', '-m', 'qux:qux', 'bar', 'baz'))
2607
def test_juju_juju_home(self):
2608
env = JujuData('qux')
2609
os.environ['JUJU_HOME'] = 'foo'
2610
client = EnvJujuClient(env, None, '/foobar/baz')
2612
def check_home(*args, **kwargs):
2613
self.assertEqual(os.environ['JUJU_HOME'], 'foo')
2615
self.assertEqual(os.environ['JUJU_HOME'], 'asdf')
2618
with patch('subprocess.check_call', side_effect=check_home):
2619
client.juju('foo', ('bar', 'baz'))
2620
client.env.juju_home = 'asdf'
2621
client.juju('foo', ('bar', 'baz'))
2623
def test_juju_extra_env(self):
2624
env = JujuData('qux')
2625
client = EnvJujuClient(env, None, 'juju')
2626
extra_env = {'JUJU': '/juju', 'JUJU_HOME': client.env.juju_home}
2628
def check_env(*args, **kwargs):
2629
self.assertEqual('/juju', os.environ['JUJU'])
2631
with patch('subprocess.check_call', side_effect=check_env) as mock:
2632
client.juju('quickstart', ('bar', 'baz'), extra_env=extra_env)
2633
mock.assert_called_with(
2634
('juju', '--show-log', 'quickstart', '-m', 'qux:qux',
2637
def test_juju_backup_with_tgz(self):
2638
env = JujuData('qux')
2639
client = EnvJujuClient(env, None, '/foobar/baz')
2643
return_value=FakePopen('foojuju-backup-24.tgzz', '', 0),
2645
backup_file = client.backup()
2646
self.assertEqual(backup_file, os.path.abspath('juju-backup-24.tgz'))
2647
assert_juju_call(self, popen_mock, client, ('baz', '--show-log',
2648
'create-backup', '-m', 'qux:qux'))
2650
def test_juju_backup_with_tar_gz(self):
2651
env = JujuData('qux')
2652
client = EnvJujuClient(env, None, '/foobar/baz')
2653
with patch('subprocess.Popen',
2654
return_value=FakePopen(
2655
'foojuju-backup-123-456.tar.gzbar', '', 0)):
2656
backup_file = client.backup()
2658
backup_file, os.path.abspath('juju-backup-123-456.tar.gz'))
2660
def test_juju_backup_no_file(self):
2661
env = JujuData('qux')
2662
client = EnvJujuClient(env, None, '/foobar/baz')
2663
with patch('subprocess.Popen', return_value=FakePopen('', '', 0)):
2664
with self.assertRaisesRegexp(
2665
Exception, 'The backup file was not found in output'):
2668
def test_juju_backup_wrong_file(self):
2669
env = JujuData('qux')
2670
client = EnvJujuClient(env, None, '/foobar/baz')
2671
with patch('subprocess.Popen',
2672
return_value=FakePopen('mumu-backup-24.tgz', '', 0)):
2673
with self.assertRaisesRegexp(
2674
Exception, 'The backup file was not found in output'):
2677
def test_juju_backup_environ(self):
2678
env = JujuData('qux')
2679
client = EnvJujuClient(env, None, '/foobar/baz')
2680
environ = client._shell_environ()
2682
def side_effect(*args, **kwargs):
2683
self.assertEqual(environ, os.environ)
2684
return FakePopen('foojuju-backup-123-456.tar.gzbar', '', 0)
2685
with patch('subprocess.Popen', side_effect=side_effect):
2687
self.assertNotEqual(environ, os.environ)
2689
def test_restore_backup(self):
2690
env = JujuData('qux')
2691
client = EnvJujuClient(env, None, '/foobar/baz')
2692
with patch.object(client, 'juju') as gjo_mock:
2693
client.restore_backup('quxx')
2694
gjo_mock.assert_called_once_with(
2696
('-b', '--constraints', 'mem=2G', '--file', 'quxx'))
2698
def test_restore_backup_async(self):
2699
env = JujuData('qux')
2700
client = EnvJujuClient(env, None, '/foobar/baz')
2701
with patch.object(client, 'juju_async') as gjo_mock:
2702
result = client.restore_backup_async('quxx')
2703
gjo_mock.assert_called_once_with('restore-backup', (
2704
'-b', '--constraints', 'mem=2G', '--file', 'quxx'))
2705
self.assertIs(gjo_mock.return_value, result)
2707
def test_enable_ha(self):
2708
env = JujuData('qux')
2709
client = EnvJujuClient(env, None, '/foobar/baz')
2710
with patch.object(client, 'juju', autospec=True) as eha_mock:
2712
eha_mock.assert_called_once_with('enable-ha', ('-n', '3'))
2714
def test_juju_async(self):
2715
env = JujuData('qux')
2716
client = EnvJujuClient(env, None, '/foobar/baz')
2717
with patch('subprocess.Popen') as popen_class_mock:
2718
with client.juju_async('foo', ('bar', 'baz')) as proc:
2723
('baz', '--show-log', 'foo', '-m', 'qux:qux',
2725
self.assertIs(proc, popen_class_mock.return_value)
2726
self.assertEqual(proc.wait.call_count, 0)
2727
proc.wait.return_value = 0
2728
proc.wait.assert_called_once_with()
2730
def test_juju_async_failure(self):
2731
env = JujuData('qux')
2732
client = EnvJujuClient(env, None, '/foobar/baz')
2733
with patch('subprocess.Popen') as popen_class_mock:
2734
with self.assertRaises(subprocess.CalledProcessError) as err_cxt:
2735
with client.juju_async('foo', ('bar', 'baz')):
2736
proc_mock = popen_class_mock.return_value
2737
proc_mock.wait.return_value = 23
2738
self.assertEqual(err_cxt.exception.returncode, 23)
2739
self.assertEqual(err_cxt.exception.cmd, (
2740
'baz', '--show-log', 'foo', '-m', 'qux:qux', 'bar', 'baz'))
2742
def test_juju_async_environ(self):
2743
env = JujuData('qux')
2744
client = EnvJujuClient(env, None, '/foobar/baz')
2745
environ = client._shell_environ()
2747
with patch('subprocess.Popen') as popen_class_mock:
2749
def check_environ(*args, **kwargs):
2750
self.assertEqual(environ, os.environ)
2752
popen_class_mock.side_effect = check_environ
2753
proc_mock.wait.return_value = 0
2754
with client.juju_async('foo', ('bar', 'baz')):
2756
self.assertNotEqual(environ, os.environ)
2758
def test_is_jes_enabled(self):
2759
# EnvJujuClient knows that JES is always enabled, and doesn't need to
2761
env = JujuData('qux')
2762
client = EnvJujuClient(env, None, '/foobar/baz')
2763
fake_popen = FakePopen(' %s' % SYSTEM, None, 0)
2764
with patch('subprocess.Popen',
2765
return_value=fake_popen) as po_mock:
2766
self.assertTrue(client.is_jes_enabled())
2767
self.assertEqual(0, po_mock.call_count)
2769
def test_get_jes_command(self):
2770
env = JujuData('qux')
2771
client = EnvJujuClient(env, None, '/foobar/baz')
2772
# Juju 1.24 and older do not have a JES command. It is an error
2773
# to call get_jes_command when is_jes_enabled is False
2774
fake_popen = FakePopen(' %s' % SYSTEM, None, 0)
2775
with patch('subprocess.Popen',
2776
return_value=fake_popen) as po_mock:
2777
self.assertEqual(KILL_CONTROLLER, client.get_jes_command())
2778
self.assertEqual(0, po_mock.call_count)
2780
def test_get_juju_timings(self):
2781
env = JujuData('foo')
2782
client = EnvJujuClient(env, None, 'my/juju/bin')
2783
client._backend.juju_timings = {("juju", "op1"): [1],
2784
("juju", "op2"): [2]}
2785
flattened_timings = client.get_juju_timings()
2786
expected = {"juju op1": [1], "juju op2": [2]}
2787
self.assertEqual(flattened_timings, expected)
2789
def test_deployer(self):
2790
client = EnvJujuClient(JujuData('foo', {'type': 'local'}),
2791
'1.23-series-arch', None)
2792
with patch.object(EnvJujuClient, 'juju') as mock:
2793
client.deployer('bundle:~juju-qa/some-bundle')
2794
mock.assert_called_with(
2795
'deployer', ('-e', 'foo:foo', '--debug', '--deploy-delay',
2796
'10', '--timeout', '3600', '--config',
2797
'bundle:~juju-qa/some-bundle'),
2798
True, include_e=False
2801
def test_deployer_with_bundle_name(self):
2802
client = EnvJujuClient(JujuData('foo', {'type': 'local'}),
2803
'2.0.0-series-arch', None)
2804
with patch.object(EnvJujuClient, 'juju') as mock:
2805
client.deployer('bundle:~juju-qa/some-bundle', 'name')
2806
mock.assert_called_with(
2807
'deployer', ('-e', 'foo:foo', '--debug', '--deploy-delay',
2808
'10', '--timeout', '3600', '--config',
2809
'bundle:~juju-qa/some-bundle', 'name'),
2810
True, include_e=False
2813
def test_quickstart_maas(self):
2814
client = EnvJujuClient(JujuData(None, {'type': 'maas'}),
2815
'1.23-series-arch', '/juju')
2816
with patch.object(EnvJujuClient, 'juju') as mock:
2817
client.quickstart('bundle:~juju-qa/some-bundle')
2818
mock.assert_called_with(
2820
('--constraints', 'mem=2G', '--no-browser',
2821
'bundle:~juju-qa/some-bundle'), False, extra_env={'JUJU': '/juju'}
2824
def test_quickstart_local(self):
2825
client = EnvJujuClient(JujuData(None, {'type': 'local'}),
2826
'1.23-series-arch', '/juju')
2827
with patch.object(EnvJujuClient, 'juju') as mock:
2828
client.quickstart('bundle:~juju-qa/some-bundle')
2829
mock.assert_called_with(
2831
('--constraints', 'mem=2G', '--no-browser',
2832
'bundle:~juju-qa/some-bundle'), True, extra_env={'JUJU': '/juju'}
2835
def test_quickstart_nonlocal(self):
2836
client = EnvJujuClient(JujuData(None, {'type': 'nonlocal'}),
2837
'1.23-series-arch', '/juju')
2838
with patch.object(EnvJujuClient, 'juju') as mock:
2839
client.quickstart('bundle:~juju-qa/some-bundle')
2840
mock.assert_called_with(
2842
('--constraints', 'mem=2G', '--no-browser',
2843
'bundle:~juju-qa/some-bundle'), False, extra_env={'JUJU': '/juju'}
2846
def test_quickstart_template(self):
2847
client = EnvJujuClient(JujuData(None, {'type': 'local'}),
2848
'1.23-series-arch', '/juju')
2849
with patch.object(EnvJujuClient, 'juju') as mock:
2850
client.quickstart('bundle:~juju-qa/some-{container}-bundle')
2851
mock.assert_called_with(
2853
'--constraints', 'mem=2G', '--no-browser',
2854
'bundle:~juju-qa/some-lxd-bundle'),
2855
True, extra_env={'JUJU': '/juju'})
2857
def test_action_do(self):
2858
client = EnvJujuClient(JujuData(None, {'type': 'local'}),
2859
'1.23-series-arch', None)
2860
with patch.object(EnvJujuClient, 'get_juju_output') as mock:
2861
mock.return_value = \
2862
"Action queued with id: 5a92ec93-d4be-4399-82dc-7431dbfd08f9"
2863
id = client.action_do("foo/0", "myaction", "param=5")
2864
self.assertEqual(id, "5a92ec93-d4be-4399-82dc-7431dbfd08f9")
2865
mock.assert_called_once_with(
2866
'run-action', 'foo/0', 'myaction', "param=5"
2869
def test_action_do_error(self):
2870
client = EnvJujuClient(JujuData(None, {'type': 'local'}),
2871
'1.23-series-arch', None)
2872
with patch.object(EnvJujuClient, 'get_juju_output') as mock:
2873
mock.return_value = "some bad text"
2874
with self.assertRaisesRegexp(Exception,
2875
"Action id not found in output"):
2876
client.action_do("foo/0", "myaction", "param=5")
2878
def test_action_fetch(self):
2879
client = EnvJujuClient(JujuData(None, {'type': 'local'}),
2880
'1.23-series-arch', None)
2881
with patch.object(EnvJujuClient, 'get_juju_output') as mock:
2882
ret = "status: completed\nfoo: bar"
2883
mock.return_value = ret
2884
out = client.action_fetch("123")
2885
self.assertEqual(out, ret)
2886
mock.assert_called_once_with(
2887
'show-action-output', '123', "--wait", "1m"
2890
def test_action_fetch_timeout(self):
2891
client = EnvJujuClient(JujuData(None, {'type': 'local'}),
2892
'1.23-series-arch', None)
2893
ret = "status: pending\nfoo: bar"
2894
with patch.object(EnvJujuClient,
2895
'get_juju_output', return_value=ret):
2896
with self.assertRaisesRegexp(Exception,
2897
"timed out waiting for action"):
2898
client.action_fetch("123")
2900
def test_action_do_fetch(self):
2901
client = EnvJujuClient(JujuData(None, {'type': 'local'}),
2902
'1.23-series-arch', None)
2903
with patch.object(EnvJujuClient, 'get_juju_output') as mock:
2904
ret = "status: completed\nfoo: bar"
2905
# setting side_effect to an iterable will return the next value
2906
# from the list each time the function is called.
2907
mock.side_effect = [
2908
"Action queued with id: 5a92ec93-d4be-4399-82dc-7431dbfd08f9",
2910
out = client.action_do_fetch("foo/0", "myaction", "param=5")
2911
self.assertEqual(out, ret)
2914
client = fake_juju_client(cls=EnvJujuClient)
2917
"Stdout": "Linux\n",
2919
"Stderr": "Permission denied (publickey,password)"}]
2920
run_output = json.dumps(run_list)
2921
with patch.object(client._backend, 'get_juju_output',
2922
return_value=run_output) as gjo_mock:
2923
result = client.run(('wname',), applications=['foo', 'bar'])
2924
self.assertEqual(run_list, result)
2925
gjo_mock.assert_called_once_with(
2926
'run', ('--format', 'json', '--application', 'foo,bar', 'wname'),
2928
['address-allocation', 'migration']), 'foo',
2929
'name:name', user_name=None)
2931
def test_list_space(self):
2932
client = EnvJujuClient(JujuData(None, {'type': 'local'}),
2933
'1.23-series-arch', None)
2934
yaml_dict = {'foo': 'bar'}
2935
output = yaml.safe_dump(yaml_dict)
2936
with patch.object(client, 'get_juju_output', return_value=output,
2937
autospec=True) as gjo_mock:
2938
result = client.list_space()
2939
self.assertEqual(result, yaml_dict)
2940
gjo_mock.assert_called_once_with('list-space')
2942
def test_add_space(self):
2943
client = EnvJujuClient(JujuData(None, {'type': 'local'}),
2944
'1.23-series-arch', None)
2945
with patch.object(client, 'juju', autospec=True) as juju_mock:
2946
client.add_space('foo-space')
2947
juju_mock.assert_called_once_with('add-space', ('foo-space'))
2949
def test_add_subnet(self):
2950
client = EnvJujuClient(JujuData(None, {'type': 'local'}),
2951
'1.23-series-arch', None)
2952
with patch.object(client, 'juju', autospec=True) as juju_mock:
2953
client.add_subnet('bar-subnet', 'foo-space')
2954
juju_mock.assert_called_once_with('add-subnet',
2955
('bar-subnet', 'foo-space'))
2957
def test__shell_environ_uses_pathsep(self):
2958
client = EnvJujuClient(JujuData('foo'), None, 'foo/bar/juju')
2959
with patch('os.pathsep', '!'):
2960
environ = client._shell_environ()
2961
self.assertRegexpMatches(environ['PATH'], r'foo/bar\!')
2963
def test_set_config(self):
2964
client = EnvJujuClient(JujuData('bar', {}), None, '/foo')
2965
with patch.object(client, 'juju') as juju_mock:
2966
client.set_config('foo', {'bar': 'baz'})
2967
juju_mock.assert_called_once_with('config', ('foo', 'bar=baz'))
2969
def test_get_config(self):
2970
def output(*args, **kwargs):
2971
return yaml.safe_dump({
2977
'description': 'bla bla',
2979
'value': '/tmp/charm-dir',
2983
expected = yaml.safe_load(output())
2984
client = EnvJujuClient(JujuData('bar', {}), None, '/foo')
2985
with patch.object(client, 'get_juju_output',
2986
side_effect=output) as gjo_mock:
2987
results = client.get_config('foo')
2988
self.assertEqual(expected, results)
2989
gjo_mock.assert_called_once_with('config', 'foo')
2991
def test_get_service_config(self):
2992
def output(*args, **kwargs):
2993
return yaml.safe_dump({
2999
'description': 'bla bla',
3001
'value': '/tmp/charm-dir',
3005
expected = yaml.safe_load(output())
3006
client = EnvJujuClient(JujuData('bar', {}), None, '/foo')
3007
with patch.object(client, 'get_juju_output', side_effect=output):
3008
results = client.get_service_config('foo')
3009
self.assertEqual(expected, results)
3011
def test_get_service_config_timesout(self):
3012
client = EnvJujuClient(JujuData('foo', {}), None, '/foo')
3013
with patch('jujupy.until_timeout', return_value=range(0)):
3014
with self.assertRaisesRegexp(
3015
Exception, 'Timed out waiting for juju get'):
3016
client.get_service_config('foo')
3018
def test_upgrade_mongo(self):
3019
client = EnvJujuClient(JujuData('bar', {}), None, '/foo')
3020
with patch.object(client, 'juju') as juju_mock:
3021
client.upgrade_mongo()
3022
juju_mock.assert_called_once_with('upgrade-mongo', ())
3024
def test_enable_feature(self):
3025
client = EnvJujuClient(JujuData('bar', {}), None, '/foo')
3026
self.assertEqual(set(), client.feature_flags)
3027
client.enable_feature('actions')
3028
self.assertEqual(set(['actions']), client.feature_flags)
3030
def test_enable_feature_invalid(self):
3031
client = EnvJujuClient(JujuData('bar', {}), None, '/foo')
3032
self.assertEqual(set(), client.feature_flags)
3033
with self.assertRaises(ValueError) as ctx:
3034
client.enable_feature('nomongo')
3035
self.assertEqual(str(ctx.exception), "Unknown feature flag: 'nomongo'")
3037
def test_is_juju1x(self):
3038
client = EnvJujuClient(None, '1.25.5', None)
3039
self.assertTrue(client.is_juju1x())
3041
def test_is_juju1x_false(self):
3042
client = EnvJujuClient(None, '2.0.0', None)
3043
self.assertFalse(client.is_juju1x())
3045
def test__get_register_command_returns_register_token(self):
3046
output = dedent("""\
3048
User "x" granted read access to model "y"
3049
Please send this command to x:
3050
juju register AaBbCc""")
3051
output_cmd = 'AaBbCc'
3052
fake_client = fake_juju_client()
3054
register_cmd = fake_client._get_register_command(output)
3055
self.assertEqual(register_cmd, output_cmd)
3057
def test_revoke(self):
3058
fake_client = fake_juju_client()
3059
username = 'fakeuser'
3061
default_permissions = 'read'
3062
default_model = fake_client.model_name
3063
default_controller = fake_client.env.controller.name
3065
with patch.object(fake_client, 'juju', return_value=True):
3066
fake_client.revoke(username)
3067
fake_client.juju.assert_called_with('revoke',
3068
('-c', default_controller,
3069
username, default_permissions,
3073
fake_client.revoke(username, model)
3074
fake_client.juju.assert_called_with('revoke',
3075
('-c', default_controller,
3076
username, default_permissions,
3080
fake_client.revoke(username, model, permissions='write')
3081
fake_client.juju.assert_called_with('revoke',
3082
('-c', default_controller,
3083
username, 'write', model),
3086
def test_add_user_perms(self):
3087
fake_client = fake_juju_client()
3088
username = 'fakeuser'
3090
# Ensure add_user returns expected value.
3092
fake_client.add_user_perms(username),
3093
get_user_register_token(username))
3096
def assert_add_user_perms(model, permissions):
3097
fake_client = fake_juju_client()
3098
username = 'fakeuser'
3099
output = get_user_register_command_info(username)
3100
if permissions is None:
3101
permissions = 'login'
3102
expected_args = [username, '-c', fake_client.env.controller.name]
3103
with patch.object(fake_client, 'get_juju_output',
3104
return_value=output) as get_output:
3105
with patch.object(fake_client, 'juju') as mock_juju:
3106
fake_client.add_user_perms(username, model, permissions)
3108
model = fake_client.env.environment
3109
get_output.assert_called_with(
3110
'add-user', *expected_args, include_e=False)
3111
if permissions == 'login':
3112
mock_juju.assert_called_once_with(
3114
('fakeuser', permissions,
3115
'-c', fake_client.env.controller.name),
3118
mock_juju.assert_called_once_with(
3120
('fakeuser', permissions,
3122
'-c', fake_client.env.controller.name),
3125
def test_assert_add_user_permissions(self):
3127
permissions = 'write'
3129
# Check using default model and permissions
3130
self.assert_add_user_perms(None, None)
3132
# Check explicit model & default permissions
3133
self.assert_add_user_perms(model, None)
3135
# Check explicit model & permissions
3136
self.assert_add_user_perms(model, permissions)
3138
# Check default model & explicit permissions
3139
self.assert_add_user_perms(None, permissions)
3141
def test_disable_user(self):
3142
env = JujuData('foo')
3143
username = 'fakeuser'
3144
client = EnvJujuClient(env, None, None)
3145
with patch.object(client, 'juju') as mock:
3146
client.disable_user(username)
3147
mock.assert_called_with(
3148
'disable-user', ('-c', 'foo', 'fakeuser'), include_e=False)
3150
def test_enable_user(self):
3151
env = JujuData('foo')
3152
username = 'fakeuser'
3153
client = EnvJujuClient(env, None, None)
3154
with patch.object(client, 'juju') as mock:
3155
client.enable_user(username)
3156
mock.assert_called_with(
3157
'enable-user', ('-c', 'foo', 'fakeuser'), include_e=False)
3159
def test_logout(self):
3160
env = JujuData('foo')
3161
client = EnvJujuClient(env, None, None)
3162
with patch.object(client, 'juju') as mock:
3164
mock.assert_called_with(
3165
'logout', ('-c', 'foo'), include_e=False)
3167
def test_create_cloned_environment(self):
3168
fake_client = fake_juju_client()
3169
fake_client.bootstrap()
3170
# fake_client_environ = fake_client._shell_environ()
3171
controller_name = 'user_controller'
3172
cloned = fake_client.create_cloned_environment(
3176
self.assertIs(fake_client.__class__, type(cloned))
3177
self.assertEqual(cloned.env.juju_home, 'fakehome')
3178
self.assertEqual(cloned.env.controller.name, controller_name)
3179
self.assertEqual(fake_client.env.controller.name, 'name')
3181
def test_list_clouds(self):
3182
env = JujuData('foo')
3183
client = EnvJujuClient(env, None, None)
3184
with patch.object(client, 'get_juju_output') as mock:
3185
client.list_clouds()
3186
mock.assert_called_with(
3187
'list-clouds', '--format', 'json', include_e=False)
3189
def test_show_controller(self):
3190
env = JujuData('foo')
3191
client = EnvJujuClient(env, None, None)
3192
with patch.object(client, 'get_juju_output') as mock:
3193
client.show_controller()
3194
mock.assert_called_with(
3195
'show-controller', '--format', 'json', include_e=False)
3197
def test_ssh_keys(self):
3198
client = EnvJujuClient(JujuData('foo'), None, None)
3199
given_output = 'ssh keys output'
3200
with patch.object(client, 'get_juju_output', autospec=True,
3201
return_value=given_output) as mock:
3202
output = client.ssh_keys()
3203
self.assertEqual(output, given_output)
3204
mock.assert_called_once_with('ssh-keys')
3206
def test_ssh_keys_full(self):
3207
client = EnvJujuClient(JujuData('foo'), None, None)
3208
given_output = 'ssh keys full output'
3209
with patch.object(client, 'get_juju_output', autospec=True,
3210
return_value=given_output) as mock:
3211
output = client.ssh_keys(full=True)
3212
self.assertEqual(output, given_output)
3213
mock.assert_called_once_with('ssh-keys', '--full')
3215
def test_add_ssh_key(self):
3216
client = EnvJujuClient(JujuData('foo'), None, None)
3217
with patch.object(client, 'get_juju_output', autospec=True,
3218
return_value='') as mock:
3219
output = client.add_ssh_key('ak', 'bk')
3220
self.assertEqual(output, '')
3221
mock.assert_called_once_with(
3222
'add-ssh-key', 'ak', 'bk', merge_stderr=True)
3224
def test_remove_ssh_key(self):
3225
client = EnvJujuClient(JujuData('foo'), None, None)
3226
with patch.object(client, 'get_juju_output', autospec=True,
3227
return_value='') as mock:
3228
output = client.remove_ssh_key('ak', 'bk')
3229
self.assertEqual(output, '')
3230
mock.assert_called_once_with(
3231
'remove-ssh-key', 'ak', 'bk', merge_stderr=True)
3233
def test_import_ssh_key(self):
3234
client = EnvJujuClient(JujuData('foo'), None, None)
3235
with patch.object(client, 'get_juju_output', autospec=True,
3236
return_value='') as mock:
3237
output = client.import_ssh_key('gh:au', 'lp:bu')
3238
self.assertEqual(output, '')
3239
mock.assert_called_once_with(
3240
'import-ssh-key', 'gh:au', 'lp:bu', merge_stderr=True)
3242
def test_list_disabled_commands(self):
3243
client = EnvJujuClient(JujuData('foo'), None, None)
3244
with patch.object(client, 'get_juju_output', autospec=True,
3245
return_value=dedent("""\
3246
- command-set: destroy-model
3247
message: Lock Models
3248
- command-set: remove-object""")) as mock:
3249
output = client.list_disabled_commands()
3250
self.assertEqual([{'command-set': 'destroy-model',
3251
'message': 'Lock Models'},
3252
{'command-set': 'remove-object'}], output)
3253
mock.assert_called_once_with('list-disabled-commands',
3256
def test_disable_command(self):
3257
client = EnvJujuClient(JujuData('foo'), None, None)
3258
with patch.object(client, 'juju', autospec=True) as mock:
3259
client.disable_command(('all', 'message'))
3260
mock.assert_called_once_with('disable-command', ('all', 'message'))
3262
def test_enable_command(self):
3263
client = EnvJujuClient(JujuData('foo'), None, None)
3264
with patch.object(client, 'juju', autospec=True) as mock:
3265
client.enable_command('all')
3266
mock.assert_called_once_with('enable-command', 'all')
3268
def test_sync_tools(self):
3269
client = EnvJujuClient(JujuData('foo'), None, None)
3270
with patch.object(client, 'juju', autospec=True) as mock:
3272
mock.assert_called_once_with('sync-tools', ())
3274
def test_sync_tools_local_dir(self):
3275
client = EnvJujuClient(JujuData('foo'), None, None)
3276
with patch.object(client, 'juju', autospec=True) as mock:
3277
client.sync_tools('/agents')
3278
mock.assert_called_once_with('sync-tools', ('--local-dir', '/agents'),
3282
class TestEnvJujuClient2B8(ClientTest):
3284
def test_remove_service(self):
3285
env = EnvJujuClient2B7(
3286
JujuData('foo', {'type': 'local'}), '1.234-76', None)
3287
with patch.object(env, 'juju') as mock_juju:
3288
env.remove_service('mondogb')
3289
mock_juju.assert_called_with('remove-service', ('mondogb',))
3291
def test_deployer(self):
3292
client = EnvJujuClient2B8(JujuData('foo', {'type': 'local'}),
3293
'1.23-series-arch', None)
3294
with patch.object(EnvJujuClient, 'juju') as mock:
3295
client.deployer('bundle:~juju-qa/some-bundle')
3296
mock.assert_called_with(
3297
'deployer', ('-e', 'local.foo:foo', '--debug', '--deploy-delay',
3298
'10', '--timeout', '3600', '--config',
3299
'bundle:~juju-qa/some-bundle'),
3300
True, include_e=False
3303
def test_deployer_with_bundle_name(self):
3304
client = EnvJujuClient2B8(JujuData('foo', {'type': 'local'}),
3305
'2.0.0-series-arch', None)
3306
with patch.object(EnvJujuClient, 'juju') as mock:
3307
client.deployer('bundle:~juju-qa/some-bundle', 'name')
3308
mock.assert_called_with(
3309
'deployer', ('-e', 'local.foo:foo', '--debug', '--deploy-delay',
3310
'10', '--timeout', '3600', '--config',
3311
'bundle:~juju-qa/some-bundle', 'name'),
3312
True, include_e=False
3316
class TestEnvJujuClient2B9(ClientTest):
3318
def test_get_model_uuid_returns_uuid(self):
3319
model_uuid = '9ed1bde9-45c6-4d41-851d-33fdba7fa194'
3320
yaml_string = dedent("""\
3324
controller-uuid: eb67e1eb-6c54-45f5-8b6a-b6243be97202
3337
last-connection: just now
3338
""".format(uuid=model_uuid))
3339
client = EnvJujuClient2B9(JujuData('foo'), None, None)
3340
with patch.object(client, 'get_juju_output') as m_get_juju_output:
3341
m_get_juju_output.return_value = yaml_string
3343
client.get_model_uuid(),
3346
m_get_juju_output.assert_called_once_with(
3347
'show-model', '--format', 'yaml')
3349
def test_add_user_perms(self):
3350
fake_client = fake_juju_client(cls=EnvJujuClient2B9)
3351
username = 'fakeuser'
3353
permissions = 'write'
3354
output = get_user_register_command_info(username)
3356
def _get_expected_args(model=None, permissions=None):
3359
'--models', model or fake_client.env.environment,
3360
'--acl', permissions or 'read',
3361
'-c', fake_client.env.controller.name]
3363
# Ensure add_user_perms returns expected value.
3365
fake_client.add_user_perms(username),
3366
get_user_register_token(username))
3368
with patch.object(fake_client, 'get_juju_output',
3369
return_value=output) as get_output:
3370
# Check using default model and permissions
3371
fake_client.add_user_perms(username)
3372
expected_args = _get_expected_args()
3373
get_output.assert_called_with(
3374
'add-user', *expected_args, include_e=False)
3376
# Check explicit model & default permissions
3377
fake_client.add_user_perms(username, model)
3378
expected_args = _get_expected_args(model)
3379
get_output.assert_called_with(
3380
'add-user', *expected_args, include_e=False)
3382
# Check explicit model & permissions
3383
fake_client.add_user_perms(username, model, permissions)
3384
expected_args = _get_expected_args(model, permissions)
3385
get_output.assert_called_with(
3386
'add-user', *expected_args, include_e=False)
3388
# Check default model & explicit permissions
3389
fake_client.add_user_perms(username, permissions=permissions)
3390
expected_args = _get_expected_args(permissions=permissions)
3391
get_output.assert_called_with(
3392
'add-user', *expected_args, include_e=False)
3394
def test_set_config(self):
3395
client = EnvJujuClient2B9(JujuData('bar', {}), None, '/foo')
3396
with patch.object(client, 'juju') as juju_mock:
3397
client.set_config('foo', {'bar': 'baz'})
3398
juju_mock.assert_called_once_with('set-config', ('foo', 'bar=baz'))
3400
def test_get_config(self):
3401
def output(*args, **kwargs):
3402
return yaml.safe_dump({
3408
'description': 'bla bla',
3410
'value': '/tmp/charm-dir',
3414
expected = yaml.safe_load(output())
3415
client = EnvJujuClient2B9(JujuData('bar', {}), None, '/foo')
3416
with patch.object(client, 'get_juju_output',
3417
side_effect=output) as gjo_mock:
3418
results = client.get_config('foo')
3419
self.assertEqual(expected, results)
3420
gjo_mock.assert_called_once_with('get-config', 'foo')
3422
def test_get_model_config(self):
3423
env = JujuData('foo', None)
3424
fake_popen = FakePopen(yaml.safe_dump({'bar': 'baz'}), None, 0)
3425
client = EnvJujuClient2B9(env, None, 'juju')
3426
with patch('subprocess.Popen', return_value=fake_popen) as po_mock:
3427
result = client.get_model_config()
3429
self, po_mock, client, (
3430
'juju', '--show-log',
3433
'--format', 'yaml'))
3434
self.assertEqual({'bar': 'baz'}, result)
3436
def test_get_env_option(self):
3437
env = JujuData('foo', None)
3438
fake_popen = FakePopen('https://example.org/juju/tools', None, 0)
3439
client = EnvJujuClient2B9(env, None, 'juju')
3440
with patch('subprocess.Popen', return_value=fake_popen) as mock:
3441
result = client.get_env_option('tools-metadata-url')
3443
mock.call_args[0][0],
3444
('juju', '--show-log', 'get-model-config', '-m', 'foo:foo',
3445
'tools-metadata-url'))
3446
self.assertEqual('https://example.org/juju/tools', result)
3448
def test_set_env_option(self):
3449
env = JujuData('foo')
3450
client = EnvJujuClient2B9(env, None, 'juju')
3451
with patch('subprocess.check_call') as mock:
3452
client.set_env_option(
3453
'tools-metadata-url', 'https://example.org/juju/tools')
3454
environ = dict(os.environ)
3455
environ['JUJU_HOME'] = client.env.juju_home
3456
mock.assert_called_with(
3457
('juju', '--show-log', 'set-model-config', '-m', 'foo:foo',
3458
'tools-metadata-url=https://example.org/juju/tools'))
3460
def test_unset_env_option(self):
3461
env = JujuData('foo')
3462
client = EnvJujuClient2B9(env, None, 'juju')
3463
with patch('subprocess.check_call') as mock:
3464
client.unset_env_option('tools-metadata-url')
3465
environ = dict(os.environ)
3466
environ['JUJU_HOME'] = client.env.juju_home
3467
mock.assert_called_with(
3468
('juju', '--show-log', 'unset-model-config', '-m', 'foo:foo',
3469
'tools-metadata-url'))
3471
def test_list_disabled_commands(self):
3472
client = EnvJujuClient2B9(JujuData('foo'), None, None)
3473
with patch.object(client, 'get_juju_output', autospec=True,
3474
return_value=dedent("""\
3475
- command-set: destroy-model
3476
message: Lock Models
3477
- command-set: remove-object""")) as mock:
3478
output = client.list_disabled_commands()
3479
self.assertEqual([{'command-set': 'destroy-model',
3480
'message': 'Lock Models'},
3481
{'command-set': 'remove-object'}], output)
3482
mock.assert_called_once_with('block list',
3485
def test_disable_command(self):
3486
client = EnvJujuClient2B9(JujuData('foo'), None, None)
3487
with patch.object(client, 'juju', autospec=True) as mock:
3488
client.disable_command(('all', 'message'))
3489
mock.assert_called_once_with('block', ('all', 'message'))
3491
def test_enable_command(self):
3492
client = EnvJujuClient2B9(JujuData('foo'), None, None)
3493
with patch.object(client, 'juju', autospec=True) as mock:
3494
client.enable_command('all')
3495
mock.assert_called_once_with('unblock', 'all')
3498
class TestEnvJujuClient2B7(ClientTest):
3500
def test_get_controller_model_name(self):
3503
{'name': 'admin', 'model-uuid': 'aaaa'},
3504
{'name': 'bar', 'model-uuid': 'bbbb'}],
3505
'current-model': 'bar'
3507
client = EnvJujuClient2B7(JujuData('foo'), None, None)
3508
with patch.object(client, 'get_models',
3509
return_value=models) as gm_mock:
3510
controller_name = client.get_controller_model_name()
3511
self.assertEqual(0, gm_mock.call_count)
3512
self.assertEqual('admin', controller_name)
3514
def test_get_controller_model_name_without_controller(self):
3517
{'name': 'bar', 'model-uuid': 'aaaa'},
3518
{'name': 'baz', 'model-uuid': 'bbbb'}],
3519
'current-model': 'bar'
3521
client = EnvJujuClient2B7(JujuData('foo'), None, None)
3522
with patch.object(client, 'get_models', return_value=models):
3523
controller_name = client.get_controller_model_name()
3524
self.assertEqual('admin', controller_name)
3526
def test_get_controller_model_name_no_models(self):
3527
client = EnvJujuClient2B7(JujuData('foo'), None, None)
3528
with patch.object(client, 'get_models', return_value={}):
3529
controller_name = client.get_controller_model_name()
3530
self.assertEqual('admin', controller_name)
3532
def test_get_controller_client(self):
3533
client = EnvJujuClient2B7(
3534
JujuData('foo', {'bar': 'baz'}, 'myhome'), None, None)
3535
controller_client = client.get_controller_client()
3536
controller_env = controller_client.env
3537
self.assertEqual('admin', controller_env.environment)
3538
self.assertEqual({'bar': 'baz', 'name': 'admin'},
3539
controller_env.config)
3542
class TestEnvJujuClient2B3(ClientTest):
3544
def test_add_model_hypenated_controller(self):
3546
'kill-controller', 'create-model', ('-c', 'foo'))
3548
def do_add_model(self, jes_command, create_cmd, controller_option):
3549
controller_client = EnvJujuClient2B3(JujuData('foo'), None, None)
3550
model_data = JujuData('bar', {'type': 'foo'})
3551
client = EnvJujuClient2B3(model_data, None, None)
3552
with patch.object(client, 'get_jes_command',
3553
return_value=jes_command):
3554
with patch.object(controller_client, 'juju') as ccj_mock:
3555
with observable_temp_file() as config_file:
3556
controller_client.add_model(model_data)
3557
ccj_mock.assert_called_once_with(
3558
create_cmd, controller_option + (
3559
'bar', '--config', config_file.name), include_e=False)
3562
class TestEnvJujuClient2B2(ClientTest):
3564
def test_get_bootstrap_args_bootstrap_series(self):
3565
env = JujuData('foo', {'type': 'bar', 'region': 'baz'})
3566
client = EnvJujuClient2B2(env, '2.0-zeta1', None)
3567
args = client.get_bootstrap_args(upload_tools=True,
3568
config_filename='config',
3569
bootstrap_series='angsty')
3570
self.assertEqual(args, (
3571
'--upload-tools', '--constraints', 'mem=2G', 'foo', 'bar/baz',
3572
'--config', 'config', '--bootstrap-series', 'angsty'))
3574
def test_get_bootstrap_args_reject_new_args(self):
3575
env = JujuData('foo', {'type': 'bar', 'region': 'baz'})
3576
client = EnvJujuClient2B2(env, '2.0-zeta1', None)
3577
base_args = {'upload_tools': True,
3578
'config_filename': 'config',
3579
'bootstrap_series': 'angsty'}
3580
with self.assertRaises(ValueError):
3581
client.get_bootstrap_args(auto_upgrade=True, **base_args)
3582
with self.assertRaises(ValueError):
3583
client.get_bootstrap_args(metadata_source='/foo', **base_args)
3584
with self.assertRaises(ValueError):
3585
client.get_bootstrap_args(to='cur', **base_args)
3586
with self.assertRaises(ValueError):
3587
client.get_bootstrap_args(agent_version='1.0.0', **base_args)
3589
def test_bootstrap_upload_tools(self):
3590
env = JujuData('foo', {'type': 'foo', 'region': 'baz'})
3591
client = EnvJujuClient2B2(env, '2.0-zeta1', None)
3592
with patch.object(client.env, 'needs_sudo', lambda: True):
3593
with observable_temp_file() as config_file:
3594
with patch.object(client, 'juju') as mock:
3595
client.bootstrap(upload_tools=True)
3596
mock.assert_called_with(
3598
'--upload-tools', '--constraints', 'mem=2G', 'foo',
3599
'foo/baz', '--config', config_file.name), include_e=False)
3601
def test_bootstrap_maas(self):
3602
env = JujuData('maas', {'type': 'foo', 'region': 'asdf'})
3603
with patch.object(EnvJujuClient, 'juju') as mock:
3604
client = EnvJujuClient2B2(env, '2.0-zeta1', None)
3605
with patch.object(client.env, 'maas', lambda: True):
3606
with observable_temp_file() as config_file:
3608
mock.assert_called_with(
3610
'--constraints', 'mem=2G', 'maas', 'foo/asdf',
3611
'--config', config_file.name, '--agent-version', '2.0'),
3614
def test_bootstrap_joyent(self):
3615
env = JujuData('joyent', {
3616
'type': 'joyent', 'sdc-url': 'https://foo.api.joyentcloud.com'})
3617
with patch.object(EnvJujuClient, 'juju', autospec=True) as mock:
3618
client = EnvJujuClient2B2(env, '2.0-zeta1', None)
3619
with patch.object(client.env, 'joyent', lambda: True):
3620
with observable_temp_file() as config_file:
3622
mock.assert_called_once_with(
3623
client, 'bootstrap', (
3624
'--constraints', 'mem=2G cpu-cores=1', 'joyent',
3625
'joyent/foo', '--config', config_file.name,
3626
'--agent-version', '2.0'), include_e=False)
3628
def test_bootstrap_async_upload_tools(self):
3629
env = JujuData('foo', {'type': 'bar', 'region': 'baz'})
3630
with patch.object(EnvJujuClient, 'juju_async', autospec=True) as mock:
3631
client = EnvJujuClient2B2(env, '2.0-zeta1', None)
3632
with observable_temp_file() as config_file:
3633
with client.bootstrap_async(upload_tools=True):
3634
mock.assert_called_with(
3635
client, 'bootstrap', (
3636
'--upload-tools', '--constraints', 'mem=2G',
3637
'foo', 'bar/baz', '--config', config_file.name),
3640
def test_bootstrap_async(self):
3641
env = JujuData('foo', {'type': 'bar', 'region': 'baz'})
3642
with patch.object(EnvJujuClient, 'juju_async', autospec=True) as mock:
3643
client = EnvJujuClient2B2(env, '2.0-zeta1', None)
3644
client.env.juju_home = 'foo'
3645
with observable_temp_file() as config_file:
3646
with client.bootstrap_async():
3647
mock.assert_called_once_with(
3648
client, 'bootstrap', (
3649
'--constraints', 'mem=2G', 'foo', 'bar/baz',
3650
'--config', config_file.name,
3651
'--agent-version', '2.0'), include_e=False)
3653
def test_bootstrap_args(self):
3654
env = JujuData('foo', {'type': 'bar', 'region': 'baz'})
3655
client = EnvJujuClient2B2(env, '2.0-zeta1', None)
3656
with patch.object(client, 'juju') as mock:
3657
with observable_temp_file() as config_file:
3658
client.bootstrap(bootstrap_series='angsty')
3659
mock.assert_called_with(
3661
'--constraints', 'mem=2G', 'foo', 'bar/baz',
3662
'--config', config_file.name,
3663
'--agent-version', '2.0',
3664
'--bootstrap-series', 'angsty'), include_e=False)
3666
def test_bootstrap(self):
3667
env = JujuData('foo', {'type': 'bar', 'region': 'baz'})
3668
with observable_temp_file() as config_file:
3669
with patch.object(EnvJujuClient, 'juju') as mock:
3670
client = EnvJujuClient2B2(env, '2.0-zeta1', None)
3672
mock.assert_called_with(
3673
'bootstrap', ('--constraints', 'mem=2G',
3675
'--config', config_file.name,
3676
'--agent-version', '2.0'), include_e=False)
3678
config = yaml.safe_load(config_file)
3679
self.assertEqual({'test-mode': True}, config)
3681
def test_get_controller_model_name(self):
3684
{'name': 'admin', 'model-uuid': 'aaaa'},
3685
{'name': 'bar', 'model-uuid': 'bbbb'}],
3686
'current-model': 'bar'
3688
client = EnvJujuClient2B2(JujuData('foo'), None, None)
3689
with patch.object(client, 'get_models',
3690
return_value=models) as gm_mock:
3691
controller_name = client.get_controller_model_name()
3692
gm_mock.assert_called_once_with()
3693
self.assertEqual('admin', controller_name)
3695
def test_get_controller_model_name_without_controller(self):
3698
{'name': 'bar', 'model-uuid': 'aaaa'},
3699
{'name': 'baz', 'model-uuid': 'bbbb'}],
3700
'current-model': 'bar'
3702
client = EnvJujuClient2B2(JujuData('foo'), None, None)
3703
with patch.object(client, 'get_models', return_value=models):
3704
controller_name = client.get_controller_model_name()
3705
self.assertEqual('foo', controller_name)
3707
def test_get_controller_model_name_no_models(self):
3708
client = EnvJujuClient2B2(JujuData('foo'), None, None)
3709
with patch.object(client, 'get_models', return_value={}):
3710
controller_name = client.get_controller_model_name()
3711
self.assertEqual('foo', controller_name)
3714
class TestEnvJujuClient2A2(TestCase):
3716
def test_raise_on_juju_data(self):
3717
env = JujuData('foo', {'type': 'bar'}, 'baz')
3718
with self.assertRaisesRegexp(
3719
IncompatibleConfigClass,
3720
'JujuData cannot be used with EnvJujuClient2A2'):
3721
EnvJujuClient2A2(env, '1.25', 'full_path')
3723
def test__shell_environ_juju_home(self):
3724
client = EnvJujuClient2A2(
3725
SimpleEnvironment('baz', {'type': 'ec2'}), '1.25-foobar', 'path',
3727
with patch.dict(os.environ, {'PATH': ''}):
3728
env = client._shell_environ()
3729
# For transition, supply both.
3730
self.assertEqual(env['JUJU_HOME'], 'asdf')
3731
self.assertEqual(env['JUJU_DATA'], 'asdf')
3733
def test_get_bootstrap_args_bootstrap_series(self):
3734
env = SimpleEnvironment('foo', {})
3735
client = EnvJujuClient2A2(env, '2.0-zeta1', 'path', 'home')
3736
args = client.get_bootstrap_args(upload_tools=True,
3737
bootstrap_series='angsty')
3738
self.assertEqual(args, (
3739
'--upload-tools', '--constraints', 'mem=2G',
3740
'--agent-version', '2.0', '--bootstrap-series', 'angsty'))
3743
class TestEnvJujuClient1X(ClientTest):
3745
def test_no_duplicate_env(self):
3746
env = SimpleEnvironment('foo', {})
3747
client = EnvJujuClient1X(env, '1.25', 'full_path')
3748
self.assertIs(env, client.env)
3750
def test_get_version(self):
3752
with patch('subprocess.check_output', return_value=value) as vsn:
3753
version = EnvJujuClient1X.get_version()
3754
self.assertEqual('5.6', version)
3755
vsn.assert_called_with(('juju', '--version'))
3757
def test_get_version_path(self):
3758
with patch('subprocess.check_output', return_value=' 4.3') as vsn:
3759
EnvJujuClient1X.get_version('foo/bar/baz')
3760
vsn.assert_called_once_with(('foo/bar/baz', '--version'))
3762
def test_get_matching_agent_version(self):
3763
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
3764
'1.23-series-arch', None)
3765
self.assertEqual('1.23.1', client.get_matching_agent_version())
3766
self.assertEqual('1.23', client.get_matching_agent_version(
3768
client = client.clone(version='1.20-beta1-series-arch')
3769
self.assertEqual('1.20-beta1.1', client.get_matching_agent_version())
3771
def test_upgrade_juju_nonlocal(self):
3772
client = EnvJujuClient1X(
3773
SimpleEnvironment('foo', {'type': 'nonlocal'}), '1.234-76', None)
3774
with patch.object(client, 'juju') as juju_mock:
3775
client.upgrade_juju()
3776
juju_mock.assert_called_with(
3777
'upgrade-juju', ('--version', '1.234'))
3779
def test_upgrade_juju_local(self):
3780
client = EnvJujuClient1X(
3781
SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None)
3782
with patch.object(client, 'juju') as juju_mock:
3783
client.upgrade_juju()
3784
juju_mock.assert_called_with(
3785
'upgrade-juju', ('--version', '1.234', '--upload-tools',))
3787
def test_upgrade_juju_no_force_version(self):
3788
client = EnvJujuClient1X(
3789
SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None)
3790
with patch.object(client, 'juju') as juju_mock:
3791
client.upgrade_juju(force_version=False)
3792
juju_mock.assert_called_with(
3793
'upgrade-juju', ('--upload-tools',))
3795
def test_upgrade_mongo_exception(self):
3796
client = EnvJujuClient1X(
3797
SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None)
3798
with self.assertRaises(UpgradeMongoNotSupported):
3799
client.upgrade_mongo()
3801
def test_get_cache_path(self):
3802
client = EnvJujuClient1X(SimpleEnvironment('foo', juju_home='/foo/'),
3803
'1.27', 'full/path', debug=True)
3804
self.assertEqual('/foo/environments/cache.yaml',
3805
client.get_cache_path())
3807
def test_full_args(self):
3808
env = SimpleEnvironment('foo')
3809
client = EnvJujuClient1X(env, None, 'my/juju/bin')
3810
full = client._full_args('bar', False, ('baz', 'qux'))
3811
self.assertEqual(('bin', '--show-log', 'bar', '-e', 'foo', 'baz',
3813
full = client._full_args('bar', True, ('baz', 'qux'))
3815
'bin', '--show-log', 'bar', '-e', 'foo',
153
def test_full_args(self):
154
env = Environment('foo', '')
155
client = EnvJujuClient(env, None, 'my/juju/bin')
156
full = client._full_args('bar', False, ('baz', 'qux'))
157
self.assertEqual(('juju', '--show-log', 'bar', '-e', 'foo', 'baz',
159
full = client._full_args('bar', True, ('baz', 'qux'))
161
'juju', '--show-log', 'bar', '-e', 'foo',
3816
162
'baz', 'qux'), full)
3817
163
client.env = None
3818
164
full = client._full_args('bar', False, ('baz', 'qux'))
3819
self.assertEqual(('bin', '--show-log', 'bar', 'baz', 'qux'), full)
165
self.assertEqual(('juju', '--show-log', 'bar', 'baz', 'qux'), full)
3821
167
def test_full_args_debug(self):
3822
env = SimpleEnvironment('foo')
3823
client = EnvJujuClient1X(env, None, 'my/juju/bin', debug=True)
168
env = Environment('foo', '')
169
client = EnvJujuClient(env, None, 'my/juju/bin')
3824
171
full = client._full_args('bar', False, ('baz', 'qux'))
3825
172
self.assertEqual((
3826
'bin', '--debug', 'bar', '-e', 'foo', 'baz', 'qux'), full)
3828
def test_full_args_controller(self):
3829
env = SimpleEnvironment('foo')
3830
client = EnvJujuClient1X(env, None, 'my/juju/bin')
3831
full = client._full_args('bar', False, ('baz', 'qux'), controller=True)
3833
'bin', '--show-log', 'bar', '-e', 'foo', 'baz', 'qux'), full)
3835
def test_full_args_action(self):
3836
env = SimpleEnvironment('foo')
3837
client = EnvJujuClient1X(env, None, 'my/juju/bin')
3838
full = client._full_args('action bar', False, ('baz', 'qux'))
3840
'bin', '--show-log', 'action', 'bar', '-e', 'foo', 'baz', 'qux'),
173
'juju', '--debug', 'bar', '-e', 'foo', 'baz', 'qux'), full)
175
def test_bootstrap_hpcloud(self):
176
env = Environment('hp', '')
177
with patch.object(env, 'hpcloud', lambda: True):
178
with patch.object(EnvJujuClient, 'juju') as mock:
179
EnvJujuClient(env, None, None).bootstrap()
180
mock.assert_called_with(
181
'bootstrap', ('--constraints', 'mem=2G'), False)
3843
183
def test_bootstrap_maas(self):
3844
env = SimpleEnvironment('maas')
3845
with patch.object(EnvJujuClient1X, 'juju') as mock:
3846
client = EnvJujuClient1X(env, None, None)
184
env = Environment('maas', '')
185
with patch.object(EnvJujuClient, 'juju') as mock:
186
client = EnvJujuClient(env, None, None)
3847
187
with patch.object(client.env, 'maas', lambda: True):
3848
188
client.bootstrap()
3849
189
mock.assert_called_with(
3850
'bootstrap', ('--constraints', 'mem=2G'), False)
3852
def test_bootstrap_joyent(self):
3853
env = SimpleEnvironment('joyent')
3854
with patch.object(EnvJujuClient1X, 'juju', autospec=True) as mock:
3855
client = EnvJujuClient1X(env, None, None)
3856
with patch.object(client.env, 'joyent', lambda: True):
3858
mock.assert_called_once_with(
3859
client, 'bootstrap', ('--constraints', 'mem=2G cpu-cores=1'),
190
'bootstrap', ('--constraints', 'mem=2G arch=amd64'), False)
3862
192
def test_bootstrap_non_sudo(self):
3863
env = SimpleEnvironment('foo')
3864
with patch.object(EnvJujuClient1X, 'juju') as mock:
3865
client = EnvJujuClient1X(env, None, None)
193
env = Environment('foo', '')
194
with patch.object(EnvJujuClient, 'juju') as mock:
195
client = EnvJujuClient(env, None, None)
3866
196
with patch.object(client.env, 'needs_sudo', lambda: False):
3867
197
client.bootstrap()
3868
198
mock.assert_called_with(
3869
199
'bootstrap', ('--constraints', 'mem=2G'), False)
3871
201
def test_bootstrap_sudo(self):
3872
env = SimpleEnvironment('foo')
3873
client = EnvJujuClient1X(env, None, None)
202
env = Environment('foo', '')
203
client = EnvJujuClient(env, None, None)
3874
204
with patch.object(client.env, 'needs_sudo', lambda: True):
3875
205
with patch.object(client, 'juju') as mock:
3876
206
client.bootstrap()
4753
540
self.assertEqual('https://example.org/juju/tools', result)
4755
542
def test_set_env_option(self):
4756
env = SimpleEnvironment('foo')
4757
client = EnvJujuClient1X(env, None, 'juju')
543
env = Environment('foo', '')
544
client = EnvJujuClient(env, None, None)
4758
545
with patch('subprocess.check_call') as mock:
4759
546
client.set_env_option(
4760
547
'tools-metadata-url', 'https://example.org/juju/tools')
4761
environ = dict(os.environ)
4762
environ['JUJU_HOME'] = client.env.juju_home
4763
548
mock.assert_called_with(
4764
549
('juju', '--show-log', 'set-env', '-e', 'foo',
4765
'tools-metadata-url=https://example.org/juju/tools'))
4767
def test_set_testing_agent_metadata_url(self):
4768
env = SimpleEnvironment(None, {'type': 'foo'})
4769
client = EnvJujuClient1X(env, None, None)
4770
with patch.object(client, 'get_env_option') as mock_get:
4771
mock_get.return_value = 'https://example.org/juju/tools'
4772
with patch.object(client, 'set_env_option') as mock_set:
4773
client.set_testing_agent_metadata_url()
4774
mock_get.assert_called_with('tools-metadata-url')
4775
mock_set.assert_called_with(
4776
'tools-metadata-url',
4777
'https://example.org/juju/testing/tools')
4779
def test_set_testing_agent_metadata_url_noop(self):
4780
env = SimpleEnvironment(None, {'type': 'foo'})
4781
client = EnvJujuClient1X(env, None, None)
4782
with patch.object(client, 'get_env_option') as mock_get:
4783
mock_get.return_value = 'https://example.org/juju/testing/tools'
4784
with patch.object(client, 'set_env_option') as mock_set:
4785
client.set_testing_agent_metadata_url()
4786
mock_get.assert_called_with('tools-metadata-url')
4787
self.assertEqual(0, mock_set.call_count)
550
'tools-metadata-url=https://example.org/juju/tools'),
4789
553
def test_juju(self):
4790
env = SimpleEnvironment('qux')
4791
client = EnvJujuClient1X(env, None, 'juju')
4792
with patch('subprocess.check_call') as mock:
4793
client.juju('foo', ('bar', 'baz'))
4794
environ = dict(os.environ)
4795
environ['JUJU_HOME'] = client.env.juju_home
554
env = Environment('qux', '')
555
client = EnvJujuClient(env, None, None)
556
with patch('sys.stdout') as stdout_mock:
557
with patch('subprocess.check_call') as mock:
558
client.juju('foo', ('bar', 'baz'))
4796
559
mock.assert_called_with(('juju', '--show-log', 'foo', '-e', 'qux',
560
'bar', 'baz'), env=os.environ)
561
stdout_mock.flush.assert_called_with()
4799
563
def test_juju_env(self):
4800
env = SimpleEnvironment('qux')
4801
client = EnvJujuClient1X(env, None, '/foobar/baz')
4803
def check_path(*args, **kwargs):
4804
self.assertRegexpMatches(os.environ['PATH'], r'/foobar\:')
4805
with patch('subprocess.check_call', side_effect=check_path):
564
env = Environment('qux', '')
565
client = EnvJujuClient(env, None, '/foobar/baz')
566
with patch('subprocess.check_call') as cc_mock:
4806
567
client.juju('foo', ('bar', 'baz'))
568
self.assertRegexpMatches(cc_mock.call_args[1]['env']['PATH'],
4808
571
def test_juju_no_check(self):
4809
env = SimpleEnvironment('qux')
4810
client = EnvJujuClient1X(env, None, 'juju')
4811
environ = dict(os.environ)
4812
environ['JUJU_HOME'] = client.env.juju_home
4813
with patch('subprocess.call') as mock:
4814
client.juju('foo', ('bar', 'baz'), check=False)
572
env = Environment('qux', '')
573
client = EnvJujuClient(env, None, None)
574
with patch('sys.stdout') as stdout_mock:
575
with patch('subprocess.call') as mock:
576
client.juju('foo', ('bar', 'baz'), check=False)
4815
577
mock.assert_called_with(('juju', '--show-log', 'foo', '-e', 'qux',
578
'bar', 'baz'), env=os.environ)
579
stdout_mock.flush.assert_called_with()
4818
581
def test_juju_no_check_env(self):
4819
env = SimpleEnvironment('qux')
4820
client = EnvJujuClient1X(env, None, '/foobar/baz')
4822
def check_path(*args, **kwargs):
4823
self.assertRegexpMatches(os.environ['PATH'], r'/foobar\:')
4824
with patch('subprocess.call', side_effect=check_path):
582
env = Environment('qux', '')
583
client = EnvJujuClient(env, None, '/foobar/baz')
584
with patch('subprocess.call') as call_mock:
4825
585
client.juju('foo', ('bar', 'baz'), check=False)
586
self.assertRegexpMatches(call_mock.call_args[1]['env']['PATH'],
4827
589
def test_juju_timeout(self):
4828
env = SimpleEnvironment('qux')
4829
client = EnvJujuClient1X(env, None, '/foobar/baz')
590
env = Environment('qux', '')
591
client = EnvJujuClient(env, None, '/foobar/baz')
4830
592
with patch('subprocess.check_call') as cc_mock:
4831
593
client.juju('foo', ('bar', 'baz'), timeout=58)
4832
594
self.assertEqual(cc_mock.call_args[0][0], (
4833
sys.executable, get_timeout_path(), '58.00', '--', 'baz',
4834
'--show-log', 'foo', '-e', 'qux', 'bar', 'baz'))
4836
def test_juju_juju_home(self):
4837
env = SimpleEnvironment('qux')
4838
os.environ['JUJU_HOME'] = 'foo'
4839
client = EnvJujuClient1X(env, None, '/foobar/baz')
4841
def check_home(*args, **kwargs):
4842
self.assertEqual(os.environ['JUJU_HOME'], 'foo')
4844
self.assertEqual(os.environ['JUJU_HOME'], 'asdf')
4847
with patch('subprocess.check_call', side_effect=check_home):
4848
client.juju('foo', ('bar', 'baz'))
4849
client.env.juju_home = 'asdf'
4850
client.juju('foo', ('bar', 'baz'))
4852
def test_juju_extra_env(self):
4853
env = SimpleEnvironment('qux')
4854
client = EnvJujuClient1X(env, None, 'juju')
4855
extra_env = {'JUJU': '/juju', 'JUJU_HOME': client.env.juju_home}
4857
def check_env(*args, **kwargs):
4858
self.assertEqual('/juju', os.environ['JUJU'])
4860
with patch('subprocess.check_call', side_effect=check_env) as mock:
4861
client.juju('quickstart', ('bar', 'baz'), extra_env=extra_env)
4862
mock.assert_called_with(
4863
('juju', '--show-log', 'quickstart', '-e', 'qux', 'bar', 'baz'))
595
'timeout', '58.00s', 'juju', '--show-log', 'foo', '-e', 'qux',
4865
598
def test_juju_backup_with_tgz(self):
4866
599
env = SimpleEnvironment('qux')
4867
client = EnvJujuClient1X(env, None, '/foobar/baz')
4869
def check_env(*args, **kwargs):
4870
self.assertEqual(os.environ['JUJU_ENV'], 'qux')
4871
return 'foojuju-backup-24.tgzz'
600
client = EnvJujuClient(env, None, '/foobar/baz')
4872
601
with patch('subprocess.check_output',
4873
side_effect=check_env) as co_mock:
4874
backup_file = client.backup()
602
return_value='foojuju-backup-24.tgzz') as co_mock:
603
with patch('sys.stdout'):
604
backup_file = client.backup()
4875
605
self.assertEqual(backup_file, os.path.abspath('juju-backup-24.tgz'))
4876
606
assert_juju_call(self, co_mock, client, ['juju', 'backup'])
607
self.assertEqual(co_mock.mock_calls[0][2]['env']['JUJU_ENV'], 'qux')
4878
609
def test_juju_backup_with_tar_gz(self):
4879
610
env = SimpleEnvironment('qux')
4880
client = EnvJujuClient1X(env, None, '/foobar/baz')
611
client = EnvJujuClient(env, None, '/foobar/baz')
4881
612
with patch('subprocess.check_output',
4882
613
return_value='foojuju-backup-123-456.tar.gzbar'):
4883
backup_file = client.backup()
614
with patch('sys.stdout'):
615
backup_file = client.backup()
4884
616
self.assertEqual(
4885
617
backup_file, os.path.abspath('juju-backup-123-456.tar.gz'))
4887
619
def test_juju_backup_no_file(self):
4888
620
env = SimpleEnvironment('qux')
4889
client = EnvJujuClient1X(env, None, '/foobar/baz')
621
client = EnvJujuClient(env, None, '/foobar/baz')
4890
622
with patch('subprocess.check_output', return_value=''):
4891
623
with self.assertRaisesRegexp(
4892
624
Exception, 'The backup file was not found in output'):
625
with patch('sys.stdout'):
4895
628
def test_juju_backup_wrong_file(self):
4896
629
env = SimpleEnvironment('qux')
4897
client = EnvJujuClient1X(env, None, '/foobar/baz')
630
client = EnvJujuClient(env, None, '/foobar/baz')
4898
631
with patch('subprocess.check_output',
4899
632
return_value='mumu-backup-24.tgz'):
4900
633
with self.assertRaisesRegexp(
4901
634
Exception, 'The backup file was not found in output'):
4904
def test_juju_backup_environ(self):
4905
env = SimpleEnvironment('qux')
4906
client = EnvJujuClient1X(env, None, '/foobar/baz')
4907
environ = client._shell_environ()
4908
environ['JUJU_ENV'] = client.env.environment
4910
def side_effect(*args, **kwargs):
4911
self.assertEqual(environ, os.environ)
4912
return 'foojuju-backup-123-456.tar.gzbar'
4913
with patch('subprocess.check_output', side_effect=side_effect):
4915
self.assertNotEqual(environ, os.environ)
4917
def test_restore_backup(self):
4918
env = SimpleEnvironment('qux')
4919
client = EnvJujuClient1X(env, None, '/foobar/baz')
4920
with patch.object(client, 'get_juju_output') as gjo_mock:
4921
result = client.restore_backup('quxx')
4922
gjo_mock.assert_called_once_with('restore', '--constraints',
4924
self.assertIs(gjo_mock.return_value, result)
4926
def test_restore_backup_async(self):
4927
env = SimpleEnvironment('qux')
4928
client = EnvJujuClient1X(env, None, '/foobar/baz')
4929
with patch.object(client, 'juju_async') as gjo_mock:
4930
result = client.restore_backup_async('quxx')
4931
gjo_mock.assert_called_once_with(
4932
'restore', ('--constraints', 'mem=2G', 'quxx'))
4933
self.assertIs(gjo_mock.return_value, result)
4935
def test_enable_ha(self):
4936
env = SimpleEnvironment('qux')
4937
client = EnvJujuClient1X(env, None, '/foobar/baz')
4938
with patch.object(client, 'juju', autospec=True) as eha_mock:
4940
eha_mock.assert_called_once_with('ensure-availability', ('-n', '3'))
4942
def test_juju_async(self):
4943
env = SimpleEnvironment('qux')
4944
client = EnvJujuClient1X(env, None, '/foobar/baz')
4945
with patch('subprocess.Popen') as popen_class_mock:
4946
with client.juju_async('foo', ('bar', 'baz')) as proc:
4947
assert_juju_call(self, popen_class_mock, client, (
4948
'baz', '--show-log', 'foo', '-e', 'qux', 'bar', 'baz'))
4949
self.assertIs(proc, popen_class_mock.return_value)
4950
self.assertEqual(proc.wait.call_count, 0)
4951
proc.wait.return_value = 0
4952
proc.wait.assert_called_once_with()
4954
def test_juju_async_failure(self):
4955
env = SimpleEnvironment('qux')
4956
client = EnvJujuClient1X(env, None, '/foobar/baz')
4957
with patch('subprocess.Popen') as popen_class_mock:
4958
with self.assertRaises(subprocess.CalledProcessError) as err_cxt:
4959
with client.juju_async('foo', ('bar', 'baz')):
4960
proc_mock = popen_class_mock.return_value
4961
proc_mock.wait.return_value = 23
4962
self.assertEqual(err_cxt.exception.returncode, 23)
4963
self.assertEqual(err_cxt.exception.cmd, (
4964
'baz', '--show-log', 'foo', '-e', 'qux', 'bar', 'baz'))
4966
def test_juju_async_environ(self):
4967
env = SimpleEnvironment('qux')
4968
client = EnvJujuClient1X(env, None, '/foobar/baz')
4969
environ = client._shell_environ()
4971
with patch('subprocess.Popen') as popen_class_mock:
4973
def check_environ(*args, **kwargs):
4974
self.assertEqual(environ, os.environ)
4976
popen_class_mock.side_effect = check_environ
4977
proc_mock.wait.return_value = 0
4978
with client.juju_async('foo', ('bar', 'baz')):
4980
self.assertNotEqual(environ, os.environ)
4982
def test_is_jes_enabled(self):
4983
env = SimpleEnvironment('qux')
4984
client = EnvJujuClient1X(env, None, '/foobar/baz')
4985
fake_popen = FakePopen(' %s' % SYSTEM, None, 0)
4986
with patch('subprocess.Popen',
4987
return_value=fake_popen) as po_mock:
4988
self.assertFalse(client.is_jes_enabled())
4989
assert_juju_call(self, po_mock, client, (
4990
'baz', '--show-log', 'help', 'commands'))
4991
# Juju 1.25 uses the system command.
4992
client = EnvJujuClient1X(env, None, '/foobar/baz')
4993
fake_popen = FakePopen(SYSTEM, None, 0)
4994
with patch('subprocess.Popen', autospec=True,
4995
return_value=fake_popen):
4996
self.assertTrue(client.is_jes_enabled())
4997
# Juju 1.26 uses the controller command.
4998
client = EnvJujuClient1X(env, None, '/foobar/baz')
4999
fake_popen = FakePopen(CONTROLLER, None, 0)
5000
with patch('subprocess.Popen', autospec=True,
5001
return_value=fake_popen):
5002
self.assertTrue(client.is_jes_enabled())
5004
def test_get_jes_command(self):
5005
env = SimpleEnvironment('qux')
5006
client = EnvJujuClient1X(env, None, '/foobar/baz')
5007
# Juju 1.24 and older do not have a JES command. It is an error
5008
# to call get_jes_command when is_jes_enabled is False
5009
fake_popen = FakePopen(' %s' % SYSTEM, None, 0)
5010
with patch('subprocess.Popen',
5011
return_value=fake_popen) as po_mock:
5012
with self.assertRaises(JESNotSupported):
5013
client.get_jes_command()
5014
assert_juju_call(self, po_mock, client, (
5015
'baz', '--show-log', 'help', 'commands'))
5016
# Juju 2.x uses the 'controller kill' command.
5017
client = EnvJujuClient1X(env, None, '/foobar/baz')
5018
fake_popen = FakePopen(CONTROLLER, None, 0)
5019
with patch('subprocess.Popen', autospec=True,
5020
return_value=fake_popen):
5021
self.assertEqual(CONTROLLER, client.get_jes_command())
5022
# Juju 1.26 uses the destroy-controller command.
5023
client = EnvJujuClient1X(env, None, '/foobar/baz')
5024
fake_popen = FakePopen(KILL_CONTROLLER, None, 0)
5025
with patch('subprocess.Popen', autospec=True,
5026
return_value=fake_popen):
5027
self.assertEqual(KILL_CONTROLLER, client.get_jes_command())
5028
# Juju 1.25 uses the 'system kill' command.
5029
client = EnvJujuClient1X(env, None, '/foobar/baz')
5030
fake_popen = FakePopen(SYSTEM, None, 0)
5031
with patch('subprocess.Popen', autospec=True,
5032
return_value=fake_popen):
5034
SYSTEM, client.get_jes_command())
5036
def test_get_juju_timings(self):
5037
env = SimpleEnvironment('foo')
5038
client = EnvJujuClient1X(env, None, 'my/juju/bin')
5039
client._backend.juju_timings = {("juju", "op1"): [1],
5040
("juju", "op2"): [2]}
5041
flattened_timings = client.get_juju_timings()
5042
expected = {"juju op1": [1], "juju op2": [2]}
5043
self.assertEqual(flattened_timings, expected)
5045
def test_deploy_bundle_1x(self):
5046
client = EnvJujuClient1X(SimpleEnvironment('an_env', None),
5047
'1.23-series-arch', None)
5048
with patch.object(client, 'juju') as mock_juju:
5049
client.deploy_bundle('bundle:~juju-qa/some-bundle')
5050
mock_juju.assert_called_with(
5051
'deployer', ('--debug', '--deploy-delay', '10', '--timeout',
5052
'3600', '--config', 'bundle:~juju-qa/some-bundle'),
5056
def test_deploy_bundle_template(self):
5057
client = EnvJujuClient1X(SimpleEnvironment('an_env', None),
5058
'1.23-series-arch', None)
5059
with patch.object(client, 'juju') as mock_juju:
5060
client.deploy_bundle('bundle:~juju-qa/some-{container}-bundle')
5061
mock_juju.assert_called_with(
5063
'--debug', '--deploy-delay', '10', '--timeout', '3600',
5064
'--config', 'bundle:~juju-qa/some-lxc-bundle',
5068
def test_deployer(self):
5069
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
5070
'1.23-series-arch', None)
5071
with patch.object(EnvJujuClient1X, 'juju') as mock:
5072
client.deployer('bundle:~juju-qa/some-bundle')
5073
mock.assert_called_with(
5074
'deployer', ('--debug', '--deploy-delay', '10', '--timeout',
5075
'3600', '--config', 'bundle:~juju-qa/some-bundle'),
5079
def test_deployer_template(self):
5080
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
5081
'1.23-series-arch', None)
5082
with patch.object(EnvJujuClient1X, 'juju') as mock:
5083
client.deployer('bundle:~juju-qa/some-{container}-bundle')
5084
mock.assert_called_with(
5086
'--debug', '--deploy-delay', '10', '--timeout', '3600',
5087
'--config', 'bundle:~juju-qa/some-lxc-bundle',
5091
def test_deployer_with_bundle_name(self):
5092
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
5093
'1.23-series-arch', None)
5094
with patch.object(EnvJujuClient1X, 'juju') as mock:
5095
client.deployer('bundle:~juju-qa/some-bundle', 'name')
5096
mock.assert_called_with(
5097
'deployer', ('--debug', '--deploy-delay', '10', '--timeout',
5098
'3600', '--config', 'bundle:~juju-qa/some-bundle',
5103
def test_quickstart_maas(self):
5104
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'maas'}),
5105
'1.23-series-arch', '/juju')
5106
with patch.object(EnvJujuClient1X, 'juju') as mock:
5107
client.quickstart('bundle:~juju-qa/some-bundle')
5108
mock.assert_called_with(
5110
('--constraints', 'mem=2G', '--no-browser',
5111
'bundle:~juju-qa/some-bundle'), False, extra_env={'JUJU': '/juju'}
5114
def test_quickstart_local(self):
5115
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
5116
'1.23-series-arch', '/juju')
5117
with patch.object(EnvJujuClient1X, 'juju') as mock:
5118
client.quickstart('bundle:~juju-qa/some-bundle')
5119
mock.assert_called_with(
5121
('--constraints', 'mem=2G', '--no-browser',
5122
'bundle:~juju-qa/some-bundle'), True, extra_env={'JUJU': '/juju'}
5125
def test_quickstart_nonlocal(self):
5126
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'nonlocal'}),
5127
'1.23-series-arch', '/juju')
5128
with patch.object(EnvJujuClient1X, 'juju') as mock:
5129
client.quickstart('bundle:~juju-qa/some-bundle')
5130
mock.assert_called_with(
5132
('--constraints', 'mem=2G', '--no-browser',
5133
'bundle:~juju-qa/some-bundle'), False, extra_env={'JUJU': '/juju'}
5136
def test_quickstart_template(self):
5137
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
5138
'1.23-series-arch', '/juju')
5139
with patch.object(EnvJujuClient1X, 'juju') as mock:
5140
client.quickstart('bundle:~juju-qa/some-{container}-bundle')
5141
mock.assert_called_with(
5143
'--constraints', 'mem=2G', '--no-browser',
5144
'bundle:~juju-qa/some-lxc-bundle'),
5145
True, extra_env={'JUJU': '/juju'})
5147
def test_list_models(self):
5148
env = SimpleEnvironment('foo', {'type': 'local'})
5149
client = EnvJujuClient1X(env, '1.23-series-arch', None)
5150
client.list_models()
5152
'INFO The model is environment foo\n',
5153
self.log_stream.getvalue())
5155
def test__get_models(self):
5162
env = SimpleEnvironment('foo', {'type': 'local'})
5163
client = fake_juju_client(cls=EnvJujuClient1X, env=env)
5164
with patch.object(client, 'get_juju_output', return_value=data):
5165
models = client._get_models()
5167
[{'name': 'foo', 'model-uuid': 'aaaa'},
5168
{'name': 'bar', 'model-uuid': 'bbbb'}],
5171
def test__get_models_exception(self):
5172
env = SimpleEnvironment('foo', {'type': 'local'})
5173
client = fake_juju_client(cls=EnvJujuClient1X, env=env)
5174
with patch.object(client, 'get_juju_output',
5175
side_effect=subprocess.CalledProcessError('a', 'b')):
5176
self.assertEqual([], client._get_models())
5178
def test_get_models(self):
5179
env = SimpleEnvironment('foo', {'type': 'local'})
5180
client = EnvJujuClient1X(env, '1.23-series-arch', None)
5181
self.assertEqual({}, client.get_models())
5183
def test_iter_model_clients(self):
5192
client = EnvJujuClient1X(SimpleEnvironment('foo', {}), None, None)
5193
with patch.object(client, 'get_juju_output', return_value=data):
5194
model_clients = list(client.iter_model_clients())
5195
self.assertEqual(2, len(model_clients))
5196
self.assertIs(client, model_clients[0])
5197
self.assertEqual('bar', model_clients[1].env.environment)
5199
def test_get_controller_model_name_no_models(self):
5200
env = SimpleEnvironment('foo', {'type': 'local'})
5201
client = EnvJujuClient1X(env, '1.23-series-arch', None)
5202
with patch.object(client, 'get_models', return_value={}):
5203
controller_name = client.get_controller_model_name()
5204
self.assertEqual('foo', controller_name)
5206
def test_get_controller_client(self):
5207
client = EnvJujuClient1X(SimpleEnvironment('foo'), {'bar': 'baz'},
5209
controller_client = client.get_controller_client()
5210
self.assertIs(client, controller_client)
5212
def test_list_controllers(self):
5213
env = SimpleEnvironment('foo', {'type': 'local'})
5214
client = EnvJujuClient1X(env, '1.23-series-arch', None)
5215
client.list_controllers()
5217
'INFO The controller is environment foo\n',
5218
self.log_stream.getvalue())
5220
def test_get_controller_endpoint_ipv4(self):
5221
env = SimpleEnvironment('foo', {'type': 'local'})
5222
client = EnvJujuClient1X(env, '1.23-series-arch', None)
5223
with patch.object(client, 'get_juju_output',
5224
return_value='10.0.0.1:17070') as gjo_mock:
5225
endpoint = client.get_controller_endpoint()
5226
self.assertEqual('10.0.0.1', endpoint)
5227
gjo_mock.assert_called_once_with('api-endpoints')
5229
def test_get_controller_endpoint_ipv6(self):
5230
env = SimpleEnvironment('foo', {'type': 'local'})
5231
client = EnvJujuClient1X(env, '1.23-series-arch', None)
5232
with patch.object(client, 'get_juju_output',
5233
return_value='[::1]:17070') as gjo_mock:
5234
endpoint = client.get_controller_endpoint()
5235
self.assertEqual('::1', endpoint)
5236
gjo_mock.assert_called_once_with('api-endpoints')
5238
def test_action_do(self):
5239
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
5240
'1.23-series-arch', None)
5241
with patch.object(EnvJujuClient1X, 'get_juju_output') as mock:
5242
mock.return_value = \
5243
"Action queued with id: 5a92ec93-d4be-4399-82dc-7431dbfd08f9"
5244
id = client.action_do("foo/0", "myaction", "param=5")
5245
self.assertEqual(id, "5a92ec93-d4be-4399-82dc-7431dbfd08f9")
5246
mock.assert_called_once_with(
5247
'action do', 'foo/0', 'myaction', "param=5"
5250
def test_action_do_error(self):
5251
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
5252
'1.23-series-arch', None)
5253
with patch.object(EnvJujuClient1X, 'get_juju_output') as mock:
5254
mock.return_value = "some bad text"
5255
with self.assertRaisesRegexp(Exception,
5256
"Action id not found in output"):
5257
client.action_do("foo/0", "myaction", "param=5")
5259
def test_action_fetch(self):
5260
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
5261
'1.23-series-arch', None)
5262
with patch.object(EnvJujuClient1X, 'get_juju_output') as mock:
5263
ret = "status: completed\nfoo: bar"
5264
mock.return_value = ret
5265
out = client.action_fetch("123")
5266
self.assertEqual(out, ret)
5267
mock.assert_called_once_with(
5268
'action fetch', '123', "--wait", "1m"
5271
def test_action_fetch_timeout(self):
5272
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
5273
'1.23-series-arch', None)
5274
ret = "status: pending\nfoo: bar"
5275
with patch.object(EnvJujuClient1X,
5276
'get_juju_output', return_value=ret):
5277
with self.assertRaisesRegexp(Exception,
5278
"timed out waiting for action"):
5279
client.action_fetch("123")
5281
def test_action_do_fetch(self):
5282
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
5283
'1.23-series-arch', None)
5284
with patch.object(EnvJujuClient1X, 'get_juju_output') as mock:
5285
ret = "status: completed\nfoo: bar"
5286
# setting side_effect to an iterable will return the next value
5287
# from the list each time the function is called.
5288
mock.side_effect = [
5289
"Action queued with id: 5a92ec93-d4be-4399-82dc-7431dbfd08f9",
5291
out = client.action_do_fetch("foo/0", "myaction", "param=5")
5292
self.assertEqual(out, ret)
5295
env = SimpleEnvironment('name', {}, 'foo')
5296
client = fake_juju_client(cls=EnvJujuClient1X, env=env)
5299
"Stdout": "Linux\n",
5301
"Stderr": "Permission denied (publickey,password)"}]
5302
run_output = json.dumps(run_list)
5303
with patch.object(client._backend, 'get_juju_output',
5304
return_value=run_output) as gjo_mock:
5305
result = client.run(('wname',), applications=['foo', 'bar'])
5306
self.assertEqual(run_list, result)
5307
gjo_mock.assert_called_once_with(
5308
'run', ('--format', 'json', '--service', 'foo,bar', 'wname'),
5310
['address-allocation', 'migration']),
5311
'foo', 'name', user_name=None)
5313
def test_list_space(self):
5314
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
5315
'1.23-series-arch', None)
5316
yaml_dict = {'foo': 'bar'}
5317
output = yaml.safe_dump(yaml_dict)
5318
with patch.object(client, 'get_juju_output', return_value=output,
5319
autospec=True) as gjo_mock:
5320
result = client.list_space()
5321
self.assertEqual(result, yaml_dict)
5322
gjo_mock.assert_called_once_with('space list')
5324
def test_add_space(self):
5325
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
5326
'1.23-series-arch', None)
5327
with patch.object(client, 'juju', autospec=True) as juju_mock:
5328
client.add_space('foo-space')
5329
juju_mock.assert_called_once_with('space create', ('foo-space'))
5331
def test_add_subnet(self):
5332
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
5333
'1.23-series-arch', None)
5334
with patch.object(client, 'juju', autospec=True) as juju_mock:
5335
client.add_subnet('bar-subnet', 'foo-space')
5336
juju_mock.assert_called_once_with('subnet add',
5337
('bar-subnet', 'foo-space'))
5339
def test__shell_environ_uses_pathsep(self):
5340
client = EnvJujuClient1X(SimpleEnvironment('foo'), None,
5342
with patch('os.pathsep', '!'):
5343
environ = client._shell_environ()
5344
self.assertRegexpMatches(environ['PATH'], r'foo/bar\!')
5346
def test_set_config(self):
5347
client = EnvJujuClient1X(SimpleEnvironment('bar', {}), None, '/foo')
5348
with patch.object(client, 'juju') as juju_mock:
5349
client.set_config('foo', {'bar': 'baz'})
5350
juju_mock.assert_called_once_with('set', ('foo', 'bar=baz'))
5352
def test_get_config(self):
5353
def output(*args, **kwargs):
5354
return yaml.safe_dump({
5360
'description': 'bla bla',
5362
'value': '/tmp/charm-dir',
5366
expected = yaml.safe_load(output())
5367
client = EnvJujuClient1X(SimpleEnvironment('bar', {}), None, '/foo')
5368
with patch.object(client, 'get_juju_output',
5369
side_effect=output) as gjo_mock:
5370
results = client.get_config('foo')
5371
self.assertEqual(expected, results)
5372
gjo_mock.assert_called_once_with('get', 'foo')
5374
def test_get_service_config(self):
5375
def output(*args, **kwargs):
5376
return yaml.safe_dump({
5382
'description': 'bla bla',
5384
'value': '/tmp/charm-dir',
5388
expected = yaml.safe_load(output())
5389
client = EnvJujuClient1X(SimpleEnvironment('bar', {}), None, '/foo')
5390
with patch.object(client, 'get_juju_output', side_effect=output):
5391
results = client.get_service_config('foo')
5392
self.assertEqual(expected, results)
5394
def test_get_service_config_timesout(self):
5395
client = EnvJujuClient1X(SimpleEnvironment('foo', {}), None, '/foo')
5396
with patch('jujupy.until_timeout', return_value=range(0)):
5397
with self.assertRaisesRegexp(
5398
Exception, 'Timed out waiting for juju get'):
5399
client.get_service_config('foo')
5401
def test_ssh_keys(self):
5402
client = EnvJujuClient1X(SimpleEnvironment('foo', {}), None, None)
5403
given_output = 'ssh keys output'
5404
with patch.object(client, 'get_juju_output', autospec=True,
5405
return_value=given_output) as mock:
5406
output = client.ssh_keys()
5407
self.assertEqual(output, given_output)
5408
mock.assert_called_once_with('authorized-keys list')
5410
def test_ssh_keys_full(self):
5411
client = EnvJujuClient1X(SimpleEnvironment('foo', {}), None, None)
5412
given_output = 'ssh keys full output'
5413
with patch.object(client, 'get_juju_output', autospec=True,
5414
return_value=given_output) as mock:
5415
output = client.ssh_keys(full=True)
5416
self.assertEqual(output, given_output)
5417
mock.assert_called_once_with('authorized-keys list', '--full')
5419
def test_add_ssh_key(self):
5420
client = EnvJujuClient1X(SimpleEnvironment('foo', {}), None, None)
5421
with patch.object(client, 'get_juju_output', autospec=True,
5422
return_value='') as mock:
5423
output = client.add_ssh_key('ak', 'bk')
5424
self.assertEqual(output, '')
5425
mock.assert_called_once_with(
5426
'authorized-keys add', 'ak', 'bk', merge_stderr=True)
5428
def test_remove_ssh_key(self):
5429
client = EnvJujuClient1X(SimpleEnvironment('foo', {}), None, None)
5430
with patch.object(client, 'get_juju_output', autospec=True,
5431
return_value='') as mock:
5432
output = client.remove_ssh_key('ak', 'bk')
5433
self.assertEqual(output, '')
5434
mock.assert_called_once_with(
5435
'authorized-keys delete', 'ak', 'bk', merge_stderr=True)
5437
def test_import_ssh_key(self):
5438
client = EnvJujuClient1X(SimpleEnvironment('foo', {}), None, None)
5439
with patch.object(client, 'get_juju_output', autospec=True,
5440
return_value='') as mock:
5441
output = client.import_ssh_key('gh:au', 'lp:bu')
5442
self.assertEqual(output, '')
5443
mock.assert_called_once_with(
5444
'authorized-keys import', 'gh:au', 'lp:bu', merge_stderr=True)
635
with patch('sys.stdout'):
5447
639
class TestUniquifyLocal(TestCase):