~andrewjbeach/juju-ci-tools/make-local-patcher

« back to all changes in this revision

Viewing changes to test_deploy_stack.py

  • Committer: Aaron Bentley
  • Date: 2015-09-02 17:46:47 UTC
  • mto: This revision was merged to the branch mainline in revision 1082.
  • Revision ID: aaron.bentley@canonical.com-20150902174647-06vmnsooo6yzd46t
Stop supplying env to subprocess calls.

Show diffs side-by-side

added added

removed removed

Lines of Context:
9
9
import sys
10
10
from unittest import (
11
11
    skipIf,
 
12
    TestCase
12
13
)
13
14
 
14
15
from mock import (
15
16
    call,
16
 
    MagicMock,
17
17
    patch,
18
18
)
19
19
import yaml
20
20
 
21
21
from deploy_stack import (
22
22
    archive_logs,
23
 
    assess_juju_relations,
24
23
    assess_juju_run,
25
 
    assess_upgrade,
26
24
    boot_context,
27
 
    BootstrapManager,
28
 
    check_token,
29
25
    copy_local_logs,
30
26
    copy_remote_logs,
31
27
    deploy_dummy_stack,
32
 
    deploy_job,
33
28
    _deploy_job,
34
29
    deploy_job_parse_args,
35
30
    destroy_environment,
38
33
    iter_remote_machines,
39
34
    get_remote_machines,
40
35
    GET_TOKEN_SCRIPT,
 
36
    assess_upgrade,
41
37
    safe_print_status,
42
38
    retain_config,
43
39
    update_env,
44
40
)
45
 
from jujuconfig import (
46
 
    get_environments_path,
47
 
    get_jenv_path,
48
 
    get_juju_home,
49
 
    )
50
41
from jujupy import (
51
42
    EnvJujuClient,
52
 
    EnvJujuClient1X,
53
 
    get_cache_path,
54
43
    get_timeout_prefix,
55
44
    get_timeout_path,
56
 
    JujuData,
57
 
    KILL_CONTROLLER,
58
45
    SimpleEnvironment,
59
46
    Status,
60
47
)
61
48
from remote import (
62
49
    _Remote,
63
50
    remote_from_address,
64
 
    SSHRemote,
65
 
)
66
 
from tests import (
67
 
    FakeHomeTestCase,
68
 
    temp_os_env,
69
 
    use_context,
70
51
)
71
52
from test_jujupy import (
72
53
    assert_juju_call,
73
 
    FakeJujuClient,
74
 
    FakePopen,
75
 
    observable_temp_file,
76
54
)
77
55
from utility import (
78
 
    LoggedException,
 
56
    setup_test_logging,
79
57
    temp_dir,
80
58
)
81
59
 
89
67
    return write_dumped_files
90
68
 
91
69
 
92
 
class DeployStackTestCase(FakeHomeTestCase):
 
70
class DeployStackTestCase(TestCase):
 
71
 
 
72
    def setUp(self):
 
73
        setup_test_logging(self)
93
74
 
94
75
    def test_destroy_environment(self):
95
 
        client = EnvJujuClient1X(
 
76
        client = EnvJujuClient(
96
77
            SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None)
97
78
        with patch.object(client,
98
79
                          'destroy_environment', autospec=True) as de_mock:
103
84
        self.assertEqual(0, dji_mock.call_count)
104
85
 
105
86
    def test_destroy_environment_with_manual_type_aws(self):
106
 
        os.environ['AWS_ACCESS_KEY'] = 'fake-juju-ci-testing-key'
107
 
        client = EnvJujuClient1X(
 
87
        client = EnvJujuClient(
108
88
            SimpleEnvironment('foo', {'type': 'manual'}), '1.234-76', None)
109
89
        with patch.object(client,
110
90
                          'destroy_environment', autospec=True) as de_mock:
111
91
            with patch('deploy_stack.destroy_job_instances',
112
92
                       autospec=True) as dji_mock:
113
 
                destroy_environment(client, 'foo')
 
93
                with patch.dict(os.environ, {'AWS_ACCESS_KEY': 'bar'}):
 
94
                    destroy_environment(client, 'foo')
114
95
        self.assertEqual(1, de_mock.call_count)
115
 
        dji_mock.assert_called_once_with('foo')
 
96
        dji_mock.assert_called_with('foo')
116
97
 
117
98
    def test_destroy_environment_with_manual_type_non_aws(self):
118
 
        client = EnvJujuClient1X(
 
99
        client = EnvJujuClient(
119
100
            SimpleEnvironment('foo', {'type': 'manual'}), '1.234-76', None)
120
101
        with patch.object(client,
121
102
                          'destroy_environment', autospec=True) as de_mock:
122
103
            with patch('deploy_stack.destroy_job_instances',
123
104
                       autospec=True) as dji_mock:
124
105
                destroy_environment(client, 'foo')
125
 
        self.assertEqual(os.environ.get('AWS_ACCESS_KEY'), None)
126
106
        self.assertEqual(1, de_mock.call_count)
127
107
        self.assertEqual(0, dji_mock.call_count)
128
108
 
129
109
    def test_assess_juju_run(self):
130
 
        env = JujuData('foo', {'type': 'nonlocal'})
 
110
        env = SimpleEnvironment('foo', {'type': 'nonlocal'})
131
111
        client = EnvJujuClient(env, None, None)
132
112
        response_ok = json.dumps(
133
113
            [{"MachineId": "1", "Stdout": "Linux\n"},
151
131
                responses = assess_juju_run(client)
152
132
 
153
133
    def test_safe_print_status(self):
154
 
        env = JujuData('foo', {'type': 'nonlocal'})
 
134
        env = SimpleEnvironment('foo', {'type': 'nonlocal'})
155
135
        client = EnvJujuClient(env, None, None)
156
136
        with patch.object(
157
137
                client, 'juju', autospec=True,
159
139
                    1, 'status', 'status error')
160
140
        ) as mock:
161
141
            safe_print_status(client)
162
 
        mock.assert_called_once_with('show-status', ('--format', 'yaml'))
 
142
        mock.assert_called_once_with('status', ())
163
143
 
164
144
    def test_update_env(self):
165
145
        env = SimpleEnvironment('foo', {'type': 'paas'})
172
152
        self.assertEqual('baz', env.config['bootstrap-host'])
173
153
        self.assertEqual('url', env.config['tools-metadata-url'])
174
154
        self.assertEqual('devel', env.config['agent-stream'])
175
 
        self.assertNotIn('region', env.config)
176
 
 
177
 
    def test_update_env_region(self):
178
 
        env = SimpleEnvironment('foo', {'type': 'paas'})
179
 
        update_env(env, 'bar', region='region-foo')
180
 
        self.assertEqual('region-foo', env.config['region'])
181
 
 
182
 
    def test_update_env_region_none(self):
183
 
        env = SimpleEnvironment('foo',
184
 
                                {'type': 'paas', 'region': 'region-foo'})
185
 
        update_env(env, 'bar', region=None)
186
 
        self.assertEqual('region-foo', env.config['region'])
187
155
 
188
156
    def test_dump_juju_timings(self):
189
 
        env = JujuData('foo', {'type': 'bar'})
 
157
        env = SimpleEnvironment('foo', {'type': 'bar'})
190
158
        client = EnvJujuClient(env, None, None)
191
159
        client.juju_timings = {("juju", "op1"): [1], ("juju", "op2"): [2]}
192
160
        expected = {"juju op1": [1], "juju op2": [2]}
197
165
                file_data = json.load(out_file)
198
166
        self.assertEqual(file_data, expected)
199
167
 
200
 
    def test_check_token(self):
201
 
        env = JujuData('foo', {'type': 'local'})
202
 
        client = EnvJujuClient(env, None, None)
203
 
        remote = SSHRemote(client, 'unit', None, series='xenial')
204
 
        with patch('deploy_stack.remote_from_unit', autospec=True,
205
 
                   return_value=remote):
206
 
            with patch.object(remote, 'run', autospec=True,
207
 
                              return_value='token') as rr_mock:
208
 
                check_token(client, 'token', timeout=0)
209
 
        rr_mock.assert_called_once_with(GET_TOKEN_SCRIPT)
210
 
        self.assertTrue(remote.use_juju_ssh)
211
 
        self.assertEqual(
212
 
            ['INFO Retrieving token.',
213
 
             "INFO Token matches expected 'token'"],
214
 
            self.log_stream.getvalue().splitlines())
215
 
 
216
 
    def test_check_token_not_found(self):
217
 
        env = JujuData('foo', {'type': 'local'})
218
 
        client = EnvJujuClient(env, None, None)
219
 
        remote = SSHRemote(client, 'unit', None, series='xenial')
220
 
        with patch('deploy_stack.remote_from_unit', autospec=True,
221
 
                   return_value=remote):
222
 
            with patch.object(remote, 'run', autospec=True,
223
 
                              return_value='') as rr_mock:
224
 
                with patch.object(remote, 'get_address',
225
 
                                  autospec=True) as ga_mock:
226
 
                    with self.assertRaisesRegexp(ValueError, "Token is ''"):
227
 
                        check_token(client, 'token', timeout=0)
228
 
        self.assertEqual(2, rr_mock.call_count)
229
 
        rr_mock.assert_called_with(GET_TOKEN_SCRIPT)
230
 
        ga_mock.assert_called_once_with()
231
 
        self.assertFalse(remote.use_juju_ssh)
232
 
        self.assertEqual(
233
 
            ['INFO Retrieving token.'],
234
 
            self.log_stream.getvalue().splitlines())
235
 
 
236
 
    def test_check_token_not_found_juju_ssh_broken(self):
237
 
        env = JujuData('foo', {'type': 'local'})
238
 
        client = EnvJujuClient(env, None, None)
239
 
        remote = SSHRemote(client, 'unit', None, series='xenial')
240
 
        with patch('deploy_stack.remote_from_unit', autospec=True,
241
 
                   return_value=remote):
242
 
            with patch.object(remote, 'run', autospec=True,
243
 
                              side_effect=['', 'token']) as rr_mock:
244
 
                with patch.object(remote, 'get_address',
245
 
                                  autospec=True) as ga_mock:
246
 
                    with self.assertRaisesRegexp(ValueError,
247
 
                                                 "Token is 'token'"):
248
 
                        check_token(client, 'token', timeout=0)
249
 
        self.assertEqual(2, rr_mock.call_count)
250
 
        rr_mock.assert_called_with(GET_TOKEN_SCRIPT)
251
 
        ga_mock.assert_called_once_with()
252
 
        self.assertFalse(remote.use_juju_ssh)
253
 
        self.assertEqual(
254
 
            ['INFO Retrieving token.',
255
 
             "INFO Token matches expected 'token'",
256
 
             'ERROR juju ssh to unit is broken.'],
257
 
            self.log_stream.getvalue().splitlines())
258
 
 
259
 
    log_level = logging.DEBUG
260
 
 
261
 
 
262
 
class DumpEnvLogsTestCase(FakeHomeTestCase):
263
 
 
264
 
    log_level = logging.DEBUG
 
168
 
 
169
class DumpEnvLogsTestCase(TestCase):
 
170
 
 
171
    def setUp(self):
 
172
        setup_test_logging(self, level=logging.DEBUG)
265
173
 
266
174
    def assert_machines(self, expected, got):
267
175
        self.assertEqual(expected, dict((k, got[k].address) for k in got))
283
191
                               autospec=True) as crl_mock:
284
192
                        with patch('deploy_stack.archive_logs',
285
193
                                   autospec=True) as al_mock:
286
 
                            env = JujuData('foo', {'type': 'nonlocal'})
 
194
                            env = SimpleEnvironment('foo',
 
195
                                                    {'type': 'nonlocal'})
287
196
                            client = EnvJujuClient(env, '1.234-76', None)
288
197
                            dump_env_logs(client, '10.10.0.1', artifacts_dir)
289
198
            al_mock.assert_called_once_with(artifacts_dir)
291
200
                ['machine-0', 'machine-1', 'machine-2'],
292
201
                sorted(os.listdir(artifacts_dir)))
293
202
        self.assertEqual(
294
 
            (client, {'0': '10.10.0.1'}), gm_mock.call_args[0])
 
203
            (client, '10.10.0.1'), gm_mock.call_args[0])
295
204
        self.assertItemsEqual(
296
205
            [(self.r0, '%s/machine-0' % artifacts_dir),
297
206
             (self.r1, '%s/machine-1' % artifacts_dir),
312
221
                               autospec=True) as crl_mock:
313
222
                        with patch('deploy_stack.archive_logs',
314
223
                                   autospec=True) as al_mock:
315
 
                            env = JujuData('foo', {'type': 'nonlocal'})
 
224
                            env = SimpleEnvironment('foo',
 
225
                                                    {'type': 'nonlocal'})
316
226
                            client = EnvJujuClient(env, '1.234-76', None)
317
227
                            dump_env_logs(client, '10.10.0.1', artifacts_dir)
318
228
            al_mock.assert_called_once_with(artifacts_dir)
319
229
            self.assertEqual(
320
230
                ['machine-2'],
321
231
                sorted(os.listdir(artifacts_dir)))
322
 
        self.assertEqual((client, {'0': '10.10.0.1'}), gm_mock.call_args[0])
 
232
        self.assertEqual(
 
233
            (client, '10.10.0.1'), gm_mock.call_args[0])
323
234
        self.assertEqual(
324
235
            [(self.r2, '%s/machine-2' % artifacts_dir)],
325
236
            [cal[0] for cal in crl_mock.call_args_list])
330
241
            self.log_stream.getvalue().splitlines())
331
242
 
332
243
    def test_dump_env_logs_local_env(self):
333
 
        env = JujuData('foo', {'type': 'local'})
 
244
        env = SimpleEnvironment('foo', {'type': 'local'})
334
245
        client = EnvJujuClient(env, '1.234-76', None)
335
246
        with temp_dir() as artifacts_dir:
336
247
            with patch('deploy_stack.get_remote_machines',
356
267
            log_path = os.path.join(log_dir, 'fake.log')
357
268
            cc_mock.assert_called_once_with(['gzip', '--best', '-f', log_path])
358
269
 
359
 
    def test_archive_logs_subdir(self):
360
 
        with temp_dir() as log_dir:
361
 
            subdir = os.path.join(log_dir, "subdir")
362
 
            os.mkdir(subdir)
363
 
            with open(os.path.join(subdir, 'fake.log'), 'w') as f:
364
 
                f.write('log contents')
365
 
            with patch('subprocess.check_call', autospec=True) as cc_mock:
366
 
                archive_logs(log_dir)
367
 
            log_path = os.path.join(subdir, 'fake.log')
368
 
            cc_mock.assert_called_once_with(['gzip', '--best', '-f', log_path])
369
 
 
370
270
    def test_archive_logs_none(self):
371
271
        with temp_dir() as log_dir:
372
272
            with patch('subprocess.check_call', autospec=True) as cc_mock:
429
329
                '-o', 'User ubuntu',
430
330
                '-o', 'UserKnownHostsFile /dev/null',
431
331
                '-o', 'StrictHostKeyChecking no',
432
 
                '-o', 'PasswordAuthentication no',
433
332
                '10.10.0.1',
434
 
                'sudo chmod -Rf go+r /var/log/cloud-init*.log'
435
 
                ' /var/log/juju/*.log'
436
 
                ' /var/lib/juju/containers/juju-*-lxc-*/'),),
 
