~ankatare/juju-ci-tools/juju-aws-add-credential

« back to all changes in this revision

Viewing changes to jujupy/tests/test_version_client.py

  • Committer: ankatare at hotmail
  • Date: 2017-01-31 03:53:09 UTC
  • mfrom: (1837.1.30 trunk)
  • Revision ID: ankatare@hotmail.com-20170131035309-8dmn8d86opr9yorl
merge trunck

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
from contextlib import contextmanager
 
2
import copy
 
3
from datetime import (
 
4
    datetime,
 
5
    timedelta,
 
6
    )
 
7
import json
 
8
import os
 
9
import subprocess
 
10
import sys
 
11
from textwrap import dedent
 
12
 
 
13
from mock import (
 
14
    call,
 
15
    Mock,
 
16
    patch,
 
17
    )
 
18
import yaml
 
19
 
 
20
from jujuconfig import (
 
21
    get_jenv_path,
 
22
    )
 
23
from jujupy import (
 
24
    AuthNotAccepted,
 
25
    fake_juju_client,
 
26
    InvalidEndpoint,
 
27
    JujuData,
 
28
    JUJU_DEV_FEATURE_FLAGS,
 
29
    JESNotSupported,
 
30
    ModelClient,
 
31
    NameNotAccepted,
 
32
    SimpleEnvironment,
 
33
    Status,
 
34
    _temp_env as temp_env,
 
35
    TypeNotAccepted,
 
36
    )
 
37
from jujupy.client import (
 
38
    CannotConnectEnv,
 
39
    GroupReporter,
 
40
    StatusItem,
 
41
    StatusNotMet,
 
42
    SYSTEM,
 
43
    UpgradeMongoNotSupported,
 
44
    WaitMachineNotPresent,
 
45
    )
 
46
from jujupy.fake import (
 
47
    FakeBackend2_1,
 
48
    )
 
49
from jujupy.version_client import (
 
50
    BootstrapMismatch,
 
51
    client_from_config,
 
52
    EnvJujuClient1X,
 
53
    EnvJujuClient22,
 
54
    EnvJujuClient24,
 
55
    EnvJujuClient25,
 
56
    ModelClientRC,
 
57
    IncompatibleConfigClass,
 
58
    Juju1XBackend,
 
59
    ModelClient2_0,
 
60
    ModelClient2_1,
 
61
    VersionNotTestedError,
 
62
    Status1X,
 
63
    )
 
64
from jujupy.tests.test_client import (
 
65
    ClientTest,
 
66
    )
 
67
from tests import (
 
68
    assert_juju_call,
 
69
    FakeHomeTestCase,
 
70
    FakePopen,
 
71
    observable_temp_file,
 
72
    TestCase
 
73
    )
 
74
from utility import (
 
75
    get_timeout_path,
 
76
    )
 
77
 
 
78
 
 
79
class TestJuju1XBackend(TestCase):
 
80
 
 
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',
 
85
                          'commands'), full)
 
86
 
 
87
 
 
88
class TestClientFromConfig(ClientTest):
 
89
 
 
90
    @contextmanager
 
91
    def assertRaisesVersionNotTested(self, version):
 
92
        with self.assertRaisesRegexp(
 
93
                VersionNotTestedError, 'juju ' + version):
 
94
            yield
 
95
 
 
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():
 
102
            yield '1.17'
 
103
            yield '1.16'
 
104
            yield '1.16.1'
 
105
            yield '1.15'
 
106
            yield '1.22.1'
 
107
            yield '1.24-alpha1'
 
108
            yield '1.24.7'
 
109
            yield '1.25.1'
 
110
            yield '1.26.1'
 
111
            yield '1.27.1'
 
112
            yield '2.0-alpha1'
 
113
            yield '2.0-alpha2'
 
114
            yield '2.0-alpha3'
 
115
            yield '2.0-beta1'
 
116
            yield '2.0-beta2'
 
117
            yield '2.0-beta3'
 
118
            yield '2.0-beta4'
 
119
            yield '2.0-beta5'
 
120
            yield '2.0-beta6'
 
121
            yield '2.0-beta7'
 
122
            yield '2.0-beta8'
 
123
            yield '2.0-beta9'
 
124
            yield '2.0-beta10'
 
125
            yield '2.0-beta11'
 
126
            yield '2.0-beta12'
 
127
            yield '2.0-beta13'
 
128
            yield '2.0-beta14'
 
129
            yield '2.0-beta15'
 
130
            yield '2.0-rc1'
 
131
            yield '2.0-rc2'
 
132
            yield '2.0-rc3'
 
133
            yield '2.0-delta1'
 
134
            yield '2.1.0'
 
135
            yield '2.2.0'
 
136
 
 
137
        context = patch.object(
 
138
            ModelClient, 'get_version',
 
139
            side_effect=juju_cmd_iterator().send)
 
140
        with context:
 
141
            self.assertIs(EnvJujuClient1X,
 
142
                          type(client_from_config('foo', None)))
 
143
 
 
144
            def test_fc(version, cls):
 
145
                if cls is not None:
 
146
                    client = client_from_config('foo', None)
 
147
                    if isinstance(client, EnvJujuClient1X):
 
