1
from contextlib import contextmanager
11
from textwrap import dedent
20
from jujuconfig import (
28
JUJU_DEV_FEATURE_FLAGS,
34
_temp_env as temp_env,
37
from jujupy.client import (
43
UpgradeMongoNotSupported,
44
WaitMachineNotPresent,
46
from jujupy.fake import (
49
from jujupy.version_client import (
57
IncompatibleConfigClass,
61
VersionNotTestedError,
64
from jujupy.tests.test_client import (
79
class TestJuju1XBackend(TestCase):
81
def test_full_args_model(self):
82
backend = Juju1XBackend('/bin/path/juju', '1.25', set(), False, None)
83
full = backend.full_args('help', ('commands',), 'test', None)
84
self.assertEqual(('juju', '--show-log', 'help', '-e', 'test',
88
class TestClientFromConfig(ClientTest):
91
def assertRaisesVersionNotTested(self, version):
92
with self.assertRaisesRegexp(
93
VersionNotTestedError, 'juju ' + version):
96
@patch.object(JujuData, 'from_config', return_value=JujuData('', {}))
97
@patch.object(SimpleEnvironment, 'from_config',
98
return_value=SimpleEnvironment('', {}))
99
@patch.object(ModelClient, 'get_full_path', return_value='fake-path')
100
def test_from_config(self, gfp_mock, se_fc_mock, jd_fc_mock):
101
def juju_cmd_iterator():
137
context = patch.object(
138
ModelClient, 'get_version',
139
side_effect=juju_cmd_iterator().send)
141
self.assertIs(EnvJujuClient1X,
142
type(client_from_config('foo', None)))
144
def test_fc(version, cls):
146
client = client_from_config('foo', None)
147
if isinstance(client, EnvJujuClient1X):
148
self.assertEqual(se_fc_mock.return_value, client.env)
150
self.assertEqual(jd_fc_mock.return_value, client.env)
151
self.assertIs(cls, type(client))
152
self.assertEqual(version, client.version)
154
with self.assertRaisesVersionNotTested(version):
155
client_from_config('foo', None)
157
test_fc('1.16', None)
158
test_fc('1.16.1', None)
159
test_fc('1.15', EnvJujuClient1X)
160
test_fc('1.22.1', EnvJujuClient22)
161
test_fc('1.24-alpha1', EnvJujuClient24)
162
test_fc('1.24.7', EnvJujuClient24)
163
test_fc('1.25.1', EnvJujuClient25)
164
test_fc('1.26.1', None)
165
test_fc('1.27.1', EnvJujuClient1X)
166
test_fc('2.0-alpha1', None)
167
test_fc('2.0-alpha2', None)
168
test_fc('2.0-alpha3', None)
169
test_fc('2.0-beta1', None)
170
test_fc('2.0-beta2', None)
171
test_fc('2.0-beta3', None)
172
test_fc('2.0-beta4', None)
173
test_fc('2.0-beta5', None)
174
test_fc('2.0-beta6', None)
175
test_fc('2.0-beta7', None)
176
test_fc('2.0-beta8', None)
177
test_fc('2.0-beta9', None)
178
test_fc('2.0-beta10', None)
179
test_fc('2.0-beta11', None)
180
test_fc('2.0-beta12', None)
181
test_fc('2.0-beta13', None)
182
test_fc('2.0-beta14', None)
183
test_fc('2.0-beta15', None)
184
test_fc('2.0-rc1', ModelClientRC)
185
test_fc('2.0-rc2', ModelClientRC)
186
test_fc('2.0-rc3', ModelClientRC)
187
test_fc('2.0-delta1', ModelClient2_0)
188
test_fc('2.1.0', ModelClient2_1)
189
test_fc('2.2.0', ModelClient)
190
with self.assertRaises(StopIteration):
191
client_from_config('foo', None)
193
def test_client_from_config_path(self):
194
with patch('subprocess.check_output', return_value=' 4.3') as vsn:
195
with patch.object(JujuData, 'from_config'):
196
client = client_from_config('foo', 'foo/bar/qux')
197
vsn.assert_called_once_with(('foo/bar/qux', '--version'))
198
self.assertNotEqual(client.full_path, 'foo/bar/qux')
199
self.assertEqual(client.full_path, os.path.abspath('foo/bar/qux'))
201
def test_client_from_config_keep_home(self):
202
env = JujuData({}, juju_home='/foo/bar')
203
with patch('subprocess.check_output', return_value='2.0.0'):
204
with patch.object(JujuData, 'from_config',
205
side_effect=lambda x: JujuData(x, {})):
206
client_from_config('foo', 'foo/bar/qux')
207
self.assertEqual('/foo/bar', env.juju_home)
209
def test_client_from_config_deadline(self):
210
deadline = datetime(2012, 11, 10, 9, 8, 7)
211
with patch('subprocess.check_output', return_value='2.0.0'):
212
with patch.object(JujuData, 'from_config',
213
side_effect=lambda x: JujuData(x, {})):
214
client = client_from_config(
215
'foo', 'foo/bar/qux', soft_deadline=deadline)
216
self.assertEqual(client._backend.soft_deadline, deadline)
219
class TestModelClient2_1(ClientTest):
221
client_version = '2.1.0'
223
client_class = ModelClient2_1
225
fake_backend_class = FakeBackend2_1
227
def test_basics(self):
230
def test_add_cloud_interactive_maas(self):
231
client = self.fake_juju_client()
234
'endpoint': 'http://bar.example.com',
236
client.add_cloud_interactive('foo', clouds['foo'])
237
self.assertEqual(client._backend.clouds, clouds)
239
def test_add_cloud_interactive_maas_invalid_endpoint(self):
240
client = self.fake_juju_client()
243
'endpoint': 'B' * 4000,
245
with self.assertRaises(InvalidEndpoint):
246
client.add_cloud_interactive('foo', clouds['foo'])
248
def test_add_cloud_interactive_manual(self):
249
client = self.fake_juju_client()
250
clouds = {'foo': {'type': 'manual', 'endpoint': '127.100.100.1'}}
251
client.add_cloud_interactive('foo', clouds['foo'])
252
self.assertEqual(client._backend.clouds, clouds)
254
def test_add_cloud_interactive_manual_invalid_endpoint(self):
255
client = self.fake_juju_client()
256
clouds = {'foo': {'type': 'manual', 'endpoint': 'B' * 4000}}
257
with self.assertRaises(InvalidEndpoint):
258
client.add_cloud_interactive('foo', clouds['foo'])
260
def get_openstack_clouds(self):
263
'endpoint': 'http://bar.example.com',
264
'auth-types': ['oauth1', 'oauth12'],
266
'harvey': {'endpoint': 'http://harvey.example.com'},
267
'steve': {'endpoint': 'http://steve.example.com'},
271
def test_add_cloud_interactive_openstack(self):
272
client = self.fake_juju_client()
273
clouds = self.get_openstack_clouds()
274
client.add_cloud_interactive('foo', clouds['foo'])
275
self.assertEqual(client._backend.clouds, clouds)
277
def test_add_cloud_interactive_openstack_invalid_endpoint(self):
278
client = self.fake_juju_client()
279
clouds = self.get_openstack_clouds()
280
clouds['foo']['endpoint'] = 'B' * 4000
281
with self.assertRaises(InvalidEndpoint):
282
client.add_cloud_interactive('foo', clouds['foo'])
284
def test_add_cloud_interactive_openstack_invalid_region_endpoint(self):
285
client = self.fake_juju_client()
286
clouds = self.get_openstack_clouds()
287
clouds['foo']['regions']['harvey']['endpoint'] = 'B' * 4000
288
with self.assertRaises(InvalidEndpoint):
289
client.add_cloud_interactive('foo', clouds['foo'])
291
def test_add_cloud_interactive_openstack_invalid_auth(self):
292
client = self.fake_juju_client()
293
clouds = self.get_openstack_clouds()
294
clouds['foo']['auth-types'] = ['invalid', 'oauth12']
295
with self.assertRaises(AuthNotAccepted):
296
client.add_cloud_interactive('foo', clouds['foo'])
298
def test_add_cloud_interactive_vsphere(self):
299
client = self.fake_juju_client()
302
'endpoint': 'http://bar.example.com',
308
client.add_cloud_interactive('foo', clouds['foo'])
309
self.assertEqual(client._backend.clouds, clouds)
311
def test_add_cloud_interactive_bogus(self):
312
client = self.fake_juju_client()
313
clouds = {'foo': {'type': 'bogus'}}
314
with self.assertRaises(TypeNotAccepted):
315
client.add_cloud_interactive('foo', clouds['foo'])
317
def test_add_cloud_interactive_invalid_name(self):
318
client = self.fake_juju_client()
319
cloud = {'type': 'manual', 'endpoint': 'example.com'}
320
with self.assertRaises(NameNotAccepted):
321
client.add_cloud_interactive('invalid/name', cloud)
324
class TestModelClientRC(ClientTest):
326
def test_bootstrap(self):
327
env = JujuData('foo', {'type': 'bar', 'region': 'baz'})
328
with observable_temp_file() as config_file:
329
with patch.object(ModelClientRC, 'juju') as mock:
330
client = ModelClientRC(env, '2.0-zeta1', None)
332
mock.assert_called_with(
333
'bootstrap', ('--constraints', 'mem=2G',
335
'--config', config_file.name,
336
'--default-model', 'foo',
337
'--agent-version', '2.0'), include_e=False)
339
config = yaml.safe_load(config_file)
340
self.assertEqual({'test-mode': True}, config)
343
class TestEnvJujuClient1X(ClientTest):
345
def test_raise_on_juju_data(self):
346
env = JujuData('foo', {'type': 'bar'}, 'baz')
347
with self.assertRaisesRegexp(
348
IncompatibleConfigClass,
349
'JujuData cannot be used with EnvJujuClient1X'):
350
EnvJujuClient1X(env, '1.25', 'full_path')
352
def test_no_duplicate_env(self):
353
env = SimpleEnvironment('foo', {})
354
client = EnvJujuClient1X(env, '1.25', 'full_path')
355
self.assertIs(env, client.env)
357
def test_not_supported(self):
358
client = EnvJujuClient1X(
359
SimpleEnvironment('foo', {}), '1.25', 'full_path')
360
with self.assertRaises(JESNotSupported):
361
client.add_user_perms('test-user')
362
with self.assertRaises(JESNotSupported):
363
client.grant('test-user', 'read')
364
with self.assertRaises(JESNotSupported):
365
client.revoke('test-user', 'read')
366
with self.assertRaises(JESNotSupported):
367
client.get_model_uuid()
369
def test_get_version(self):
371
with patch('subprocess.check_output', return_value=value) as vsn:
372
version = EnvJujuClient1X.get_version()
373
self.assertEqual('5.6', version)
374
vsn.assert_called_with(('juju', '--version'))
376
def test_get_version_path(self):
377
with patch('subprocess.check_output', return_value=' 4.3') as vsn:
378
EnvJujuClient1X.get_version('foo/bar/baz')
379
vsn.assert_called_once_with(('foo/bar/baz', '--version'))
381
def test_get_matching_agent_version(self):
382
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
383
'1.23-series-arch', None)
384
self.assertEqual('1.23.1', client.get_matching_agent_version())
385
self.assertEqual('1.23', client.get_matching_agent_version(
387
client = client.clone(version='1.20-beta1-series-arch')
388
self.assertEqual('1.20-beta1.1', client.get_matching_agent_version())
390
def test_upgrade_juju_nonlocal(self):
391
client = EnvJujuClient1X(
392
SimpleEnvironment('foo', {'type': 'nonlocal'}), '1.234-76', None)
393
with patch.object(client, 'juju') as juju_mock:
394
client.upgrade_juju()
395
juju_mock.assert_called_with(
396
'upgrade-juju', ('--version', '1.234'))
398
def test_upgrade_juju_local(self):
399
client = EnvJujuClient1X(
400
SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None)
401
with patch.object(client, 'juju') as juju_mock:
402
client.upgrade_juju()
403
juju_mock.assert_called_with(
404
'upgrade-juju', ('--version', '1.234', '--upload-tools',))
406
def test_upgrade_juju_no_force_version(self):
407
client = EnvJujuClient1X(
408
SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None)
409
with patch.object(client, 'juju') as juju_mock:
410
client.upgrade_juju(force_version=False)
411
juju_mock.assert_called_with(
412
'upgrade-juju', ('--upload-tools',))
414
def test_upgrade_mongo_exception(self):
415
client = EnvJujuClient1X(
416
SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None)
417
with self.assertRaises(UpgradeMongoNotSupported):
418
client.upgrade_mongo()
420
def test_get_cache_path(self):
421
client = EnvJujuClient1X(SimpleEnvironment('foo', juju_home='/foo/'),
422
'1.27', 'full/path', debug=True)
423
self.assertEqual('/foo/environments/cache.yaml',
424
client.get_cache_path())
426
def test_bootstrap_maas(self):
427
env = SimpleEnvironment('maas')
428
with patch.object(EnvJujuClient1X, 'juju') as mock:
429
client = EnvJujuClient1X(env, None, None)
430
with patch.object(client.env, 'maas', lambda: True):
432
mock.assert_called_once_with('bootstrap', ('--constraints', 'mem=2G'))
434
def test_bootstrap_joyent(self):
435
env = SimpleEnvironment('joyent')
436
with patch.object(EnvJujuClient1X, 'juju', autospec=True) as mock:
437
client = EnvJujuClient1X(env, None, None)
438
with patch.object(client.env, 'joyent', lambda: True):
440
mock.assert_called_once_with(
441
client, 'bootstrap', ('--constraints', 'mem=2G cpu-cores=1'))
443
def test_bootstrap(self):
444
env = SimpleEnvironment('foo')
445
client = EnvJujuClient1X(env, None, None)
446
with patch.object(client, 'juju') as mock:
448
mock.assert_called_with('bootstrap', ('--constraints', 'mem=2G'))
450
def test_bootstrap_upload_tools(self):
451
env = SimpleEnvironment('foo')
452
client = EnvJujuClient1X(env, None, None)
453
with patch.object(client, 'juju') as mock:
454
client.bootstrap(upload_tools=True)
455
mock.assert_called_with(
456
'bootstrap', ('--upload-tools', '--constraints', 'mem=2G'))
458
def test_bootstrap_args(self):
459
env = SimpleEnvironment('foo', {})
460
client = EnvJujuClient1X(env, None, None)
461
with self.assertRaisesRegexp(
463
'--bootstrap-series angsty does not match default-series:'
465
client.bootstrap(bootstrap_series='angsty')
467
'default-series': 'angsty',
469
with patch.object(client, 'juju') as mock:
470
client.bootstrap(bootstrap_series='angsty')
471
mock.assert_called_with('bootstrap', ('--constraints', 'mem=2G'))
473
def test_bootstrap_async(self):
474
env = SimpleEnvironment('foo')
475
with patch.object(ModelClient, 'juju_async', autospec=True) as mock:
476
client = EnvJujuClient1X(env, None, None)
477
client.env.juju_home = 'foo'
478
with client.bootstrap_async():
479
mock.assert_called_once_with(
480
client, 'bootstrap', ('--constraints', 'mem=2G'))
482
def test_bootstrap_async_upload_tools(self):
483
env = SimpleEnvironment('foo')
484
with patch.object(ModelClient, 'juju_async', autospec=True) as mock:
485
client = EnvJujuClient1X(env, None, None)
486
with client.bootstrap_async(upload_tools=True):
487
mock.assert_called_with(
488
client, 'bootstrap', ('--upload-tools', '--constraints',
491
def test_get_bootstrap_args_bootstrap_series(self):
492
env = SimpleEnvironment('foo', {})
493
client = EnvJujuClient1X(env, None, None)
494
with self.assertRaisesRegexp(
496
'--bootstrap-series angsty does not match default-series:'
498
client.get_bootstrap_args(upload_tools=True,
499
bootstrap_series='angsty')
500
env.update_config({'default-series': 'angsty'})
501
args = client.get_bootstrap_args(upload_tools=True,
502
bootstrap_series='angsty')
503
self.assertEqual(args, ('--upload-tools', '--constraints', 'mem=2G'))
505
def test_create_environment_system(self):
506
self.do_create_environment(
507
'system', 'system create-environment', ('-s', 'foo'))
509
def test_create_environment_controller(self):
510
self.do_create_environment(
511
'controller', 'controller create-environment', ('-c', 'foo'))
513
def test_create_environment_hypenated_controller(self):
514
self.do_create_environment(
515
'kill-controller', 'create-environment', ('-c', 'foo'))
517
def do_create_environment(self, jes_command, create_cmd,
519
controller_client = EnvJujuClient1X(SimpleEnvironment('foo'), '1.26.1',
521
model_env = SimpleEnvironment('bar', {'type': 'foo'})
522
with patch.object(controller_client, 'get_jes_command',
523
return_value=jes_command):
524
with patch.object(controller_client, 'juju') as juju_mock:
525
with observable_temp_file() as config_file:
526
controller_client.add_model(model_env)
527
juju_mock.assert_called_once_with(
528
create_cmd, controller_option + (
529
'bar', '--config', config_file.name), include_e=False)
531
def test_destroy_environment(self):
532
env = SimpleEnvironment('foo', {'type': 'ec2'})
533
client = EnvJujuClient1X(env, None, None)
534
with patch.object(client, 'juju') as mock:
535
client.destroy_environment()
536
mock.assert_called_with(
537
'destroy-environment', ('foo', '--force', '-y'),
538
check=False, include_e=False, timeout=600)
540
def test_destroy_environment_no_force(self):
541
env = SimpleEnvironment('foo', {'type': 'ec2'})
542
client = EnvJujuClient1X(env, None, None)
543
with patch.object(client, 'juju') as mock:
544
client.destroy_environment(force=False)
545
mock.assert_called_with(
546
'destroy-environment', ('foo', '-y'),
547
check=False, include_e=False, timeout=600)
549
def test_destroy_environment_azure(self):
550
env = SimpleEnvironment('foo', {'type': 'azure'})
551
client = EnvJujuClient1X(env, None, None)
552
with patch.object(client, 'juju') as mock:
553
client.destroy_environment(force=False)
554
mock.assert_called_with(
555
'destroy-environment', ('foo', '-y'),
556
check=False, include_e=False, timeout=1800)
558
def test_destroy_environment_gce(self):
559
env = SimpleEnvironment('foo', {'type': 'gce'})
560
client = EnvJujuClient1X(env, None, None)
561
with patch.object(client, 'juju') as mock:
562
client.destroy_environment(force=False)
563
mock.assert_called_with(
564
'destroy-environment', ('foo', '-y'),
565
check=False, include_e=False, timeout=1200)
567
def test_destroy_environment_delete_jenv(self):
568
env = SimpleEnvironment('foo', {'type': 'ec2'})
569
client = EnvJujuClient1X(env, None, None)
570
with patch.object(client, 'juju'):
571
with temp_env({}) as juju_home:
572
client.env.juju_home = juju_home
573
jenv_path = get_jenv_path(juju_home, 'foo')
574
os.makedirs(os.path.dirname(jenv_path))
576
self.assertTrue(os.path.exists(jenv_path))
577
client.destroy_environment(delete_jenv=True)
578
self.assertFalse(os.path.exists(jenv_path))
580
def test_destroy_model(self):
581
env = SimpleEnvironment('foo', {'type': 'ec2'})
582
client = EnvJujuClient1X(env, None, None)
583
with patch.object(client, 'juju') as mock:
584
client.destroy_model()
585
mock.assert_called_with(
586
'destroy-environment', ('foo', '-y'),
587
check=False, include_e=False, timeout=600)
589
def test_kill_controller(self):
590
client = EnvJujuClient1X(
591
SimpleEnvironment('foo', {'type': 'ec2'}), None, None)
592
with patch.object(client, 'juju') as juju_mock:
593
client.kill_controller()
594
juju_mock.assert_called_once_with(
595
'destroy-environment', ('foo', '--force', '-y'), check=False,
596
include_e=False, timeout=600)
598
def test_kill_controller_check(self):
599
client = EnvJujuClient1X(
600
SimpleEnvironment('foo', {'type': 'ec2'}), None, None)
601
with patch.object(client, 'juju') as juju_mock:
602
client.kill_controller(check=True)
603
juju_mock.assert_called_once_with(
604
'destroy-environment', ('foo', '--force', '-y'), check=True,
605
include_e=False, timeout=600)
607
def test_destroy_controller(self):
608
client = EnvJujuClient1X(
609
SimpleEnvironment('foo', {'type': 'ec2'}), None, None)
610
with patch.object(client, 'juju') as juju_mock:
611
client.destroy_controller()
612
juju_mock.assert_called_once_with(
613
'destroy-environment', ('foo', '-y'),
614
include_e=False, timeout=600)
616
def test_get_juju_output(self):
617
env = SimpleEnvironment('foo')
618
client = EnvJujuClient1X(env, None, 'juju')
619
fake_popen = FakePopen('asdf', None, 0)
620
with patch('subprocess.Popen', return_value=fake_popen) as mock:
621
result = client.get_juju_output('bar')
622
self.assertEqual('asdf', result)
623
self.assertEqual((('juju', '--show-log', 'bar', '-e', 'foo'),),
626
def test_get_juju_output_accepts_varargs(self):
627
env = SimpleEnvironment('foo')
628
fake_popen = FakePopen('asdf', None, 0)
629
client = EnvJujuClient1X(env, None, 'juju')
630
with patch('subprocess.Popen', return_value=fake_popen) as mock:
631
result = client.get_juju_output('bar', 'baz', '--qux')
632
self.assertEqual('asdf', result)
633
self.assertEqual((('juju', '--show-log', 'bar', '-e', 'foo', 'baz',
634
'--qux'),), mock.call_args[0])
636
def test_get_juju_output_stderr(self):
637
env = SimpleEnvironment('foo')
638
fake_popen = FakePopen('Hello', 'Error!', 1)
639
client = EnvJujuClient1X(env, None, 'juju')
640
with self.assertRaises(subprocess.CalledProcessError) as exc:
641
with patch('subprocess.Popen', return_value=fake_popen):
642
client.get_juju_output('bar')
643
self.assertEqual(exc.exception.output, 'Hello')
644
self.assertEqual(exc.exception.stderr, 'Error!')
646
def test_get_juju_output_full_cmd(self):
647
env = SimpleEnvironment('foo')
648
fake_popen = FakePopen(None, 'Hello!', 1)
649
client = EnvJujuClient1X(env, None, 'juju')
650
with self.assertRaises(subprocess.CalledProcessError) as exc:
651
with patch('subprocess.Popen', return_value=fake_popen):
652
client.get_juju_output('bar', '--baz', 'qux')
654
('juju', '--show-log', 'bar', '-e', 'foo', '--baz', 'qux'),
657
def test_get_juju_output_accepts_timeout(self):
658
env = SimpleEnvironment('foo')
659
fake_popen = FakePopen('asdf', None, 0)
660
client = EnvJujuClient1X(env, None, 'juju')
661
with patch('subprocess.Popen', return_value=fake_popen) as po_mock:
662
client.get_juju_output('bar', timeout=5)
664
po_mock.call_args[0][0],
665
(sys.executable, get_timeout_path(), '5.00', '--', 'juju',
666
'--show-log', 'bar', '-e', 'foo'))
668
def test__shell_environ_juju_home(self):
669
client = EnvJujuClient1X(
670
SimpleEnvironment('baz', {'type': 'ec2'}), '1.25-foobar', 'path',
672
env = client._shell_environ()
673
self.assertEqual(env['JUJU_HOME'], 'asdf')
674
self.assertNotIn('JUJU_DATA', env)
676
def test_juju_output_supplies_path(self):
677
env = SimpleEnvironment('foo')
678
client = EnvJujuClient1X(env, None, '/foobar/bar')
680
def check_path(*args, **kwargs):
681
self.assertRegexpMatches(os.environ['PATH'], r'/foobar\:')
682
return FakePopen(None, None, 0)
683
with patch('subprocess.Popen', autospec=True,
684
side_effect=check_path):
685
client.get_juju_output('cmd', 'baz')
687
def test_get_status(self):
688
output_text = dedent("""\
693
env = SimpleEnvironment('foo')
694
client = EnvJujuClient1X(env, None, None)
695
with patch.object(client, 'get_juju_output',
696
return_value=output_text) as gjo_mock:
697
result = client.get_status()
698
gjo_mock.assert_called_once_with(
699
'status', '--format', 'yaml', controller=False)
700
self.assertEqual(Status1X, type(result))
701
self.assertEqual(['a', 'b', 'c'], result.status)
703
def test_get_status_retries_on_error(self):
704
env = SimpleEnvironment('foo')
705
client = EnvJujuClient1X(env, None, None)
708
def get_juju_output(command, *args, **kwargs):
709
if client.attempt == 1:
712
raise subprocess.CalledProcessError(1, command)
714
with patch.object(client, 'get_juju_output', get_juju_output):
717
def test_get_status_raises_on_timeout_1(self):
718
env = SimpleEnvironment('foo')
719
client = EnvJujuClient1X(env, None, None)
721
def get_juju_output(command, *args, **kwargs):
722
raise subprocess.CalledProcessError(1, command)
724
with patch.object(client, 'get_juju_output',
725
side_effect=get_juju_output):
726
with patch('jujupy.client.until_timeout',
727
lambda x: iter([None, None])):
728
with self.assertRaisesRegexp(
729
Exception, 'Timed out waiting for juju status'):
732
def test_get_status_raises_on_timeout_2(self):
733
env = SimpleEnvironment('foo')
734
client = EnvJujuClient1X(env, None, None)
735
with patch('jujupy.client.until_timeout',
736
return_value=iter([1])) as mock_ut:
737
with patch.object(client, 'get_juju_output',
738
side_effect=StopIteration):
739
with self.assertRaises(StopIteration):
740
client.get_status(500)
741
mock_ut.assert_called_with(500)
743
def test_get_status_controller(self):
749
env = SimpleEnvironment('foo')
750
client = EnvJujuClient1X(env, None, None)
751
with patch.object(client, 'get_juju_output',
752
return_value=output_text) as gjo_mock:
753
client.get_status(controller=True)
754
gjo_mock.assert_called_once_with(
755
'status', '--format', 'yaml', controller=True)
758
def make_status_yaml(key, machine_value, unit_value):
769
""".format(key, machine_value, unit_value))
771
def test_deploy_non_joyent(self):
772
env = EnvJujuClient1X(
773
SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None)
774
with patch.object(env, 'juju') as mock_juju:
775
env.deploy('mondogb')
776
mock_juju.assert_called_with('deploy', ('mondogb',))
778
def test_deploy_joyent(self):
779
env = EnvJujuClient1X(
780
SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None)
781
with patch.object(env, 'juju') as mock_juju:
782
env.deploy('mondogb')
783
mock_juju.assert_called_with('deploy', ('mondogb',))
785
def test_deploy_repository(self):
786
env = EnvJujuClient1X(
787
SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None)
788
with patch.object(env, 'juju') as mock_juju:
789
env.deploy('mondogb', '/home/jrandom/repo')
790
mock_juju.assert_called_with(
791
'deploy', ('mondogb', '--repository', '/home/jrandom/repo'))
793
def test_deploy_to(self):
794
env = EnvJujuClient1X(
795
SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None)
796
with patch.object(env, 'juju') as mock_juju:
797
env.deploy('mondogb', to='0')
798
mock_juju.assert_called_with(
799
'deploy', ('mondogb', '--to', '0'))
801
def test_deploy_service(self):
802
env = EnvJujuClient1X(
803
SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None)
804
with patch.object(env, 'juju') as mock_juju:
805
env.deploy('local:mondogb', service='my-mondogb')
806
mock_juju.assert_called_with(
807
'deploy', ('local:mondogb', 'my-mondogb',))
809
def test_upgrade_charm(self):
810
client = EnvJujuClient1X(
811
SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None)
812
with patch.object(client, 'juju') as mock_juju:
813
client.upgrade_charm('foo-service',
814
'/bar/repository/angsty/mongodb')
815
mock_juju.assert_called_once_with(
816
'upgrade-charm', ('foo-service', '--repository',
819
def test_remove_service(self):
820
client = EnvJujuClient1X(
821
SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None)
822
with patch.object(client, 'juju') as mock_juju:
823
client.remove_service('mondogb')
824
mock_juju.assert_called_with('destroy-service', ('mondogb',))
826
def test_status_until_always_runs_once(self):
827
client = EnvJujuClient1X(
828
SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None)
829
status_txt = self.make_status_yaml('agent-state', 'started', 'started')
830
with patch.object(client, 'get_juju_output', return_value=status_txt):
831
result = list(client.status_until(-1))
833
[r.status for r in result], [Status.from_text(status_txt).status])
835
def test_status_until_timeout(self):
836
client = EnvJujuClient1X(
837
SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None)
838
status_txt = self.make_status_yaml('agent-state', 'started', 'started')
839
status_yaml = yaml.safe_load(status_txt)
841
def until_timeout_stub(timeout, start=None):
842
return iter([None, None])
844
with patch.object(client, 'get_juju_output', return_value=status_txt):
845
with patch('jujupy.client.until_timeout',
846
side_effect=until_timeout_stub) as ut_mock:
847
result = list(client.status_until(30, 70))
849
[r.status for r in result], [status_yaml] * 3)
850
# until_timeout is called by status as well as status_until.
851
self.assertEqual(ut_mock.mock_calls,
852
[call(60), call(30, start=70), call(60), call(60)])
854
def test_add_ssh_machines(self):
855
client = EnvJujuClient1X(SimpleEnvironment('foo'), None, 'juju')
856
with patch('subprocess.check_call', autospec=True) as cc_mock:
857
client.add_ssh_machines(['m-foo', 'm-bar', 'm-baz'])
858
assert_juju_call(self, cc_mock, client, (
859
'juju', '--show-log', 'add-machine', '-e', 'foo', 'ssh:m-foo'), 0)
860
assert_juju_call(self, cc_mock, client, (
861
'juju', '--show-log', 'add-machine', '-e', 'foo', 'ssh:m-bar'), 1)
862
assert_juju_call(self, cc_mock, client, (
863
'juju', '--show-log', 'add-machine', '-e', 'foo', 'ssh:m-baz'), 2)
864
self.assertEqual(cc_mock.call_count, 3)
866
def test_add_ssh_machines_retry(self):
867
client = EnvJujuClient1X(SimpleEnvironment('foo'), None, 'juju')
868
with patch('subprocess.check_call', autospec=True,
869
side_effect=[subprocess.CalledProcessError(None, None),
870
None, None, None]) as cc_mock:
871
client.add_ssh_machines(['m-foo', 'm-bar', 'm-baz'])
872
assert_juju_call(self, cc_mock, client, (
873
'juju', '--show-log', 'add-machine', '-e', 'foo', 'ssh:m-foo'), 0)
874
self.pause_mock.assert_called_once_with(30)
875
assert_juju_call(self, cc_mock, client, (
876
'juju', '--show-log', 'add-machine', '-e', 'foo', 'ssh:m-foo'), 1)
877
assert_juju_call(self, cc_mock, client, (
878
'juju', '--show-log', 'add-machine', '-e', 'foo', 'ssh:m-bar'), 2)
879
assert_juju_call(self, cc_mock, client, (
880
'juju', '--show-log', 'add-machine', '-e', 'foo', 'ssh:m-baz'), 3)
881
self.assertEqual(cc_mock.call_count, 4)
883
def test_add_ssh_machines_fail_on_second_machine(self):
884
client = EnvJujuClient1X(SimpleEnvironment('foo'), None, 'juju')
885
with patch('subprocess.check_call', autospec=True, side_effect=[
886
None, subprocess.CalledProcessError(None, None), None, None
888
with self.assertRaises(subprocess.CalledProcessError):
889
client.add_ssh_machines(['m-foo', 'm-bar', 'm-baz'])
890
assert_juju_call(self, cc_mock, client, (
891
'juju', '--show-log', 'add-machine', '-e', 'foo', 'ssh:m-foo'), 0)
892
assert_juju_call(self, cc_mock, client, (
893
'juju', '--show-log', 'add-machine', '-e', 'foo', 'ssh:m-bar'), 1)
894
self.assertEqual(cc_mock.call_count, 2)
896
def test_add_ssh_machines_fail_on_second_attempt(self):
897
client = EnvJujuClient1X(SimpleEnvironment('foo'), None, 'juju')
898
with patch('subprocess.check_call', autospec=True, side_effect=[
899
subprocess.CalledProcessError(None, None),
900
subprocess.CalledProcessError(None, None)]) as cc_mock:
901
with self.assertRaises(subprocess.CalledProcessError):
902
client.add_ssh_machines(['m-foo', 'm-bar', 'm-baz'])
903
assert_juju_call(self, cc_mock, client, (
904
'juju', '--show-log', 'add-machine', '-e', 'foo', 'ssh:m-foo'), 0)
905
assert_juju_call(self, cc_mock, client, (
906
'juju', '--show-log', 'add-machine', '-e', 'foo', 'ssh:m-foo'), 1)
907
self.assertEqual(cc_mock.call_count, 2)
909
def test_wait_for_started(self):
910
value = self.make_status_yaml('agent-state', 'started', 'started')
911
client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
912
with patch.object(client, 'get_juju_output', return_value=value):
913
client.wait_for_started()
915
def test_wait_for_started_timeout(self):
916
value = self.make_status_yaml('agent-state', 'pending', 'started')
917
client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
918
with patch('jujupy.client.until_timeout',
919
lambda x, start=None: range(1)):
920
with patch.object(client, 'get_juju_output', return_value=value):
922
with patch.object(GroupReporter, '_write', autospec=True,
923
side_effect=lambda _, s: writes.append(s)):
924
with self.assertRaisesRegexp(
926
'Timed out waiting for agents to start in local'):
927
client.wait_for_started()
928
self.assertEqual(writes, ['pending: 0', ' .', '\n'])
930
def test_wait_for_started_start(self):
931
value = self.make_status_yaml('agent-state', 'started', 'pending')
932
client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
933
now = datetime.now() + timedelta(days=1)
934
with patch('utility.until_timeout.now', return_value=now):
935
with patch.object(client, 'get_juju_output', return_value=value):
937
with patch.object(GroupReporter, '_write', autospec=True,
938
side_effect=lambda _, s: writes.append(s)):
939
with self.assertRaisesRegexp(
941
'Timed out waiting for agents to start in local'):
942
client.wait_for_started(start=now - timedelta(1200))
943
self.assertEqual(writes, ['pending: jenkins/0', '\n'])
945
def test_wait_for_started_logs_status(self):
946
value = self.make_status_yaml('agent-state', 'pending', 'started')
947
client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
948
with patch.object(client, 'get_juju_output', return_value=value):
950
with patch.object(GroupReporter, '_write', autospec=True,
951
side_effect=lambda _, s: writes.append(s)):
952
with self.assertRaisesRegexp(
954
'Timed out waiting for agents to start in local'):
955
client.wait_for_started(0)
956
self.assertEqual(writes, ['pending: 0', '\n'])
957
self.assertEqual(self.log_stream.getvalue(), 'ERROR %s\n' % value)
959
def test_wait_for_subordinate_units(self):
980
client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
981
now = datetime.now() + timedelta(days=1)
982
with patch('utility.until_timeout.now', return_value=now):
983
with patch.object(client, 'get_juju_output', return_value=value):
985
'jujupy.client.GroupReporter.update') as update_mock:
987
'jujupy.client.GroupReporter.finish'
989
client.wait_for_subordinate_units(
990
'jenkins', 'sub1', start=now - timedelta(1200))
991
self.assertEqual([], update_mock.call_args_list)
992
finish_mock.assert_called_once_with()
994
def test_wait_for_multiple_subordinate_units(self):
1005
agent-state: started
1009
agent-state: started
1011
client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
1012
now = datetime.now() + timedelta(days=1)
1013
with patch('utility.until_timeout.now', return_value=now):
1014
with patch.object(client, 'get_juju_output', return_value=value):
1016
'jujupy.client.GroupReporter.update') as update_mock:
1018
'jujupy.client.GroupReporter.finish'
1020
client.wait_for_subordinate_units(
1021
'ubuntu', 'sub', start=now - timedelta(1200))
1022
self.assertEqual([], update_mock.call_args_list)
1023
finish_mock.assert_called_once_with()
1025
def test_wait_for_subordinate_units_checks_slash_in_unit_name(self):
1029
agent-state: started
1036
agent-state: started
1038
client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
1039
now = datetime.now() + timedelta(days=1)
1040
with patch('utility.until_timeout.now', return_value=now):
1041
with patch.object(client, 'get_juju_output', return_value=value):
1042
with self.assertRaisesRegexp(
1044
'Timed out waiting for agents to start in local'):
1045
client.wait_for_subordinate_units(
1046
'jenkins', 'sub1', start=now - timedelta(1200))
1048
def test_wait_for_subordinate_units_no_subordinate(self):
1052
agent-state: started
1057
agent-state: started
1059
client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
1060
now = datetime.now() + timedelta(days=1)
1061
with patch('utility.until_timeout.now', return_value=now):
1062
with patch.object(client, 'get_juju_output', return_value=value):
1063
with self.assertRaisesRegexp(
1065
'Timed out waiting for agents to start in local'):
1066
client.wait_for_subordinate_units(
1067
'jenkins', 'sub1', start=now - timedelta(1200))
1069
def test_wait_for_workload(self):
1070
initial_status = Status1X.from_text("""\
1082
final_status = Status(copy.deepcopy(initial_status.status), None)
1083
final_status.status['services']['jenkins']['units']['jenkins/0'][
1084
'workload-status']['current'] = 'active'
1085
client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
1087
with patch('utility.until_timeout', autospec=True, return_value=[1]):
1088
with patch.object(client, 'get_status', autospec=True,
1089
side_effect=[initial_status, final_status]):
1090
with patch.object(GroupReporter, '_write', autospec=True,
1091
side_effect=lambda _, s: writes.append(s)):
1092
client.wait_for_workloads()
1093
self.assertEqual(writes, ['waiting: jenkins/0', '\n'])
1095
def test_wait_for_workload_all_unknown(self):
1096
status = Status.from_text("""\
1108
client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
1110
with patch('utility.until_timeout', autospec=True, return_value=[]):
1111
with patch.object(client, 'get_status', autospec=True,
1112
return_value=status):
1113
with patch.object(GroupReporter, '_write', autospec=True,
1114
side_effect=lambda _, s: writes.append(s)):
1115
client.wait_for_workloads(timeout=1)
1116
self.assertEqual(writes, [])
1118
def test_wait_for_workload_no_workload_status(self):
1119
status = Status.from_text("""\
1126
client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
1128
with patch('utility.until_timeout', autospec=True, return_value=[]):
1129
with patch.object(client, 'get_status', autospec=True,
1130
return_value=status):
1131
with patch.object(GroupReporter, '_write', autospec=True,
1132
side_effect=lambda _, s: writes.append(s)):
1133
client.wait_for_workloads(timeout=1)
1134
self.assertEqual(writes, [])
1136
def test_wait_for_ha(self):
1137
value = yaml.safe_dump({
1139
'0': {'state-server-member-status': 'has-vote'},
1140
'1': {'state-server-member-status': 'has-vote'},
1141
'2': {'state-server-member-status': 'has-vote'},
1145
client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
1146
with patch.object(client, 'get_juju_output', return_value=value):
1147
client.wait_for_ha()
1149
def test_wait_for_ha_no_has_vote(self):
1150
value = yaml.safe_dump({
1152
'0': {'state-server-member-status': 'no-vote'},
1153
'1': {'state-server-member-status': 'no-vote'},
1154
'2': {'state-server-member-status': 'no-vote'},
1158
client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
1159
with patch.object(client, 'get_juju_output', return_value=value):
1161
with patch('jujupy.client.until_timeout', autospec=True,
1162
return_value=[2, 1]):
1163
with patch.object(GroupReporter, '_write', autospec=True,
1164
side_effect=lambda _, s: writes.append(s)):
1165
with self.assertRaisesRegexp(
1167
'Timed out waiting for voting to be enabled.'):
1168
client.wait_for_ha()
1169
self.assertEqual(writes[:2], ['no-vote: 0, 1, 2', ' .'])
1170
self.assertEqual(writes[2:-1], ['.'] * (len(writes) - 3))
1171
self.assertEqual(writes[-1:], ['\n'])
1173
def test_wait_for_ha_timeout(self):
1174
value = yaml.safe_dump({
1176
'0': {'state-server-member-status': 'has-vote'},
1177
'1': {'state-server-member-status': 'has-vote'},
1181
client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
1182
status = client.status_class.from_text(value)
1183
with patch('jujupy.client.until_timeout',
1184
lambda x, start=None: range(0)):
1185
with patch.object(client, 'get_status', return_value=status):
1186
with self.assertRaisesRegexp(
1188
'Timed out waiting for voting to be enabled.'):
1189
client.wait_for_ha()
1191
def test_wait_for_deploy_started(self):
1192
value = yaml.safe_dump({
1194
'0': {'agent-state': 'started'},
1199
'jenkins/1': {'baz': 'qux'}
1204
client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
1205
with patch.object(client, 'get_juju_output', return_value=value):
1206
client.wait_for_deploy_started()
1208
def test_wait_for_deploy_started_timeout(self):
1209
value = yaml.safe_dump({
1211
'0': {'agent-state': 'started'},
1215
client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
1216
with patch('jujupy.client.until_timeout', lambda x: range(0)):
1217
with patch.object(client, 'get_juju_output', return_value=value):
1218
with self.assertRaisesRegexp(
1220
'Timed out waiting for applications to start.'):
1221
client.wait_for_deploy_started()
1223
def test_wait_for_version(self):
1224
value = self.make_status_yaml('agent-version', '1.17.2', '1.17.2')
1225
client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
1226
with patch.object(client, 'get_juju_output', return_value=value):
1227
client.wait_for_version('1.17.2')
1229
def test_wait_for_version_timeout(self):
1230
value = self.make_status_yaml('agent-version', '1.17.2', '1.17.1')
1231
client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
1233
with patch('jujupy.client.until_timeout',
1234
lambda x, start=None: [x]):
1235
with patch.object(client, 'get_juju_output', return_value=value):
1236
with patch.object(GroupReporter, '_write', autospec=True,
1237
side_effect=lambda _, s: writes.append(s)):
1238
with self.assertRaisesRegexp(
1239
StatusNotMet, 'Some versions did not update'):
1240
client.wait_for_version('1.17.2')
1241
self.assertEqual(writes, ['1.17.1: jenkins/0', ' .', '\n'])
1243
def test_wait_for_version_handles_connection_error(self):
1244
err = subprocess.CalledProcessError(2, 'foo')
1245
err.stderr = 'Unable to connect to environment'
1246
err = CannotConnectEnv(err)
1247
status = self.make_status_yaml('agent-version', '1.17.2', '1.17.2')
1248
actions = [err, status]
1250
def get_juju_output_fake(*args, **kwargs):
1251
action = actions.pop(0)
1252
if isinstance(action, Exception):
1257
client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
1258
with patch.object(client, 'get_juju_output', get_juju_output_fake):
1259
client.wait_for_version('1.17.2')
1261
def test_wait_for_version_raises_non_connection_error(self):
1262
err = Exception('foo')
1263
status = self.make_status_yaml('agent-version', '1.17.2', '1.17.2')
1264
actions = [err, status]
1266
def get_juju_output_fake(*args, **kwargs):
1267
action = actions.pop(0)
1268
if isinstance(action, Exception):
1273
client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
1274
with patch.object(client, 'get_juju_output', get_juju_output_fake):
1275
with self.assertRaisesRegexp(Exception, 'foo'):
1276
client.wait_for_version('1.17.2')
1278
def test_wait_just_machine_0(self):
1279
value = yaml.safe_dump({
1281
'0': {'agent-state': 'started'},
1284
client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
1285
with patch.object(client, 'get_juju_output', return_value=value):
1286
client.wait_for(WaitMachineNotPresent('1'), quiet=True)
1288
def test_wait_just_machine_0_timeout(self):
1289
value = yaml.safe_dump({
1291
'0': {'agent-state': 'started'},
1292
'1': {'agent-state': 'started'},
1295
client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
1296
with patch.object(client, 'get_juju_output', return_value=value), \
1297
patch('jujupy.client.until_timeout',
1298
lambda x, start=None: range(1)), \
1299
self.assertRaisesRegexp(
1301
'Timed out waiting for machine removal 1'):
1302
client.wait_for(WaitMachineNotPresent('1'), quiet=True)
1304
def test_set_model_constraints(self):
1305
client = EnvJujuClient1X(SimpleEnvironment('bar', {}), None, '/foo')
1306
with patch.object(client, 'juju') as juju_mock:
1307
client.set_model_constraints({'bar': 'baz'})
1308
juju_mock.assert_called_once_with('set-constraints', ('bar=baz',))
1310
def test_get_model_config(self):
1311
env = SimpleEnvironment('foo', None)
1312
fake_popen = FakePopen(yaml.safe_dump({'bar': 'baz'}), None, 0)
1313
client = EnvJujuClient1X(env, None, 'juju')
1314
with patch('subprocess.Popen', return_value=fake_popen) as po_mock:
1315
result = client.get_model_config()
1317
self, po_mock, client, (
1318
'juju', '--show-log', 'get-env', '-e', 'foo'))
1319
self.assertEqual({'bar': 'baz'}, result)
1321
def test_get_env_option(self):
1322
env = SimpleEnvironment('foo', None)
1323
fake_popen = FakePopen('https://example.org/juju/tools', None, 0)
1324
client = EnvJujuClient1X(env, None, 'juju')
1325
with patch('subprocess.Popen', return_value=fake_popen) as mock:
1326
result = client.get_env_option('tools-metadata-url')
1328
mock.call_args[0][0],
1329
('juju', '--show-log', 'get-env', '-e', 'foo',
1330
'tools-metadata-url'))
1331
self.assertEqual('https://example.org/juju/tools', result)
1333
def test_set_env_option(self):
1334
env = SimpleEnvironment('foo')
1335
client = EnvJujuClient1X(env, None, 'juju')
1336
with patch('subprocess.check_call') as mock:
1337
client.set_env_option(
1338
'tools-metadata-url', 'https://example.org/juju/tools')
1339
environ = dict(os.environ)
1340
environ['JUJU_HOME'] = client.env.juju_home
1341
mock.assert_called_with(
1342
('juju', '--show-log', 'set-env', '-e', 'foo',
1343
'tools-metadata-url=https://example.org/juju/tools'))
1345
def test_unset_env_option(self):
1346
env = SimpleEnvironment('foo')
1347
client = EnvJujuClient1X(env, None, 'juju')
1348
with patch('subprocess.check_call') as mock:
1349
client.unset_env_option('tools-metadata-url')
1350
environ = dict(os.environ)
1351
environ['JUJU_HOME'] = client.env.juju_home
1352
mock.assert_called_with(
1353
('juju', '--show-log', 'set-env', '-e', 'foo',
1354
'tools-metadata-url='))
1357
def run_model_defaults_test(self, operation_name):
1358
env = SimpleEnvironment('foo', {'type': 'local'})
1359
client = EnvJujuClient1X(env, '1.23-series-arch', None)
1361
self.assertEqual('INFO No model-defaults stored for client '
1362
'(attempted {}).\n'.format(operation_name),
1363
self.log_stream.getvalue())
1365
def test_get_model_defaults(self):
1366
with self.run_model_defaults_test('get') as client:
1367
client.get_model_defaults('some-key')
1369
def test_set_model_defaults(self):
1370
with self.run_model_defaults_test('set') as client:
1371
client.set_model_defaults('some-key', 'some-value')
1373
def test_unset_model_defaults(self):
1374
with self.run_model_defaults_test('unset') as client:
1375
client.unset_model_defaults('some-key')
1377
def test_set_testing_agent_metadata_url(self):
1378
env = SimpleEnvironment(None, {'type': 'foo'})
1379
client = EnvJujuClient1X(env, None, None)
1380
with patch.object(client, 'get_env_option') as mock_get:
1381
mock_get.return_value = 'https://example.org/juju/tools'
1382
with patch.object(client, 'set_env_option') as mock_set:
1383
client.set_testing_agent_metadata_url()
1384
mock_get.assert_called_with('tools-metadata-url')
1385
mock_set.assert_called_with(
1386
'tools-metadata-url',
1387
'https://example.org/juju/testing/tools')
1389
def test_set_testing_agent_metadata_url_noop(self):
1390
env = SimpleEnvironment(None, {'type': 'foo'})
1391
client = EnvJujuClient1X(env, None, None)
1392
with patch.object(client, 'get_env_option') as mock_get:
1393
mock_get.return_value = 'https://example.org/juju/testing/tools'
1394
with patch.object(client, 'set_env_option') as mock_set:
1395
client.set_testing_agent_metadata_url()
1396
mock_get.assert_called_with('tools-metadata-url')
1397
self.assertEqual(0, mock_set.call_count)
1399
def test_juju(self):
1400
env = SimpleEnvironment('qux')
1401
client = EnvJujuClient1X(env, None, 'juju')
1402
with patch('subprocess.check_call') as mock:
1403
client.juju('foo', ('bar', 'baz'))
1404
environ = dict(os.environ)
1405
environ['JUJU_HOME'] = client.env.juju_home
1406
mock.assert_called_with(('juju', '--show-log', 'foo', '-e', 'qux',
1409
def test_juju_env(self):
1410
env = SimpleEnvironment('qux')
1411
client = EnvJujuClient1X(env, None, '/foobar/baz')
1413
def check_path(*args, **kwargs):
1414
self.assertRegexpMatches(os.environ['PATH'], r'/foobar\:')
1415
with patch('subprocess.check_call', side_effect=check_path):
1416
client.juju('foo', ('bar', 'baz'))
1418
def test_juju_no_check(self):
1419
env = SimpleEnvironment('qux')
1420
client = EnvJujuClient1X(env, None, 'juju')
1421
environ = dict(os.environ)
1422
environ['JUJU_HOME'] = client.env.juju_home
1423
with patch('subprocess.call') as mock:
1424
client.juju('foo', ('bar', 'baz'), check=False)
1425
mock.assert_called_with(('juju', '--show-log', 'foo', '-e', 'qux',
1428
def test_juju_no_check_env(self):
1429
env = SimpleEnvironment('qux')
1430
client = EnvJujuClient1X(env, None, '/foobar/baz')
1432
def check_path(*args, **kwargs):
1433
self.assertRegexpMatches(os.environ['PATH'], r'/foobar\:')
1434
with patch('subprocess.call', side_effect=check_path):
1435
client.juju('foo', ('bar', 'baz'), check=False)
1437
def test_juju_timeout(self):
1438
env = SimpleEnvironment('qux')
1439
client = EnvJujuClient1X(env, None, '/foobar/baz')
1440
with patch('subprocess.check_call') as cc_mock:
1441
client.juju('foo', ('bar', 'baz'), timeout=58)
1442
self.assertEqual(cc_mock.call_args[0][0], (
1443
sys.executable, get_timeout_path(), '58.00', '--', 'baz',
1444
'--show-log', 'foo', '-e', 'qux', 'bar', 'baz'))
1446
def test_juju_juju_home(self):
1447
env = SimpleEnvironment('qux')
1448
os.environ['JUJU_HOME'] = 'foo'
1449
client = EnvJujuClient1X(env, None, '/foobar/baz')
1451
def check_home(*args, **kwargs):
1452
self.assertEqual(os.environ['JUJU_HOME'], 'foo')
1454
self.assertEqual(os.environ['JUJU_HOME'], 'asdf')
1457
with patch('subprocess.check_call', side_effect=check_home):
1458
client.juju('foo', ('bar', 'baz'))
1459
client.env.juju_home = 'asdf'
1460
client.juju('foo', ('bar', 'baz'))
1462
def test_juju_extra_env(self):
1463
env = SimpleEnvironment('qux')
1464
client = EnvJujuClient1X(env, None, 'juju')
1465
extra_env = {'JUJU': '/juju', 'JUJU_HOME': client.env.juju_home}
1467
def check_env(*args, **kwargs):
1468
self.assertEqual('/juju', os.environ['JUJU'])
1470
with patch('subprocess.check_call', side_effect=check_env) as mock:
1471
client.juju('quickstart', ('bar', 'baz'), extra_env=extra_env)
1472
mock.assert_called_with(
1473
('juju', '--show-log', 'quickstart', '-e', 'qux', 'bar', 'baz'))
1475
def test_juju_backup_with_tgz(self):
1476
env = SimpleEnvironment('qux')
1477
client = EnvJujuClient1X(env, None, '/foobar/baz')
1479
def check_env(*args, **kwargs):
1480
self.assertEqual(os.environ['JUJU_ENV'], 'qux')
1481
return 'foojuju-backup-24.tgzz'
1482
with patch('subprocess.check_output',
1483
side_effect=check_env) as co_mock:
1484
backup_file = client.backup()
1485
self.assertEqual(backup_file, os.path.abspath('juju-backup-24.tgz'))
1486
assert_juju_call(self, co_mock, client, ['juju', 'backup'])
1488
def test_juju_backup_with_tar_gz(self):
1489
env = SimpleEnvironment('qux')
1490
client = EnvJujuClient1X(env, None, '/foobar/baz')
1491
with patch('subprocess.check_output',
1492
return_value='foojuju-backup-123-456.tar.gzbar'):
1493
backup_file = client.backup()
1495
backup_file, os.path.abspath('juju-backup-123-456.tar.gz'))
1497
def test_juju_backup_no_file(self):
1498
env = SimpleEnvironment('qux')
1499
client = EnvJujuClient1X(env, None, '/foobar/baz')
1500
with patch('subprocess.check_output', return_value=''):
1501
with self.assertRaisesRegexp(
1502
Exception, 'The backup file was not found in output'):
1505
def test_juju_backup_wrong_file(self):
1506
env = SimpleEnvironment('qux')
1507
client = EnvJujuClient1X(env, None, '/foobar/baz')
1508
with patch('subprocess.check_output',
1509
return_value='mumu-backup-24.tgz'):
1510
with self.assertRaisesRegexp(
1511
Exception, 'The backup file was not found in output'):
1514
def test_juju_backup_environ(self):
1515
env = SimpleEnvironment('qux')
1516
client = EnvJujuClient1X(env, None, '/foobar/baz')
1517
environ = client._shell_environ()
1518
environ['JUJU_ENV'] = client.env.environment
1520
def side_effect(*args, **kwargs):
1521
self.assertEqual(environ, os.environ)
1522
return 'foojuju-backup-123-456.tar.gzbar'
1523
with patch('subprocess.check_output', side_effect=side_effect):
1525
self.assertNotEqual(environ, os.environ)
1527
def test_restore_backup(self):
1528
env = SimpleEnvironment('qux')
1529
client = EnvJujuClient1X(env, None, '/foobar/baz')
1530
with patch.object(client, 'get_juju_output') as gjo_mock:
1531
result = client.restore_backup('quxx')
1532
gjo_mock.assert_called_once_with('restore', '--constraints',
1534
self.assertIs(gjo_mock.return_value, result)
1536
def test_restore_backup_async(self):
1537
env = SimpleEnvironment('qux')
1538
client = EnvJujuClient1X(env, None, '/foobar/baz')
1539
with patch.object(client, 'juju_async') as gjo_mock:
1540
result = client.restore_backup_async('quxx')
1541
gjo_mock.assert_called_once_with(
1542
'restore', ('--constraints', 'mem=2G', 'quxx'))
1543
self.assertIs(gjo_mock.return_value, result)
1545
def test_enable_ha(self):
1546
env = SimpleEnvironment('qux')
1547
client = EnvJujuClient1X(env, None, '/foobar/baz')
1548
with patch.object(client, 'juju', autospec=True) as eha_mock:
1550
eha_mock.assert_called_once_with('ensure-availability', ('-n', '3'))
1552
def test_juju_async(self):
1553
env = SimpleEnvironment('qux')
1554
client = EnvJujuClient1X(env, None, '/foobar/baz')
1555
with patch('subprocess.Popen') as popen_class_mock:
1556
with client.juju_async('foo', ('bar', 'baz')) as proc:
1557
assert_juju_call(self, popen_class_mock, client, (
1558
'baz', '--show-log', 'foo', '-e', 'qux', 'bar', 'baz'))
1559
self.assertIs(proc, popen_class_mock.return_value)
1560
self.assertEqual(proc.wait.call_count, 0)
1561
proc.wait.return_value = 0
1562
proc.wait.assert_called_once_with()
1564
def test_juju_async_failure(self):
1565
env = SimpleEnvironment('qux')
1566
client = EnvJujuClient1X(env, None, '/foobar/baz')
1567
with patch('subprocess.Popen') as popen_class_mock:
1568
with self.assertRaises(subprocess.CalledProcessError) as err_cxt:
1569
with client.juju_async('foo', ('bar', 'baz')):
1570
proc_mock = popen_class_mock.return_value
1571
proc_mock.wait.return_value = 23
1572
self.assertEqual(err_cxt.exception.returncode, 23)
1573
self.assertEqual(err_cxt.exception.cmd, (
1574
'baz', '--show-log', 'foo', '-e', 'qux', 'bar', 'baz'))
1576
def test_juju_async_environ(self):
1577
env = SimpleEnvironment('qux')
1578
client = EnvJujuClient1X(env, None, '/foobar/baz')
1579
environ = client._shell_environ()
1581
with patch('subprocess.Popen') as popen_class_mock:
1583
def check_environ(*args, **kwargs):
1584
self.assertEqual(environ, os.environ)
1586
popen_class_mock.side_effect = check_environ
1587
proc_mock.wait.return_value = 0
1588
with client.juju_async('foo', ('bar', 'baz')):
1590
self.assertNotEqual(environ, os.environ)
1592
def test_is_jes_enabled(self):
1593
env = SimpleEnvironment('qux')
1594
client = EnvJujuClient1X(env, None, '/foobar/baz')
1595
fake_popen = FakePopen(' %s' % SYSTEM, None, 0)
1596
with patch('subprocess.Popen',
1597
return_value=fake_popen) as po_mock:
1598
self.assertIsFalse(client.is_jes_enabled())
1599
self.assertEqual(0, po_mock.call_count)
1601
def test_get_jes_command(self):
1602
env = SimpleEnvironment('qux')
1603
client = EnvJujuClient1X(env, None, '/foobar/baz')
1604
# Juju 1.24 and older do not have a JES command. It is an error
1605
# to call get_jes_command when is_jes_enabled is False
1606
fake_popen = FakePopen(' %s' % SYSTEM, None, 0)
1607
with patch('subprocess.Popen',
1608
return_value=fake_popen) as po_mock:
1609
with self.assertRaises(JESNotSupported):
1610
client.get_jes_command()
1611
self.assertEqual(0, po_mock.call_count)
1613
def test_get_juju_timings(self):
1614
env = SimpleEnvironment('foo')
1615
client = EnvJujuClient1X(env, None, 'my/juju/bin')
1616
client._backend.juju_timings = {("juju", "op1"): [1],
1617
("juju", "op2"): [2]}
1618
flattened_timings = client.get_juju_timings()
1619
expected = {"juju op1": [1], "juju op2": [2]}
1620
self.assertEqual(flattened_timings, expected)
1622
def test_deploy_bundle_1x(self):
1623
client = EnvJujuClient1X(SimpleEnvironment('an_env', None),
1624
'1.23-series-arch', None)
1625
with patch.object(client, 'juju') as mock_juju:
1626
client.deploy_bundle('bundle:~juju-qa/some-bundle')
1627
mock_juju.assert_called_with(
1628
'deployer', ('--debug', '--deploy-delay', '10', '--timeout',
1629
'3600', '--config', 'bundle:~juju-qa/some-bundle'))
1631
def test_deploy_bundle_template(self):
1632
client = EnvJujuClient1X(SimpleEnvironment('an_env', None),
1633
'1.23-series-arch', None)
1634
with patch.object(client, 'juju') as mock_juju:
1635
client.deploy_bundle('bundle:~juju-qa/some-{container}-bundle')
1636
mock_juju.assert_called_with(
1638
'--debug', '--deploy-delay', '10', '--timeout', '3600',
1639
'--config', 'bundle:~juju-qa/some-lxc-bundle'))
1641
def test_deployer(self):
1642
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
1643
'1.23-series-arch', None)
1644
with patch.object(EnvJujuClient1X, 'juju') as mock:
1645
client.deployer('bundle:~juju-qa/some-bundle')
1646
mock.assert_called_with(
1647
'deployer', ('--debug', '--deploy-delay', '10', '--timeout',
1648
'3600', '--config', 'bundle:~juju-qa/some-bundle'))
1650
def test_deployer_template(self):
1651
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
1652
'1.23-series-arch', None)
1653
with patch.object(EnvJujuClient1X, 'juju') as mock:
1654
client.deployer('bundle:~juju-qa/some-{container}-bundle')
1655
mock.assert_called_with(
1657
'--debug', '--deploy-delay', '10', '--timeout', '3600',
1658
'--config', 'bundle:~juju-qa/some-lxc-bundle'))
1660
def test_deployer_with_bundle_name(self):
1661
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
1662
'1.23-series-arch', None)
1663
with patch.object(EnvJujuClient1X, 'juju') as mock:
1664
client.deployer('bundle:~juju-qa/some-bundle', 'name')
1665
mock.assert_called_with(
1666
'deployer', ('--debug', '--deploy-delay', '10', '--timeout',
1667
'3600', '--config', 'bundle:~juju-qa/some-bundle',
1670
def test_quickstart_maas(self):
1671
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'maas'}),
1672
'1.23-series-arch', '/juju')
1673
with patch.object(EnvJujuClient1X, 'juju') as mock:
1674
client.quickstart('bundle:~juju-qa/some-bundle')
1675
mock.assert_called_with(
1676
'quickstart', ('--constraints', 'mem=2G', '--no-browser',
1677
'bundle:~juju-qa/some-bundle'),
1678
extra_env={'JUJU': '/juju'})
1680
def test_quickstart_local(self):
1681
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
1682
'1.23-series-arch', '/juju')
1683
with patch.object(EnvJujuClient1X, 'juju') as mock:
1684
client.quickstart('bundle:~juju-qa/some-bundle')
1685
mock.assert_called_with(
1686
'quickstart', ('--constraints', 'mem=2G', '--no-browser',
1687
'bundle:~juju-qa/some-bundle'),
1688
extra_env={'JUJU': '/juju'})
1690
def test_quickstart_template(self):
1691
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
1692
'1.23-series-arch', '/juju')
1693
with patch.object(EnvJujuClient1X, 'juju') as mock:
1694
client.quickstart('bundle:~juju-qa/some-{container}-bundle')
1695
mock.assert_called_with(
1697
'--constraints', 'mem=2G', '--no-browser',
1698
'bundle:~juju-qa/some-lxc-bundle'),
1699
extra_env={'JUJU': '/juju'})
1701
def test_list_models(self):
1702
env = SimpleEnvironment('foo', {'type': 'local'})
1703
client = EnvJujuClient1X(env, '1.23-series-arch', None)
1704
client.list_models()
1706
'INFO The model is environment foo\n',
1707
self.log_stream.getvalue())
1709
def test__get_models(self):
1716
env = SimpleEnvironment('foo', {'type': 'local'})
1717
client = fake_juju_client(cls=EnvJujuClient1X, env=env)
1718
with patch.object(client, 'get_juju_output', return_value=data):
1719
models = client._get_models()
1721
[{'name': 'foo', 'model-uuid': 'aaaa'},
1722
{'name': 'bar', 'model-uuid': 'bbbb'}],
1725
def test__get_models_exception(self):
1726
env = SimpleEnvironment('foo', {'type': 'local'})
1727
client = fake_juju_client(cls=EnvJujuClient1X, env=env)
1728
with patch.object(client, 'get_juju_output',
1729
side_effect=subprocess.CalledProcessError('a', 'b')):
1730
self.assertEqual([], client._get_models())
1732
def test_get_models(self):
1733
env = SimpleEnvironment('foo', {'type': 'local'})
1734
client = EnvJujuClient1X(env, '1.23-series-arch', None)
1735
self.assertEqual({}, client.get_models())
1737
def test_iter_model_clients(self):
1746
client = EnvJujuClient1X(SimpleEnvironment('foo', {}), None, None)
1747
with patch.object(client, 'get_juju_output', return_value=data):
1748
model_clients = list(client.iter_model_clients())
1749
self.assertEqual(2, len(model_clients))
1750
self.assertIs(client, model_clients[0])
1751
self.assertEqual('bar', model_clients[1].env.environment)
1753
def test_get_controller_client(self):
1754
client = EnvJujuClient1X(SimpleEnvironment('foo'), {'bar': 'baz'},
1756
controller_client = client.get_controller_client()
1757
self.assertIs(client, controller_client)
1759
def test_list_controllers(self):
1760
env = SimpleEnvironment('foo', {'type': 'local'})
1761
client = EnvJujuClient1X(env, '1.23-series-arch', None)
1762
client.list_controllers()
1764
'INFO The controller is environment foo\n',
1765
self.log_stream.getvalue())
1767
def test_get_controller_model_name(self):
1768
env = SimpleEnvironment('foo', {'type': 'local'})
1769
client = EnvJujuClient1X(env, '1.23-series-arch', None)
1770
controller_name = client.get_controller_model_name()
1771
self.assertEqual('foo', controller_name)
1773
def test_get_controller_endpoint_ipv4(self):
1774
env = SimpleEnvironment('foo', {'type': 'local'})
1775
client = EnvJujuClient1X(env, '1.23-series-arch', None)
1776
with patch.object(client, 'get_juju_output',
1777
return_value='10.0.0.1:17070') as gjo_mock:
1778
endpoint = client.get_controller_endpoint()
1779
self.assertEqual(('10.0.0.1', '17070'), endpoint)
1780
gjo_mock.assert_called_once_with('api-endpoints')
1782
def test_get_controller_endpoint_ipv6(self):
1783
env = SimpleEnvironment('foo', {'type': 'local'})
1784
client = EnvJujuClient1X(env, '1.23-series-arch', None)
1785
with patch.object(client, 'get_juju_output',
1786
return_value='[::1]:17070') as gjo_mock:
1787
endpoint = client.get_controller_endpoint()
1788
self.assertEqual(('::1', '17070'), endpoint)
1789
gjo_mock.assert_called_once_with('api-endpoints')
1791
def test_action_do(self):
1792
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
1793
'1.23-series-arch', None)
1794
with patch.object(EnvJujuClient1X, 'get_juju_output') as mock:
1795
mock.return_value = \
1796
"Action queued with id: 5a92ec93-d4be-4399-82dc-7431dbfd08f9"
1797
id = client.action_do("foo/0", "myaction", "param=5")
1798
self.assertEqual(id, "5a92ec93-d4be-4399-82dc-7431dbfd08f9")
1799
mock.assert_called_once_with(
1800
'action do', 'foo/0', 'myaction', "param=5"
1803
def test_action_do_error(self):
1804
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
1805
'1.23-series-arch', None)
1806
with patch.object(EnvJujuClient1X, 'get_juju_output') as mock:
1807
mock.return_value = "some bad text"
1808
with self.assertRaisesRegexp(Exception,
1809
"Action id not found in output"):
1810
client.action_do("foo/0", "myaction", "param=5")
1812
def test_action_fetch(self):
1813
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
1814
'1.23-series-arch', None)
1815
with patch.object(EnvJujuClient1X, 'get_juju_output') as mock:
1816
ret = "status: completed\nfoo: bar"
1817
mock.return_value = ret
1818
out = client.action_fetch("123")
1819
self.assertEqual(out, ret)
1820
mock.assert_called_once_with(
1821
'action fetch', '123', "--wait", "1m"
1824
def test_action_fetch_timeout(self):
1825
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
1826
'1.23-series-arch', None)
1827
ret = "status: pending\nfoo: bar"
1828
with patch.object(EnvJujuClient1X,
1829
'get_juju_output', return_value=ret):
1830
with self.assertRaisesRegexp(Exception,
1831
"timed out waiting for action"):
1832
client.action_fetch("123")
1834
def test_action_do_fetch(self):
1835
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
1836
'1.23-series-arch', None)
1837
with patch.object(EnvJujuClient1X, 'get_juju_output') as mock:
1838
ret = "status: completed\nfoo: bar"
1839
# setting side_effect to an iterable will return the next value
1840
# from the list each time the function is called.
1841
mock.side_effect = [
1842
"Action queued with id: 5a92ec93-d4be-4399-82dc-7431dbfd08f9",
1844
out = client.action_do_fetch("foo/0", "myaction", "param=5")
1845
self.assertEqual(out, ret)
1848
env = SimpleEnvironment('name', {}, 'foo')
1849
client = fake_juju_client(cls=EnvJujuClient1X, env=env)
1852
"Stdout": "Linux\n",
1854
"Stderr": "Permission denied (publickey,password)"}]
1855
run_output = json.dumps(run_list)
1856
with patch.object(client._backend, 'get_juju_output',
1857
return_value=run_output) as gjo_mock:
1858
result = client.run(('wname',), applications=['foo', 'bar'])
1859
self.assertEqual(run_list, result)
1860
gjo_mock.assert_called_once_with(
1861
'run', ('--format', 'json', '--service', 'foo,bar', 'wname'),
1862
frozenset(['migration']),
1863
'foo', 'name', user_name=None)
1865
def test_list_space(self):
1866
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
1867
'1.23-series-arch', None)
1868
yaml_dict = {'foo': 'bar'}
1869
output = yaml.safe_dump(yaml_dict)
1870
with patch.object(client, 'get_juju_output', return_value=output,
1871
autospec=True) as gjo_mock:
1872
result = client.list_space()
1873
self.assertEqual(result, yaml_dict)
1874
gjo_mock.assert_called_once_with('space list')
1876
def test_add_space(self):
1877
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
1878
'1.23-series-arch', None)
1879
with patch.object(client, 'juju', autospec=True) as juju_mock:
1880
client.add_space('foo-space')
1881
juju_mock.assert_called_once_with('space create', ('foo-space'))
1883
def test_add_subnet(self):
1884
client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),
1885
'1.23-series-arch', None)
1886
with patch.object(client, 'juju', autospec=True) as juju_mock:
1887
client.add_subnet('bar-subnet', 'foo-space')
1888
juju_mock.assert_called_once_with('subnet add',
1889
('bar-subnet', 'foo-space'))
1891
def test__shell_environ_uses_pathsep(self):
1892
client = EnvJujuClient1X(SimpleEnvironment('foo'), None,
1894
with patch('os.pathsep', '!'):
1895
environ = client._shell_environ()
1896
self.assertRegexpMatches(environ['PATH'], r'foo/bar\!')
1898
def test_set_config(self):
1899
client = EnvJujuClient1X(SimpleEnvironment('bar', {}), None, '/foo')
1900
with patch.object(client, 'juju') as juju_mock:
1901
client.set_config('foo', {'bar': 'baz'})
1902
juju_mock.assert_called_once_with('set', ('foo', 'bar=baz'))
1904
def test_get_config(self):
1905
def output(*args, **kwargs):
1906
return yaml.safe_dump({
1912
'description': 'bla bla',
1914
'value': '/tmp/charm-dir',
1918
expected = yaml.safe_load(output())
1919
client = EnvJujuClient1X(SimpleEnvironment('bar', {}), None, '/foo')
1920
with patch.object(client, 'get_juju_output',
1921
side_effect=output) as gjo_mock:
1922
results = client.get_config('foo')
1923
self.assertEqual(expected, results)
1924
gjo_mock.assert_called_once_with('get', 'foo')
1926
def test_get_service_config(self):
1927
def output(*args, **kwargs):
1928
return yaml.safe_dump({
1934
'description': 'bla bla',
1936
'value': '/tmp/charm-dir',
1940
expected = yaml.safe_load(output())
1941
client = EnvJujuClient1X(SimpleEnvironment('bar', {}), None, '/foo')
1942
with patch.object(client, 'get_juju_output', side_effect=output):
1943
results = client.get_service_config('foo')
1944
self.assertEqual(expected, results)
1946
def test_get_service_config_timesout(self):
1947
client = EnvJujuClient1X(SimpleEnvironment('foo', {}), None, '/foo')
1948
with patch('jujupy.client.until_timeout', return_value=range(0)):
1949
with self.assertRaisesRegexp(
1950
Exception, 'Timed out waiting for juju get'):
1951
client.get_service_config('foo')
1953
def test_ssh_keys(self):
1954
client = EnvJujuClient1X(SimpleEnvironment('foo', {}), None, None)
1955
given_output = 'ssh keys output'
1956
with patch.object(client, 'get_juju_output', autospec=True,
1957
return_value=given_output) as mock:
1958
output = client.ssh_keys()
1959
self.assertEqual(output, given_output)
1960
mock.assert_called_once_with('authorized-keys list')
1962
def test_ssh_keys_full(self):
1963
client = EnvJujuClient1X(SimpleEnvironment('foo', {}), None, None)
1964
given_output = 'ssh keys full output'
1965
with patch.object(client, 'get_juju_output', autospec=True,
1966
return_value=given_output) as mock:
1967
output = client.ssh_keys(full=True)
1968
self.assertEqual(output, given_output)
1969
mock.assert_called_once_with('authorized-keys list', '--full')
1971
def test_add_ssh_key(self):
1972
client = EnvJujuClient1X(SimpleEnvironment('foo', {}), None, None)
1973
with patch.object(client, 'get_juju_output', autospec=True,
1974
return_value='') as mock:
1975
output = client.add_ssh_key('ak', 'bk')
1976
self.assertEqual(output, '')
1977
mock.assert_called_once_with(
1978
'authorized-keys add', 'ak', 'bk', merge_stderr=True)
1980
def test_remove_ssh_key(self):
1981
client = EnvJujuClient1X(SimpleEnvironment('foo', {}), None, None)
1982
with patch.object(client, 'get_juju_output', autospec=True,
1983
return_value='') as mock:
1984
output = client.remove_ssh_key('ak', 'bk')
1985
self.assertEqual(output, '')
1986
mock.assert_called_once_with(
1987
'authorized-keys delete', 'ak', 'bk', merge_stderr=True)
1989
def test_import_ssh_key(self):
1990
client = EnvJujuClient1X(SimpleEnvironment('foo', {}), None, None)
1991
with patch.object(client, 'get_juju_output', autospec=True,
1992
return_value='') as mock:
1993
output = client.import_ssh_key('gh:au', 'lp:bu')
1994
self.assertEqual(output, '')
1995
mock.assert_called_once_with(
1996
'authorized-keys import', 'gh:au', 'lp:bu', merge_stderr=True)
1998
def test_disable_commands_properties(self):
1999
client = EnvJujuClient1X(SimpleEnvironment('foo'), None, None)
2001
'destroy-environment', client.command_set_destroy_model)
2002
self.assertEqual('remove-object', client.command_set_remove_object)
2003
self.assertEqual('all-changes', client.command_set_all)
2005
def test_list_disabled_commands(self):
2006
client = EnvJujuClient1X(SimpleEnvironment('foo'), None, None)
2007
with patch.object(client, 'get_juju_output', autospec=True,
2008
return_value=dedent("""\
2009
- command-set: destroy-model
2010
message: Lock Models
2011
- command-set: remove-object""")) as mock:
2012
output = client.list_disabled_commands()
2013
self.assertEqual([{'command-set': 'destroy-model',
2014
'message': 'Lock Models'},
2015
{'command-set': 'remove-object'}], output)
2016
mock.assert_called_once_with('block list',
2019
def test_disable_command(self):
2020
client = EnvJujuClient1X(SimpleEnvironment('foo'), None, None)
2021
with patch.object(client, 'juju', autospec=True) as mock:
2022
client.disable_command('all', 'message')
2023
mock.assert_called_once_with('block all', ('message', ))
2025
def test_enable_command(self):
2026
client = EnvJujuClient1X(SimpleEnvironment('foo'), None, None)
2027
with patch.object(client, 'juju', autospec=True) as mock:
2028
client.enable_command('all')
2029
mock.assert_called_once_with('unblock', 'all')
2032
class TestEnvJujuClient25(ClientTest):
2034
client_class = EnvJujuClient25
2036
def test_enable_jes(self):
2037
client = self.client_class(
2038
SimpleEnvironment('baz', {}),
2039
'1.25-foobar', 'path')
2040
with self.assertRaises(JESNotSupported):
2043
def test_disable_jes(self):
2044
client = self.client_class(
2045
SimpleEnvironment('baz', {}),
2046
'1.25-foobar', 'path')
2047
client.feature_flags.add('jes')
2048
client.disable_jes()
2049
self.assertNotIn('jes', client.feature_flags)
2051
def test_clone_unchanged(self):
2052
client1 = self.client_class(
2053
SimpleEnvironment('foo'), '1.27', 'full/path', debug=True)
2054
client2 = client1.clone()
2055
self.assertIsNot(client1, client2)
2056
self.assertIs(type(client1), type(client2))
2057
self.assertIs(client1.env, client2.env)
2058
self.assertEqual(client1.version, client2.version)
2059
self.assertEqual(client1.full_path, client2.full_path)
2060
self.assertIs(client1.debug, client2.debug)
2061
self.assertEqual(client1._backend, client2._backend)
2063
def test_clone_changed(self):
2064
client1 = self.client_class(
2065
SimpleEnvironment('foo'), '1.27', 'full/path', debug=True)
2066
env2 = SimpleEnvironment('bar')
2067
client2 = client1.clone(env2, '1.28', 'other/path', debug=False,
2068
cls=EnvJujuClient1X)
2069
self.assertIs(EnvJujuClient1X, type(client2))
2070
self.assertIs(env2, client2.env)
2071
self.assertEqual('1.28', client2.version)
2072
self.assertEqual('other/path', client2.full_path)
2073
self.assertIs(False, client2.debug)
2075
def test_clone_defaults(self):
2076
client1 = self.client_class(
2077
SimpleEnvironment('foo'), '1.27', 'full/path', debug=True)
2078
client2 = client1.clone()
2079
self.assertIsNot(client1, client2)
2080
self.assertIs(self.client_class, type(client2))
2081
self.assertEqual(set(), client2.feature_flags)
2084
class TestEnvJujuClient22(ClientTest):
2086
client_class = EnvJujuClient22
2088
def test__shell_environ(self):
2089
client = self.client_class(
2090
SimpleEnvironment('baz', {'type': 'ec2'}), '1.22-foobar', 'path')
2091
env = client._shell_environ()
2092
self.assertEqual(env.get(JUJU_DEV_FEATURE_FLAGS), 'actions')
2094
def test__shell_environ_juju_home(self):
2095
client = self.client_class(
2096
SimpleEnvironment('baz', {'type': 'ec2'}), '1.22-foobar', 'path',
2098
env = client._shell_environ()
2099
self.assertEqual(env['JUJU_HOME'], 'asdf')
2102
class TestEnvJujuClient24(ClientTest):
2104
client_class = EnvJujuClient24
2106
def test_no_jes(self):
2107
client = self.client_class(
2108
SimpleEnvironment('baz', {}),
2109
'1.25-foobar', 'path')
2110
with self.assertRaises(JESNotSupported):
2112
client._use_jes = True
2113
env = client._shell_environ()
2114
self.assertNotIn('jes', env.get(JUJU_DEV_FEATURE_FLAGS, '').split(","))
2116
def test_add_ssh_machines(self):
2117
client = self.client_class(SimpleEnvironment('foo', {}), None, 'juju')
2118
with patch('subprocess.check_call', autospec=True) as cc_mock:
2119
client.add_ssh_machines(['m-foo', 'm-bar', 'm-baz'])
2120
assert_juju_call(self, cc_mock, client, (
2121
'juju', '--show-log', 'add-machine', '-e', 'foo', 'ssh:m-foo'), 0)
2122
assert_juju_call(self, cc_mock, client, (
2123
'juju', '--show-log', 'add-machine', '-e', 'foo', 'ssh:m-bar'), 1)
2124
assert_juju_call(self, cc_mock, client, (
2125
'juju', '--show-log', 'add-machine', '-e', 'foo', 'ssh:m-baz'), 2)
2126
self.assertEqual(cc_mock.call_count, 3)
2128
def test_add_ssh_machines_no_retry(self):
2129
client = self.client_class(SimpleEnvironment('foo', {}), None, 'juju')
2130
with patch('subprocess.check_call', autospec=True,
2131
side_effect=[subprocess.CalledProcessError(None, None),
2132
None, None, None]) as cc_mock:
2133
with self.assertRaises(subprocess.CalledProcessError):
2134
client.add_ssh_machines(['m-foo', 'm-bar', 'm-baz'])
2135
assert_juju_call(self, cc_mock, client, (
2136
'juju', '--show-log', 'add-machine', '-e', 'foo', 'ssh:m-foo'))
2139
class TestStatus1X(FakeHomeTestCase):
2141
def test_model_name(self):
2142
status = Status1X({'environment': 'bar'}, '')
2143
self.assertEqual('bar', status.model_name)
2145
def test_get_applications_gets_services(self):
2147
'services': {'service': {}},
2148
'applications': {'application': {}},
2150
self.assertEqual({'service': {}}, status.get_applications())
2152
def test_condense_status(self):
2153
status = Status1X({}, '')
2154
self.assertEqual(status.condense_status(
2155
{'agent-state': 'started',
2156
'agent-state-info': 'all good',
2157
'agent-version': '1.25.1'}),
2158
{'current': 'started', 'message': 'all good',
2159
'version': '1.25.1'})
2161
def test_condense_status_no_info(self):
2162
status = Status1X({}, '')
2163
self.assertEqual(status.condense_status(
2164
{'agent-state': 'started',
2165
'agent-version': '1.25.1'}),
2166
{'current': 'started', 'version': '1.25.1'})
2169
def run_iter_status():
2171
'environment': 'fake-unit-test',
2174
'agent-state': 'started',
2175
'agent-state-info': 'all good',
2176
'agent-version': '1.25.1',
2183
'agent-state': 'started',
2184
'agent-version': '1.25.1',
2187
'workload-status': {
2188
'current': 'active',
2191
'current': 'executing',
2193
'agent-state': 'started',
2194
'agent-version': '1.25.1',
2200
'current': 'active',
2204
'agent-state': 'started',
2205
'agent-version': '1.25.1',
2211
for sub_status in status.iter_status():
2214
def test_iter_status_range(self):
2215
status_set = set([(status_item.item_name, status_item.status_name,
2216
status_item.current)
2217
for status_item in self.run_iter_status()])
2218
APP = StatusItem.APPLICATION
2219
WORK = StatusItem.WORKLOAD
2220
JUJU = StatusItem.JUJU
2222
('0', JUJU, 'started'), ('dummy-sink/0', JUJU, 'started'),
2223
('dummy-sink/1', JUJU, 'executing'),
2224
('dummy-sink/1', WORK, 'active'), ('dummy-source', APP, 'active'),
2225
('dummy-source/0', JUJU, 'started'),
2228
def test_iter_status_data(self):
2229
iterator = self.run_iter_status()
2230
self.assertEqual(iterator.next().status,
2231
dict(current='started', message='all good',
2235
def fast_timeout(count):