333
                'sudo chmod go+r /var/log/juju/*'),),
437
334
            cc_mock.call_args_list[0][0])
438
335
        self.assertEqual(
439
336
            (get_timeout_prefix(120) + (
440
 
                'scp', '-rC',
 
337
                'scp', '-C',
441
338
                '-o', 'User ubuntu',
442
339
                '-o', 'UserKnownHostsFile /dev/null',
443
340
                '-o', 'StrictHostKeyChecking no',
444
 
                '-o', 'PasswordAuthentication no',
445
341
                '10.10.0.1:/var/log/cloud-init*.log',
446
342
                '10.10.0.1:/var/log/juju/*.log',
447
 
                '10.10.0.1:/var/lib/juju/containers/juju-*-lxc-*/',
448
343
                '/foo'),),
449
344
            cc_mock.call_args_list[1][0])
450
345
 
472
367
                copy_remote_logs(remote_from_address('10.10.0.1'), '/foo')
473
368
        self.assertEqual(2, co.call_count)
474
369
        self.assertEqual(
475
 
            ["DEBUG ssh -o 'User ubuntu' -o 'UserKnownHostsFile /dev/null' "
476
 
             "-o 'StrictHostKeyChecking no' -o 'PasswordAuthentication no' "
477
 
             "10.10.0.1 'sudo chmod -Rf go+r /var/log/cloud-init*.log "
478
 
             "/var/log/juju/*.log /var/lib/juju/containers/juju-*-lxc-*/'",
479
 
             'WARNING Could not allow access to the juju logs:',
 
370
            ['WARNING Could not allow access to the juju logs:',
480
371
             'WARNING None',
481
372
             'WARNING Could not retrieve some or all logs:',
482
 
             'WARNING CalledProcessError()'],
 
373
             'WARNING None'],
483
374
            self.log_stream.getvalue().splitlines())
484
375
 
485
376
    def test_get_machines_for_logs(self):
486
377
        client = EnvJujuClient(
487
 
            JujuData('cloud', {'type': 'ec2'}), '1.23.4', None)
 
378
            SimpleEnvironment('cloud', {'type': 'ec2'}), '1.23.4', None)
488
379
        status = Status.from_text("""\
489
380
            machines:
490
381
              "0":
494
385
            """)
495
386
        with patch.object(client, 'get_status', autospec=True,
496
387
                          return_value=status):
497
 
            machines = get_remote_machines(client, {})
 
388
            machines = get_remote_machines(client, None)
498
389
        self.assert_machines(
499
390
            {'0': '10.11.12.13', '1': '10.11.12.14'}, machines)
500
391
 
501
392
    def test_get_machines_for_logs_with_boostrap_host(self):
502
393
        client = EnvJujuClient(
503
 
            JujuData('cloud', {'type': 'ec2'}), '1.23.4', None)
 
394
            SimpleEnvironment('cloud', {'type': 'ec2'}), '1.23.4', None)
504
395
        status = Status.from_text("""\
505
396
            machines:
506
397
              "0":
508
399
            """)
509
400
        with patch.object(client, 'get_status', autospec=True,
510
401
                          return_value=status):
511
 
            machines = get_remote_machines(client, {'0': '10.11.111.222'})
 
402
            machines = get_remote_machines(client, '10.11.111.222')
512
403
        self.assert_machines({'0': '10.11.111.222'}, machines)
513
404
 
514
405
    def test_get_machines_for_logs_with_no_addresses(self):
515
406
        client = EnvJujuClient(
516
 
            JujuData('cloud', {'type': 'ec2'}), '1.23.4', None)
 
407
            SimpleEnvironment('cloud', {'type': 'ec2'}), '1.23.4', None)
517
408
        with patch.object(client, 'get_status', autospec=True,
518
409
                          side_effect=Exception):
519
 
            machines = get_remote_machines(client, {'0': '10.11.111.222'})
 
410
            machines = get_remote_machines(client, '10.11.111.222')
520
411
        self.assert_machines({'0': '10.11.111.222'}, machines)
521
412
 
522
413
    @patch('subprocess.check_call')
526
417
            'name': 'foo',
527
418
            'maas-server': 'http://bar/MASS/',
528
419
            'maas-oauth': 'baz'}
529
 
        client = EnvJujuClient(JujuData('cloud', config), '1.23.4', None)
 
420
        client = EnvJujuClient(
 
421
            SimpleEnvironment('cloud', config), '1.23.4', None)
530
422
        status = Status.from_text("""\
531
423
            machines:
532
424
              "0":
542
434
            }
543
435
            with patch('substrate.MAASAccount.get_allocated_ips',
544
436
                       autospec=True, return_value=allocated_ips):
545
 
                machines = get_remote_machines(client, {'0': 'node1.maas'})
 
437
                machines = get_remote_machines(client, 'node1.maas')
546
438
        self.assert_machines(
547
439
            {'0': '10.11.12.13', '1': '10.11.12.14'}, machines)
548
440
 
549
441
    def test_iter_remote_machines(self):
550
442
        client = EnvJujuClient(
551
 
            JujuData('cloud', {'type': 'ec2'}), '1.23.4', None)
 
443
            SimpleEnvironment('cloud', {'type': 'ec2'}), '1.23.4', None)
552
444
        status = Status.from_text("""\
553
445
            machines:
554
446
              "0":
565
457
 
566
458
    def test_iter_remote_machines_with_series(self):
567
459
        client = EnvJujuClient(
568
 
            JujuData('cloud', {'type': 'ec2'}), '1.23.4', None)
 
460
            SimpleEnvironment('cloud', {'type': 'ec2'}), '1.23.4', None)