148
                        self.assertEqual(se_fc_mock.return_value, client.env)
 
149
                    else:
 
150
                        self.assertEqual(jd_fc_mock.return_value, client.env)
 
151
                    self.assertIs(cls, type(client))
 
152
                    self.assertEqual(version, client.version)
 
153
                else:
 
154
                    with self.assertRaisesVersionNotTested(version):
 
155
                        client_from_config('foo', None)
 
156
 
 
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)
 
192
 
 
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'))
 
200
 
 
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)
 
208
 
 
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)
 
217
 
 
218
 
 
219
class TestModelClient2_1(ClientTest):
 
220
 
 
221
    client_version = '2.1.0'
 
222
 
 
223
    client_class = ModelClient2_1
 
224
 
 
225
    fake_backend_class = FakeBackend2_1
 
226
 
 
227
    def test_basics(self):
 
228
        self.check_basics()
 
229
 
 
230
    def test_add_cloud_interactive_maas(self):
 
231
        client = self.fake_juju_client()
 
232
        clouds = {'foo': {
 
233
            'type': 'maas',
 
234
            'endpoint': 'http://bar.example.com',
 
235
            }}
 
236
        client.add_cloud_interactive('foo', clouds['foo'])
 
237
        self.assertEqual(client._backend.clouds, clouds)
 
238
 
 
239
    def test_add_cloud_interactive_maas_invalid_endpoint(self):
 
240
        client = self.fake_juju_client()
 
241
        clouds = {'foo': {
 
242
            'type': 'maas',
 
243
            'endpoint': 'B' * 4000,
 
244
            }}
 
245
        with self.assertRaises(InvalidEndpoint):
 
246
            client.add_cloud_interactive('foo', clouds['foo'])
 
247
 
 
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)
 
253
 
 
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'])
 
259
 
 
260
    def get_openstack_clouds(self):
 
261
        return {'foo': {
 
262
            'type': 'openstack',
 
263
            'endpoint': 'http://bar.example.com',
 
264
            'auth-types': ['oauth1', 'oauth12'],
 
265
            'regions': {
 
266
                'harvey': {'endpoint': 'http://harvey.example.com'},
 
267
                'steve': {'endpoint': 'http://steve.example.com'},
 
268
                }
 
269
            }}
 
270
 
 
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)
 
276
 
 
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'])
 
283
 
 
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'])
 
290
 
 
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'])
 
297
 
 
298
    def test_add_cloud_interactive_vsphere(self):
 
299
        client = self.fake_juju_client()
 
300
        clouds = {'foo': {
 
301
            'type': 'vsphere',
 
302
            'endpoint': 'http://bar.example.com',
 
303
            'regions': {
 
304
                'harvey': {},
 
305
                'steve': {},
 
306
                }
 
307
            }}
 
308
        client.add_cloud_interactive('foo', clouds['foo'])
 
309
        self.assertEqual(client._backend.clouds, clouds)
 
310
 
 
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'])
 
316
 
 
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)
 
322
 
 
323
 
 
324
class TestModelClientRC(ClientTest):
 
325
 
 
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)
 
331
                client.bootstrap()
 
332
                mock.assert_called_with(
 
333
                    'bootstrap', ('--constraints', 'mem=2G',
 
334
                                  'foo', 'bar/baz',
 
335
                                  '--config', config_file.name,
 
336
                                  '--default-model', 'foo',
 
337
                                  '--agent-version', '2.0'), include_e=False)
 
338
                config_file.seek(0)
 
339
                config = yaml.safe_load(config_file)
 
340
        self.assertEqual({'test-mode': True}, config)
 
341
 
 
342
 
 
343
class TestEnvJujuClient1X(ClientTest):
 
344
 
 
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')
 
351
 
 
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)
 
356
 
 
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()
 
368
 
 
369
    def test_get_version(self):
 
370
        value = ' 5.6 \n'
 
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'))
 
375
 
 
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'))
 
380
 
 
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(
 
386
                         no_build=True))
 
387
        client = client.clone(version='1.20-beta1-series-arch')
 
388
        self.assertEqual('1.20-beta1.1', client.get_matching_agent_version())
 
389
 
 
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'))
 
397
 
 
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',))
 
405
 
 
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',))
 
413
 
 
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()
 
419
 
 
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())
 
425
 
 
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):
 
431
                client.bootstrap()
 
432
        mock.assert_called_once_with('bootstrap', ('--constraints', 'mem=2G'))
 
433
 
 
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):
 
439
                client.bootstrap()
 
440
        mock.assert_called_once_with(
 
441
            client, 'bootstrap', ('--constraints', 'mem=2G cpu-cores=1'))
 
442
 
 
443
    def test_bootstrap(self):
 
444
        env = SimpleEnvironment('foo')
 
445
        client = EnvJujuClient1X(env, None, None)
 
446
        with patch.object(client, 'juju') as mock:
 
447
            client.bootstrap()
 
448
        mock.assert_called_with('bootstrap', ('--constraints', 'mem=2G'))
 
449
 
 
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'))
 
457
 
 
458
    def test_bootstrap_args(self):
 
459
        env = SimpleEnvironment('foo', {})
 