569
461
        status = Status.from_text("""\
570
462
            machines:
571
463
              "0":
600
492
        rj_mock.assert_called_with('src', 'dst')
601
493
 
602
494
 
603
 
class TestDeployDummyStack(FakeHomeTestCase):
604
 
 
605
 
    def test_deploy_dummy_stack_sets_centos_constraints(self):
606
 
        env = JujuData('foo', {'type': 'maas'})
607
 
        client = EnvJujuClient(env, None, '/foo/juju')
608
 
        client.version = '2.0.0'
609
 
        with patch('subprocess.check_call', autospec=True) as cc_mock:
610
 
            with patch.object(EnvJujuClient, 'wait_for_started'):
611
 
                with patch('deploy_stack.get_random_string',
612
 
                           return_value='fake-token', autospec=True):
613
 
                    deploy_dummy_stack(client, 'centos')
614
 
        assert_juju_call(self, cc_mock, client,
615
 
                         ('juju', '--show-log', 'set-model-constraints', '-m',
616
 
                          'foo', 'tags=MAAS_NIC_1'), 0)
617
 
 
618
 
    def test_assess_juju_relations(self):
619
 
        env = JujuData('foo', {'type': 'nonlocal'})
620
 
        client = EnvJujuClient(env, None, '/foo/juju')
621
 
        with patch.object(client, 'get_juju_output', side_effect='fake-token',
622
 
                          autospec=True):
623
 
            with patch('subprocess.check_call', autospec=True) as cc_mock:
624
 
                with patch('deploy_stack.get_random_string',
625
 
                           return_value='fake-token', autospec=True):
626
 
                    with patch('deploy_stack.check_token',
627
 
                               autospec=True) as ct_mock:
628
 
                        assess_juju_relations(client)
629
 
        assert_juju_call(self, cc_mock, client, (
630
 
            'juju', '--show-log', 'set-config', '-m', 'foo', 'dummy-source',
631
 
            'token=fake-token'), 0)
632
 
        ct_mock.assert_called_once_with(client, 'fake-token')
 
495
class TestDeployDummyStack(TestCase):
 
496
 
 
497
    def setUp(self):
 
498
        setup_test_logging(self)
633
499
 
634
500
    def test_deploy_dummy_stack(self):
635
 
        env = JujuData('foo', {'type': 'nonlocal'})
 
501
        env = SimpleEnvironment('foo', {'type': 'nonlocal'})
636
502
        client = EnvJujuClient(env, None, '/foo/juju')
637
503
        status = yaml.safe_dump({
638
504
            'machines': {'0': {'agent-state': 'started'}},
643
509
            }
644
510
        })
645
511
 
646
 
        def output(*args, **kwargs):
 
512
        def output(args, **kwargs):
647
513
            output = {
648
 
                ('show-status', '--format', 'yaml'): status,
649
 
                ('ssh', 'dummy-sink/0', GET_TOKEN_SCRIPT): 'fake-token',
 
514
                ('juju', '--show-log', 'status', '-e', 'foo'): status,
 
515
                get_timeout_prefix(120) + (
 
516
                    'juju', '--show-log', 'ssh', '-e', 'foo', 'dummy-sink/0',
 
517
                    GET_TOKEN_SCRIPT): 'fake-token',
650
518
            }
651
519
            return output[args]
652
520
 
653
 
        client.version = '2.0.0'
654
 
        with patch.object(client, 'get_juju_output', side_effect=output,
655
 
                          autospec=True) as gjo_mock:
656
 
            with patch('subprocess.check_call', autospec=True) as cc_mock:
657
 
                with patch('deploy_stack.get_random_string',
658
 
                           return_value='fake-token', autospec=True):
659
 
                    with patch('sys.stdout', autospec=True):
660
 
                        with temp_os_env('JUJU_REPOSITORY', '/tmp/repo'):
661
 
                            deploy_dummy_stack(client, 'bar-')
662
 
        assert_juju_call(self, cc_mock, client, (
663
 
            'juju', '--show-log', 'deploy', '-m', 'foo',
664
 
            '/tmp/repo/charms/dummy-source', '--series', 'bar-'), 0)
665
 
        assert_juju_call(self, cc_mock, client, (
666
 
            'juju', '--show-log', 'deploy', '-m', 'foo',
667
 
            '/tmp/repo/charms/dummy-sink', '--series', 'bar-'), 1)
668
 
        assert_juju_call(self, cc_mock, client, (
669
 
            'juju', '--show-log', 'add-relation', '-m', 'foo',
670
 
            'dummy-source', 'dummy-sink'), 2)
671
 
        assert_juju_call(self, cc_mock, client, (
672
 
            'juju', '--show-log', 'expose', '-m', 'foo', 'dummy-sink'), 3)
673
 
        self.assertEqual(cc_mock.call_count, 4)
674
 
        self.assertEqual(
675
 
            [
676
 
                call('show-status', '--format', 'yaml', admin=False)
677
 
            ],
678
 
            gjo_mock.call_args_list)
679
 
 
680
 
        client.version = '1.25.0'
681
 
        with patch.object(client, 'get_juju_output', side_effect=output,
682
 
                          autospec=True) as gjo_mock:
683
 
            with patch('subprocess.check_call', autospec=True) as cc_mock:
684
 
                with patch('deploy_stack.get_random_string',
685
 
                           return_value='fake-token', autospec=True):
686
 
                    with patch('sys.stdout', autospec=True):
687
 
                        with temp_os_env('JUJU_REPOSITORY', '/tmp/repo'):
688
 
                            deploy_dummy_stack(client, 'bar-')
689
 
        assert_juju_call(self, cc_mock, client, (
690
 
            'juju', '--show-log', 'deploy', '-m', 'foo',
691
 
            'local:bar-/dummy-source', '--series', 'bar-'), 0)
692
 
        assert_juju_call(self, cc_mock, client, (
693
 
            'juju', '--show-log', 'deploy', '-m', 'foo',
694
 
            'local:bar-/dummy-sink', '--series', 'bar-'), 1)
 
521
        with patch('subprocess.check_output', side_effect=output,
 
522
                   autospec=True) as co_mock:
 
523
            with patch('subprocess.check_call', autospec=True) as cc_mock:
 
524
                with patch('deploy_stack.get_random_string',
 
525
                           return_value='fake-token', autospec=True):
 
526
                    with patch('sys.stdout', autospec=True):
 
527
                        deploy_dummy_stack(client, 'bar-')
 
528
        assert_juju_call(self, cc_mock, client, (
 
529
            'juju', '--show-log', 'deploy', '-e', 'foo', 'bar-dummy-source'),
 
530
            0)
 
531
        assert_juju_call(self, cc_mock, client, (
 
532
            'juju', '--show-log', 'set', '-e', 'foo', 'dummy-source',
 
533
            'token=fake-token'), 1)
 
534
        assert_juju_call(self, cc_mock, client, (
 
535
            'juju', '--show-log', 'deploy', '-e', 'foo', 'bar-dummy-sink'), 2)
 
536
        assert_juju_call(self, cc_mock, client, (
 
537
            'juju', '--show-log', 'add-relation', '-e', 'foo',
 
538
            'dummy-source', 'dummy-sink'), 3)
 
539
        assert_juju_call(self, cc_mock, client, (
 
540
            'juju', '--show-log', 'expose', '-e', 'foo', 'dummy-sink'), 4)
 
541
        self.assertEqual(cc_mock.call_count, 5)
 
542
        assert_juju_call(self, co_mock, client, (
 
543
            'juju', '--show-log', 'status', '-e', 'foo'), 0,
 
544
            assign_stderr=True)
 
545
        assert_juju_call(self, co_mock, client, (
 
546
            'juju', '--show-log', 'status', '-e', 'foo'), 1,
 
547
            assign_stderr=True)
 
548
        assert_juju_call(self, co_mock, client, get_timeout_prefix(120) + (
 
549
            'juju', '--show-log', 'ssh', '-e', 'foo', 'dummy-sink/0',
 
550
            GET_TOKEN_SCRIPT), 2, assign_stderr=True)
 
551
        self.assertEqual(co_mock.call_count, 3)
695
552
 
696
553
 
697
554
def fake_SimpleEnvironment(name):
702
559
    return EnvJujuClient(env=env, version='1.2.3.4', full_path=path)
703
560
 
704
561
 
705
 
class FakeBootstrapManager:
706
 
 
707
 
    def __init__(self, client):
708
 
        self.client = client
709
 
        self.tear_down_client = client
710
 
        self.entered_top = False
711
 
        self.exited_top = False
712
 
        self.entered_bootstrap = False
713
 
        self.exited_bootstrap = False
714
 
        self.entered_runtime = False
715
 
        self.exited_runtime = False
716
 
        self.torn_down = False
717
 
        self.permanent = False
718
 
        self.known_hosts = {'0': '0.example.org'}
719
 
 
720
 
    @contextmanager
721
 
    def top_context(self):
722
 
        try:
723
 
            self.entered_top = True
724
 
            yield ['bar']
725
 
        finally:
726
 
            self.exited_top = True
727
 
 
728
 
    @contextmanager
729
 
    def bootstrap_context(self, machines):
730
 
        initial_home = self.client.env.juju_home
731
 
        self.client.env.environment = self.client.env.environment + '-temp'
732
 
        try:
733
 
            self.entered_bootstrap = True
734
 
            self.client.env.juju_home = os.path.join(initial_home, 'isolated')
735
 
            yield
736
 
        finally:
737
 
            self.exited_bootstrap = True
738
 
            if not self.permanent:
739
 
                self.client.env.juju_home = initial_home
740
 
 
741
 
    @contextmanager
742
 
    def runtime_context(self, machines):
743
 
        try:
744
 
            self.entered_runtime = True
745
 
            yield
746
 
        finally:
747
 
            self.tear_down()
748
 
            self.exited_runtime = True
749
 
 
750
 
    def tear_down(self):
751
 
        self.tear_down_client.destroy_environment()
752
 
        self.tear_down_client.torn_down = True
753
 
 
754
 
    @contextmanager
755
 
    def booted_context(self, upload_tools):
756
 
        with self.top_context() as machines:
757
 
            with self.bootstrap_context(machines):
758
 
                self.client.bootstrap(upload_tools)
759
 
            with self.runtime_context(machines):
760
 
                yield machines
761
 
 
762
 
 
763
 
class TestDeployJob(FakeHomeTestCase):
764
 
 
765
 
    @contextmanager
766
 
    def ds_cxt(self):
767
 
        env = JujuData('foo', {})
768
 
        client = fake_EnvJujuClient(env)
769
 
        bc_cxt = patch('jujupy.EnvJujuClient.by_version',
770
 
                       return_value=client)
771
 
        fc_cxt = patch('jujupy.SimpleEnvironment.from_config',
772
 
                       return_value=env)
773
 
        mgr = MagicMock()
774
 
        bm_cxt = patch('deploy_stack.BootstrapManager', autospec=True,
775
 
                       return_value=mgr)
776
 
        juju_cxt = patch('deploy_stack.EnvJujuClient.juju', autospec=True)
777
 
        ajr_cxt = patch('deploy_stack.assess_juju_run', autospec=True)
778
 
        dds_cxt = patch('deploy_stack.deploy_dummy_stack', autospec=True)
779
 
        with bc_cxt, fc_cxt, bm_cxt as bm_mock, juju_cxt, ajr_cxt, dds_cxt:
780
 
            yield client, bm_mock
 
562
class TestDeployJob(TestCase):
781
563
 
782
564
    @skipIf(sys.platform in ('win32', 'darwin'),
783
565
            'Not supported on Windown and OS X')
784
 
    def test_background_chaos_used(self):
785
 
        args = Namespace(
786
 
            env='base', juju_bin='/fake/juju', logs='log', temp_env_name='foo',
787
 
            charm_prefix=None, bootstrap_host=None, machine=None,
788
 
            series='trusty', debug=False, agent_url=None, agent_stream=None,
789
 
            keep_env=False, upload_tools=False, with_chaos=1, jes=False,
790
 
            region=None, verbose=False, upgrade=False,
791
 
        )
792
 
        with self.ds_cxt():
793
 
            with patch('deploy_stack.background_chaos',
794
 
                       autospec=True) as bc_mock:
795
 
                with patch('deploy_stack.assess_juju_relations',
796
 
                           autospec=True):
797
 
                    with patch('subprocess.Popen', autospec=True,
798
 
                               return_value=FakePopen('', '', 0)):
799
 
                        _deploy_job(args, 'local:trusty/', 'trusty')
800
 
        self.assertEqual(bc_mock.call_count, 1)
 
566
    @patch('jujupy.EnvJujuClient.by_version', side_effect=fake_EnvJujuClient)
 
567
    @patch('jujupy.SimpleEnvironment.from_config',
 
568
           side_effect=fake_SimpleEnvironment)
 
569
    @patch('deploy_stack.boot_context', autospec=True)
 
570
    @patch('deploy_stack.EnvJujuClient.juju', autospec=True)
 
571
    def test_background_chaos_used(self, *args):
 
572
        with patch('deploy_stack.background_chaos', autospec=True) as bc_mock:
 
573
            with patch('deploy_stack.deploy_dummy_stack', autospec=True):
 
574
                with patch('deploy_stack.assess_juju_run', autospec=True):
 
575
                    _deploy_job('foo', None, None, '', None, None, None, 'log',
 
576
                                None, None, None, None, None, None, 1, False,
 
577
                                False,)
801
578
        self.assertEqual(bc_mock.mock_calls[0][1][0], 'foo')
802
579
        self.assertEqual(bc_mock.mock_calls[0][1][2], 'log')
803
580
        self.assertEqual(bc_mock.mock_calls[0][1][3], 1)
804
581
 
805
582
    @skipIf(sys.platform in ('win32', 'darwin'),
806
583
            'Not supported on Windown and OS X')
807
 
    def test_background_chaos_not_used(self):
808
 
        args = Namespace(
809
 
            env='base', juju_bin='/fake/juju', logs='log', temp_env_name='foo',
810
 
            charm_prefix=None, bootstrap_host=None, machine=None,
811
 
            series='trusty', debug=False, agent_url=None, agent_stream=None,
812
 
            keep_env=False, upload_tools=False, with_chaos=0, jes=False,
813
 
            region=None, verbose=False, upgrade=False,
814
 
        )
815
 
        with self.ds_cxt():
816
 
            with patch('deploy_stack.background_chaos',
817
 
                       autospec=True) as bc_mock:
818
 
                with patch('deploy_stack.assess_juju_relations',
819
 
                           autospec=True):
820
 
                    with patch('subprocess.Popen', autospec=True,
821
 
                               return_value=FakePopen('', '', 0)):
822
 
                        _deploy_job(args, 'local:trusty/', 'trusty')
 
584
    @patch('jujupy.EnvJujuClient.by_version', side_effect=fake_EnvJujuClient)
 
585
    @patch('jujupy.SimpleEnvironment.from_config',
 
586
           side_effect=fake_SimpleEnvironment)
 
587
    @patch('deploy_stack.boot_context', autospec=True)
 
588
    @patch('deploy_stack.EnvJujuClient.juju', autospec=True)
 
589
    def test_background_chaos_not_used(self, *args):
 
590
        with patch('deploy_stack.background_chaos', autospec=True) as bc_mock:
 
591
            with patch('deploy_stack.deploy_dummy_stack', autospec=True):
 
592
                with patch('deploy_stack.assess_juju_run', autospec=True):
 
593
                    _deploy_job('foo', None, None, '', None, None, None, None,
 
594
                                None, None, None, None, None, None, 0, False,
 
595
                                False)
823
596
        self.assertEqual(bc_mock.call_count, 0)
824
597
 
825
 
    def test_region(self):
826
 
        args = Namespace(
827
 
            env='base', juju_bin='/fake/juju', logs='log', temp_env_name='foo',
828
 
            charm_prefix=None, bootstrap_host=None, machine=None,
829
 
            series='trusty', debug=False, agent_url=None, agent_stream=None,
830
 
            keep_env=False, upload_tools=False, with_chaos=0, jes=False,
831
 
            region='region-foo', verbose=False, upgrade=False,
832
 
        )
833
 
        with self.ds_cxt() as (client, bm_mock):
834
 
            with patch('deploy_stack.assess_juju_relations',
835
 
                       autospec=True):
836
 
                with patch('subprocess.Popen', autospec=True,
837
 
                           return_value=FakePopen('', '', 0)):
838
 
                    _deploy_job(args, 'local:trusty/', 'trusty')
839
 
                    jes = client.is_jes_enabled()
840
 
        bm_mock.assert_called_once_with(
841
 
            'foo', client, client, None, None, 'trusty', None, None,
842
 
            'region-foo', 'log', False, permanent=jes, jes_enabled=jes)
843
 
 
844
 
    def test_deploy_job_changes_series_with_win(self):
845
 
        args = Namespace(
846
 
            series='windows', temp_env_name=None, env=None, upgrade=None,
847
 
            charm_prefix=None, bootstrap_host=None, machine=None, logs=None,
848
 
            debug=None, juju_bin=None, agent_url=None, agent_stream=None,
849
 
            keep_env=None, upload_tools=None, with_chaos=None, jes=None,
850
 
            region=None, verbose=None)
851
 
        with patch('deploy_stack.deploy_job_parse_args', return_value=args,
852
 
                   autospec=True):
853
 
            with patch('deploy_stack._deploy_job', autospec=True) as ds_mock:
854
 
                deploy_job()
855
 
        ds_mock.assert_called_once_with(args, 'windows', 'trusty')
856
 
 
857
 
    def test_deploy_job_changes_series_with_centos(self):
858
 
        args = Namespace(
859
 
            series='centos', temp_env_name=None, env=None, upgrade=None,
860
 
            charm_prefix=None, bootstrap_host=None, machine=None, logs=None,
861
 
            debug=None, juju_bin=None, agent_url=None, agent_stream=None,
862
 
            keep_env=None, upload_tools=None, with_chaos=None, jes=None,
863
 
            region=None, verbose=None)
864
 
        with patch('deploy_stack.deploy_job_parse_args', return_value=args,
865
 
                   autospec=True):
866
 
            with patch('deploy_stack._deploy_job', autospec=True) as ds_mock:
867
 
                deploy_job()
868
 
        ds_mock.assert_called_once_with(args, 'centos', 'trusty')
869
 
 
870
 
 
871
 
class TestTestUpgrade(FakeHomeTestCase):
 
598
 
 
599
class TestTestUpgrade(TestCase):
872
600
 
873
601
    RUN_UNAME = (
874
602
        'juju', '--show-log', 'run', '-e', 'foo', '--format', 'json',
875
603
        '--service', 'dummy-source,dummy-sink', 'uname')
876
 
    STATUS = (
877
 
        'juju', '--show-log', 'show-status', '-m', 'foo', '--format', 'yaml')
878
 
    GET_ENV = ('juju', '--show-log', 'get-model-config', '-m', 'foo',
 
604
    VERSION = ('/bar/juju', '--version')
 
605
    STATUS = ('juju', '--show-log', 'status', '-e', 'foo')
 
606
    GET_ENV = ('juju', '--show-log', 'get-env', '-e', 'foo',
879
607
               'tools-metadata-url')
880
608
 
 
609
    def setUp(self):
 
610
        setup_test_logging(self)
 
611
 
881
612
    @classmethod
882
613
    def upgrade_output(cls, args, **kwargs):
883
614
        status = yaml.safe_dump({
884
615
            'machines': {'0': {
885
616
                'agent-state': 'started',
886
 
                'agent-version': '2.0-alpha3'}},
 
617
                'agent-version': '1.38'}},
887
618
            'services': {}})
888
619
        juju_run_out = json.dumps([
889
620
            {"MachineId": "1", "Stdout": "Linux\n"},
891
622
        output = {
892
623
            cls.STATUS: status,
893
624
            cls.RUN_UNAME: juju_run_out,
 
625
            cls.VERSION: '1.38',
894
626
            cls.GET_ENV: 'testing'
895
627
        }
896
 
        return FakePopen(output[args], '', 0)
 
628
        return output[args]
897
629
 
898
630
    @contextmanager
899
631
    def upgrade_mocks(self):
900
 
        with patch('subprocess.Popen', side_effect=self.upgrade_output,
 
632
        with patch('subprocess.check_output', side_effect=self.upgrade_output,
901
633
                   autospec=True) as co_mock:
902
634
            with patch('subprocess.check_call', autospec=True) as cc_mock:
903
635
                with patch('deploy_stack.check_token', autospec=True):
904
636
                    with patch('deploy_stack.get_random_string',
905
637
                               return_value="FAKETOKEN", autospec=True):
906
 
                        with patch('jujupy.EnvJujuClient.get_version',
907
 
                                   side_effect=lambda cls:
908
 
                                   '2.0-alpha3-arch-series'):
 
638
                        with patch('sys.stdout', autospec=True):
909
639
                            yield (co_mock, cc_mock)
910
640
 
911
641
    def test_assess_upgrade(self):
912
 
        env = JujuData('foo', {'type': 'foo'})
 
642
        env = SimpleEnvironment('foo', {'type': 'foo'})
913
643
        old_client = EnvJujuClient(env, None, '/foo/juju')
914
644
        with self.upgrade_mocks() as (co_mock, cc_mock):
915
645
            assess_upgrade(old_client, '/bar/juju')
916
646
        new_client = EnvJujuClient(env, None, '/bar/juju')
917
647
        assert_juju_call(self, cc_mock, new_client, (
918
 
            'juju', '--show-log', 'upgrade-juju', '-m', 'foo', '--version',
919
 
            '2.0-alpha3'), 0)
920
 
        self.assertEqual(cc_mock.call_count, 1)
921
 
        assert_juju_call(self, co_mock, new_client, self.GET_ENV, 0)
922
 
        assert_juju_call(self, co_mock, new_client, self.GET_ENV, 1)
923
 
        assert_juju_call(self, co_mock, new_client, self.STATUS, 2)
924
 
        self.assertEqual(co_mock.call_count, 3)
 
648
            'juju', '--show-log', 'upgrade-juju', '-e', 'foo', '--version',
 
649
            '1.38'), 0)
 
650
        assert_juju_call(self, cc_mock, new_client, (
 
651
            'juju', '--show-log', 'set', '-e', 'foo', 'dummy-source',
 
652
            'token=FAKETOKEN'), 1)
 
653
        self.assertEqual(cc_mock.call_count, 2)
 
654
        self.assertEqual(co_mock.mock_calls[0], call(self.VERSION))
 
655
        assert_juju_call(self, co_mock, new_client, self.GET_ENV, 1,
 
656
                         assign_stderr=True)
 
657
        assert_juju_call(self, co_mock, new_client, self.GET_ENV, 2,
 
658
                         assign_stderr=True)
 
659
        assert_juju_call(self, co_mock, new_client, self.STATUS, 3,
 
660
                         assign_stderr=True)
 
661
        assert_juju_call(self, co_mock, new_client, self.RUN_UNAME, 4,
 
662
                         assign_stderr=True)
 
663
        self.assertEqual(co_mock.call_count, 5)
925
664
 
926
665
    def test_mass_timeout(self):
927
666
        config = {'type': 'foo'}
928
 
        old_client = EnvJujuClient(JujuData('foo', config), None, '/foo/juju')
 
667
        old_client = EnvJujuClient(SimpleEnvironment('foo', config),
 
668
                                   None, '/foo/juju')
929
669
        with self.upgrade_mocks():
930
670
            with patch.object(EnvJujuClient, 'wait_for_version') as wfv_mock:
931
671
                assess_upgrade(old_client, '/bar/juju')
932
 
            wfv_mock.assert_called_once_with('2.0-alpha3', 600)
 
672
            wfv_mock.assert_called_once_with('1.38', 600)
933
673
            config['type'] = 'maas'
934
674
            with patch.object(EnvJujuClient, 'wait_for_version') as wfv_mock:
935
675
                assess_upgrade(old_client, '/bar/juju')
936
 
        wfv_mock.assert_called_once_with('2.0-alpha3', 1200)
937
 
 
938
 
 
939
 
class TestBootstrapManager(FakeHomeTestCase):
940
 
 
941
 
    def test_from_args(self):
942
 
        args = Namespace(
943
 
            env='foo', juju_bin='bar', debug=True, temp_env_name='baz',
944
 
            bootstrap_host='example.org', machine=['example.com'],
945
 
            series='angsty', agent_url='qux', agent_stream='escaped',
946
 
            region='eu-west-northwest-5', logs='pine', keep_env=True)
947
 
        with patch.object(SimpleEnvironment, 'from_config') as fc_mock:
948
 
            with patch.object(EnvJujuClient, 'by_version') as bv_mock:
949
 
                bs_manager = BootstrapManager.from_args(args)
950
 
        fc_mock.assert_called_once_with('foo')
951
 
        bv_mock.assert_called_once_with(fc_mock.return_value, 'bar',
952
 
                                        debug=True)
953
 
        self.assertEqual('baz', bs_manager.temp_env_name)
954
 
        self.assertIs(bv_mock.return_value, bs_manager.client)
955
 
        self.assertIs(bv_mock.return_value, bs_manager.tear_down_client)
956
 
        self.assertEqual('example.org', bs_manager.bootstrap_host)
957
 
        self.assertEqual(['example.com'], bs_manager.machines)
958
 
        self.assertEqual('angsty', bs_manager.series)
959
 
        self.assertEqual('qux', bs_manager.agent_url)
960
 
        self.assertEqual('escaped', bs_manager.agent_stream)
961
 
        self.assertEqual('eu-west-northwest-5', bs_manager.region)
962
 
        self.assertIs(True, bs_manager.keep_env)
963
 
        self.assertEqual('pine', bs_manager.log_dir)
964
 
        jes_enabled = bs_manager.client.is_jes_enabled.return_value
965
 
        self.assertEqual(jes_enabled, bs_manager.permanent)
966
 
        self.assertEqual(jes_enabled, bs_manager.jes_enabled)
967
 
        self.assertEqual({'0': 'example.org'}, bs_manager.known_hosts)
968
 
 
969
 
    def test_jes_not_permanent(self):
970
 
        with self.assertRaisesRegexp(ValueError, 'Cannot set permanent False'
971
 
                                     ' if jes_enabled is True.'):
972
 
            BootstrapManager(
973
 
                jes_enabled=True, permanent=False,
974
 
                temp_env_name=None, client=None, tear_down_client=None,
975
 
                bootstrap_host=None, machines=[], series=None, agent_url=None,
976
 
                agent_stream=None, region=None, log_dir=None, keep_env=None)
977
 
 
978
 
    def test_aws_machines_updates_bootstrap_host(self):
979
 
        client = FakeJujuClient()
980
 
        client.env.config['type'] = 'manual'
981
 
        bs_manager = BootstrapManager(
982
 
            'foobar', client, client, None, [], None, None, None, None,
983
 
            client.env.juju_home, False, False, False)
984
 
        with patch('deploy_stack.run_instances',
985
 
                   return_value=[('foo', 'aws.example.org')]):
986
 
            with patch('deploy_stack.destroy_job_instances'):
987
 
                with bs_manager.aws_machines():
988
 
                    self.assertEqual({'0': 'aws.example.org'},
989
 
                                     bs_manager.known_hosts)
990
 
 
991
 
    def test_from_args_no_host(self):
992
 
        args = Namespace(
993
 
            env='foo', juju_bin='bar', debug=True, temp_env_name='baz',
994
 
            bootstrap_host=None, machine=['example.com'],
995
 
            series='angsty', agent_url='qux', agent_stream='escaped',
996
 
            region='eu-west-northwest-5', logs='pine', keep_env=True)
997
 
        with patch.object(SimpleEnvironment, 'from_config'):
998
 
            with patch.object(EnvJujuClient, 'by_version'):
999
 
                bs_manager = BootstrapManager.from_args(args)
1000
 
        self.assertIs(None, bs_manager.bootstrap_host)
1001
 
        self.assertEqual({}, bs_manager.known_hosts)
1002
 
 
1003
 
    def make_client(self):
1004
 
        client = MagicMock()
1005
 
        client.env = SimpleEnvironment(
1006
 
            'foo', {'type': 'baz'}, use_context(self, temp_dir()))
1007
 
        client.is_jes_enabled.return_value = False
1008
 
        client.get_matching_agent_version.return_value = '3.14'
1009
 
        client.get_cache_path.return_value = get_cache_path(
1010
 
            client.env.juju_home)
1011
 
        return client
1012
 
 
1013
 
    def test_bootstrap_context_tear_down(self):
1014
 
        client = FakeJujuClient()
1015
 
        client.env.juju_home = use_context(self, temp_dir())
1016
 
        initial_home = client.env.juju_home
1017
 
        bs_manager = BootstrapManager(
1018
 
            'foobar', client, client, None, [], None, None, None, None,
1019
 
            client.env.juju_home, False, False, False)
1020
 
 
1021
 
        def check_config(client_, jes_enabled, try_jes=False):
1022
 
            self.assertEqual(0, client.is_jes_enabled.call_count)
1023
 
            jenv_path = get_jenv_path(client.env.juju_home, 'foobar')
1024
 
            self.assertFalse(os.path.exists(jenv_path))
1025
 
            environments_path = get_environments_path(client.env.juju_home)
1026
 
            self.assertTrue(os.path.isfile(environments_path))
1027
 
            self.assertNotEqual(initial_home, client.env.juju_home)
1028
 
 
1029
 
        ije_cxt = patch.object(client, 'is_jes_enabled')
1030
 
        with patch('deploy_stack.tear_down',
1031
 
                   side_effect=check_config) as td_mock, ije_cxt:
1032
 
            with bs_manager.bootstrap_context([]):
1033
 
                td_mock.assert_called_once_with(client, False, try_jes=True)
1034
 
 
1035
 
    def test_bootstrap_context_tear_down_jenv(self):
1036
 
        client = self.make_client()
1037
 
        initial_home = client.env.juju_home
1038
 
        jenv_path = get_jenv_path(client.env.juju_home, 'foobar')
1039
 
        os.makedirs(os.path.dirname(jenv_path))
1040
 
        with open(jenv_path, 'w'):
1041
 
            pass
1042
 
 
1043
 
        bs_manager = BootstrapManager(
1044
 
            'foobar', client, client, None, [], None, None, None, None,
1045
 
            client.env.juju_home, False, False, False)
1046
 
 
1047
 
        def check_config(client_, jes_enabled, try_jes=False):
1048
 
            self.assertEqual(0, client.is_jes_enabled.call_count)
1049
 
            self.assertTrue(os.path.isfile(jenv_path))
1050
 
            environments_path = get_environments_path(client.env.juju_home)
1051
 
            self.assertFalse(os.path.exists(environments_path))
1052
 
            self.assertEqual(initial_home, client.env.juju_home)
1053
 
 
1054
 
        with patch('deploy_stack.tear_down',
1055
 
                   side_effect=check_config) as td_mock:
1056
 
            with bs_manager.bootstrap_context([]):
1057
 
                td_mock.assert_called_once_with(client, False, try_jes=False)
1058
 
 
1059
 
    def test_bootstrap_context_tear_down_client(self):
1060
 
        client = self.make_client()
1061
 
        tear_down_client = self.make_client()
1062
 
        tear_down_client.env = client.env
1063
 
        bs_manager = BootstrapManager(
1064
 
            'foobar', client, tear_down_client, None, [], None, None, None,
1065
 
            None, client.env.juju_home, False, False, False)
1066
 
 
1067
 
        def check_config(client_, jes_enabled, try_jes=False):
1068
 
            self.assertEqual(0, client.is_jes_enabled.call_count)
1069
 
            tear_down_client.is_jes_enabled.assert_called_once_with()
1070
 
 
1071
 
        with patch('deploy_stack.tear_down',
1072
 
                   side_effect=check_config) as td_mock:
1073
 
            with bs_manager.bootstrap_context([]):
1074
 
                td_mock.assert_called_once_with(tear_down_client,
1075
 
                                                False, try_jes=True)
1076
 
 
1077
 
    def test_bootstrap_context_tear_down_client_jenv(self):
1078
 
        client = self.make_client()
1079
 
        tear_down_client = self.make_client()
1080
 
        tear_down_client.env = client.env
1081
 
        jenv_path = get_jenv_path(client.env.juju_home, 'foobar')
1082
 
        os.makedirs(os.path.dirname(jenv_path))
1083
 
        with open(jenv_path, 'w'):
1084
 
            pass
1085
 
 
1086
 
        bs_manager = BootstrapManager(
1087
 
            'foobar', client, tear_down_client,
1088
 
            None, [], None, None, None, None, client.env.juju_home, False,
1089
 
            False, False)
1090
 
 
1091
 
        def check_config(client_, jes_enabled, try_jes=False):
1092
 
            self.assertEqual(0, client.is_jes_enabled.call_count)
1093
 
            tear_down_client.is_jes_enabled.assert_called_once_with()
1094
 
 
1095
 
        with patch('deploy_stack.tear_down',
1096
 
                   side_effect=check_config) as td_mock:
1097
 
            with bs_manager.bootstrap_context([]):
1098
 
                td_mock.assert_called_once_with(tear_down_client, False,
1099
 
                                                try_jes=False)
1100
 
 
1101
 
    def test_bootstrap_context_no_set_home(self):
1102
 
        orig_home = get_juju_home()
1103
 
        client = self.make_client()
1104
 
        jenv_path = get_jenv_path(client.env.juju_home, 'foobar')
1105
 
        os.makedirs(os.path.dirname(jenv_path))
1106
 
        with open(jenv_path, 'w'):
1107
 
            pass
1108
 
 
1109
 
        bs_manager = BootstrapManager(
1110
 
            'foobar', client, client, None, [], None, None, None, None,
1111
 
            client.env.juju_home, False, False, False)
1112
 
        with bs_manager.bootstrap_context([]):
1113
 
            self.assertEqual(orig_home, get_juju_home())
1114
 
 
1115
 
    def test_bootstrap_context_calls_update_env(self):
1116
 
        client = FakeJujuClient()
1117
 
        client.env.juju_home = use_context(self, temp_dir())
1118
 
        ue_mock = use_context(
1119
 
            self, patch('deploy_stack.update_env', wraps=update_env))
1120
 
        wfp_mock = use_context(
1121
 
            self, patch('deploy_stack.wait_for_port', autospec=True))
1122
 
        bs_manager = BootstrapManager(
1123
 
            'bar', client, client, None,
1124
 
            [], 'wacky', 'url', 'devel', None, client.env.juju_home, False,
1125
 
            True, True)
1126
 
        bs_manager.known_hosts['0'] = 'bootstrap.example.org'
1127
 
        with bs_manager.bootstrap_context([]):
1128
 
            pass
1129
 
        ue_mock.assert_called_with(
1130
 
            client.env, 'bar', series='wacky',
1131
 
            bootstrap_host='bootstrap.example.org',
1132
 
            agent_url='url', agent_stream='devel', region=None)
1133
 
        wfp_mock.assert_called_once_with(
1134
 
            'bootstrap.example.org', 22, timeout=120)
1135
 
 
1136
 
    def test_bootstrap_context_calls_update_env_omit(self):
1137
 
        client = FakeJujuClient()
1138
 
        client.env.juju_home = use_context(self, temp_dir())
1139
 
        ue_mock = use_context(
1140
 
            self, patch('deploy_stack.update_env', wraps=update_env))
1141
 
        wfp_mock = use_context(
1142
 
            self, patch('deploy_stack.wait_for_port', autospec=True))
1143
 
        bs_manager = BootstrapManager(
1144
 
            'bar', client, client, None,
1145
 
            [], 'wacky', 'url', 'devel', None, client.env.juju_home, True,
1146
 
            True, True)
1147
 
        bs_manager.known_hosts['0'] = 'bootstrap.example.org'
1148
 
        with bs_manager.bootstrap_context(
1149
 
                [], omit_config={'bootstrap_host', 'series'}):
1150
 
            pass
1151
 
        ue_mock.assert_called_with(client.env, 'bar', agent_url='url',
1152
 
                                   agent_stream='devel', region=None)
1153
 
        wfp_mock.assert_called_once_with(
1154
 
            'bootstrap.example.org', 22, timeout=120)
1155
 
 
1156
 
    def test_tear_down_requires_same_env(self):
1157
 
        client = self.make_client()
1158
 
        client.env.juju_home = 'foobar'
1159
 
        tear_down_client = self.make_client()
1160
 
        tear_down_client.env.juju_home = 'barfoo'
1161
 
        bs_manager = BootstrapManager(
1162
 
            'foobar', client, tear_down_client,
1163
 
            None, [], None, None, None, None, client.env.juju_home, False,
1164
 
            False, False)
1165
 
 
1166
 
        def check_home(foo, bar, try_jes):
1167
 
            self.assertEqual(client.env.juju_home,
1168
 
                             tear_down_client.env.juju_home)
1169
 
 
1170
 
        with self.assertRaisesRegexp(AssertionError,
1171
 
                                     'Tear down client needs same env'):
1172
 
            with patch('deploy_stack.tear_down', autospec=True,
1173
 
                       side_effect=check_home):
1174
 
                bs_manager.tear_down()
1175
 
        self.assertEqual('barfoo', tear_down_client.env.juju_home)
1176
 
 
1177
 
    def test_dump_all_no_jes_one_model(self):
1178
 
        client = FakeJujuClient()
1179
 
        client.bootstrap()
1180
 
        with temp_dir() as log_dir:
1181
 
            bs_manager = BootstrapManager(
1182
 
                'foobar', client, client,
1183
 
                None, [], None, None, None, None, log_dir, False,
1184
 
                False, jes_enabled=False)
1185
 
            with patch('deploy_stack.dump_env_logs_known_hosts'):
1186
 
                with patch.object(client, 'iter_model_clients') as imc_mock:
1187
 
                    bs_manager.dump_all_logs()
1188
 
        self.assertEqual(0, imc_mock.call_count)
1189
 
 
1190
 
    def test_dump_all_multi_model(self):
1191
 
        client = FakeJujuClient(jes_enabled=True)
1192
 
        client.bootstrap()
1193
 
        with temp_dir() as log_dir:
1194
 
            bs_manager = BootstrapManager(
1195
 
                'foobar', client, client,
1196
 
                None, [], None, None, None, None, log_dir, False,
1197
 
                permanent=True, jes_enabled=True)
1198
 
            with patch('deploy_stack.dump_env_logs_known_hosts') as del_mock:
1199
 
                bs_manager.dump_all_logs()
1200
 
 
1201
 
        clients = dict((c[1][0].env.environment, c[1][0])
1202
 
                       for c in del_mock.mock_calls)
1203
 
 
1204
 
        self.assertItemsEqual(
1205
 
            [call(client, os.path.join(log_dir, 'name'), None, {}),
1206
 
             call(clients['admin'], os.path.join(log_dir, 'admin'),
1207
 
                  'foo/models/cache.yaml', {})],
1208
 
            del_mock.mock_calls)
1209
 
 
1210
 
    def test_dump_all_multi_model_iter_failure(self):
1211
 
        client = FakeJujuClient(jes_enabled=True)
1212
 
        client.bootstrap()
1213
 
        with temp_dir() as log_dir:
1214
 
            bs_manager = BootstrapManager(
1215
 
                'foobar', client, client,
1216
 
                None, [], None, None, None, None, log_dir, False,
1217
 
                permanent=True, jes_enabled=True)
1218
 
            with patch('deploy_stack.dump_env_logs_known_hosts') as del_mock:
1219
 
                with patch.object(client, 'iter_model_clients',
1220
 
                                  side_effect=Exception):
1221
 
                    bs_manager.dump_all_logs()
1222
 
 
1223
 
        clients = dict((c[1][0].env.environment, c[1][0])
1224
 
                       for c in del_mock.mock_calls)
1225
 
 
1226
 
        self.assertItemsEqual(
1227
 
            [call(client, os.path.join(log_dir, 'name'), None, {}),
1228
 
             call(clients['admin'], os.path.join(log_dir, 'admin'),
1229
 
                  'foo/models/cache.yaml', {})],
1230
 
            del_mock.mock_calls)
1231
 
 
1232
 
    def test_dump_all_logs_uses_known_hosts(self):
1233
 
        client = FakeJujuClient()
1234
 
        with temp_dir() as log_dir:
1235
 
            bs_manager = BootstrapManager(
1236
 
                'foobar', client, client,
1237
 
                None, [], None, None, None, None, log_dir, False,
1238
 
                False, False)
1239
 
            bs_manager.known_hosts['2'] = 'example.org'
1240
 
            client.bootstrap()
1241
 
            with patch('deploy_stack.dump_env_logs_known_hosts') as del_mock:
1242
 
                bs_manager.dump_all_logs()
1243
 
        del_mock.assert_called_once_with(
1244
 
            client, os.path.join(log_dir, 'name'),
1245
 
            'foo/environments/name.jenv', {
1246
 
                '2': 'example.org',
1247
 
                })
1248
 
 
1249
 
    def test_runtime_context_looks_up_host(self):
1250
 
        client = FakeJujuClient()
1251
 
        client.bootstrap()
1252
 
        bs_manager = BootstrapManager(
1253
 
            'foobar', client, client,
1254
 
            None, [], None, None, None, None, client.env.juju_home, False,
1255
 
            False, False)
1256
 
        with patch.object(bs_manager, 'dump_all_logs', autospec=True):
1257
 
            with bs_manager.runtime_context([]):
1258
 
                self.assertEqual({
1259
 
                    '0': '0.example.com'}, bs_manager.known_hosts)
1260
 
 
1261
 
    @patch('deploy_stack.dump_env_logs_known_hosts', autospec=True)
1262
 
    def test_runtime_context_addable_machines_no_known_hosts(self, del_mock):
1263
 
        client = FakeJujuClient()
1264
 
        client.bootstrap()
1265
 
        bs_manager = BootstrapManager(
1266
 
            'foobar', client, client,
1267
 
            None, [], None, None, None, None, client.env.juju_home, False,
1268
 
            False, False)
1269
 
        bs_manager.known_hosts = {}
1270
 
        with patch.object(bs_manager.client, 'add_ssh_machines',
1271
 
                          autospec=True) as ads_mock:
1272
 
            with patch.object(bs_manager, 'dump_all_logs', autospec=True):
1273
 
                with bs_manager.runtime_context(['baz']):
1274
 
                    ads_mock.assert_called_once_with(['baz'])
1275
 
 
1276
 
    @patch('deploy_stack.BootstrapManager.dump_all_logs', autospec=True)
1277
 
    def test_runtime_context_addable_machines_with_known_hosts(self, dal_mock):
1278
 
        client = FakeJujuClient()
1279
 
        with temp_dir() as log_dir:
1280
 
            bs_manager = BootstrapManager(
1281
 
                'foobar', client, client,
1282
 
                None, [], None, None, None, None, log_dir, False,
1283
 
                False, False)
1284
 
            bs_manager.known_hosts['0'] = 'example.org'
1285
 
            with patch.object(bs_manager.client, 'add_ssh_machines',
1286
 
                              autospec=True) as ads_mock:
1287
 
                with bs_manager.runtime_context(['baz']):
1288
 
                    ads_mock.assert_called_once_with(['baz'])
1289
 
 
1290
 
    def test_booted_context_handles_logged_exception(self):
1291
 
        client = FakeJujuClient()
1292
 
        bs_manager = BootstrapManager(
1293
 
            'foobar', client, client,
1294
 
            None, [], None, None, None, None, client.env.juju_home, False,
1295
 
            False, False)
1296
 
        with temp_dir() as juju_home:
1297
 
            client.env.juju_home = juju_home
1298
 
            with self.assertRaises(SystemExit):
1299
 
                with bs_manager.booted_context(False):
1300
 
                    raise LoggedException()
1301
 
 
1302
 
    def test_booted_context_omits_supported(self):
1303
 
        client = FakeJujuClient(jes_enabled=True)
1304
 
        client.env.juju_home = use_context(self, temp_dir())
1305
 
        client.bootstrap_replaces = {'agent-version', 'series',
1306
 
                                     'bootstrap-host', 'agent-stream'}
1307
 
        ue_mock = use_context(
1308
 
            self, patch('deploy_stack.update_env', wraps=update_env))
1309
 
        wfp_mock = use_context(
1310
 
            self, patch('deploy_stack.wait_for_port', autospec=True))
1311
 
        bs_manager = BootstrapManager(
1312
 
            'bar', client, client, 'bootstrap.example.org',
1313
 
            [], 'wacky', 'url', 'devel', None, client.env.juju_home, False,
1314
 
            True, True)
1315
 
        with patch.object(bs_manager, 'runtime_context'):
1316
 
            with bs_manager.booted_context([]):
1317
 
                pass
1318
 
        self.assertEqual({
1319
 
            'name': 'bar',
1320
 
            'default-series': 'wacky',
1321
 
            'tools-metadata-url': 'url',
1322
 
            'type': 'foo',
1323
 
            }, client.get_model_config())
1324
 
        ue_mock.assert_called_with(client.env, 'bar', agent_url='url',
1325
 
                                   region=None)
1326
 
        wfp_mock.assert_called_once_with(
1327
 
            'bootstrap.example.org', 22, timeout=120)
1328
 
 
1329
 
 
1330
 
class TestBootContext(FakeHomeTestCase):
 
676
        wfv_mock.assert_called_once_with('1.38', 1200)
 
677
 
 
678
 
 
679
class TestBootContext(TestCase):
1331
680
 
1332
681
    def setUp(self):
1333
 
        super(TestBootContext, self).setUp()
 
682
        self.addContext(patch('subprocess.Popen', side_effect=Exception))
1334
683
        self.addContext(patch('sys.stdout'))
1335
684
 
1336
685
    def addContext(self, cxt):
1338
687
 
1339
688
        :return: The value emitted by cxt.__enter__.
1340
689
        """
1341
 
        return use_context(self, cxt)
 
690
        result = cxt.__enter__()
 
691
        self.addCleanup(lambda: cxt.__exit__(None, None, None))
 
692
        return result
1342
693
 
1343
694
    @contextmanager
1344
 
    def bc_context(self, client, log_dir=None, jes=None, keep_env=False):
1345
 
        dal_mock = self.addContext(
1346
 
            patch('deploy_stack.BootstrapManager.dump_all_logs'))
 
695
    def bc_context(self, client, log_dir=None, jes=False, keep_env=False):
 
696
        dl_mock = self.addContext(patch('deploy_stack.dump_env_logs'))
1347
697
        self.addContext(patch('deploy_stack.get_machine_dns_name',
1348
698
                              return_value='foo', autospec=True))
1349
 
        c_mock = self.addContext(patch('subprocess.call', autospec=True,
1350
 
                                 return_value=0))
 
699
        c_mock = self.addContext(patch('subprocess.call', autospec=True))
1351
700
        if jes:
1352
 
            output = jes
1353
 
            po_count = 0
 
701
            co_return = 'system'
1354
702
        else:
1355
 
            output = ''
1356
 
            po_count = 2
1357
 
        with patch('subprocess.Popen', autospec=True,
1358
 
                   return_value=FakePopen(output, '', 0)) as po_mock:
 
703
            co_return = ''
 
704
        with patch('subprocess.check_output', autospec=True,
 
705
                   return_value=co_return) as co_mock:
1359
706
            yield
1360
 
        for help_index in range(po_count):
1361
 
            assert_juju_call(self, po_mock, client, (
1362
 
                'juju', '--show-log', 'help', 'commands'),
1363
 
                call_index=help_index)
1364
 
        self.assertEqual(po_count, po_mock.call_count)
1365
 
        dal_mock.assert_called_once_with()
 
707
        assert_juju_call(self, co_mock, client, (
 
708
            'juju', '--show-log', 'help', 'commands'), assign_stderr=True)
 
709
        if jes:
 
710
            runtime_config = os.path.join(client.juju_home, 'environments',
 
711
                                          'cache.yaml')
 
712
        else:
 
713
            runtime_config = os.path.join(client.juju_home, 'environments',
 
714
                                          'bar.jenv')
 
715
        dl_mock.assert_called_once_with(
 
716
            client, 'foo', log_dir, runtime_config=runtime_config)
1366
717
        if keep_env:
1367
 
            tear_down_count = 1
 
718
            self.assertEqual(c_mock.call_count, 0)
1368
719
        else:
1369
 
            tear_down_count = 2
1370
 
        for call_index in range(tear_down_count):
1371
720
            if jes:
1372
721
                assert_juju_call(
1373
722
                    self, c_mock, client, get_timeout_prefix(600) + (
1374
 
                        'juju', '--show-log', jes, 'bar', '-y'), call_index)
 
723
                        'juju', '--show-log', 'system', 'kill', 'bar', '-y'))