460
        client = EnvJujuClient1X(env, None, None)
 
461
        with self.assertRaisesRegexp(
 
462
                BootstrapMismatch,
 
463
                '--bootstrap-series angsty does not match default-series:'
 
464
                ' None'):
 
465
            client.bootstrap(bootstrap_series='angsty')
 
466
        env.update_config({
 
467
            'default-series': 'angsty',
 
468
            })
 
469
        with patch.object(client, 'juju') as mock:
 
470
            client.bootstrap(bootstrap_series='angsty')
 
471
        mock.assert_called_with('bootstrap', ('--constraints', 'mem=2G'))
 
472
 
 
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'))
 
481
 
 
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',
 
489
                                          'mem=2G'))
 
490
 
 
491
    def test_get_bootstrap_args_bootstrap_series(self):
 
492
        env = SimpleEnvironment('foo', {})
 
493
        client = EnvJujuClient1X(env, None, None)
 
494
        with self.assertRaisesRegexp(
 
495
                BootstrapMismatch,
 
496
                '--bootstrap-series angsty does not match default-series:'
 
497
                ' None'):
 
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'))
 
504
 
 
505
    def test_create_environment_system(self):
 
506
        self.do_create_environment(
 
507
            'system', 'system create-environment', ('-s', 'foo'))
 
508
 
 
509
    def test_create_environment_controller(self):
 
510
        self.do_create_environment(
 
511
            'controller', 'controller create-environment', ('-c', 'foo'))
 
512
 
 
513
    def test_create_environment_hypenated_controller(self):
 
514
        self.do_create_environment(
 
515
            'kill-controller', 'create-environment', ('-c', 'foo'))
 
516
 
 
517
    def do_create_environment(self, jes_command, create_cmd,
 
518
                              controller_option):
 
519
        controller_client = EnvJujuClient1X(SimpleEnvironment('foo'), '1.26.1',
 
520
                                            None)
 
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)
 
530
 
 
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)
 
539
 
 
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)
 
548
 
 
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)
 
557
 
 
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)
 
566
 
 
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))
 
575
                open(jenv_path, 'w')
 
576
                self.assertTrue(os.path.exists(jenv_path))
 
577
                client.destroy_environment(delete_jenv=True)
 
578
                self.assertFalse(os.path.exists(jenv_path))
 
579
 
 
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)
 
588
 
 
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)
 
597
 
 
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)
 
606
 
 
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)
 
615
 
 
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'),),
 
624
                         mock.call_args[0])
 
625
 
 
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])
 
635
 
 
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!')
 
645
 
 
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')
 
653
        self.assertEqual(
 
654
            ('juju', '--show-log', 'bar', '-e', 'foo', '--baz', 'qux'),
 
655
            exc.exception.cmd)
 
656
 
 
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)
 
663
        self.assertEqual(
 
664
            po_mock.call_args[0][0],
 
665
            (sys.executable, get_timeout_path(), '5.00', '--', 'juju',
 
666
             '--show-log', 'bar', '-e', 'foo'))
 
667
 
 
668
    def test__shell_environ_juju_home(self):
 
669
        client = EnvJujuClient1X(
 
670
            SimpleEnvironment('baz', {'type': 'ec2'}), '1.25-foobar', 'path',
 
671
            'asdf')
 
672
        env = client._shell_environ()
 
673
        self.assertEqual(env['JUJU_HOME'], 'asdf')
 
674
        self.assertNotIn('JUJU_DATA', env)
 
675
 
 
676
    def test_juju_output_supplies_path(self):
 
677
        env = SimpleEnvironment('foo')
 
678
        client = EnvJujuClient1X(env, None, '/foobar/bar')
 
679
 
 
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')
 
686
 
 
687
    def test_get_status(self):
 
688
        output_text = dedent("""\
 
689
                - a
 
690
                - b
 
691
                - c
 
692
                """)
 
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)
 
702
 
 
703
    def test_get_status_retries_on_error(self):
 
704
        env = SimpleEnvironment('foo')
 
705
        client = EnvJujuClient1X(env, None, None)
 
706
        client.attempt = 0
 
707
 
 
708
        def get_juju_output(command, *args, **kwargs):
 
709
            if client.attempt == 1:
 
710
                return '"hello"'
 
711
            client.attempt += 1
 
712
            raise subprocess.CalledProcessError(1, command)
 
713
 
 
714
        with patch.object(client, 'get_juju_output', get_juju_output):
 
715
            client.get_status()
 
716
 
 
717
    def test_get_status_raises_on_timeout_1(self):
 
718
        env = SimpleEnvironment('foo')
 
719
        client = EnvJujuClient1X(env, None, None)
 
720
 
 
721
        def get_juju_output(command, *args, **kwargs):
 
722
            raise subprocess.CalledProcessError(1, command)
 
723
 
 
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'):
 
730
                    client.get_status()
 
731
 
 
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)
 
742
 
 
743
    def test_get_status_controller(self):
 
744
        output_text = """\
 
745
            - a
 
746
            - b
 
747
            - c
 
748
        """
 
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)
 
756
 
 
757
    @staticmethod
 
758
    def make_status_yaml(key, machine_value, unit_value):
 