1375
724
            else:
1376
725
                assert_juju_call(
1377
726
                    self, c_mock, client, get_timeout_prefix(600) + (
1378
727
                        'juju', '--show-log', 'destroy-environment', 'bar',
1379
 
                        '-y'), call_index)
1380
 
        self.assertEqual(tear_down_count, c_mock.call_count)
 
728
                        '--force', '-y'))
1381
729
 
1382
730
    def test_bootstrap_context(self):
1383
731
        cc_mock = self.addContext(patch('subprocess.check_call'))
1384
 
        client = EnvJujuClient(JujuData(
1385
 
            'foo', {'type': 'paas', 'region': 'qux'}), '1.23', 'path')
1386
 
        with self.bc_context(client, 'log_dir', jes='kill-controller'):
1387
 
            with observable_temp_file() as config_file:
1388
 
                with boot_context('bar', client, None, [], None, None, None,
1389
 
                                  'log_dir', keep_env=False,
1390
 
                                  upload_tools=False):
1391
 
                    pass
1392
 
        assert_juju_call(self, cc_mock, client, (
1393
 
            'juju', '--show-log', 'bootstrap', '--constraints',
1394
 
            'mem=2G', 'bar', 'paas/qux', '--config', config_file.name,
1395
 
            '--default-model', 'bar', '--agent-version', '1.23'), 0)