759
        return dedent("""\
 
760
            environment: foo
 
761
            machines:
 
762
              "0":
 
763
                {0}: {1}
 
764
            services:
 
765
              jenkins:
 
766
                units:
 
767
                  jenkins/0:
 
768
                    {0}: {2}
 
769
        """.format(key, machine_value, unit_value))
 
770
 
 
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',))
 
777
 
 
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',))
 
784
 
 
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'))
 
792
 
 
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'))
 
800
 
 
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',))
 
808
 
 
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',
 
817
                              '/bar/repository',))
 
818
 
 
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',))
 
825
 
 
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))
 
832
        self.assertEqual(
 
833
            [r.status for r in result], [Status.from_text(status_txt).status])
 
834
 
 
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)
 
840
 
 
841
        def until_timeout_stub(timeout, start=None):
 
842
            return iter([None, None])
 
843
 
 
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))
 
848
        self.assertEqual(
 
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)])
 
853
 
 
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)
 
865
 
 
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)
 
882
 
 
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
 
887
                ]) as cc_mock:
 
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)
 
895
 
 
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)
 
908
 
 
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()
 
914
 
 
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):
 
921
                writes = []
 
922
                with patch.object(GroupReporter, '_write', autospec=True,
 
923
                                  side_effect=lambda _, s: writes.append(s)):
 
924
                    with self.assertRaisesRegexp(
 
925
                            StatusNotMet,
 
926
                            'Timed out waiting for agents to start in local'):
 
927
                        client.wait_for_started()
 
928
                self.assertEqual(writes, ['pending: 0', ' .', '\n'])
 
929
 
 
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):
 
936
                writes = []
 
937
                with patch.object(GroupReporter, '_write', autospec=True,
 
938
                                  side_effect=lambda _, s: writes.append(s)):
 
939
                    with self.assertRaisesRegexp(
 
940
                            StatusNotMet,
 
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'])
 
944
 
 
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):
 
949
            writes = []
 
950
            with patch.object(GroupReporter, '_write', autospec=True,
 
951
                              side_effect=lambda _, s: writes.append(s)):
 
952
                with self.assertRaisesRegexp(
 
953
                        StatusNotMet,
 
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)
 
958
 
 
959
    def test_wait_for_subordinate_units(self):
 
960
        value = dedent("""\
 
961
            machines:
 
962
              "0":
 
963
                agent-state: started
 
964
            services:
 
965
              jenkins:
 
966
                units:
 
967
                  jenkins/0:
 
968
                    subordinates:
 
969
                      sub1/0:
 
970
                        agent-state: started
 
971
              ubuntu:
 
972
                units:
 
973
                  ubuntu/0:
 
974
                    subordinates:
 
975
                      sub2/0:
 
976
                        agent-state: started
 
977
                      sub3/0:
 
978
                        agent-state: started
 
979
        """)
 
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):
 
984
                with patch(
 
985
                        'jujupy.client.GroupReporter.update') as update_mock:
 
986
                    with patch(
 
987
                            'jujupy.client.GroupReporter.finish'
 
988
                            ) as finish_mock:
 
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()
 
993
 
 
994
    def test_wait_for_multiple_subordinate_units(self):
 
995
        value = dedent("""\
 
996
            machines:
 
997
              "0":
 
998
                agent-state: started
 
999
            services:
 
1000
              ubuntu:
 
1001
                units:
 
1002
                  ubuntu/0:
 
1003
                    subordinates:
 
1004
                      sub/0:
 
1005
                        agent-state: started
 
1006
                  ubuntu/1:
 
1007
                    subordinates:
 
1008
                      sub/1:
 
1009
                        agent-state: started
 
1010
        """)
 
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):
 
1015
                with patch(
 
1016
                        'jujupy.client.GroupReporter.update') as update_mock:
 
1017
                    with patch(
 
1018
                            'jujupy.client.GroupReporter.finish'
 
1019
                            ) as finish_mock:
 
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()
 
1024
 
 
1025
    def test_wait_for_subordinate_units_checks_slash_in_unit_name(self):
 
1026
        value = dedent("""\
 
1027
            machines:
 
1028
              "0":
 
1029
                agent-state: started
 
1030
            services:
 
1031
              jenkins:
 
1032
                units:
 
1033
                  jenkins/0:
 
1034
                    subordinates:
 
1035
                      sub1:
 
1036
                        agent-state: started
 
1037
        """)
 
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(
 
1043
                        StatusNotMet,
 
1044
                        'Timed out waiting for agents to start in local'):
 
1045
                    client.wait_for_subordinate_units(
 
1046
                        'jenkins', 'sub1', start=now - timedelta(1200))
 
1047
 
 
1048
    def test_wait_for_subordinate_units_no_subordinate(self):
 
1049
        value = dedent("""\
 
1050
            machines:
 
1051
              "0":
 
1052
                agent-state: started
 
1053
            services:
 
1054
              jenkins:
 
1055
                units:
 
1056
                  jenkins/0:
 
1057
                    agent-state: started
 
1058
        """)
 
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(
 
1064
                        StatusNotMet,
 
1065
                        'Timed out waiting for agents to start in local'):
 