1396
 
        assert_juju_call(self, cc_mock, client, (
1397
 
            'juju', '--show-log', 'show-status', '-m', 'bar',
1398
 
            '--format', 'yaml'), 1)
1399
 
 
1400
 
    def test_bootstrap_context_non_jes(self):
1401
 
        cc_mock = self.addContext(patch('subprocess.check_call'))
1402
 
        client = EnvJujuClient1X(SimpleEnvironment(
 
732
        client = EnvJujuClient(SimpleEnvironment(
1403
733
            'foo', {'type': 'paas'}), '1.23', 'path')
1404
734
        with self.bc_context(client, 'log_dir'):
1405
735
            with boot_context('bar', client, None, [], None, None, None,
1409
739
            'juju', '--show-log', 'bootstrap', '-e', 'bar', '--constraints',
1410
740
            'mem=2G'), 0)
1411
741
        assert_juju_call(self, cc_mock, client, (
1412
 
            'juju', '--show-log', 'status', '-e', 'bar',
1413
 
            '--format', 'yaml'), 1)
 
742
            'juju', '--show-log', 'status', '-e', 'bar'), 1)
1414
743
 
1415
744
    def test_keep_env(self):
1416
745
        cc_mock = self.addContext(patch('subprocess.check_call'))
1417
 
        client = EnvJujuClient(JujuData(
1418
 
            'foo', {'type': 'paas', 'region': 'qux'}), '1.23', 'path')