1066
                    client.wait_for_subordinate_units(
 
1067
                        'jenkins', 'sub1', start=now - timedelta(1200))
 
1068
 
 
1069
    def test_wait_for_workload(self):
 
1070
        initial_status = Status1X.from_text("""\
 
1071
            services:
 
1072
              jenkins:
 
1073
                units:
 
1074
                  jenkins/0:
 
1075
                    workload-status:
 
1076
                      current: waiting
 
1077
                  subordinates:
 
1078
                    ntp/0:
 
1079
                      workload-status:
 
1080
                        current: unknown
 
1081
        """)
 
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)
 
1086
        writes = []
 
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'])
 
1094
 
 
1095
    def test_wait_for_workload_all_unknown(self):
 
1096
        status = Status.from_text("""\
 
1097
            services:
 
1098
              jenkins:
 
1099
                units:
 
1100
                  jenkins/0:
 
1101
                    workload-status:
 
1102
                      current: unknown
 
1103
                  subordinates:
 
1104
                    ntp/0:
 
1105
                      workload-status:
 
1106
                        current: unknown
 
1107
        """)
 
1108
        client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
 
1109
        writes = []
 
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, [])
 
1117
 
 
1118
    def test_wait_for_workload_no_workload_status(self):
 
1119
        status = Status.from_text("""\
 
1120
            services:
 
1121
              jenkins:
 
1122
                units:
 
1123
                  jenkins/0:
 
1124
                    agent-state: active
 
1125
        """)
 
1126
        client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
 
1127
        writes = []
 
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, [])
 
1135
 
 
1136
    def test_wait_for_ha(self):
 
1137
        value = yaml.safe_dump({
 
1138
            'machines': {
 
1139
                '0': {'state-server-member-status': 'has-vote'},
 
1140
                '1': {'state-server-member-status': 'has-vote'},
 
1141
                '2': {'state-server-member-status': 'has-vote'},
 
1142
            },
 
1143
            'services': {},
 
1144
        })
 
1145
        client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
 
1146
        with patch.object(client, 'get_juju_output', return_value=value):
 
1147
            client.wait_for_ha()
 
1148
 
 
1149
    def test_wait_for_ha_no_has_vote(self):
 
1150
        value = yaml.safe_dump({
 
1151
            'machines': {
 
1152
                '0': {'state-server-member-status': 'no-vote'},
 
1153
                '1': {'state-server-member-status': 'no-vote'},
 
1154
                '2': {'state-server-member-status': 'no-vote'},
 
1155
            },
 
1156
            'services': {},
 
1157
        })
 
1158
        client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
 
1159
        with patch.object(client, 'get_juju_output', return_value=value):
 
1160
            writes = []
 
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(
 
1166
                            StatusNotMet,
 
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'])
 
1172
 
 
1173
    def test_wait_for_ha_timeout(self):
 
1174
        value = yaml.safe_dump({
 
1175
            'machines': {
 
1176
                '0': {'state-server-member-status': 'has-vote'},
 
1177
                '1': {'state-server-member-status': 'has-vote'},
 
1178
            },
 
1179
            'services': {},
 
1180
        })
 
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(
 
1187
                        StatusNotMet,
 
1188
                        'Timed out waiting for voting to be enabled.'):
 
1189
                    client.wait_for_ha()
 
1190
 
 
1191
    def test_wait_for_deploy_started(self):
 
1192
        value = yaml.safe_dump({
 
1193
            'machines': {
 
1194
                '0': {'agent-state': 'started'},
 
1195
            },
 
1196
            'services': {
 
1197
                'jenkins': {
 
1198
                    'units': {
 
1199
                        'jenkins/1': {'baz': 'qux'}
 
1200
                    }
 
1201
                }
 
1202
            }
 
1203
        })
 
1204
        client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
 
1205
        with patch.object(client, 'get_juju_output', return_value=value):
 
1206
            client.wait_for_deploy_started()
 
1207
 
 
1208
    def test_wait_for_deploy_started_timeout(self):
 
1209
        value = yaml.safe_dump({
 
1210
            'machines': {
 
1211
                '0': {'agent-state': 'started'},
 
1212
            },
 
1213
            'services': {},
 
1214
        })
 
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(
 
1219
                        StatusNotMet,
 
1220
                        'Timed out waiting for applications to start.'):
 
1221
                    client.wait_for_deploy_started()
 
1222
 
 
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')
 
1228
 
 
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)
 
1232
        writes = []
 
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'])
 
1242
 
 
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]
 
1249
 
 
1250
        def get_juju_output_fake(*args, **kwargs):
 
1251
            action = actions.pop(0)
 
1252
            if isinstance(action, Exception):
 
1253
                raise action
 
1254
            else:
 
1255
                return action
 
1256
 
 
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')
 
1260
 
 
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]
 
1265
 
 
1266
        def get_juju_output_fake(*args, **kwargs):
 
1267
            action = actions.pop(0)
 
1268
            if isinstance(action, Exception):
 
1269
                raise action
 
1270
            else:
 
1271
                return action
 
1272
 
 
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')
 
1277
 
 
1278
    def test_wait_just_machine_0(self):
 
1279
        value = yaml.safe_dump({
 
1280
            'machines': {
 
1281
                '0': {'agent-state': 'started'},
 
1282
            },
 
1283
        })
 
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)
 
1287
 
 
1288
    def test_wait_just_machine_0_timeout(self):
 
1289
        value = yaml.safe_dump({
 
1290
            'machines': {
 
1291
                '0': {'agent-state': 'started'},
 
1292
                '1': {'agent-state': 'started'},
 
1293
            },
 
1294
        })
 
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(
 
1300
                Exception,
 
1301
                'Timed out waiting for machine removal 1'):
 
1302
            client.wait_for(WaitMachineNotPresent('1'), quiet=True)
 
1303
 
 
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',))
 
1309
 
 
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()
 
1316
        assert_juju_call(
 
1317
            self, po_mock, client, (
 
1318
                'juju', '--show-log', 'get-env', '-e', 'foo'))
 
1319
        self.assertEqual({'bar': 'baz'}, result)
 
1320
 
 
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')
 
1327
        self.assertEqual(
 
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)
 
1332
 
 
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'))
 
1344
 
 
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='))
 
1355
 
 
1356
    @contextmanager
 
1357
    def run_model_defaults_test(self, operation_name):
 
1358
        env = SimpleEnvironment('foo', {'type': 'local'})
 
1359
        client = EnvJujuClient1X(env, '1.23-series-arch', None)
 
1360
        yield client
 
1361
        self.assertEqual('INFO No model-defaults stored for client '
 
1362
                         '(attempted {}).\n'.format(operation_name),
 
1363
                         self.log_stream.getvalue())
 
1364
 
 
1365
    def test_get_model_defaults(self):
 
1366
        with self.run_model_defaults_test('get') as client:
 
1367
            client.get_model_defaults('some-key')
 
1368
 
 
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')
 
1372
 
 
1373
    def test_unset_model_defaults(self):
 
1374
        with self.run_model_defaults_test('unset') as client:
 
1375
            client.unset_model_defaults('some-key')
 
1376
 
 
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')
 
1388
 
 
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)
 
1398
 
 
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',
 
1407
                                 'bar', 'baz'))
 
1408
 
 
1409
    def test_juju_env(self):
 
1410
        env = SimpleEnvironment('qux')
 
1411
        client = EnvJujuClient1X(env, None, '/foobar/baz')
 
1412
 
 
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'))
 
1417
 
 
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',
 
1426
                                 'bar', 'baz'))
 
1427
 
 
1428
    def test_juju_no_check_env(self):
 
1429
        env = SimpleEnvironment('qux')
 
1430
        client = EnvJujuClient1X(env, None, '/foobar/baz')
 
1431
 
 
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)
 
1436
 
 
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'))
 
1445
 
 
1446
    def test_juju_juju_home(self):
 
1447
        env = SimpleEnvironment('qux')
 
1448
        os.environ['JUJU_HOME'] = 'foo'
 
1449
        client = EnvJujuClient1X(env, None, '/foobar/baz')
 
1450
 
 
1451
        def check_home(*args, **kwargs):
 
1452
            self.assertEqual(os.environ['JUJU_HOME'], 'foo')
 
1453
            yield
 
1454
            self.assertEqual(os.environ['JUJU_HOME'], 'asdf')
 
1455
            yield
 
1456
 
 
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'))
 
1461
 
 
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}
 
1466
 
 
1467
        def check_env(*args, **kwargs):
 
1468
            self.assertEqual('/juju', os.environ['JUJU'])
 
1469
 
 
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'))
 
1474
 
 
1475
    def test_juju_backup_with_tgz(self):
 
1476
        env = SimpleEnvironment('qux')
 
1477
        client = EnvJujuClient1X(env, None, '/foobar/baz')
 
1478
 
 
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'])
 
1487
 
 
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()
 
1494
        self.assertEqual(
 
1495
            backup_file, os.path.abspath('juju-backup-123-456.tar.gz'))
 
1496
 
 
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'):
 
1503
                client.backup()
 
1504
 
 
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'):
 
1512
                client.backup()
 
1513
 
 
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
 
1519
 
 
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):
 
1524
            client.backup()
 
1525
            self.assertNotEqual(environ, os.environ)
 
1526
 
 
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',
 
1533
                                         'mem=2G', 'quxx')
 
1534
        self.assertIs(gjo_mock.return_value, result)
 
1535
 
 
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)
 
1544
 
 
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:
 
1549
            client.enable_ha()
 
1550
        eha_mock.assert_called_once_with('ensure-availability', ('-n', '3'))
 
1551
 
 
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()
 
1563
 
 
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'))
 
1575
 
 
1576
    def test_juju_async_environ(self):
 
1577
        env = SimpleEnvironment('qux')
 
1578
        client = EnvJujuClient1X(env, None, '/foobar/baz')
 
1579
        environ = client._shell_environ()
 
1580
        proc_mock = Mock()
 
1581
        with patch('subprocess.Popen') as popen_class_mock:
 
1582
 
 
1583
            def check_environ(*args, **kwargs):
 
1584
                self.assertEqual(environ, os.environ)
 
1585
                return proc_mock
 