1419
 
        with self.bc_context(client, keep_env=True, jes='kill-controller'):
1420
 
            with observable_temp_file() as config_file:
1421
 
                with boot_context('bar', client, None, [], None, None, None,
1422
 
                                  None, keep_env=True, upload_tools=False):
1423
 
                    pass
1424
 
        assert_juju_call(self, cc_mock, client, (
1425
 
            'juju', '--show-log', 'bootstrap', '--constraints',
1426
 
            'mem=2G', 'bar', 'paas/qux', '--config', config_file.name,
1427
 
            '--default-model', 'bar', '--agent-version', '1.23'), 0)
1428
 
        assert_juju_call(self, cc_mock, client, (
1429
 
            'juju', '--show-log', 'show-status', '-m', 'bar',
1430
 
            '--format', 'yaml'), 1)
1431
 
 
1432
 
    def test_keep_env_non_jes(self):
1433
 
        cc_mock = self.addContext(patch('subprocess.check_call'))
1434
 
        client = EnvJujuClient1X(SimpleEnvironment(
 
746
        client = EnvJujuClient(SimpleEnvironment(
1435
747
            'foo', {'type': 'paas'}), '1.23', 'path')
1436
748
        with self.bc_context(client, keep_env=True):
1437
749
            with boot_context('bar', client, None, [], None, None, None, None,
1441
753
            'juju', '--show-log', 'bootstrap', '-e', 'bar', '--constraints',
1442
754
            'mem=2G'), 0)
1443
755
        assert_juju_call(self, cc_mock, client, (
1444
 
            'juju', '--show-log', 'status', '-e', 'bar',
1445
 
            '--format', 'yaml'), 1)
 
756
            'juju', '--show-log', 'status', '-e', 'bar'), 1)