1586
            popen_class_mock.side_effect = check_environ
 
1587
            proc_mock.wait.return_value = 0
 
1588
            with client.juju_async('foo', ('bar', 'baz')):
 
1589
                pass
 
1590
            self.assertNotEqual(environ, os.environ)
 
1591
 
 
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)
 
1600
 
 
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)
 
1612
 
 
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)
 
1621
 
 
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'))
 
1630
 
 
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(
 
1637
            'deployer', (
 
1638
                '--debug', '--deploy-delay', '10', '--timeout', '3600',
 
1639
                '--config', 'bundle:~juju-qa/some-lxc-bundle'))
 
1640
 
 
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'))
 
1649
 
 
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(
 
1656
            'deployer', (
 
1657
                '--debug', '--deploy-delay', '10', '--timeout', '3600',
 
1658
                '--config', 'bundle:~juju-qa/some-lxc-bundle'))
 
1659
 
 
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',
 
1668
                         'name'))
 
1669
 
 
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'})
 
1679
 
 
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'})
 
1689
 
 
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(
 
1696
            'quickstart', (
 
1697
                '--constraints', 'mem=2G', '--no-browser',
 
1698
                'bundle:~juju-qa/some-lxc-bundle'),
 
1699
            extra_env={'JUJU': '/juju'})
 
1700
 
 
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()
 
1705
        self.assertEqual(
 
1706
            'INFO The model is environment foo\n',
 
1707
            self.log_stream.getvalue())
 
1708
 
 
1709
    def test__get_models(self):
 
1710
        data = """\
 
1711
            - name: foo
 
1712
              model-uuid: aaaa
 
1713
            - name: bar
 
1714
              model-uuid: bbbb
 
1715
        """
 
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()
 
1720
            self.assertEqual(
 
1721
                [{'name': 'foo', 'model-uuid': 'aaaa'},
 
1722
                 {'name': 'bar', 'model-uuid': 'bbbb'}],
 
1723
                models)
 
1724
 
 
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())
 
1731
 
 
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())
 
1736
 
 
1737
    def test_iter_model_clients(self):
 
1738
        data = """\
 
1739
            - name: foo
 
1740
              model-uuid: aaaa
 
1741
              owner: admin@local
 
1742
            - name: bar
 
1743
              model-uuid: bbbb
 
1744
              owner: admin@local
 
1745
        """
 
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)
 
1752
 
 
1753
    def test_get_controller_client(self):
 
1754
        client = EnvJujuClient1X(SimpleEnvironment('foo'), {'bar': 'baz'},
 
1755
                                 'myhome')
 
1756
        controller_client = client.get_controller_client()
 
1757
        self.assertIs(client, controller_client)
 
1758
 
 
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()
 
1763
        self.assertEqual(
 
1764
            'INFO The controller is environment foo\n',
 
1765
            self.log_stream.getvalue())
 
1766
 
 
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)
 
1772
 
 
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')
 
1781
 
 
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')
 
1790
 
 
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"
 
1801
        )
 
1802
 
 
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")
 
1811
 
 
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"
 
1822
        )
 
1823
 
 
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")
 
1833
 
 
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",
 
1843
                ret]
 
1844
            out = client.action_do_fetch("foo/0", "myaction", "param=5")
 
1845
            self.assertEqual(out, ret)
 
1846
 
 
1847
    def test_run(self):
 
1848
        env = SimpleEnvironment('name', {}, 'foo')
 
1849
        client = fake_juju_client(cls=EnvJujuClient1X, env=env)
 
1850
        run_list = [
 
1851
            {"MachineId": "1",
 
1852
             "Stdout": "Linux\n",
 
1853
             "ReturnCode": 255,
 
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)
 
1864
 
 
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')
 
1875
 
 
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'))
 
1882
 
 
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'))
 
1890
 
 
1891
    def test__shell_environ_uses_pathsep(self):
 
1892
        client = EnvJujuClient1X(SimpleEnvironment('foo'), None,
 
1893
                                 'foo/bar/juju')
 
1894
        with patch('os.pathsep', '!'):
 
1895
            environ = client._shell_environ()
 
1896
        self.assertRegexpMatches(environ['PATH'], r'foo/bar\!')
 
1897
 
 
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'))
 
1903
 
 
1904
    def test_get_config(self):
 
1905
        def output(*args, **kwargs):
 
1906
            return yaml.safe_dump({
 
1907
                'charm': 'foo',
 
1908
                'service': 'foo',
 
1909
                'settings': {
 
1910
                    'dir': {
 
1911
                        'default': 'true',
 
1912
                        'description': 'bla bla',
 
1913
                        'type': 'string',
 
1914
                        'value': '/tmp/charm-dir',
 
1915
                    }
 
1916
                }
 
1917
            })
 
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')
 
1925
 
 
1926
    def test_get_service_config(self):
 
1927
        def output(*args, **kwargs):
 
1928
            return yaml.safe_dump({
 
1929
                'charm': 'foo',
 
1930
                'service': 'foo',
 
1931
                'settings': {
 
1932
                    'dir': {
 
1933
                        'default': 'true',
 
1934
                        'description': 'bla bla',
 
1935
                        'type': 'string',
 
1936
                        'value': '/tmp/charm-dir',
 
1937
                    }
 
1938
                }
 
1939
            })
 
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)
 
1945
 
 
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')
 
1952
 
 
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')
 
1961
 
 
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')
 
1970
 
 
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)
 
1979
 
 
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)
 
1988
 
 
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)
 
1997
 
 
1998
    def test_disable_commands_properties(self):
 
1999
        client = EnvJujuClient1X(SimpleEnvironment('foo'), None, None)
 
2000
        self.assertEqual(
 
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)
 
2004
 
 
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',
 
2017
                                     '--format', 'yaml')
 
2018
 
 
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', ))
 
2024
 
 
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')
 
2030
 
 
2031
 
 
2032
class TestEnvJujuClient25(ClientTest):
 
2033
 
 
2034
    client_class = EnvJujuClient25
 
2035
 
 
2036
    def test_enable_jes(self):
 
2037
        client = self.client_class(
 
2038
            SimpleEnvironment('baz', {}),
 
2039
            '1.25-foobar', 'path')
 
2040
        with self.assertRaises(JESNotSupported):
 
2041
            client.enable_jes()
 
2042
 
 
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)
 
2050
 
 
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)
 
2062
 
 
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)
 
2074
 
 
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)
 
2082
 
 
2083
 
 
2084
class TestEnvJujuClient22(ClientTest):
 
2085
 
 
2086
    client_class = EnvJujuClient22
 
2087
 
 
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')
 
2093
 
 
2094
    def test__shell_environ_juju_home(self):
 
2095
        client = self.client_class(
 
2096
            SimpleEnvironment('baz', {'type': 'ec2'}), '1.22-foobar', 'path',
 
2097
            'asdf')
 
2098
        env = client._shell_environ()
 
2099
        self.assertEqual(env['JUJU_HOME'], 'asdf')
 
2100
 
 
2101
 
 
2102
class TestEnvJujuClient24(ClientTest):
 
2103
 
 
2104
    client_class = EnvJujuClient24
 
2105
 
 
2106
    def test_no_jes(self):
 
2107
        client = self.client_class(
 
2108
            SimpleEnvironment('baz', {}),
 
2109
            '1.25-foobar', 'path')
 
2110
        with self.assertRaises(JESNotSupported):
 
2111
            client.enable_jes()
 
2112
        client._use_jes = True
 
2113
        env = client._shell_environ()
 
2114
        self.assertNotIn('jes', env.get(JUJU_DEV_FEATURE_FLAGS, '').split(","))
 
2115
 
 
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)
 
2127
 
 
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'))
 
2137
 
 
2138
 
 
2139
class TestStatus1X(FakeHomeTestCase):
 
2140
 
 
2141
    def test_model_name(self):
 
2142
        status = Status1X({'environment': 'bar'}, '')
 
2143
        self.assertEqual('bar', status.model_name)
 
2144
 
 
2145
    def test_get_applications_gets_services(self):
 
2146
        status = Status1X({
 
2147
            'services': {'service': {}},
 
2148
            'applications': {'application': {}},
 
2149
            }, '')
 
2150
        self.assertEqual({'service': {}}, status.get_applications())
 
2151
 
 
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'})
 
2160
 
 
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'})
 
2167
 
 
2168
    @staticmethod
 
2169
    def run_iter_status():
 
2170
        status = Status1X({
 
2171
            'environment': 'fake-unit-test',
 
2172
            'machines': {
 
2173
                '0': {
 
2174
                    'agent-state': 'started',
 
2175
                    'agent-state-info': 'all good',
 
2176
                    'agent-version': '1.25.1',
 
2177
                    },
 
2178
                },
 
2179
            'services': {
 
2180
                'dummy-sink': {
 
2181
                    'units': {
 
2182
                        'dummy-sink/0': {
 
2183
                            'agent-state': 'started',
 
2184
                            'agent-version': '1.25.1',
 
2185
                            },
 
2186
                        'dummy-sink/1': {
 
2187
                            'workload-status': {
 
2188
                                'current': 'active',
 
2189
                                },
 
2190
                            'agent-status': {
 
2191
                                'current': 'executing',
 
2192
                                },
 
2193
                            'agent-state': 'started',
 
2194
                            'agent-version': '1.25.1',
 
2195
                            },
 
2196
                        }
 
2197
                    },
 
2198
                'dummy-source': {
 
2199
                    'service-status': {
 
2200
                        'current': 'active',
 
2201
                        },
 
2202
                    'units': {
 
2203
                        'dummy-source/0': {
 
2204
                            'agent-state': 'started',
 
2205
                            'agent-version': '1.25.1',
 
2206
                            }
 
2207
                        }
 
2208
                    },
 
2209
                },
 
2210
            }, '')
 
2211
        for sub_status in status.iter_status():
 
2212
            yield sub_status
 
2213
 
 
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
 
2221
        self.assertEqual({
 
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'),
 
2226
            }, status_set)
 
2227
 
 
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',
 
2232
                              version='1.25.1'))
 
2233
 
 
2234
 
 
2235
def fast_timeout(count):
 
2236
    if False:
 
2237
        yield