1446
757
 
1447
758
    def test_upload_tools(self):
1448
759
        cc_mock = self.addContext(patch('subprocess.check_call'))
1449
 
        client = EnvJujuClient(JujuData(
1450
 
            'foo', {'type': 'paas', 'region': 'qux'}), '1.23', 'path')
1451
 
        with self.bc_context(client, jes='kill-controller'):
1452
 
            with observable_temp_file() as config_file:
1453
 
                with boot_context('bar', client, None, [], None, None, None,
1454
 
                                  None, keep_env=False, upload_tools=True):
1455
 
                    pass
1456
 
        assert_juju_call(self, cc_mock, client, (
1457
 
            'juju', '--show-log', 'bootstrap', '--upload-tools',
1458
 
            '--constraints', 'mem=2G', 'bar', 'paas/qux', '--config',
1459
 
            config_file.name, '--default-model', 'bar'), 0)
1460
 
 
1461
 
    def test_upload_tools_non_jes(self):
1462
 
        cc_mock = self.addContext(patch('subprocess.check_call'))
1463
 
        client = EnvJujuClient1X(SimpleEnvironment(
 
760
        client = EnvJujuClient(SimpleEnvironment(
1464
761
            'foo', {'type': 'paas'}), '1.23', 'path')
1465
762
        with self.bc_context(client):
1466
763
            with boot_context('bar', client, None, [], None, None, None, None,
1470
767
            'juju', '--show-log', 'bootstrap', '-e', 'bar', '--upload-tools',
1471
768
            '--constraints', 'mem=2G'), 0)
1472
769
 
1473
 
    def test_calls_update_env_2(self):
1474
 
        cc_mock = self.addContext(patch('subprocess.check_call'))
1475
 
        client = EnvJujuClient(JujuData(
1476
 
            'foo', {'type': 'paas', 'region': 'qux'}), '1.23', 'path')
1477
 
        ue_mock = self.addContext(
1478
 
            patch('deploy_stack.update_env', wraps=update_env))
1479
 
        with self.bc_context(client, jes='kill-controller'):
1480
 
            with observable_temp_file() as config_file:
1481
 
                with boot_context('bar', client, None, [], 'wacky', 'url',
1482
 
                                  'devel', None, keep_env=False,
1483
 
                                  upload_tools=False):
1484
 
                    pass
1485
 
        ue_mock.assert_called_with(
1486
 
            client.env, 'bar', agent_url='url', agent_stream='devel',
1487
 
            series='wacky', bootstrap_host=None, region=None)
1488
 
        assert_juju_call(self, cc_mock, client, (
1489
 
            'juju', '--show-log', 'bootstrap', '--constraints', 'mem=2G',
1490
 
            'bar', 'paas/qux', '--config', config_file.name,
1491
 
            '--default-model', 'bar', '--agent-version', '1.23',
1492
 
            '--bootstrap-series', 'wacky'), 0)
1493
 
 
1494
 
    def test_calls_update_env_1(self):
1495
 
        cc_mock = self.addContext(patch('subprocess.check_call'))
1496
 
        client = EnvJujuClient1X(SimpleEnvironment(
1497
 
            'foo', {'type': 'paas'}), '1.23', 'path')
1498
 
        ue_mock = self.addContext(
1499
 
            patch('deploy_stack.update_env', wraps=update_env))
1500
 
        with self.bc_context(client):
1501
 
            with boot_context('bar', client, None, [], 'wacky', 'url', 'devel',
1502
 
                              None, keep_env=False, upload_tools=False):
1503
 
                pass
1504
 
        ue_mock.assert_called_with(
1505
 
            client.env, 'bar', series='wacky', bootstrap_host=None,
1506
 
            agent_url='url', agent_stream='devel', region=None)
1507
 
        assert_juju_call(self, cc_mock, client, (
1508
 
            'juju', '--show-log', 'bootstrap', '-e', 'bar',
1509
 
            '--constraints', 'mem=2G'), 0)
1510
 
 
1511
 
    def test_calls_update_env_non_jes(self):
1512
 
        cc_mock = self.addContext(patch('subprocess.check_call'))
1513
 
        client = EnvJujuClient1X(SimpleEnvironment(
1514
 
            'foo', {'type': 'paas'}), '1.23', 'path')
1515
 
        ue_mock = self.addContext(
1516
 
            patch('deploy_stack.update_env', wraps=update_env))
1517
 
        with self.bc_context(client):
1518
 
            with boot_context('bar', client, None, [], 'wacky', 'url', 'devel',
1519
 
                              None, keep_env=False, upload_tools=False):
1520
 
                pass
1521
 
        ue_mock.assert_called_with(
1522
 
            client.env, 'bar', series='wacky', bootstrap_host=None,
1523
 
            agent_url='url', agent_stream='devel', region=None)
 
770
    def test_calls_update_env(self):
 
771
        cc_mock = self.addContext(patch('subprocess.check_call'))
 
772
        client = EnvJujuClient(SimpleEnvironment(
 
773
            'foo', {'type': 'paas'}), '1.23', 'path')
 
774
        ue_mock = self.addContext(
 
775
            patch('deploy_stack.update_env', wraps=update_env))
 
776
        with self.bc_context(client):
 
777
            with boot_context('bar', client, None, [], 'wacky', 'url', 'devel',
 
778
                              None, keep_env=False, upload_tools=False):
 
779
                pass
 
780
        ue_mock.assert_called_with(
 
781
            client.env, 'bar', series='wacky', bootstrap_host=None,
 
782
            agent_url='url', agent_stream='devel')
1524
783
        assert_juju_call(self, cc_mock, client, (
1525
784
            'juju', '--show-log', 'bootstrap', '-e', 'bar',
1526
785
            '--constraints', 'mem=2G'), 0)
1530
789
        class FakeException(Exception):
1531
790
            """A sentry exception to be raised by bootstrap."""
1532
791
 
1533
 
        client = EnvJujuClient(JujuData(
1534
 
            'foo', {'type': 'paas'}), '1.23', 'path')
1535
 
        self.addContext(patch('deploy_stack.get_machine_dns_name',
1536
 
                              return_value='foo'))
1537
 
        self.addContext(patch('subprocess.check_call'))
1538
 
        call_mock = self.addContext(patch('subprocess.call', return_value=0))
1539
 
        po_mock = self.addContext(patch(
1540
 
            'subprocess.Popen', autospec=True,
1541
 
            return_value=FakePopen('kill-controller', '', 0)))
1542
 
        self.addContext(patch('deploy_stack.wait_for_port'))
1543
 
        fake_exception = FakeException()
1544
 
        self.addContext(patch.object(client, 'bootstrap',
1545
 
                                     side_effect=fake_exception))
1546
 
        crl_mock = self.addContext(patch('deploy_stack.copy_remote_logs'))
1547
 
        al_mock = self.addContext(patch('deploy_stack.archive_logs'))
1548
 
        le_mock = self.addContext(patch('logging.exception'))
1549
 
        with self.assertRaises(SystemExit):
1550
 
            with boot_context('bar', client, 'baz', [], None, None, None,
1551
 
                              'log_dir', keep_env=False, upload_tools=True):
1552
 
                pass
1553
 
        le_mock.assert_called_once_with(fake_exception)
1554
 
        self.assertEqual(crl_mock.call_count, 1)
1555
 
        call_args = crl_mock.call_args[0]
1556
 
        self.assertIsInstance(call_args[0], _Remote)
1557
 
        self.assertEqual(call_args[0].get_address(), 'baz')
1558
 
        self.assertEqual(call_args[1], 'log_dir')
1559
 
        al_mock.assert_called_once_with('log_dir')
1560
 
        timeout_path = get_timeout_path()
1561
 
        assert_juju_call(self, call_mock, client, (
1562
 
            sys.executable, timeout_path, '600.00', '--',
1563
 
            'juju', '--show-log', 'kill-controller', 'bar', '-y'
1564
 
            ), 0)
1565
 
        assert_juju_call(self, call_mock, client, (
1566
 
            sys.executable, timeout_path, '600.00', '--',
1567
 
            'juju', '--show-log', 'kill-controller', 'bar', '-y'
1568
 
            ), 1)
1569
 
        self.assertEqual(2, call_mock.call_count)
1570
 
        self.assertEqual(0, po_mock.call_count)
1571
 
 
1572
 
    def test_with_bootstrap_failure_non_jes(self):
1573
 
 
1574
 
        class FakeException(Exception):
1575
 
            """A sentry exception to be raised by bootstrap."""
1576
 
 
1577
 
        client = EnvJujuClient1X(SimpleEnvironment(
1578
 
            'foo', {'type': 'paas'}), '1.23', 'path')
1579
 
        self.addContext(patch('deploy_stack.get_machine_dns_name',
1580
 
                              return_value='foo'))
1581
 
        self.addContext(patch('subprocess.check_call'))
1582
 
        call_mock = self.addContext(patch('subprocess.call', return_value=0))
1583
 
        po_mock = self.addContext(patch('subprocess.Popen', autospec=True,
1584
 
                                        return_value=FakePopen('', '', 0)))
1585
 
        self.addContext(patch('deploy_stack.wait_for_port'))
1586
 
        fake_exception = FakeException()
1587
 
        self.addContext(patch.object(client, 'bootstrap',
1588
 
                                     side_effect=fake_exception))
1589
 
        crl_mock = self.addContext(patch('deploy_stack.copy_remote_logs'))
1590
 
        al_mock = self.addContext(patch('deploy_stack.archive_logs'))
1591
 
        le_mock = self.addContext(patch('logging.exception'))
1592
 
        with self.assertRaises(SystemExit):
1593
 
            with boot_context('bar', client, 'baz', [], None, None, None,
1594
 
                              'log_dir', keep_env=False, upload_tools=True):
1595
 
                pass
1596
 
        le_mock.assert_called_once_with(fake_exception)
1597
 
        self.assertEqual(crl_mock.call_count, 1)
1598
 
        call_args = crl_mock.call_args[0]
1599
 
        self.assertIsInstance(call_args[0], _Remote)
1600
 
        self.assertEqual(call_args[0].get_address(), 'baz')
1601
 
        self.assertEqual(call_args[1], 'log_dir')
1602
 
        al_mock.assert_called_once_with('log_dir')
1603
 
        timeout_path = get_timeout_path()
1604
 
        assert_juju_call(self, call_mock, client, (
1605
 
            sys.executable, timeout_path, '600.00', '--',
1606
 
            'juju', '--show-log', 'destroy-environment', 'bar', '-y'
1607
 
            ), 0)
1608
 
        assert_juju_call(self, call_mock, client, (
1609
 
            sys.executable, timeout_path, '600.00', '--',
1610
 
            'juju', '--show-log', 'destroy-environment', 'bar', '-y'
1611
 
            ), 1)
1612
 
        self.assertEqual(2, call_mock.call_count)
1613
 
        assert_juju_call(self, po_mock, client, (
1614
 
            'juju', '--show-log', 'help', 'commands'), 0)
1615
 
        assert_juju_call(self, po_mock, client, (
1616
 
            'juju', '--show-log', 'help', 'commands'), 1)
1617
 
        self.assertEqual(2, po_mock.call_count)
 
792
        client = EnvJujuClient(SimpleEnvironment(
 
793
            'foo', {'type': 'paas'}), '1.23', 'path')
 
794
        self.addContext(patch('deploy_stack.get_machine_dns_name',
 
795
                              return_value='foo'))
 
796
        self.addContext(patch('subprocess.check_call'))
 
797
        call_mock = self.addContext(patch('subprocess.call'))
 
798
        co_mock = self.addContext(patch('subprocess.check_output',
 
799
                                        return_value=''))
 
800
        self.addContext(patch('deploy_stack.wait_for_port'))
 
801
        self.addContext(patch.object(client, 'bootstrap',
 
802
                                     side_effect=FakeException))
 
803
        crl_mock = self.addContext(patch('deploy_stack.copy_remote_logs'))
 
804
        al_mock = self.addContext(patch('deploy_stack.archive_logs'))
 
805
        with self.assertRaises(FakeException):
 
806
            with boot_context('bar', client, 'baz', [], None, None, None,
 
807
                              'log_dir', keep_env=False, upload_tools=True):
 
808
                pass
 
809
        self.assertEqual(crl_mock.call_count, 1)
 
810
        call_args = crl_mock.call_args[0]
 
811
        self.assertIsInstance(call_args[0], _Remote)
 
812
        self.assertEqual(call_args[0].get_address(), 'baz')
 
813
        self.assertEqual(call_args[1], 'log_dir')
 
814
        al_mock.assert_called_once_with('log_dir')
 
815
        timeout_path = get_timeout_path()
 
816
        assert_juju_call(self, call_mock, client, (
 
817
            sys.executable, timeout_path, '600.00', '--',
 
818
            'juju', '--show-log', 'destroy-environment', 'bar', '--force',
 
819
            '-y'
 
820
            ))
 
821
        assert_juju_call(self, co_mock, client, (
 
822
            'juju', '--show-log', 'help', 'commands'), assign_stderr=True)
1618
823
 
1619
824
    def test_jes(self):
1620
825
        self.addContext(patch('subprocess.check_call', autospec=True))
1621
 
        client = EnvJujuClient(JujuData(
1622
 
            'foo', {'type': 'paas', 'region': 'qux'}), '1.26', 'path')
1623
 
        with self.bc_context(client, 'log_dir', jes=KILL_CONTROLLER):
 
826
        client = EnvJujuClient(SimpleEnvironment(
 
827
            'foo', {'type': 'paas'}), '1.23', 'path')
 
828
        with self.bc_context(client, 'log_dir', jes=True):
1624
829
            with boot_context('bar', client, None, [], None, None, None,
1625
830
                              'log_dir', keep_env=False, upload_tools=False):
1626
831
                pass
1627
832
 
1628
 
    def test_region(self):
1629
 
        self.addContext(patch('subprocess.check_call', autospec=True))
1630
 
        client = EnvJujuClient(JujuData(
1631
 
            'foo', {'type': 'paas'}), '1.23', 'path')
1632
 
        with self.bc_context(client, 'log_dir', jes='kill-controller'):
1633
 
            with boot_context('bar', client, None, [], None, None, None,
1634
 
                              'log_dir', keep_env=False, upload_tools=False,
1635
 
                              region='steve'):
1636
 
                pass
1637
 
        self.assertEqual('steve', client.env.config['region'])
1638
 
 
1639
 
    def test_region_non_jes(self):
1640
 
        self.addContext(patch('subprocess.check_call', autospec=True))
1641
 
        client = EnvJujuClient1X(SimpleEnvironment(
1642
 
            'foo', {'type': 'paas'}), '1.23', 'path')
1643
 
        with self.bc_context(client, 'log_dir'):
1644
 
            with boot_context('bar', client, None, [], None, None, None,
1645
 
                              'log_dir', keep_env=False, upload_tools=False,
1646
 
                              region='steve'):
1647
 
                pass
1648
 
        self.assertEqual('steve', client.env.config['region'])
1649
 
 
1650
 
 
1651
 
class TestDeployJobParseArgs(FakeHomeTestCase):
 
833
 
 
834
class TestDeployJobParseArgs(TestCase):
1652
835
 
1653
836
    def test_deploy_job_parse_args(self):
1654
 
        args = deploy_job_parse_args(['foo', 'bar/juju', 'baz', 'qux'])
 
837
        args = deploy_job_parse_args(['foo', 'bar', 'baz', 'qux'])
1655
838
        self.assertEqual(args, Namespace(
1656
839
            agent_stream=None,
1657
840
            agent_url=None,
1662
845
            keep_env=False,
1663
846
            logs='baz',
1664
847
            machine=[],
1665
 
            juju_bin='bar/juju',
 
848
            juju_bin='bar',
1666
849
            series=None,
1667
850
            upgrade=False,
1668
851
            verbose=logging.INFO,
1669
852
            upload_tools=False,
1670
853
            with_chaos=0,
1671
854
            jes=False,
1672
 
            region=None,
 
855
            pre_destroy=False,
1673
856
        ))
1674
857
 
1675
858
    def test_upload_tools(self):
1676
859
        args = deploy_job_parse_args(
1677
 
            ['foo', 'bar/juju', 'baz', 'qux', '--upload-tools'])
 
860
            ['foo', 'bar', 'baz', 'qux', '--upload-tools'])
1678
861
        self.assertEqual(args.upload_tools, True)
1679
862
 
1680
863
    def test_agent_stream(self):
1681
864
        args = deploy_job_parse_args(
1682
 
            ['foo', 'bar/juju', 'baz', 'qux', '--agent-stream', 'wacky'])
 
865
            ['foo', 'bar', 'baz', 'qux', '--agent-stream', 'wacky'])
1683
866
        self.assertEqual('wacky', args.agent_stream)
1684
867
 
1685
868
    def test_jes(self):
1686
869
        args = deploy_job_parse_args(
1687
 
            ['foo', 'bar/juju', 'baz', 'qux', '--jes'])
 
870
            ['foo', 'bar', 'baz', 'qux', '--jes'])
1688
871
        self.assertIs(args.jes, True)