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

« back to all changes in this revision

Viewing changes to test_industrial_test.py

  • Committer: Aaron Bentley
  • Date: 2015-07-27 19:19:56 UTC
  • mto: This revision was merged to the branch mainline in revision 1048.
  • Revision ID: aaron.bentley@canonical.com-20150727191956-uz4gdxuv28qhvv94
Use per-client JUJU_HOME.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# coding=utf-8
 
1
__metaclass__ = type
 
2
 
2
3
from argparse import Namespace
3
4
from collections import OrderedDict
4
 
from contextlib import (
5
 
    closing,
6
 
    contextmanager,
7
 
    )
8
 
import json
 
5
from contextlib import contextmanager
9
6
import os
10
7
from tempfile import (
11
8
    mkdtemp,
12
9
    NamedTemporaryFile,
13
10
    )
14
11
from textwrap import dedent
 
12
from unittest import TestCase
15
13
 
16
14
from boto.ec2.securitygroup import SecurityGroup
17
15
from mock import (
21
19
    )
22
20
import yaml
23
21
 
24
 
from fakejuju import (
25
 
    fake_juju_client,
26
 
    )
27
22
from industrial_test import (
28
 
    AttemptSuite,
29
 
    AttemptSuiteFactory,
30
23
    BACKUP,
31
24
    BackupRestoreAttempt,
32
25
    BootstrapAttempt,
42
35
    maybe_write_json,
43
36
    MultiIndustrialTest,
44
37
    parse_args,
45
 
    PrepareUpgradeJujuAttempt,
46
38
    QUICK,
47
39
    StageInfo,
48
40
    SteppedStageAttempt,
52
44
from jujuconfig import get_euca_env
53
45
from jujupy import (
54
46
    EnvJujuClient,
55
 
    EnvJujuClient1X,
56
 
    get_timeout_prefix,
57
 
    JujuData,
58
 
    KVM_MACHINE,
59
 
    LXC_MACHINE,
60
 
    LXD_MACHINE,
61
47
    SimpleEnvironment,
62
 
    Status,
63
48
    _temp_env,
64
49
    )
65
 
from substrate import (
66
 
    AWSAccount,
67
 
    AzureARMAccount,
68
 
    )
69
 
from tests import (
70
 
    FakeHomeTestCase,
71
 
    parse_error,
72
 
    TestCase,
73
 
    use_context,
74
 
)
75
 
from tests.test_deploy_stack import FakeBootstrapManager
76
 
from tests.test_jujupy import (
77
 
    assert_juju_call,
78
 
    FakePopen,
79
 
    observable_temp_file,
80
 
    )
81
 
from tests.test_substrate import (
 
50
from substrate import AWSAccount
 
51
from test_jujupy import assert_juju_call
 
52
from test_substrate import (
82
53
    get_aws_env,
83
 
    get_azure_config,
84
54
    get_os_config,
85
55
    make_os_security_group_instance,
86
56
    make_os_security_groups,
87
57
    )
88
 
from utility import (
89
 
    LoggedException,
90
 
    temp_dir,
91
 
    )
92
 
 
93
 
 
94
 
__metaclass__ = type
95
 
 
96
 
 
97
 
def get_aws_juju_data():
98
 
    aws_env = get_aws_env()
99
 
    return JujuData(aws_env.environment, aws_env.config)
100
 
 
101
 
 
102
 
class JujuPyTestCase(FakeHomeTestCase):
103
 
 
104
 
    def setUp(self):
105
 
        super(JujuPyTestCase, self).setUp()
106
 
        patcher = patch('jujupy.pause')
107
 
        self.addCleanup(patcher.stop)
108
 
        patcher.start()
109
 
 
110
 
        patcher = patch('jujupy.GroupReporter._write')
111
 
        self.addCleanup(patcher.stop)
112
 
        patcher.start()
113
 
 
114
 
        patcher = patch('jujupy.until_timeout', return_value=[2, 1])
115
 
        self.addCleanup(patcher.stop)
116
 
        patcher.start()
 
58
from test_utility import parse_error
117
59
 
118
60
 
119
61
def iter_steps_validate_info(test, stage, client):
139
81
        unexpected, 'Unexpected test_id: {}'.format(result['test_id']))
140
82
 
141
83
 
142
 
def patch_status(client, *statuses):
143
 
    """
144
 
    Replace calls to EnvJujuClient.get_status with preformed output.
145
 
 
146
 
    If client is None, all clients will have the given status for the duration
147
 
    of the patch, otherwise only the given client is modified.
148
 
 
149
 
    If more than one status argument is passed, they will be returned in
150
 
    sequence, otherwise every call will return the single given status.
151
 
 
152
 
    Triva, the plural of status is Latin is statūs.
153
 
    """
154
 
    kwargs = {}
155
 
    if len(statuses) > 1:
156
 
        kwargs['side_effect'] = (Status.from_text(json.dumps(s))
157
 
                                 for s in statuses).next
158
 
    else:
159
 
        kwargs['return_value'] = Status.from_text(json.dumps(statuses[0]))
160
 
    if client is not None:
161
 
        return patch.object(client, 'get_status', autospec=True, **kwargs)
162
 
    return patch('jujupy.EnvJujuClient.get_status', autospec=True, **kwargs)
163
 
 
164
 
 
165
84
class TestParseArgs(TestCase):
166
85
 
167
86
    def test_parse_args(self):
177
96
            args = parse_args(['rai', 'new-juju'])
178
97
        self.assertRegexpMatches(
179
98
            stderr.getvalue(), '.*error: too few arguments.*')
180
 
        with parse_error(self) as stderr:
181
 
            args = parse_args(['rai', 'new-juju', QUICK])
182
 
        self.assertRegexpMatches(
183
 
            stderr.getvalue(), '.*error: too few arguments.*')
184
 
        args = parse_args(['rai', 'new-juju', QUICK, 'log-dir'])
 
99
        args = parse_args(['rai', 'new-juju', QUICK])
185
100
        self.assertEqual(args.env, 'rai')
186
101
        self.assertEqual(args.new_juju_path, 'new-juju')
187
 
        self.assertEqual(args.log_dir, 'log-dir')
188
102
        self.assertEqual(args.suite, [QUICK])
189
 
        self.assertIs(args.agent_stream, None)
190
103
 
191
104
    def test_parse_args_attempts(self):
192
 
        args = parse_args(['rai', 'new-juju', QUICK, 'log-dir'])
 
105
        args = parse_args(['rai', 'new-juju', QUICK])
193
106
        self.assertEqual(args.attempts, 2)
194
 
        args = parse_args(['rai', 'new-juju', '--attempts', '3', QUICK,
195
 
                           'log-dir'])
 
107
        args = parse_args(['rai', 'new-juju', '--attempts', '3', QUICK])
196
108
        self.assertEqual(args.attempts, 3)
197
109
 
198
110
    def test_parse_args_json_file(self):
199
 
        args = parse_args(['rai', 'new-juju', QUICK, 'log-dir'])
 
111
        args = parse_args(['rai', 'new-juju', QUICK])
200
112
        self.assertIs(args.json_file, None)
201
 
        args = parse_args(['rai', 'new-juju', '--json-file', 'foobar', QUICK,
202
 
                           'log-dir'])
 
113
        args = parse_args(['rai', 'new-juju', '--json-file', 'foobar', QUICK])
203
114
        self.assertEqual(args.json_file, 'foobar')
204
115
 
205
116
    def test_parse_args_suite(self):
206
 
        args = parse_args(['rai', 'new-juju', 'full', 'log-dir'])
 
117
        args = parse_args(['rai', 'new-juju', 'full'])
207
118
        self.assertEqual(args.suite, [FULL])
208
 
        args = parse_args(['rai', 'new-juju', QUICK, 'log-dir'])
 
119
        args = parse_args(['rai', 'new-juju', QUICK])
209
120
        self.assertEqual(args.suite, [QUICK])
210
 
        args = parse_args(['rai', 'new-juju', DENSITY, 'log-dir'])
 
121
        args = parse_args(['rai', 'new-juju', DENSITY])
211
122
        self.assertEqual(args.suite, [DENSITY])
212
 
        args = parse_args(['rai', 'new-juju', BACKUP, 'log-dir'])
 
123
        args = parse_args(['rai', 'new-juju', BACKUP])
213
124
        self.assertEqual(args.suite, [BACKUP])
214
125
        with parse_error(self) as stderr:
215
 
            args = parse_args(['rai', 'new-juju', 'foo', 'log-dir'])
 
126
            args = parse_args(['rai', 'new-juju', 'foo'])
216
127
        self.assertRegexpMatches(
217
128
            stderr.getvalue(), ".*argument suite: invalid choice: 'foo'.*")
218
129
 
219
130
    def test_parse_args_multi_suite(self):
220
 
        args = parse_args(['rai', 'new-juju', 'full,quick', 'log-dir'])
 
131
        args = parse_args(['rai', 'new-juju', 'full,quick'])
221
132
        self.assertEqual(args.suite, [FULL, QUICK])
222
133
        with parse_error(self) as stderr:
223
 
            args = parse_args(['rai', 'new-juju', 'full,foo', 'log-dir'])
 
134
            args = parse_args(['rai', 'new-juju', 'full,foo'])
224
135
        self.assertRegexpMatches(
225
136
            stderr.getvalue(), ".*argument suite: invalid choice: 'foo'.*")
226
137
 
227
138
    def test_parse_args_agent_url(self):
228
 
        args = parse_args(['rai', 'new-juju', QUICK, 'log-dir'])
 
139
        args = parse_args(['rai', 'new-juju', QUICK])
229
140
        self.assertEqual(args.new_agent_url, None)
230
141
        args = parse_args(['rai', 'new-juju', '--new-agent-url',
231
 
                           'http://example.org', QUICK, 'log-dir'])
 
142
                           'http://example.org', QUICK])
232
143
        self.assertEqual(args.new_agent_url, 'http://example.org')
233
144
 
234
145
    def test_parse_args_debug(self):
235
 
        args = parse_args(['rai', 'new-juju', QUICK, 'log-dir'])
 
146
        args = parse_args(['rai', 'new-juju', QUICK])
236
147
        self.assertEqual(args.debug, False)
237
 
        args = parse_args(['rai', 'new-juju', '--debug', QUICK, 'log-dir'])
 
148
        args = parse_args(['rai', 'new-juju', '--debug', QUICK])
238
149
        self.assertEqual(args.debug, True)
239
150
 
240
151
    def test_parse_args_old_stable(self):
241
 
        args = parse_args(['rai', 'new-juju', QUICK, 'log-dir',
242
 
                           '--old-stable', 'asdf'])
 
152
        args = parse_args(['rai', 'new-juju', QUICK, '--old-stable', 'asdf'])
243
153
        self.assertEqual(args.old_stable, 'asdf')
244
 
        args = parse_args(['rai', 'new-juju', QUICK, 'log-dir'])
245
 
        self.assertIs(args.old_stable, None)
246
 
 
247
 
    def test_parse_args_agent_stream(self):
248
 
        args = parse_args(['rai', 'new-juju', QUICK, 'log-dir',
249
 
                           '--agent-stream', 'asdf'])
250
 
        self.assertEqual(args.agent_stream, 'asdf')
251
 
        args = parse_args(['rai', 'new-juju', QUICK, 'log-dir'])
 
154
        args = parse_args(['rai', 'new-juju', QUICK])
252
155
        self.assertIs(args.old_stable, None)
253
156
 
254
157
 
255
158
class FakeStepAttempt:
256
159
 
257
 
    def __init__(self, result, new_path=None):
 
160
    def __init__(self, result):
258
161
        self.result = result
259
 
        self.stage = StageInfo(result[0][0], '{} title'.format(result[0][0]))
260
 
        self.new_path = new_path
261
 
 
262
 
    @classmethod
263
 
    def from_result(cls, old, new, test_id='foo-id', new_path=None):
264
 
        """Alternate constructor for backwards-compatibility.
265
 
 
266
 
        Allows tests that used FakeAttempt to be adapted with minimal changes.
267
 
        """
268
 
        return cls([(test_id, old, new)], new_path)
269
 
 
270
 
    def __eq__(self, other):
271
 
        return (
272
 
            type(self) == type(other) and self.result == other.result)
273
 
 
274
 
    def get_test_info(self):
275
 
        return {self.result[0][0]: {'title': self.result[0][0]}}
276
 
 
277
 
    def get_bootstrap_client(self, client):
278
 
        return client
279
162
 
280
163
    def iter_test_results(self, old, new):
281
164
        return iter(self.result)
282
165
 
283
 
    def iter_steps(self, client):
284
 
        yield self.stage.as_result()
285
 
        if self.new_path is not None and client.full_path == self.new_path:
286
 
            result_value = self.result[0][2]
287
 
        else:
288
 
            result_value = self.result[0][1]
289
 
        if isinstance(result_value, BaseException):
290
 
            raise result_value
291
 
        yield self.stage.as_result(result_value)
 
166
 
 
167
class FakeAttempt(FakeStepAttempt):
 
168
 
 
169
    def __init__(self, old_result, new_result, test_id='foo-id'):
 
170
        super(FakeAttempt, self).__init__([(test_id, old_result, new_result)])
 
171
 
 
172
    def do_stage(self, old_client, new_client):
 
173
        return self.result[0]
292
174
 
293
175
 
294
176
class FakeAttemptClass:
298
180
    normal methods on FakeAttemptClass.
299
181
    """
300
182
 
301
 
    def factory(self, upgrade_sequence, attempt_stream):
 
183
    def factory(self, upgrade_sequence):
302
184
        return self()
303
185
 
304
 
    def __init__(self, title, *result, **kwargs):
 
186
    def __init__(self, title, *result):
305
187
        self.title = title
306
188
        self.test_id = '{}-id'.format(title)
307
189
        self.result = result
308
 
        self.new_path = kwargs.get('new_path')
309
190
 
310
191
    def get_test_info(self):
311
192
        return {self.test_id: {'title': self.title}}
312
193
 
313
194
    def __call__(self):
314
 
        return FakeStepAttempt.from_result(*self.result, test_id=self.test_id,
315
 
                                           new_path=self.new_path)
 
195
        return FakeAttempt(*self.result, test_id=self.test_id)
 
196
 
 
197
 
 
198
class StubJujuClient:
 
199
 
 
200
    def destroy_environment(self, delete_jenv=False):
 
201
        pass
316
202
 
317
203
 
318
204
@contextmanager
324
210
        yield
325
211
 
326
212
 
327
 
def fake_bootstrap_manager(self, temp_env_name, client, *args, **kwargs):
328
 
    return FakeBootstrapManager(client)
329
 
 
330
 
 
331
213
class TestMultiIndustrialTest(TestCase):
332
214
 
333
215
    def test_from_args(self):
334
216
        args = Namespace(
335
217
            env='foo', new_juju_path='new-path', attempts=7, suite=[DENSITY],
336
 
            log_dir='log-dir', new_agent_url=None, debug=False,
337
 
            old_stable=None, agent_stream=None)
 
218
            new_agent_url=None, debug=False, old_stable=None)
338
219
        with temp_env('foo'):
339
220
            mit = MultiIndustrialTest.from_args(args, QUICK)
340
221
        self.assertEqual(mit.env, 'foo')
341
222
        self.assertEqual(mit.new_juju_path, 'new-path')
342
223
        self.assertEqual(mit.attempt_count, 7)
343
224
        self.assertEqual(mit.max_attempts, 14)
344
 
        self.assertEqual(mit.log_parent_dir, 'log-dir')
345
 
        self.assertIs(mit.agent_stream, None)
346
225
        self.assertEqual(
347
 
            mit.stages, AttemptSuiteFactory([]))
 
226
            mit.stages, [BootstrapAttempt, DestroyEnvironmentAttempt])
348
227
        args = Namespace(
349
228
            env='bar', new_juju_path='new-path2', attempts=6, suite=[FULL],
350
 
            log_dir='log-dir2', new_agent_url=None, debug=False,
351
 
            old_stable=None, agent_stream=None)
 
229
            new_agent_url=None, debug=False, old_stable=None)
352
230
        with temp_env('bar'):
353
231
            mit = MultiIndustrialTest.from_args(args, FULL)
354
232
        self.assertEqual(mit.env, 'bar')
355
233
        self.assertEqual(mit.new_juju_path, 'new-path2')
356
234
        self.assertEqual(mit.attempt_count, 6)
357
235
        self.assertEqual(mit.max_attempts, 12)
358
 
        self.assertEqual(mit.log_parent_dir, 'log-dir2')
359
 
        self.assertIs(mit.agent_stream, None)
360
236
        self.assertEqual(
361
 
            mit.stages, AttemptSuiteFactory([
362
 
                UpgradeCharmAttempt, DeployManyAttempt,
363
 
                BackupRestoreAttempt, EnsureAvailabilityAttempt]))
 
237
            mit.stages, [
 
238
                BootstrapAttempt, UpgradeCharmAttempt, DeployManyAttempt,
 
239
                BackupRestoreAttempt, EnsureAvailabilityAttempt,
 
240
                DestroyEnvironmentAttempt])
364
241
 
365
242
    def test_from_args_maas(self):
366
243
        args = Namespace(
367
 
            env='foo', new_juju_path='new-path', log_dir='log-dir',
368
 
            attempts=7, new_agent_url=None, debug=False, old_stable=None,
369
 
            agent_stream=None)
 
244
            env='foo', new_juju_path='new-path', attempts=7,
 
245
            new_agent_url=None, debug=False, old_stable=None)
370
246
        with temp_env('foo', {'type': 'maas'}):
371
247
            mit = MultiIndustrialTest.from_args(args, DENSITY)
372
248
        self.assertEqual(
373
 
            mit.stages, AttemptSuiteFactory([DeployManyAttempt]))
 
249
            mit.stages, [
 
250
                BootstrapAttempt, DeployManyAttempt,
 
251
                DestroyEnvironmentAttempt])
374
252
 
375
253
    def test_from_args_debug(self):
376
254
        args = Namespace(
377
 
            env='foo', new_juju_path='new-path', log_dir='log-dir',
378
 
            attempts=7, new_agent_url=None, debug=False, old_stable=None,
379
 
            agent_stream=None)
 
255
            env='foo', new_juju_path='new-path', attempts=7,
 
256
            new_agent_url=None, debug=False, old_stable=None)
380
257
        with temp_env('foo', {'type': 'maas'}):
381
258
            mit = MultiIndustrialTest.from_args(args, DENSITY)
382
259
            self.assertEqual(mit.debug, False)
386
263
 
387
264
    def test_from_args_really_old_path(self):
388
265
        args = Namespace(
389
 
            env='foo', new_juju_path='new-path', log_dir='log-dir',
390
 
            attempts=7, new_agent_url=None, debug=False,
391
 
            old_stable='really-old-path', agent_stream=None)
 
266
            env='foo', new_juju_path='new-path', attempts=7,
 
267
            new_agent_url=None, debug=False, old_stable='really-old-path')
392
268
        with temp_env('foo'):
393
269
            mit = MultiIndustrialTest.from_args(args, FULL)
394
270
        self.assertEqual(mit.really_old_path, 'really-old-path')
395
271
        args = Namespace(
396
 
            env='bar', new_juju_path='new-path2', log_dir='log-dir',
397
 
            attempts=6, new_agent_url=None, debug=False, old_stable=None,
398
 
            agent_stream=None)
 
272
            env='bar', new_juju_path='new-path2', attempts=6,
 
273
            new_agent_url=None, debug=False, old_stable=None)
399
274
        with temp_env('bar'):
400
275
            mit = MultiIndustrialTest.from_args(args, FULL)
401
276
        self.assertIs(mit.really_old_path, None)
402
277
 
403
 
    def test_from_args_agent_stream(self):
404
 
        args = Namespace(
405
 
            env='foo', new_juju_path='new-path', log_dir='log-dir',
406
 
            attempts=7, new_agent_url=None, debug=False, old_stable=None,
407
 
            agent_stream='foo-stream')
408
 
        with temp_env('foo', {'type': 'maas'}):
409
 
            mit = MultiIndustrialTest.from_args(args, DENSITY)
410
 
            self.assertEqual(mit.debug, False)
411
 
            args.debug = True
412
 
            mit = MultiIndustrialTest.from_args(args, DENSITY)
413
 
            self.assertEqual(mit.agent_stream, 'foo-stream')
 
278
    def test_get_stages(self):
 
279
        self.assertEqual(
 
280
            MultiIndustrialTest.get_stages(QUICK, {'type': 'foo'}),
 
281
            [BootstrapAttempt, DestroyEnvironmentAttempt])
 
282
 
 
283
        self.assertEqual(
 
284
            MultiIndustrialTest.get_stages(FULL, {'type': 'foo'}), [
 
285
                BootstrapAttempt, UpgradeCharmAttempt, DeployManyAttempt,
 
286
                BackupRestoreAttempt, EnsureAvailabilityAttempt,
 
287
                DestroyEnvironmentAttempt])
 
288
        self.assertEqual(
 
289
            MultiIndustrialTest.get_stages(DENSITY, {'type': 'foo'}), [
 
290
                BootstrapAttempt, DeployManyAttempt,
 
291
                DestroyEnvironmentAttempt])
 
292
        self.assertEqual(
 
293
            MultiIndustrialTest.get_stages(BACKUP, {'type': 'foo'}), [
 
294
                BootstrapAttempt, BackupRestoreAttempt,
 
295
                DestroyEnvironmentAttempt])
 
296
 
 
297
    def test_get_stages_maas(self):
 
298
        self.assertEqual(
 
299
            MultiIndustrialTest.get_stages(QUICK, {'type': 'maas'}),
 
300
            [BootstrapAttempt, DestroyEnvironmentAttempt])
 
301
        self.assertEqual(
 
302
            MultiIndustrialTest.get_stages(FULL, {'type': 'maas'}), [
 
303
                BootstrapAttempt, UpgradeCharmAttempt,
 
304
                DeployManyAttempt, BackupRestoreAttempt,
 
305
                EnsureAvailabilityAttempt, DestroyEnvironmentAttempt])
 
306
        self.assertEqual(
 
307
            MultiIndustrialTest.get_stages(DENSITY, {'type': 'maas'}), [
 
308
                BootstrapAttempt, DeployManyAttempt,
 
309
                DestroyEnvironmentAttempt])
414
310
 
415
311
    def test_density_suite(self):
416
312
        args = Namespace(
417
313
            env='foo', new_juju_path='new-path', attempts=7,
418
 
            log_dir='log-dir', new_agent_url=None, debug=False,
419
 
            old_stable=None, agent_stream=None)
 
314
            new_agent_url=None, debug=False, old_stable=None)
420
315
        with temp_env('foo'):
421
316
            mit = MultiIndustrialTest.from_args(args, DENSITY)
422
317
        self.assertEqual(
423
 
            mit.stages, AttemptSuiteFactory([DeployManyAttempt]))
 
318
            mit.stages, [BootstrapAttempt, DeployManyAttempt,
 
319
                         DestroyEnvironmentAttempt])
424
320
 
425
321
    def test_backup_suite(self):
426
322
        args = Namespace(
427
323
            env='foo', new_juju_path='new-path', attempts=7,
428
 
            log_dir='log-dir', new_agent_url=None, debug=False,
429
 
            old_stable=None, agent_stream=None)
 
324
            new_agent_url=None, debug=False, old_stable=None)
430
325
        with temp_env('foo'):
431
326
            mit = MultiIndustrialTest.from_args(args, BACKUP)
432
327
        self.assertEqual(
433
 
            mit.stages, AttemptSuiteFactory([BackupRestoreAttempt]))
 
328
            mit.stages, [BootstrapAttempt, BackupRestoreAttempt,
 
329
                         DestroyEnvironmentAttempt])
434
330
 
435
331
    def test_from_args_new_agent_url(self):
436
332
        args = Namespace(
437
333
            env='foo', new_juju_path='new-path', attempts=7,
438
 
            log_dir='log-dir', new_agent_url='http://example.net',
439
 
            debug=False, old_stable=None, agent_stream=None)
 
334
            new_agent_url='http://example.net', debug=False, old_stable=None)
440
335
        with temp_env('foo'):
441
336
            mit = MultiIndustrialTest.from_args(args, suite=QUICK)
442
337
        self.assertEqual(mit.new_agent_url, 'http://example.net')
443
338
 
444
339
    def test_init(self):
445
340
        mit = MultiIndustrialTest('foo-env', 'bar-path', [
446
 
            DestroyEnvironmentAttempt, BootstrapAttempt], 'log-dir', 5)
 
341
            DestroyEnvironmentAttempt, BootstrapAttempt], 5)
447
342
        self.assertEqual(mit.env, 'foo-env')
448
343
        self.assertEqual(mit.new_juju_path, 'bar-path')
449
344
        self.assertEqual(mit.stages, [DestroyEnvironmentAttempt,
450
345
                                      BootstrapAttempt])
451
346
        self.assertEqual(mit.attempt_count, 5)
452
 
        self.assertEqual(mit.log_parent_dir, 'log-dir')
453
347
 
454
348
    def test_make_results(self):
455
 
        mit = MultiIndustrialTest('foo-env', 'bar-path', AttemptSuiteFactory([
456
 
            DestroyEnvironmentAttempt]), 5)
 
349
        mit = MultiIndustrialTest('foo-env', 'bar-path', [
 
350
            DestroyEnvironmentAttempt, BootstrapAttempt], 5)
457
351
        results = mit.make_results()
458
352
        self.assertEqual(results, {'results': [
459
353
            {'attempts': 0, 'old_failures': 0, 'new_failures': 0,
460
 
             'title': 'bootstrap', 'test_id': 'bootstrap', 'report_on': True},
461
 
            {'attempts': 0, 'old_failures': 0, 'new_failures': 0,
462
 
             'title': 'Prepare suite tests', 'test_id': 'prepare-suite',
463
 
             'report_on': False},
464
 
            {'attempts': 0, 'old_failures': 0, 'new_failures': 0,
465
354
             'title': 'destroy environment', 'test_id': 'destroy-env',
466
355
             'report_on': True},
467
356
            {'attempts': 0, 'old_failures': 0, 'new_failures': 0,
468
357
             'title': 'check substrate clean', 'test_id': 'substrate-clean',
469
358
             'report_on': True},
 
359
            {'attempts': 0, 'old_failures': 0, 'new_failures': 0,
 
360
             'title': 'bootstrap', 'test_id': 'bootstrap', 'report_on': True},
470
361
        ]})
471
362
 
472
363
    def test_make_results_report_on(self):
477
368
                return {'no-report': {
478
369
                    'title': 'No report', 'report_on': False}}
479
370
 
480
 
        mit = MultiIndustrialTest('foo-env', 'bar-path', AttemptSuiteFactory([
481
 
            NoReportOn]), 5)
 
371
        mit = MultiIndustrialTest('foo-env', 'bar-path', [
 
372
            BootstrapAttempt, NoReportOn], 5)
482
373
        results = mit.make_results()
483
374
        self.assertEqual(results, {'results': [
484
375
            {
490
381
                'new_failures': 0,
491
382
            },
492
383
            {
493
 
                'test_id': 'prepare-suite',
494
 
                'title': 'Prepare suite tests',
495
 
                'report_on': False,
496
 
                'attempts': 0,
497
 
                'old_failures': 0,
498
 
                'new_failures': 0,
499
 
            },
500
 
            {
501
384
                'test_id': 'no-report',
502
385
                'title': 'No report',
503
386
                'report_on': False,
505
388
                'old_failures': 0,
506
389
                'new_failures': 0,
507
390
            },
508
 
            {
509
 
                'test_id': 'destroy-env',
510
 
                'title': 'destroy environment',
511
 
                'report_on': True,
512
 
                'attempts': 0,
513
 
                'old_failures': 0,
514
 
                'new_failures': 0,
515
 
            },
516
 
            {
517
 
                'test_id': 'substrate-clean',
518
 
                'title': 'check substrate clean',
519
 
                'report_on': True,
520
 
                'attempts': 0,
521
 
                'old_failures': 0,
522
 
                'new_failures': 0,
523
 
            },
524
391
        ]})
525
392
 
526
 
    @staticmethod
527
 
    @contextmanager
528
 
    def patch_client(by_version):
529
 
        with patch('industrial_test.client_from_config',
530
 
                   side_effect=by_version):
531
 
            with patch('jujupy.SimpleEnvironment.from_config',
532
 
                       side_effect=lambda x: SimpleEnvironment(x, {})):
533
 
                with patch.object(EnvJujuClient, 'get_full_path',
534
 
                                  side_effect=lambda: 'juju'):
535
 
                    yield
536
 
 
537
393
    def test_make_industrial_test(self):
538
 
        mit = MultiIndustrialTest('foo-env', 'bar-path', AttemptSuiteFactory([
539
 
            DestroyEnvironmentAttempt]), 'log-dir', 5)
540
 
        with self.patch_client(
541
 
            lambda x, y=None, debug=False: fake_juju_client(
542
 
                JujuData(x, {}, juju_home=''), full_path=y)):
543
 
            industrial = mit.make_industrial_test()
544
 
        old_client = industrial.old_client
545
 
        self.assertEqual((old_client.env, old_client.full_path), (
546
 
            JujuData('foo-env-old', {'name': 'foo-env-old'}, juju_home=''),
547
 
            None))
548
 
        new_client = industrial.new_client
549
 
        self.assertEqual((new_client.env, new_client.full_path), (
550
 
            JujuData('foo-env-new', {'name': 'foo-env-new'}, juju_home=''),
551
 
            'bar-path'))
552
 
        self.assertEqual(len(industrial.stage_attempts), 1)
553
 
        self.assertEqual([mit.stages], [sa.attempt_list for sa in
554
 
                         industrial.stage_attempts])
 
394
        mit = MultiIndustrialTest('foo-env', 'bar-path', [
 
395
            DestroyEnvironmentAttempt, BootstrapAttempt], 5)
 
396
        side_effect = lambda x, y=None, debug=False: (x, y)
 
397
        with patch('jujupy.EnvJujuClient.by_version', side_effect=side_effect):
 
398
            with patch('jujupy.SimpleEnvironment.from_config',
 
399
                       side_effect=lambda x: SimpleEnvironment(x, {})):
 
400
                industrial = mit.make_industrial_test()
 
401
        self.assertEqual(industrial.old_client,
 
402
                         (SimpleEnvironment('foo-env-old', {}), None))
 
403
        self.assertEqual(industrial.new_client,
 
404
                         (SimpleEnvironment('foo-env-new', {}), 'bar-path'))
 
405
        self.assertEqual(len(industrial.stage_attempts), 2)
 
406
        for stage, attempt in zip(mit.stages, industrial.stage_attempts):
 
407
            self.assertIs(type(attempt), stage)
555
408
 
556
409
    def test_make_industrial_test_new_agent_url(self):
557
 
        mit = MultiIndustrialTest('foo-env', 'bar-path',
558
 
                                  AttemptSuiteFactory([]), 'log-dir',
 
410
        mit = MultiIndustrialTest('foo-env', 'bar-path', [],
559
411
                                  new_agent_url='http://example.com')
560
 
        with self.patch_client(
561
 
                lambda x, y=None, debug=False: fake_juju_client(full_path=y)):
562
 
            industrial = mit.make_industrial_test()
 
412
        side_effect = lambda x, y=None, debug=False: (x, y)
 
413
        with patch('jujupy.EnvJujuClient.by_version', side_effect=side_effect):
 
414
            with patch('jujupy.SimpleEnvironment.from_config',
 
415
                       side_effect=lambda x: SimpleEnvironment(x, {})):
 
416
                industrial = mit.make_industrial_test()
563
417
        self.assertEqual(
564
 
            (industrial.new_client.env, industrial.new_client.full_path), (
565
 
                JujuData('foo-env-new', {
566
 
                    'type': 'foo',
567
 
                    'default-series': 'angsty',
568
 
                    'region': 'bar',
569
 
                    'name': 'foo-env-new',
570
 
                    'tools-metadata-url': 'http://example.com',
571
 
                    }, 'foo'),
 
418
            industrial.new_client, (
 
419
                SimpleEnvironment('foo-env-new', {
 
420
                    'tools-metadata-url': 'http://example.com'}),
572
421
                'bar-path')
573
422
            )
574
423
 
575
424
    def test_make_industrial_test_debug(self):
576
 
        mit = MultiIndustrialTest('foo-env', 'bar-path',
577
 
                                  AttemptSuiteFactory([]), 'log-dir',
 
425
        mit = MultiIndustrialTest('foo-env', 'bar-path', [],
578
426
                                  new_agent_url='http://example.com')
579
 
 
580
 
        def side_effect(x, y=None, debug=False):
581
 
            return fake_juju_client(env=JujuData(x, {}, juju_home='x'),
582
 
                                    full_path=y, debug=debug)
583
 
 
584
 
        with self.patch_client(side_effect):
585
 
            industrial = mit.make_industrial_test()
586
 
        self.assertEqual(industrial.new_client.debug, False)
587
 
        self.assertEqual(industrial.old_client.debug, False)
588
 
        mit.debug = True
589
 
        with self.patch_client(side_effect):
590
 
            industrial = mit.make_industrial_test()
591
 
        self.assertEqual(industrial.new_client.debug, True)
592
 
        self.assertEqual(industrial.old_client.debug, True)
 
427
        side_effect = lambda x, y=None, debug=False: debug
 
428
        with patch('jujupy.EnvJujuClient.by_version', side_effect=side_effect):
 
429
            with patch('jujupy.SimpleEnvironment.from_config',
 
430
                       side_effect=lambda x: SimpleEnvironment(x, {})):
 
431
                industrial = mit.make_industrial_test()
 
432
                self.assertEqual(industrial.new_client, False)
 
433
                self.assertEqual(industrial.old_client, False)
 
434
                mit.debug = True
 
435
                industrial = mit.make_industrial_test()
 
436
                self.assertEqual(industrial.new_client, True)
 
437
                self.assertEqual(industrial.old_client, True)
593
438
 
594
439
    def test_update_results(self):
595
 
        mit = MultiIndustrialTest('foo-env', 'bar-path',
596
 
                                  AttemptSuiteFactory([]), 2)
 
440
        mit = MultiIndustrialTest('foo-env', 'bar-path', [
 
441
            DestroyEnvironmentAttempt, BootstrapAttempt], 2)
597
442
        results = mit.make_results()
598
 
        mit.update_results([('bootstrap', True, False)], results)
 
443
        mit.update_results([('destroy-env', True, False)], results)
599
444
        expected = {'results': [
600
 
            {'title': 'bootstrap', 'test_id': 'bootstrap',
 
445
            {'title': 'destroy environment', 'test_id': 'destroy-env',
601
446
             'attempts': 1, 'new_failures': 1, 'old_failures': 0,
602
447
             'report_on': True},
603
 
            {'title': 'Prepare suite tests', 'test_id': 'prepare-suite',
604
 
             'attempts': 0,
605
 
             'new_failures': 0, 'old_failures': 0, 'report_on': False},
606
 
            {'title': 'destroy environment', 'test_id': 'destroy-env',
607
 
             'attempts': 0,
 
448
            {'title': 'check substrate clean', 'test_id': 'substrate-clean',
 
449
             'attempts': 0, 'new_failures': 0, 'old_failures': 0,
 
450
             'report_on': True},
 
451
            {'title': 'bootstrap', 'test_id': 'bootstrap', 'attempts': 0,
608
452
             'new_failures': 0, 'old_failures': 0, 'report_on': True},
609
 
            {'title': 'check substrate clean', 'test_id': 'substrate-clean',
610
 
             'attempts': 0, 'new_failures': 0, 'old_failures': 0,
611
 
             'report_on': True},
612
453
            ]}
613
454
        self.assertEqual(results, expected)
614
 
        mit.update_results([
615
 
            ('bootstrap', True, True), ('prepare-suite', True, True),
616
 
            ('destroy-env', False, True), ('substrate-clean', True, True)
617
 
            ], results)
 
455
        mit.update_results(
 
456
            [('destroy-env', True, True), ('substrate-clean', True, True),
 
457
             ('bootstrap', False, True)],
 
458
            results)
618
459
        self.assertEqual(results, {'results': [
619
 
            {'title': 'bootstrap', 'test_id': 'bootstrap',
 
460
            {'title': 'destroy environment', 'test_id': 'destroy-env',
620
461
             'attempts': 2, 'new_failures': 1, 'old_failures': 0,
621
462
             'report_on': True},
622
 
            {'title': 'Prepare suite tests', 'test_id': 'prepare-suite',
623
 
             'attempts': 1, 'new_failures': 0, 'old_failures': 0,
624
 
             'report_on': False},
625
 
            {'title': 'destroy environment', 'test_id': 'destroy-env',
626
 
             'attempts': 1, 'new_failures': 0, 'old_failures': 1, 'report_on':
627
 
             True},
628
463
            {'title': 'check substrate clean', 'test_id': 'substrate-clean',
629
464
             'attempts': 1, 'new_failures': 0, 'old_failures': 0,
630
465
             'report_on': True},
 
466
            {'title': 'bootstrap', 'test_id': 'bootstrap', 'attempts': 1,
 
467
             'new_failures': 0, 'old_failures': 1, 'report_on': True},
631
468
            ]})
632
469
        mit.update_results(
633
 
            [('bootstrap', False, False), ('prepare-suite', True, True),
634
 
             ('destroy-env', False, False), ('substrate-clean', True, True)],
 
470
            [('destroy-env', False, False), ('substrate-clean', True, True),
 
471
             ('bootstrap', False, False)],
635
472
            results)
636
473
        expected = {'results': [
637
 
            {'title': 'bootstrap', 'test_id': 'bootstrap',
 
474
            {'title': 'destroy environment', 'test_id': 'destroy-env',
638
475
             'attempts': 2, 'new_failures': 1, 'old_failures': 0,
639
476
             'report_on': True},
640
 
            {'title': 'Prepare suite tests', 'test_id': 'prepare-suite',
641
 
             'attempts': 2, 'new_failures': 0, 'old_failures': 0,
642
 
             'report_on': False},
643
 
            {'title': 'destroy environment', 'test_id': 'destroy-env',
644
 
             'attempts': 2, 'new_failures': 1, 'old_failures': 2,
645
 
             'report_on': True},
646
477
            {'title': 'check substrate clean', 'test_id': 'substrate-clean',
647
478
             'attempts': 2, 'new_failures': 0, 'old_failures': 0,
648
479
             'report_on': True},
 
480
            {'title': 'bootstrap', 'test_id': 'bootstrap', 'attempts': 2,
 
481
             'new_failures': 1, 'old_failures': 2, 'report_on': True},
649
482
            ]}
650
483
        self.assertEqual(results, expected)
651
484
 
652
485
    def test_run_tests(self):
653
 
        log_dir = use_context(self, temp_dir())
654
 
        mit = MultiIndustrialTest('foo-env', 'bar-path', AttemptSuiteFactory([
655
 
            FakeAttemptClass('foo', True, True, new_path='bar-path'),
656
 
            FakeAttemptClass('bar', True, False, new_path='bar-path'),
657
 
            ]), log_dir, 5, 10)
658
 
 
659
 
        def side_effect(env, full_path=None, debug=False):
660
 
            return fake_juju_client(None, full_path, debug)
661
 
 
662
 
        with self.patch_client(side_effect):
663
 
            with patch('industrial_test.BootstrapManager',
664
 
                       side_effect=fake_bootstrap_manager):
 
486
        mit = MultiIndustrialTest('foo-env', 'bar-path', [
 
487
            FakeAttemptClass('foo', True, True),
 
488
            FakeAttemptClass('bar', True, False),
 
489
            ], 5, 10)
 
490
        side_effect = lambda x, y=None, debug=False: StubJujuClient()
 
491
        with patch('jujupy.EnvJujuClient.by_version', side_effect=side_effect):
 
492
            with patch('jujupy.SimpleEnvironment.from_config',
 
493
                       side_effect=lambda x: SimpleEnvironment(x, {})):
665
494
                results = mit.run_tests()
666
495
        self.assertEqual(results, {'results': [
667
 
            {'title': 'bootstrap', 'test_id': 'bootstrap', 'attempts': 5,
668
 
             'old_failures': 0, 'new_failures': 0, 'report_on': True},
669
 
            {'title': 'Prepare suite tests', 'test_id': 'prepare-suite',
670
 
             'attempts': 5, 'old_failures': 0, 'new_failures': 0,
671
 
             'report_on': False},
672
496
            {'title': 'foo', 'test_id': 'foo-id', 'attempts': 5,
673
497
             'old_failures': 0, 'new_failures': 0, 'report_on': True},
674
498
            {'title': 'bar', 'test_id': 'bar-id', 'attempts': 5,
675
499
             'old_failures': 0, 'new_failures': 5, 'report_on': True},
676
 
            {'title': 'destroy environment', 'test_id': 'destroy-env',
677
 
             'attempts': 0, 'old_failures': 0, 'new_failures': 0,
678
 
             'report_on': True},
679
 
            {'title': 'check substrate clean', 'test_id': 'substrate-clean',
680
 
             'attempts': 0, 'old_failures': 0, 'new_failures': 0,
681
 
             'report_on': True},
682
500
            ]})
683
501
 
684
502
    def test_run_tests_max_attempts(self):
685
 
        log_dir = use_context(self, temp_dir())
686
 
        mit = MultiIndustrialTest('foo-env', 'bar-path', AttemptSuiteFactory([
687
 
            FakeAttemptClass('foo', True, False, new_path='bar-path'),
688
 
            FakeAttemptClass('bar', True, False, new_path='bar-path'),
689
 
            ]), log_dir, 5, 6)
690
 
 
691
 
        def side_effect(env, full_path=None, debug=False):
692
 
            return fake_juju_client(None, full_path, debug)
693
 
 
694
 
        with self.patch_client(side_effect):
695
 
            with patch('industrial_test.BootstrapManager',
696
 
                       side_effect=fake_bootstrap_manager):
 
503
        mit = MultiIndustrialTest('foo-env', 'bar-path', [
 
504
            FakeAttemptClass('foo', True, False),
 
505
            FakeAttemptClass('bar', True, False),
 
506
            ], 5, 6)
 
507
        side_effect = lambda x, y=None, debug=False: StubJujuClient()
 
508
        with patch('jujupy.EnvJujuClient.by_version', side_effect=side_effect):
 
509
            with patch('jujupy.SimpleEnvironment.from_config',
 
510
                       side_effect=lambda x: SimpleEnvironment(x, {})):
697
511
                results = mit.run_tests()
698
512
        self.assertEqual(results, {'results': [
699
 
            {'title': 'bootstrap', 'test_id': 'bootstrap', 'attempts': 5,
700
 
             'old_failures': 0, 'new_failures': 0, 'report_on': True},
701
 
            {'title': 'Prepare suite tests', 'test_id': 'prepare-suite',
702
 
             'attempts': 5, 'old_failures': 0, 'new_failures': 0,
703
 
             'report_on': False},
704
513
            {'title': 'foo', 'test_id': 'foo-id', 'attempts': 5,
705
514
             'old_failures': 0, 'new_failures': 5, 'report_on': True},
706
515
            {'title': 'bar', 'test_id': 'bar-id', 'attempts': 0,
707
516
             'old_failures': 0, 'new_failures': 0, 'report_on': True},
708
 
            {'title': 'destroy environment', 'test_id': 'destroy-env',
709
 
             'attempts': 0, 'old_failures': 0, 'new_failures': 0,
710
 
             'report_on': True},
711
 
            {'title': 'check substrate clean', 'test_id': 'substrate-clean',
712
 
             'attempts': 0, 'old_failures': 0, 'new_failures': 0,
713
 
             'report_on': True},
714
517
            ]})
715
518
 
716
519
    def test_run_tests_max_attempts_less_than_attempt_count(self):
717
 
        log_dir = use_context(self, temp_dir())
718
 
        mit = MultiIndustrialTest(
719
 
            'foo-env', 'bar-path', AttemptSuiteFactory([
720
 
                FakeAttemptClass('foo', True, False, new_path='bar-path'),
721
 
                FakeAttemptClass('bar', True, False, new_path='bar-path')],
722
 
                ),
723
 
            log_dir, 5, 4)
724
 
 
725
 
        def side_effect(env, full_path=None, debug=False):
726
 
            return fake_juju_client(None, full_path, debug)
727
 
 
728
 
        with self.patch_client(side_effect):
729
 
            with patch('industrial_test.BootstrapManager',
730
 
                       side_effect=fake_bootstrap_manager):
 
520
        mit = MultiIndustrialTest('foo-env', 'bar-path', [
 
521
            FakeAttemptClass('foo', True, False),
 
522
            FakeAttemptClass('bar', True, False),
 
523
            ], 5, 4)
 
524
        side_effect = lambda x, y=None, debug=False: StubJujuClient()
 
525
        with patch('jujupy.EnvJujuClient.by_version', side_effect=side_effect):
 
526
            with patch('jujupy.SimpleEnvironment.from_config',
 
527
                       side_effect=lambda x: SimpleEnvironment(x, {})):
731
528
                results = mit.run_tests()
732
 
        expected = [
733
 
            {'title': 'bootstrap', 'test_id': 'bootstrap', 'attempts': 4,
734
 
             'old_failures': 0, 'new_failures': 0, 'report_on': True},
735
 
            {'title': 'Prepare suite tests', 'test_id': 'prepare-suite',
736
 
             'attempts': 4, 'old_failures': 0, 'new_failures': 0,
737
 
             'report_on': False},
 
529
        self.assertEqual(results, {'results': [
738
530
            {'title': 'foo', 'test_id': 'foo-id', 'attempts': 4,
739
531
             'old_failures': 0, 'new_failures': 4, 'report_on': True},
740
532
            {'title': 'bar', 'test_id': 'bar-id', 'attempts': 0,
741
533
             'old_failures': 0, 'new_failures': 0, 'report_on': True},
742
 
            {'title': 'destroy environment', 'test_id': 'destroy-env',
743
 
             'attempts': 0, 'old_failures': 0, 'new_failures': 0,
744
 
             'report_on': True},
745
 
            {'title': 'check substrate clean', 'test_id': 'substrate-clean',
746
 
             'attempts': 0, 'old_failures': 0, 'new_failures': 0,
747
 
             'report_on': True},
748
 
            ]
749
 
        self.assertEqual(results, {'results': expected})
 
534
            ]})
750
535
 
751
536
    @staticmethod
752
537
    def get_results_1():
840
625
            """))
841
626
 
842
627
 
843
 
class TestIndustrialTest(JujuPyTestCase):
 
628
class TestIndustrialTest(TestCase):
844
629
 
845
630
    def test_init(self):
846
631
        old_client = object()
852
637
        self.assertIs(attempt_list, industrial.stage_attempts)
853
638
 
854
639
    def test_from_args(self):
855
 
        def side_effect(x, y=None, debug=False):
856
 
            return fake_juju_client(env=JujuData(x, {}), full_path=y)
857
 
        with patch('industrial_test.client_from_config',
858
 
                   side_effect=side_effect):
859
 
            industrial = IndustrialTest.from_args(
860
 
                'foo', 'new-juju-path', [])
 
640
        side_effect = lambda x, y=None, debug=False: (x, y)
 
641
        with patch('jujupy.EnvJujuClient.by_version', side_effect=side_effect):
 
642
            with patch('jujupy.SimpleEnvironment.from_config',
 
643
                       side_effect=lambda x: SimpleEnvironment(x, {})):
 
644
                industrial = IndustrialTest.from_args(
 
645
                    'foo', 'new-juju-path', [])
861
646
        self.assertIsInstance(industrial, IndustrialTest)
862
 
        old_client = industrial.old_client
863
 
        self.assertEqual((old_client.env, old_client.full_path), (
864
 
            JujuData('foo-old', {'name': 'foo-old'}), None))
865
 
        new_client = industrial.new_client
866
 
        self.assertEqual((new_client.env, new_client.full_path), (
867
 
            JujuData('foo-new', {'name': 'foo-new'}),
868
 
            'new-juju-path'))
869
 
        self.assertNotEqual(old_client.env.environment,
870
 
                            new_client.env.environment)
 
647
        self.assertEqual(industrial.old_client,
 
648
                         (SimpleEnvironment('foo-old', {}), None))
 
649
        self.assertEqual(industrial.new_client,
 
650
                         (SimpleEnvironment('foo-new', {}), 'new-juju-path'))
 
651
        self.assertNotEqual(industrial.old_client[0].environment,
 
652
                            industrial.new_client[0].environment)
871
653
 
872
654
    def test_from_args_debug(self):
873
 
        def side_effect(x, y=None, debug=False):
874
 
            return fake_juju_client(full_path=y, debug=debug)
875
 
        with patch('industrial_test.client_from_config',
876
 
                   side_effect=side_effect):
 
655
        side_effect = lambda x, y=None, debug=False: debug
 
656
        with patch('jujupy.EnvJujuClient.by_version', side_effect=side_effect):
877
657
            with patch('jujupy.SimpleEnvironment.from_config'):
878
658
                industrial = IndustrialTest.from_args(
879
659
                    'foo', 'new-juju-path', [], debug=False)
880
 
                self.assertEqual(industrial.old_client.debug, False)
881
 
                self.assertEqual(industrial.new_client.debug, False)
 
660
                self.assertEqual(industrial.old_client, False)
 
661
                self.assertEqual(industrial.new_client, False)
882
662
                industrial = IndustrialTest.from_args(
883
663
                    'foo', 'new-juju-path', [], debug=True)
884
 
                self.assertEqual(industrial.old_client.debug, True)
885
 
                self.assertEqual(industrial.new_client.debug, True)
 
664
                self.assertEqual(industrial.old_client, True)
 
665
                self.assertEqual(industrial.new_client, True)
886
666
 
887
667
    def test_run_stages(self):
888
668
        old_client = FakeEnvJujuClient('old')
889
669
        new_client = FakeEnvJujuClient('new')
890
670
        industrial = IndustrialTest(old_client, new_client, [
891
 
            FakeStepAttempt.from_result(True, True),
892
 
            FakeStepAttempt.from_result(True, True)])
 
671
            FakeAttempt(True, True), FakeAttempt(True, True)])
893
672
        with patch('subprocess.call') as cc_mock:
894
673
            result = industrial.run_stages()
895
674
            self.assertItemsEqual(result, [('foo-id', True, True),
897
676
        self.assertEqual(len(cc_mock.mock_calls), 0)
898
677
 
899
678
    def test_run_stages_old_fail(self):
900
 
        old_client = fake_juju_client()
901
 
        new_client = fake_juju_client(full_path='bar-path')
 
679
        old_client = FakeEnvJujuClient('old')
 
680
        new_client = FakeEnvJujuClient('new')
902
681
        industrial = IndustrialTest(old_client, new_client, [
903
 
            FakeStepAttempt.from_result(False, True),
904
 
            FakeStepAttempt.from_result(True, True)])
905
 
        suite_factory = AttemptSuiteFactory([
906
 
            FakeAttemptClass('foo', False, True, new_path='bar-path'),
907
 
            FakeAttemptClass('bar', True, True, new_path='bar-path')])
908
 
        log_dir = use_context(self, temp_dir())
909
 
        suite = suite_factory.factory([], log_dir, None)
910
 
        industrial = IndustrialTest(old_client, new_client, [suite])
911
 
        with patch('industrial_test.BootstrapManager',
912
 
                   fake_bootstrap_manager):
 
682
            FakeAttempt(False, True), FakeAttempt(True, True)])
 
683
        with patch('subprocess.call') as cc_mock:
913
684
            result = industrial.run_stages()
914
 
            self.assertItemsEqual(result, [
915
 
                ('bootstrap', True, True),
916
 
                ('prepare-suite', True, True),
917
 
                ('foo-id', False, True)])
918
 
        self.assertEqual('controller-killed',
919
 
                         old_client._backend.controller_state.state)
920
 
        self.assertEqual('controller-killed',
921
 
                         new_client._backend.controller_state.state)
 
685
            self.assertItemsEqual(result, [('foo-id', False, True)])
 
686
        assert_juju_call(self, cc_mock, old_client,
 
687
                         ('timeout', '600.00s', 'juju', '--show-log',
 
688
                          'destroy-environment', 'old', '--force', '-y'), 0)
 
689
        assert_juju_call(self, cc_mock, new_client,
 
690
                         ('timeout', '600.00s', 'juju', '--show-log',
 
691
                          'destroy-environment', 'new', '--force', '-y'), 1)
922
692
 
923
693
    def test_run_stages_new_fail(self):
924
 
        old_client = fake_juju_client()
925
 
        new_client = fake_juju_client(full_path='bar-path')
926
 
        log_dir = use_context(self, temp_dir())
927
 
        suite_factory = AttemptSuiteFactory([
928
 
            FakeAttemptClass('foo', True, False, new_path='bar-path'),
929
 
            FakeAttemptClass('bar', True, True, new_path='bar-path')])
930
 
        suite = suite_factory.factory([], log_dir, None)
931
 
        industrial = IndustrialTest(old_client, new_client, [suite])
932
 
        with patch('industrial_test.BootstrapManager',
933
 
                   fake_bootstrap_manager):
 
694
        old_client = FakeEnvJujuClient('old')
 
695
        new_client = FakeEnvJujuClient('new')
 
696
        industrial = IndustrialTest(old_client, new_client, [
 
697
            FakeAttempt(True, False), FakeAttempt(True, True)])
 
698
        with patch('subprocess.call') as cc_mock:
934
699
            result = industrial.run_stages()
935
 
            self.assertItemsEqual(result, [
936
 
                ('bootstrap', True, True),
937
 
                ('prepare-suite', True, True),
938
 
                ('foo-id', True, False)])
939
 
        self.assertEqual('controller-killed',
940
 
                         old_client._backend.controller_state.state)
941
 
        self.assertEqual('controller-killed',
942
 
                         new_client._backend.controller_state.state)
 
700
            self.assertItemsEqual(result, [('foo-id', True, False)])
 
701
        assert_juju_call(self, cc_mock, old_client,
 
702
                         ('timeout', '600.00s', 'juju', '--show-log',
 
703
                          'destroy-environment', 'old', '--force', '-y'), 0)
 
704
        assert_juju_call(self, cc_mock, new_client,
 
705
                         ('timeout', '600.00s', 'juju', '--show-log',
 
706
                          'destroy-environment', 'new', '--force', '-y'), 1)
943
707
 
944
708
    def test_run_stages_both_fail(self):
945
 
        old_client = fake_juju_client()
946
 
        new_client = fake_juju_client()
947
 
        log_dir = use_context(self, temp_dir())
948
 
        suite = AttemptSuiteFactory([
949
 
            FakeAttemptClass('foo', False, False),
950
 
            FakeAttemptClass('bar', True, True)]).factory([], log_dir,
951
 
                                                          'foo-stream')
952
 
        industrial = IndustrialTest(old_client, new_client, [suite])
953
 
        with patch('industrial_test.BootstrapManager',
954
 
                   fake_bootstrap_manager):
 
709
        old_client = FakeEnvJujuClient('old')
 
710
        new_client = FakeEnvJujuClient('new')
 
711
        industrial = IndustrialTest(old_client, new_client, [
 
712
            FakeAttempt(False, False), FakeAttempt(True, True)])
 
713
        with patch('subprocess.call') as cc_mock:
955
714
            result = industrial.run_stages()
956
 
            self.assertItemsEqual(result, [
957
 
                ('bootstrap', True, True),
958
 
                ('prepare-suite', True, True),
959
 
                ('foo-id', False, False)])
960
 
        self.assertEqual('controller-killed',
961
 
                         old_client._backend.controller_state.state)
962
 
        self.assertEqual('controller-killed',
963
 
                         new_client._backend.controller_state.state)
 
715
            self.assertItemsEqual(result, [('foo-id', False, False)])
 
716
        assert_juju_call(self, cc_mock, old_client,
 
717
                         ('timeout', '600.00s', 'juju', '--show-log',
 
718
                          'destroy-environment', 'old', '--force', '-y'), 0)
 
719
        assert_juju_call(self, cc_mock, new_client,
 
720
                         ('timeout', '600.00s', 'juju', '--show-log',
 
721
                          'destroy-environment', 'new', '--force', '-y'), 1)
964
722
 
965
723
    def test_run_stages_recover_failure(self):
966
 
        old_client = fake_juju_client()
967
 
        new_client = fake_juju_client()
 
724
        old_client = FakeEnvJujuClient('old')
 
725
        new_client = FakeEnvJujuClient('new')
968
726
        fsa = FakeStepAttempt([('foo', True, False), ('bar', True, True)])
969
727
        industrial = IndustrialTest(old_client, new_client, [
970
 
            fsa, FakeStepAttempt.from_result(True, True)])
 
728
            fsa, FakeAttempt(True, True)])
971
729
        self.assertEqual(list(industrial.run_stages()), [
972
730
            ('foo', True, False), ('bar', True, True), ('foo-id', True, True)])
973
731
 
976
734
        new_client = FakeEnvJujuClient('new')
977
735
        fsa = FakeStepAttempt([('foo', True, True), ('bar', False, True)])
978
736
        industrial = IndustrialTest(old_client, new_client, [
979
 
            fsa, FakeStepAttempt.from_result(True, True)])
980
 
        with patch.object(old_client, 'kill_controller'):
981
 
            with patch.object(new_client, 'kill_controller'):
 
737
            fsa, FakeAttempt(True, True)])
 
738
        with patch.object(old_client, 'destroy_environment'):
 
739
            with patch.object(new_client, 'destroy_environment'):
982
740
                self.assertEqual(list(industrial.run_stages()), [
983
741
                    ('foo', True, True), ('bar', False, True)])
984
742
 
985
743
    def test_run_stages_raises_cannot_upgrade_to_old_client(self):
986
744
        old = FakeEnvJujuClient()
987
745
        new = FakeEnvJujuClient()
988
 
        industrial = IndustrialTest(old, new, [PrepareUpgradeJujuAttempt({})])
 
746
        industrial = IndustrialTest(old, new, [UpgradeJujuAttempt({})])
989
747
        with self.assertRaises(CannotUpgradeToOldClient):
990
748
            list(industrial.run_stages())
991
749
 
 
750
    def test_destroy_both_even_with_exception(self):
 
751
        old_client = FakeEnvJujuClient('old')
 
752
        new_client = FakeEnvJujuClient('new')
 
753
        industrial = IndustrialTest(old_client, new_client, [
 
754
            FakeAttempt(False, False), FakeAttempt(True, True)])
 
755
        with patch.object(old_client, 'destroy_environment',
 
756
                          side_effect=Exception) as oc_mock:
 
757
            with patch.object(new_client, 'destroy_environment',
 
758
                              side_effect=Exception) as nc_mock:
 
759
                with self.assertRaises(Exception):
 
760
                    industrial.destroy_both()
 
761
        oc_mock.assert_called_once_with(delete_jenv=True)
 
762
        nc_mock.assert_called_once_with(delete_jenv=True)
 
763
 
992
764
    def test_run_attempt(self):
993
 
        old_client = fake_juju_client()
994
 
        new_client = fake_juju_client()
995
 
        attempt = FakeStepAttempt.from_result(True, True)
996
 
        log_dir = use_context(self, temp_dir())
997
 
        suite = AttemptSuiteFactory([attempt]).factory([], log_dir, None)
998
 
        industrial = IndustrialTest(old_client, new_client,
999
 
                                    [suite])
 
765
        old_client = FakeEnvJujuClient('old')
 
766
        new_client = FakeEnvJujuClient('new')
 
767
        attempt = FakeAttempt(True, True)
 
768
        industrial = IndustrialTest(old_client, new_client, [attempt])
1000
769
 
1001
770
        def iter_test_results(old, new):
1002
771
            raise Exception
1005
774
        with patch.object(attempt, 'iter_test_results',
1006
775
                          iter_test_results):
1007
776
            with patch('logging.exception') as le_mock:
1008
 
                with patch('industrial_test.BootstrapManager',
1009
 
                           fake_bootstrap_manager):
1010
 
                    industrial.run_attempt()
1011
 
        self.assertEqual(2, le_mock.call_count)
1012
 
        self.assertEqual('controller-killed',
1013
 
                         old_client._backend.controller_state.state)
1014
 
        self.assertEqual('controller-killed',
1015
 
                         new_client._backend.controller_state.state)
1016
 
 
1017
 
 
1018
 
class TestSteppedStageAttempt(JujuPyTestCase):
 
777
                with patch.object(industrial, 'destroy_both') as db_mock:
 
778
                    with self.assertRaises(SystemExit):
 
779
                        industrial.run_attempt()
 
780
        self.assertEqual(1, le_mock.call_count)
 
781
        self.assertEqual(db_mock.mock_calls, [call(), call()])
 
782
 
 
783
 
 
784
class TestSteppedStageAttempt(TestCase):
1019
785
 
1020
786
    def test__iter_for_result_premature_results(self):
1021
 
        iterator = (x for x in [{'test_id': 'foo-id', 'result': True}])
 
787
        iterator = iter([{'test_id': 'foo-id', 'result': True}])
1022
788
        with self.assertRaisesRegexp(ValueError, 'Result before declaration.'):
1023
789
            list(SteppedStageAttempt._iter_for_result(iterator))
1024
790
 
1025
791
    def test__iter_for_result_many(self):
1026
 
        iterator = (x for x in [
 
792
        iterator = iter([
1027
793
            {'test_id': 'foo-id'},
1028
794
            {'test_id': 'foo-id', 'result': True},
1029
795
            {'test_id': 'bar-id'},
1048
814
        le_mock.assert_called_once_with(error)
1049
815
 
1050
816
    def test_iter_for_result_id_change(self):
1051
 
        iterator = (x for x in [
 
817
        iterator = iter([
1052
818
            {'test_id': 'foo-id'}, {'test_id': 'bar-id'}])
1053
819
        with self.assertRaisesRegexp(ValueError, 'ID changed without result.'):
1054
820
            list(SteppedStageAttempt._iter_for_result(iterator))
1065
831
            list(SteppedStageAttempt._iter_for_result(iterator()))
1066
832
 
1067
833
    def test_iter_for_result_id_change_result(self):
1068
 
        iterator = (x for x in [
 
834
        iterator = iter([
1069
835
            {'test_id': 'foo-id'}, {'test_id': 'bar-id', 'result': True}])
1070
836
        with self.assertRaisesRegexp(ValueError, 'ID changed without result.'):
1071
837
            list(SteppedStageAttempt._iter_for_result(iterator))
1072
838
 
1073
839
    def test__iter_test_results_success(self):
1074
 
        old_iter = (x for x in [
 
840
        old_iter = iter([
1075
841
            None, {'test_id': 'foo-id', 'result': True}])
1076
 
        new_iter = (x for x in [
 
842
        new_iter = iter([
1077
843
            None, {'test_id': 'foo-id', 'result': False}])
1078
844
 
1079
845
        class StubSA(SteppedStageAttempt):
1083
849
                return {'foo-id': {'title': 'foo-id'}}
1084
850
 
1085
851
        self.assertItemsEqual(
1086
 
            StubSA()._iter_test_results(old_iter, new_iter),
 
852
            StubSA._iter_test_results(old_iter, new_iter),
1087
853
            [('foo-id', True, False)])
1088
854
 
1089
855
    def test__iter_test_results_interleaved(self):
1090
856
        # Using a single iterator for both proves that they are interleaved.
1091
857
        # Otherwise, we'd get Result before declaration.
1092
 
        both_iter = (x for x in [
 
858
        both_iter = iter([
1093
859
            None, None,
1094
860
            {'test_id': 'foo-id', 'result': True},
1095
861
            {'test_id': 'foo-id', 'result': False},
1102
868
                return {'foo-id': {'title': 'foo-id'}}
1103
869
 
1104
870
        self.assertItemsEqual(
1105
 
            StubSA()._iter_test_results(both_iter, both_iter),
 
871
            StubSA._iter_test_results(both_iter, both_iter),
1106
872
            [('foo-id', True, False)])
1107
873
 
1108
874
    def test__iter_test_results_id_mismatch(self):
1109
 
        old_iter = (x for x in [
 
875
        old_iter = iter([
1110
876
            None, {'test_id': 'foo-id', 'result': True}])
1111
 
        new_iter = (x for x in [
 
877
        new_iter = iter([
1112
878
            None, {'test_id': 'bar-id', 'result': False}])
1113
 
        with self.assertRaises(LoggedException) as exc:
1114
 
            list(SteppedStageAttempt()._iter_test_results(old_iter, new_iter))
1115
 
        self.assertEqual(ValueError('Test id mismatch: foo-id bar-id').args,
1116
 
                         exc.exception.exception.args)
 
879
        with self.assertRaisesRegexp(ValueError, 'Test id mismatch.'):
 
880
            list(SteppedStageAttempt._iter_test_results(old_iter, new_iter))
1117
881
 
1118
882
    def test__iter_test_results_many(self):
1119
 
        old_iter = (x for x in [
 
883
        old_iter = iter([
1120
884
            None, {'test_id': 'foo-id', 'result': True},
1121
885
            None, {'test_id': 'bar-id', 'result': False},
1122
886
            ])
1123
 
        new_iter = (x for x in [
 
887
        new_iter = iter([
1124
888
            None, {'test_id': 'foo-id', 'result': False},
1125
889
            None, {'test_id': 'bar-id', 'result': False},
1126
890
            ])
1134
898
                    'bar-id': {'title': 'bar-id'},
1135
899
                }
1136
900
        self.assertItemsEqual(
1137
 
            StubSA()._iter_test_results(old_iter, new_iter),
 
901
            StubSA._iter_test_results(old_iter, new_iter),
1138
902
            [('foo-id', True, False), ('bar-id', False, False)])
1139
903
 
1140
904
    def test_iter_test_results(self):
1173
937
            def __init__(self):
1174
938
                super(StubSA, self).__init__()
1175
939
 
1176
 
        self.assertIs(type(StubSA.factory(['a', 'b', 'c'], None)), StubSA)
 
940
        self.assertIs(type(StubSA.factory(['a', 'b', 'c'])), StubSA)
1177
941
 
1178
942
    def test_get_test_info(self):
1179
943
 
1189
953
            ('bar-id', {'title': 'Bar title', 'report_on': False})]))
1190
954
 
1191
955
 
1192
 
def FakeEnvJujuClient(name='steve', version='1.2', full_path='/jbin/juju'):
1193
 
    juju_data = JujuData(name, {'type': 'fake', 'region': 'regionx'})
1194
 
    juju_data.credentials = {'credentials': {'fake': {'creds': {}}}}
1195
 
    return EnvJujuClient(
1196
 
        juju_data, version, full_path)
1197
 
 
1198
 
 
1199
 
class FakeEnvJujuClient1X(EnvJujuClient1X):
1200
 
 
1201
 
    def __init__(self, name='steve', version='1.2', full_path='/jbin/juju'):
1202
 
        super(FakeEnvJujuClient1X, self).__init__(
1203
 
            SimpleEnvironment(name, {'type': 'fake'}), version, full_path)
1204
 
 
1205
 
 
1206
 
class TestBootstrapAttempt(JujuPyTestCase):
 
956
class FakeEnvJujuClient(EnvJujuClient):
 
957
 
 
958
    def __init__(self, name='steve'):
 
959
        super(FakeEnvJujuClient, self).__init__(
 
960
            SimpleEnvironment(name, {'type': 'fake'}), '1.2', '/jbin/juju')
 
961
 
 
962
    def wait_for_started(self, start=None):
 
963
        with patch('sys.stdout'):
 
964
            return super(FakeEnvJujuClient, self).wait_for_started(0.1,
 
965
                                                                   start=start)
 
966
 
 
967
    def wait_for_ha(self):
 
968
        with patch('sys.stdout'):
 
969
            return super(FakeEnvJujuClient, self).wait_for_ha(0.01)
 
970
 
 
971
    def status_until(self, *args, **kwargs):
 
972
        yield self.get_status()
 
973
        yield self.get_status()
 
974
 
 
975
    def juju(self, *args, **kwargs):
 
976
        # Suppress stdout for juju commands.
 
977
        with patch('sys.stdout'):
 
978
            return super(FakeEnvJujuClient, self).juju(*args, **kwargs)
 
979
 
 
980
 
 
981
class TestBootstrapAttempt(TestCase):
1207
982
 
1208
983
    def test_iter_steps(self):
1209
984
        client = FakeEnvJujuClient()
1210
985
        bootstrap = BootstrapAttempt()
1211
986
        boot_iter = iter_steps_validate_info(self, bootstrap, client)
1212
987
        self.assertEqual(boot_iter.next(), {'test_id': 'bootstrap'})
1213
 
        with observable_temp_file() as config_file:
1214
 
            with patch('subprocess.Popen') as popen_mock:
1215
 
                self.assertEqual(boot_iter.next(), {'test_id': 'bootstrap'})
1216
 
            assert_juju_call(self, popen_mock, client, (
1217
 
                'juju', '--show-log', 'bootstrap', '--constraints', 'mem=2G',
1218
 
                'steve', 'fake/regionx', '--config', config_file.name,
1219
 
                '--default-model', 'steve', '--agent-version', '1.2'))
1220
 
            statuses = [
1221
 
                {'machines': {'0': {'agent-state': 'pending'}},
1222
 
                 'applications': {}},
1223
 
                {'machines': {'0': {'agent-state': 'started'}},
1224
 
                 'applicaions': {}},
1225
 
            ]
1226
 
            popen_mock.return_value.wait.return_value = 0
 
988
        with patch('subprocess.Popen') as popen_mock:
1227
989
            self.assertEqual(boot_iter.next(), {'test_id': 'bootstrap'})
1228
 
        with patch_status(client, *statuses) as gs_mock:
 
990
        assert_juju_call(self, popen_mock, client, (
 
991
            'juju', '--show-log', 'bootstrap', '-e', 'steve',
 
992
            '--constraints', 'mem=2G'))
 
993
        statuses = (yaml.safe_dump(x) for x in [
 
994
            {'machines': {'0': {'agent-state': 'pending'}}, 'services': {}},
 
995
            {'machines': {'0': {'agent-state': 'started'}}, 'services': {}},
 
996
            ])
 
997
        popen_mock.return_value.wait.return_value = 0
 
998
        self.assertEqual(boot_iter.next(), {'test_id': 'bootstrap'})
 
999
        with patch('subprocess.check_output',
 
1000
                   side_effect=lambda x, **y: statuses.next()) as mock_co:
1229
1001
            self.assertEqual(boot_iter.next(),
1230
1002
                             {'test_id': 'bootstrap', 'result': True})
1231
 
        self.assertEqual(2, gs_mock.call_count)
1232
 
 
1233
 
 
1234
 
class TestDestroyEnvironmentAttempt(JujuPyTestCase):
 
1003
        for num in range(2):
 
1004
            assert_juju_call(self, mock_co, client, (
 
1005
                'juju', '--show-log', 'status', '-e', 'steve'), num,
 
1006
                assign_stderr=True)
 
1007
 
 
1008
 
 
1009
class TestDestroyEnvironmentAttempt(TestCase):
1235
1010
 
1236
1011
    def test_iter_steps(self):
1237
1012
        client = FakeEnvJujuClient()
1238
1013
        destroy_env = DestroyEnvironmentAttempt()
1239
1014
        iterator = iter_steps_validate_info(self, destroy_env, client)
1240
1015
        self.assertEqual({'test_id': 'destroy-env'}, iterator.next())
1241
 
        with patch.object(client, 'get_jes_command',
1242
 
                          return_value='kill-controller'):
1243
 
            with patch.object(destroy_env, 'get_security_groups') as gsg_mock:
1244
 
                with patch('subprocess.call', return_value=0) as mock_cc:
1245
 
                    self.assertEqual(iterator.next(), {
1246
 
                        'test_id': 'destroy-env', 'result': True})
1247
 
        gsg_mock.assert_called_once_with(client)
1248
 
        assert_juju_call(self, mock_cc, client, get_timeout_prefix(600) + (
1249
 
            'juju', '--show-log', 'kill-controller', 'steve', '-y'))
1250
 
        self.assertEqual(iterator.next(), {'test_id': 'substrate-clean'})
1251
 
        with patch.object(destroy_env, 'check_security_groups') as csg_mock:
1252
 
            self.assertEqual(iterator.next(),
1253
 
                             {'test_id': 'substrate-clean', 'result': True})
1254
 
        csg_mock.assert_called_once_with(client, gsg_mock.return_value)
1255
 
 
1256
 
    def test_iter_steps_non_jes(self):
1257
 
        client = FakeEnvJujuClient1X()
1258
 
        destroy_env = DestroyEnvironmentAttempt()
1259
 
        iterator = iter_steps_validate_info(self, destroy_env, client)
1260
 
        self.assertEqual({'test_id': 'destroy-env'}, iterator.next())
1261
 
        with patch.object(client, 'is_jes_enabled', return_value=False):
1262
 
            with patch.object(destroy_env, 'get_security_groups') as gsg_mock:
1263
 
                with patch('subprocess.call', return_value=0) as mock_cc:
1264
 
                    self.assertEqual(iterator.next(), {
1265
 
                        'test_id': 'destroy-env', 'result': True})
1266
 
        gsg_mock.assert_called_once_with(client)
1267
 
        assert_juju_call(self, mock_cc, client, get_timeout_prefix(600) + (
1268
 
            'juju', '--show-log', 'destroy-environment', 'steve', '-y'))
 
1016
        with patch('subprocess.call') as mock_cc:
 
1017
            with patch.object(destroy_env, 'get_security_groups') as gsg_mock:
 
1018
                self.assertEqual(iterator.next(), {
 
1019
                    'test_id': 'destroy-env', 'result': True})
 
1020
        gsg_mock.assert_called_once_with(client)
 
1021
        assert_juju_call(self, mock_cc, client, (
 
1022
            'timeout', '600.00s', 'juju', '--show-log', 'destroy-environment',
 
1023
            'steve', '-y'))
1269
1024
        self.assertEqual(iterator.next(), {'test_id': 'substrate-clean'})
1270
1025
        with patch.object(destroy_env, 'check_security_groups') as csg_mock:
1271
1026
            self.assertEqual(iterator.next(),
1275
1030
    def test_iter_test_results(self):
1276
1031
        client = FakeEnvJujuClient()
1277
1032
        destroy_env = DestroyEnvironmentAttempt()
1278
 
        with patch('subprocess.call'):
1279
 
            with patch.object(client, 'get_jes_command',
1280
 
                              return_value='kill-controller'):
1281
 
                output = list(destroy_env.iter_test_results(client, client))
 
1033
        with patch('subprocess.check_call'):
 
1034
            output = list(destroy_env.iter_test_results(client, client))
1282
1035
        self.assertEqual(output, [
1283
1036
            ('destroy-env', True, True), ('substrate-clean', True, True)])
1284
1037
 
1285
 
    def test_iter_steps_failure(self):
1286
 
        client = FakeEnvJujuClient()
1287
 
        destroy_env = DestroyEnvironmentAttempt()
1288
 
        iterator = iter_steps_validate_info(self, destroy_env, client)
1289
 
        self.assertEqual({'test_id': 'destroy-env'}, iterator.next())
1290
 
        with patch('subprocess.call', return_value=1) as mock_cc:
1291
 
            with patch.object(client, 'get_jes_command',
1292
 
                              return_value='kill-controller'):
1293
 
                with patch.object(destroy_env,
1294
 
                                  'get_security_groups') as gsg_mock:
1295
 
                    with patch.object(client, 'kill_controller',
1296
 
                                      side_effect=Exception) as kc_mock:
1297
 
                        with self.assertRaises(Exception):
1298
 
                            iterator.next()
1299
 
        kc_mock.assert_called_once_with()
1300
 
        gsg_mock.assert_called_once_with(client)
1301
 
        self.assertEqual(0, mock_cc.call_count)
1302
 
        with self.assertRaises(StopIteration):
1303
 
            iterator.next()
1304
 
 
1305
 
    def test_iter_steps_failure_non_jes(self):
1306
 
        client = FakeEnvJujuClient1X()
1307
 
        destroy_env = DestroyEnvironmentAttempt()
1308
 
        iterator = iter_steps_validate_info(self, destroy_env, client)
1309
 
        self.assertEqual({'test_id': 'destroy-env'}, iterator.next())
1310
 
        with patch('subprocess.call', return_value=1) as mock_cc:
1311
 
            with patch.object(client, 'is_jes_enabled', return_value=False):
1312
 
                with patch.object(destroy_env,
1313
 
                                  'get_security_groups') as gsg_mock:
1314
 
                    self.assertEqual(iterator.next(), {
1315
 
                        'test_id': 'destroy-env', 'result': False})
1316
 
        gsg_mock.assert_called_once_with(client)
1317
 
        assert_juju_call(self, mock_cc, client, get_timeout_prefix(600) + (
1318
 
            'juju', '--show-log', 'destroy-environment', 'steve', '-y'))
1319
 
        with self.assertRaises(StopIteration):
1320
 
            iterator.next()
1321
 
 
1322
 
    def test_iter_steps_kill_controller(self):
1323
 
        client = fake_juju_client()
1324
 
        client.bootstrap()
1325
 
        destroy_env = DestroyEnvironmentAttempt()
1326
 
        iterator = iter_steps_validate_info(self, destroy_env, client)
1327
 
        with closing(iterator):
1328
 
            self.assertEqual({'test_id': 'destroy-env'}, iterator.next())
1329
 
            self.assertEqual(iterator.next(), {
1330
 
                'test_id': 'destroy-env', 'result': True})
1331
 
        self.assertEqual('controller-killed',
1332
 
                         client._backend.controller_state.state)
1333
 
 
1334
1038
    @staticmethod
1335
1039
    def get_aws_client():
1336
1040
        client = FakeEnvJujuClient()
1340
1044
    @staticmethod
1341
1045
    def get_openstack_client():
1342
1046
        client = FakeEnvJujuClient()
1343
 
        client.env.clouds = {'clouds': {'quxxx': {
1344
 
            'type': 'openstack', 'endpoint': 'qux',
1345
 
            }}}
1346
1047
        client.env.config = get_os_config()
1347
 
        client.env.credentials = {'credentials': {'quxxx': {'creds': {
1348
 
            }}}}
1349
1048
        return client
1350
1049
 
1351
1050
    def test_get_security_groups_aws(self):
1352
1051
        client = self.get_aws_client()
1353
1052
        destroy_env = DestroyEnvironmentAttempt()
1354
 
        status = {'machines': {
 
1053
        yaml_instances = yaml.safe_dump({'machines': {
1355
1054
            'foo': {'instance-id': 'foo-id'},
1356
 
        }}
 
1055
            }})
1357
1056
        aws_instances = [
1358
1057
            MagicMock(instances=[MagicMock(groups=[
1359
1058
                SecurityGroup(id='foo', name='bar'),
1363
1062
                SecurityGroup(id='quxx-id', name='quxx'),
1364
1063
                ])]),
1365
1064
        ]
1366
 
        aws_client = MagicMock()
1367
 
        aws_client.get_all_instances.return_value = aws_instances
1368
1065
        with patch(
1369
 
                'substrate.ec2.connect_to_region') as gec_mock:
1370
 
            with patch_status(client, status):
 
1066
                'substrate.AWSAccount.get_ec2_connection') as gec_mock:
 
1067
            with patch('subprocess.check_output', return_value=yaml_instances):
1371
1068
                gai_mock = gec_mock.return_value.get_all_instances
1372
1069
                gai_mock.return_value = aws_instances
1373
1070
                self.assertEqual(destroy_env.get_security_groups(client), {
1374
1071
                    'baz': 'qux', 'foo': 'bar', 'quxx-id': 'quxx'
1375
1072
                    })
1376
 
        self.assert_ec2_connection_call(gec_mock)
 
1073
        gec_mock.assert_called_once_with()
1377
1074
        gai_mock.assert_called_once_with(instance_ids=['foo-id'])
1378
1075
 
1379
1076
    def test_get_security_groups_openstack(self):
1380
1077
        client = self.get_openstack_client()
1381
1078
        destroy_env = DestroyEnvironmentAttempt()
1382
 
        status = {'machines': {
 
1079
        yaml_instances = yaml.safe_dump({'machines': {
1383
1080
            'foo': {'instance-id': 'bar-id'},
1384
1081
            'bar': {'instance-id': 'baz-qux-id'},
1385
 
        }}
 
1082
            }})
1386
1083
        os_instances = [
1387
1084
            make_os_security_group_instance(['bar']),
1388
1085
            make_os_security_group_instance(['baz', 'qux']),
1393
1090
            os_client.servers.list.return_value = os_instances
1394
1091
            security_groups = make_os_security_groups(['bar', 'baz', 'qux'])
1395
1092
            os_client.security_groups.list.return_value = security_groups
1396
 
            with patch_status(client, status):
 
1093
            with patch('subprocess.check_output', return_value=yaml_instances):
1397
1094
                self.assertEqual(destroy_env.get_security_groups(client), {
1398
1095
                    'baz-id': 'baz', 'bar-id': 'bar', 'qux-id': 'qux'
1399
1096
                    })
1409
1106
    def test_check_security_groups_match(self):
1410
1107
        client = self.get_aws_client()
1411
1108
        destroy_env = DestroyEnvironmentAttempt()
1412
 
        aws_client = MagicMock()
1413
 
        aws_client.get_all_security_groups.return_value = list(
1414
 
            self.make_group())
1415
 
        with patch('substrate.ec2.connect_to_region',
1416
 
                   return_value=aws_client) as ctr_mock:
 
1109
        output = (
 
1110
            'GROUP\tfoo-id\t\tfoo-group\n'
 
1111
            'GROUP\tbaz-id\t\tbaz-group\n'
 
1112
        )
 
1113
        with patch('subprocess.check_output', return_value=output) as co_mock:
1417
1114
            with self.assertRaisesRegexp(
1418
1115
                Exception, (
1419
1116
                    r'Security group\(s\) not cleaned up: foo-group.')):
1421
1118
                               lambda x: iter([None])):
1422
1119
                        destroy_env.check_security_groups(
1423
1120
                            client, {'foo-id': 'foo', 'bar-id': 'bar'})
1424
 
        aws_client.get_all_security_groups.assert_called_once_with(
1425
 
            filters={'description': 'juju group'})
1426
 
        self.assert_ec2_connection_call(ctr_mock)
1427
 
 
1428
 
    def make_group(self):
1429
 
        for name in ['foo', 'baz']:
1430
 
            group = MagicMock()
1431
 
            group.name = name + '-group'
1432
 
            group.id = name + '-id'
1433
 
            yield group
 
1121
        with AWSAccount.manager_from_config(client.env.config) as aws:
 
1122
            env = aws.get_environ()
 
1123
        co_mock.assert_called_once_with(
 
1124
            ['euca-describe-groups', '--filter', 'description=juju group'],
 
1125
            env=env)
1434
1126
 
1435
1127
    def test_check_security_groups_no_match(self):
1436
1128
        client = self.get_aws_client()
1437
1129
        destroy_env = DestroyEnvironmentAttempt()
1438
 
        aws_client = MagicMock()
1439
 
        aws_client.get_all_security_groups.return_value = list(
1440
 
            self.make_group())
1441
 
        with patch('substrate.ec2.connect_to_region',
1442
 
                   return_value=aws_client) as ctr_mock:
 
1130
        output = (
 
1131
            'GROUP\tfoo-id\t\tfoo-group\n'
 
1132
            'GROUP\tbaz-id\t\tbaz-group\n'
 
1133
        )
 
1134
        with patch('subprocess.check_output', return_value=output) as co_mock:
1443
1135
                destroy_env.check_security_groups(
1444
1136
                    client, {'bar-id': 'bar'})
1445
 
        aws_client.get_all_security_groups.assert_called_once_with(
1446
 
            filters={'description': 'juju group'})
1447
 
        self.assert_ec2_connection_call(ctr_mock)
1448
 
 
1449
 
    def assert_ec2_connection_call(self, ctr_mock):
1450
 
        ctr_mock.assert_called_once_with(
1451
 
            'ca-west', aws_access_key_id='skeleton-key',
1452
 
            aws_secret_access_key='secret-skeleton-key')
 
1137
        with AWSAccount.manager_from_config(client.env.config) as aws:
 
1138
            env = aws.get_environ()
 
1139
        co_mock.assert_called_once_with(
 
1140
            ['euca-describe-groups', '--filter', 'description=juju group'],
 
1141
            env=env)
1453
1142
 
1454
1143
    def test_check_security_groups_non_aws(self):
1455
1144
        client = FakeEnvJujuClient()
1460
1149
        self.assertEqual(co_mock.call_count, 0)
1461
1150
 
1462
1151
 
1463
 
class TestEnsureAvailabilityAttempt(JujuPyTestCase):
 
1152
class TestEnsureAvailabilityAttempt(TestCase):
 
1153
 
 
1154
    def setUp(self):
 
1155
        patcher = patch('jujupy.pause')
 
1156
        self.addCleanup(patcher.stop)
 
1157
        self.pause_mock = patcher.start()
1464
1158
 
1465
1159
    def test_iter_steps(self):
1466
1160
        client = FakeEnvJujuClient()
1467
 
        controller_client = client.get_controller_client()
1468
1161
        ensure_av = EnsureAvailabilityAttempt()
1469
1162
        ensure_iter = iter_steps_validate_info(self, ensure_av, client)
1470
1163
        self.assertEqual(ensure_iter.next(), {
1471
1164
            'test_id': 'ensure-availability-n3'})
1472
1165
        with patch('subprocess.check_call') as cc_mock:
1473
 
            with patch.object(client, 'get_controller_client',
1474
 
                              return_value=controller_client, autospec=True):
1475
 
                self.assertEqual(ensure_iter.next(), {
1476
 
                    'test_id': 'ensure-availability-n3'})
1477
 
        assert_juju_call(
1478
 
            self,
1479
 
            cc_mock, client,
1480
 
            ('juju', '--show-log', 'enable-ha', '-m',
1481
 
             'steve:{}'.format(controller_client.env.environment), '-n', '3'))
1482
 
        status = {
 
1166
            self.assertEqual(ensure_iter.next(), {
 
1167
                'test_id': 'ensure-availability-n3'})
 
1168
        assert_juju_call(self, cc_mock, client, (
 
1169
            'juju', '--show-log', 'ensure-availability', '-e', 'steve', '-n',
 
1170
            '3'))
 
1171
        value = yaml.safe_dump({
1483
1172
            'machines': {
1484
 
                '0': {'controller-member-status': 'has-vote'},
1485
 
                '1': {'controller-member-status': 'has-vote'},
1486
 
                '2': {'controller-member-status': 'has-vote'},
 
1173
                '0': {'state-server-member-status': 'has-vote'},
 
1174
                '1': {'state-server-member-status': 'has-vote'},
 
1175
                '2': {'state-server-member-status': 'has-vote'},
1487
1176
                },
1488
 
            'applications': {},
1489
 
        }
1490
 
        with patch_status(controller_client, status) as gs_mock:
 
1177
            'services': {},
 
1178
            })
 
1179
        with patch('subprocess.check_output', return_value=value) as co_mock:
1491
1180
            self.assertEqual(ensure_iter.next(), {
1492
1181
                'test_id': 'ensure-availability-n3', 'result': True})
1493
 
        gs_mock.assert_called_once_with(controller=True)
 
1182
        assert_juju_call(self, co_mock, client, (
 
1183
            'juju', '--show-log', 'status', '-e', 'steve'), assign_stderr=True)
1494
1184
 
1495
1185
    def test_iter_steps_failure(self):
1496
1186
        client = FakeEnvJujuClient()
1498
1188
        ensure_iter = iter_steps_validate_info(self, ensure_av, client)
1499
1189
        ensure_iter.next()
1500
1190
        with patch('subprocess.check_call'):
1501
 
            controller_client = client.get_controller_client()
1502
 
            with patch.object(client, 'get_controller_client',
1503
 
                              return_value=controller_client, autospec=True):
1504
 
                ensure_iter.next()
1505
 
        status = {
 
1191
            ensure_iter.next()
 
1192
        ensure_av = EnsureAvailabilityAttempt()
 
1193
        client = FakeEnvJujuClient()
 
1194
        output = yaml.safe_dump({
1506
1195
            'machines': {
1507
1196
                '0': {'state-server-member-status': 'has-vote'},
1508
1197
                '1': {'state-server-member-status': 'has-vote'},
1509
1198
                },
1510
 
            'applications': {},
1511
 
        }
1512
 
        with patch_status(controller_client, status) as gs_mock:
 
1199
            'services': {},
 
1200
            })
 
1201
        with patch('subprocess.check_output', return_value=output):
1513
1202
            with self.assertRaisesRegexp(
1514
1203
                    Exception, 'Timed out waiting for voting to be enabled.'):
1515
1204
                ensure_iter.next()
1516
 
        self.assertEqual(2, gs_mock.call_count)
1517
 
 
1518
 
 
1519
 
class TestDeployManyAttempt(JujuPyTestCase):
1520
 
 
1521
 
    def predict_add_machine_calls(self, deploy_many, machine_type):
 
1205
 
 
1206
 
 
1207
class TestDeployManyAttempt(TestCase):
 
1208
 
 
1209
    def predict_add_machine_calls(self, deploy_many):
1522
1210
        for host in range(1, deploy_many.host_count + 1):
1523
1211
            for container in range(deploy_many.container_count):
1524
 
                target = '{}:{}'.format(machine_type, host)
 
1212
                target = 'lxc:{}'.format(host)
1525
1213
                service = 'ubuntu{}x{}'.format(host, container)
1526
 
                yield ('juju', '--show-log', 'deploy', '-m', 'steve:steve',
1527
 
                       'ubuntu', service, '--to', target, '--series', 'angsty')
 
1214
                yield ('juju', '--show-log', 'deploy', '-e', 'steve', '--to',
 
1215
                       target, 'ubuntu', service)
1528
1216
 
1529
1217
    def predict_remove_machine_calls(self, deploy_many):
1530
1218
        total_guests = deploy_many.host_count * deploy_many.container_count
1531
1219
        for guest in range(100, total_guests + 100):
1532
 
            yield ('juju', '--show-log', 'remove-machine', '-m', 'steve:steve',
 
1220
            yield ('juju', '--show-log', 'remove-machine', '-e', 'steve',
1533
1221
                   '--force', str(guest))
1534
1222
 
1535
1223
    def test_iter_steps(self):
1536
 
        machine_started = {'juju-status': {'current': 'idle'}}
1537
 
        unit_started = {'agent-status': {'current': 'idle'}}
1538
 
        client = FakeEnvJujuClient()
1539
 
        client.env.config['default-series'] = 'angsty'
1540
 
        self.do_iter_steps(client, LXD_MACHINE, machine_started, unit_started)
1541
 
 
1542
 
    def test_iter_steps_1x(self):
1543
 
        started_state = {'agent-state': 'started'}
1544
 
        client = FakeEnvJujuClient()
1545
 
        with patch.object(EnvJujuClient, 'supported_container_types',
1546
 
                          frozenset([KVM_MACHINE, LXC_MACHINE])):
1547
 
            client.env.config['default-series'] = 'angsty'
1548
 
            self.do_iter_steps(client, LXC_MACHINE, started_state,
1549
 
                               started_state)
1550
 
 
1551
 
    def do_iter_steps(self, client, machine_type, machine_started,
1552
 
                      unit_started):
 
1224
        client = FakeEnvJujuClient()
1553
1225
        deploy_many = DeployManyAttempt(9, 11)
1554
1226
        deploy_iter = iter_steps_validate_info(self, deploy_many, client)
1555
1227
        self.assertEqual(deploy_iter.next(), {'test_id': 'add-machine-many'})
1556
 
        status = {
1557
 
            'machines': {'0': dict(machine_started)},
1558
 
            'applications': {},
1559
 
        }
1560
 
        with patch_status(client, status):
 
1228
        status = yaml.safe_dump({
 
1229
            'machines': {'0': {'agent-state': 'started'}},
 
1230
            'services': {},
 
1231
            })
 
1232
        with patch('subprocess.check_output', return_value=status):
1561
1233
            with patch('subprocess.check_call') as mock_cc:
1562
1234
                self.assertEqual(deploy_iter.next(),
1563
1235
                                 {'test_id': 'add-machine-many'})
1564
1236
        for index in range(deploy_many.host_count):
1565
1237
            assert_juju_call(self, mock_cc, client, (
1566
 
                'juju', '--show-log', 'add-machine',
1567
 
                '-m', 'steve:steve'), index)
 
1238
                'juju', '--show-log', 'add-machine', '-e', 'steve'), index)
1568
1239
 
1569
 
        status = {
1570
 
            'machines': dict((str(x), dict(machine_started))
 
1240
        status = yaml.safe_dump({
 
1241
            'machines': dict((str(x), {'agent-state': 'started'})
1571
1242
                             for x in range(deploy_many.host_count + 1)),
1572
 
            'applications': {},
1573
 
        }
1574
 
        with patch_status(client, status):
 
1243
            'services': {},
 
1244
            })
 
1245
        with patch('subprocess.check_output', return_value=status):
1575
1246
                self.assertEqual(
1576
1247
                    deploy_iter.next(),
1577
1248
                    {'test_id': 'add-machine-many', 'result': True})
1579
1250
                         {'test_id': 'ensure-machines'})
1580
1251
        self.assertEqual(deploy_iter.next(),
1581
1252
                         {'test_id': 'ensure-machines'})
1582
 
        with patch_status(client, status):
 
1253
        with patch('subprocess.check_output', return_value=status):
1583
1254
            self.assertEqual(deploy_iter.next(),
1584
1255
                             {'test_id': 'ensure-machines', 'result': True})
1585
1256
        self.assertEqual(deploy_iter.next(),
1588
1259
            self.assertEqual(deploy_iter.next(),
1589
1260
                             {'test_id': 'deploy-many'})
1590
1261
 
1591
 
        calls = self.predict_add_machine_calls(deploy_many, machine_type)
 
1262
        calls = self.predict_add_machine_calls(deploy_many)
1592
1263
        for num, args in enumerate(calls):
1593
1264
            assert_juju_call(self, mock_cc, client, args, num)
1594
1265
        service_names = []
1595
1266
        for host in range(1, deploy_many.host_count + 1):
1596
1267
            for container in range(deploy_many.container_count):
1597
1268
                service_names.append('ubuntu{}x{}'.format(host, container))
1598
 
        applications = {}
1599
 
        for num, service_name in enumerate(service_names):
1600
 
            foo = {'machine': str(num + 100)}
1601
 
            foo.update(unit_started)
1602
 
            units = {
1603
 
                'foo': foo,
1604
 
                }
1605
 
            applications[service_name] = {'units': units}
1606
 
        status = {
1607
 
            'machines': {'0': dict(machine_started)},
1608
 
            'applications': applications,
1609
 
        }
1610
 
        with patch_status(client, status):
 
1269
        services = dict((service_name, {
 
1270
            'units': {
 
1271
                'foo': {'machine': str(num + 100), 'agent-state': 'started'}
 
1272
                }})
 
1273
            for num, service_name in enumerate(service_names))
 
1274
        status = yaml.safe_dump({
 
1275
            'machines': {'0': {'agent-state': 'started'}},
 
1276
            'services': services,
 
1277
            })
 
1278
        with patch('subprocess.check_output', return_value=status):
1611
1279
            self.assertEqual(deploy_iter.next(),
1612
1280
                             {'test_id': 'deploy-many', 'result': True})
1613
1281
 
1614
1282
        self.assertEqual(deploy_iter.next(),
1615
 
                         {'test_id': 'remove-machine-many-container'})
1616
 
        with patch_status(client, status):
 
1283
                         {'test_id': 'remove-machine-many-lxc'})
 
1284
        with patch('subprocess.check_output', return_value=status):
1617
1285
            with patch('subprocess.check_call') as mock_cc:
1618
1286
                self.assertEqual(
1619
1287
                    deploy_iter.next(),
1620
 
                    {'test_id': 'remove-machine-many-container'})
 
1288
                    {'test_id': 'remove-machine-many-lxc'})
1621
1289
        calls = self.predict_remove_machine_calls(deploy_many)
1622
1290
        for num, args in enumerate(calls):
1623
1291
            assert_juju_call(self, mock_cc, client, args, num)
1624
 
        statuses = [
1625
 
            {'machines': {'100': dict(machine_started)}, 'applications': {}},
1626
 
            {'machines': {}, 'applications': {}},
1627
 
        ]
1628
 
        with patch_status(client, *statuses) as status_mock:
 
1292
        statuses = (yaml.safe_dump(x) for x in [
 
1293
            {'machines': {'100': {'agent-state': 'started'}}, 'services': {}},
 
1294
            {'machines': {}, 'services': {}}])
 
1295
        with patch('subprocess.check_output',
 
1296
                   side_effect=lambda x, **y: statuses.next()) as mock_co:
1629
1297
            self.assertEqual(
1630
1298
                deploy_iter.next(),
1631
 
                {'test_id': 'remove-machine-many-container', 'result': True})
1632
 
        self.assertEqual(2, status_mock.call_count)
 
1299
                {'test_id': 'remove-machine-many-lxc', 'result': True})
 
1300
        for num in range(2):
 
1301
            assert_juju_call(self, mock_co, client, (
 
1302
                'juju', '--show-log', 'status', '-e', 'steve'), num,
 
1303
                assign_stderr=True)
1633
1304
        self.assertEqual(deploy_iter.next(), {
1634
1305
            'test_id': 'remove-machine-many-instance'})
1635
1306
        with patch('subprocess.check_call') as mock_cc:
1638
1309
                {'test_id': 'remove-machine-many-instance'})
1639
1310
        for num in range(deploy_many.host_count):
1640
1311
            assert_juju_call(self, mock_cc, client, (
1641
 
                'juju', '--show-log', 'remove-machine', '-m', 'steve:steve',
 
1312
                'juju', '--show-log', 'remove-machine', '-e', 'steve',
1642
1313
                str(num + 1)), num)
1643
1314
 
1644
 
        statuses = [
1645
 
            {'machines': {'1': dict(machine_started)}, 'applications': {}},
1646
 
            {'machines': {}, 'applications': {}},
1647
 
        ]
1648
 
        with patch_status(client, *statuses) as status_mock:
 
1315
        statuses = (yaml.safe_dump(x) for x in [
 
1316
            {'machines': {'1': {'agent-state': 'started'}}, 'services': {}},
 
1317
            {'machines': {}, 'services': {}}])
 
1318
 
 
1319
        with patch('subprocess.check_output',
 
1320
                   side_effect=lambda x, **y: statuses.next()) as mock_co:
1649
1321
            self.assertEqual(
1650
1322
                deploy_iter.next(),
1651
1323
                {'test_id': 'remove-machine-many-instance', 'result': True})
1652
 
        self.assertEqual(2, status_mock.call_count)
 
1324
        for num in range(2):
 
1325
            assert_juju_call(self, mock_co, client, (
 
1326
                'juju', '--show-log', 'status', '-e', 'steve'), num,
 
1327
                assign_stderr=True)
1653
1328
 
1654
 
    def test_iter_step_failure(self):
 
1329
    @patch('logging.error')
 
1330
    def test_iter_step_failure(self, le_mock):
1655
1331
        deploy_many = DeployManyAttempt()
1656
1332
        client = FakeEnvJujuClient()
1657
 
        client.env.config['default-series'] = 'angsty'
1658
1333
        deploy_iter = iter_steps_validate_info(self, deploy_many, client)
1659
1334
        self.assertEqual(deploy_iter.next(), {'test_id': 'add-machine-many'})
1660
 
        status = {
 
1335
        status = yaml.safe_dump({
1661
1336
            'machines': {'0': {'agent-state': 'started'}},
1662
 
            'applications': {},
1663
 
        }
1664
 
        with patch_status(client, status):
 
1337
            'services': {},
 
1338
            })
 
1339
        with patch('subprocess.check_output', return_value=status):
1665
1340
            with patch('subprocess.check_call') as mock_cc:
1666
1341
                self.assertEqual(deploy_iter.next(),
1667
1342
                                 {'test_id': 'add-machine-many'})
1668
1343
        for index in range(deploy_many.host_count):
1669
1344
            assert_juju_call(self, mock_cc, client, (
1670
 
                'juju', '--show-log', 'add-machine',
1671
 
                '-m', 'steve:steve'), index)
 
1345
                'juju', '--show-log', 'add-machine', '-e', 'steve'), index)
1672
1346
 
1673
 
        status = {
 
1347
        status = yaml.safe_dump({
1674
1348
            'machines': dict((str(x), {'agent-state': 'started'})
1675
1349
                             for x in range(deploy_many.host_count + 1)),
1676
 
            'applications': {},
1677
 
        }
1678
 
        with patch_status(client, status):
 
1350
            'services': {},
 
1351
            })
 
1352
        with patch('subprocess.check_output', return_value=status):
1679
1353
                self.assertEqual(
1680
1354
                    deploy_iter.next(),
1681
1355
                    {'test_id': 'add-machine-many', 'result': True})
1683
1357
                         {'test_id': 'ensure-machines'})
1684
1358
        self.assertEqual(deploy_iter.next(),
1685
1359
                         {'test_id': 'ensure-machines'})
1686
 
        with patch_status(client, status):
 
1360
        with patch('subprocess.check_output', return_value=status):
1687
1361
            self.assertEqual(deploy_iter.next(),
1688
1362
                             {'test_id': 'ensure-machines', 'result': True})
1689
1363
        self.assertEqual(deploy_iter.next(),
1691
1365
        with patch('subprocess.check_call') as mock_cc:
1692
1366
            self.assertEqual(deploy_iter.next(),
1693
1367
                             {'test_id': 'deploy-many'})
1694
 
        status = {
 
1368
        output = yaml.safe_dump({
1695
1369
            'machines': {
1696
1370
                '0': {'agent-state': 'pending'},
1697
1371
                },
1698
 
            'applications': {},
1699
 
        }
1700
 
        with patch_status(client, status):
 
1372
            'services': {},
 
1373
            })
 
1374
        with patch('subprocess.check_output', return_value=output):
1701
1375
            with self.assertRaisesRegexp(
1702
1376
                    Exception,
1703
1377
                    'Timed out waiting for agents to start in steve.'):
1704
1378
                deploy_iter.next()
1705
1379
 
1706
 
    def test_iter_step_add_machine_failure(self):
 
1380
    @patch('logging.error')
 
1381
    def test_iter_step_add_machine_failure(self, le_mock):
1707
1382
        deploy_many = DeployManyAttempt()
1708
1383
        client = FakeEnvJujuClient()
1709
 
        client.env.config['default-series'] = 'angsty'
1710
1384
        deploy_iter = iter_steps_validate_info(self, deploy_many, client)
1711
1385
        self.assertEqual(deploy_iter.next(), {'test_id': 'add-machine-many'})
1712
 
        status = {
 
1386
        status = yaml.safe_dump({
1713
1387
            'machines': {'0': {'agent-state': 'started'}},
1714
 
            'applications': {},
1715
 
        }
1716
 
        with patch_status(client, status) as gs_mock:
 
1388
            'services': {},
 
1389
            })
 
1390
        with patch('subprocess.check_output', return_value=status):
1717
1391
            with patch('subprocess.check_call') as mock_cc:
1718
1392
                self.assertEqual(deploy_iter.next(),
1719
1393
                                 {'test_id': 'add-machine-many'})
1720
1394
        for index in range(deploy_many.host_count):
1721
1395
            assert_juju_call(self, mock_cc, client, (
1722
 
                'juju', '--show-log', 'add-machine',
1723
 
                '-m', 'steve:steve'), index)
1724
 
        gs_mock.assert_called_once_with()
 
1396
                'juju', '--show-log', 'add-machine', '-e', 'steve'), index)
1725
1397
 
1726
 
        status = {
 
1398
        status = yaml.safe_dump({
1727
1399
            'machines': dict((str(x), {'agent-state': 'pending'})
1728
1400
                             for x in range(deploy_many.host_count + 1)),
1729
 
            'applications': {},
1730
 
        }
1731
 
        with patch_status(client, status) as gs_mock:
1732
 
            self.assertEqual(deploy_iter.next(),
1733
 
                             {'test_id': 'add-machine-many', 'result': False})
 
1401
            'services': {},
 
1402
            })
 
1403
        with patch('subprocess.check_output', return_value=status):
 
1404
                self.assertEqual(
 
1405
                    deploy_iter.next(),
 
1406
                    {'test_id': 'add-machine-many', 'result': False})
1734
1407
        self.assertEqual(deploy_iter.next(),
1735
1408
                         {'test_id': 'ensure-machines'})
 
1409
        status = yaml.safe_dump({
 
1410
            'machines': dict((str(x), {'agent-state': 'started'})
 
1411
                             for x in range(deploy_many.host_count + 1)),
 
1412
            'services': {},
 
1413
            })
1736
1414
        with patch('subprocess.check_call') as mock_cc:
1737
1415
            self.assertEqual({'test_id': 'ensure-machines'},
1738
1416
                             deploy_iter.next())
1739
1417
        for x in range(deploy_many.host_count):
1740
1418
            assert_juju_call(self, mock_cc, client, (
1741
 
                'juju', '--show-log', 'remove-machine', '-m', 'steve:steve',
 
1419
                'juju', '--show-log', 'destroy-machine', '-e', 'steve',
1742
1420
                '--force', str((x + 1))), x * 2)
1743
1421
            assert_juju_call(self, mock_cc, client, (
1744
 
                'juju', '--show-log', 'add-machine',
1745
 
                '-m', 'steve:steve'), x * 2 + 1)
1746
 
 
1747
 
        status = {
1748
 
            'machines': dict((str(x), {'agent-state': 'started'})
1749
 
                             for x in range(deploy_many.host_count + 1)),
1750
 
            'applications': {},
1751
 
        }
1752
 
        with patch_status(client, status) as gs_mock:
 
1422
                'juju', '--show-log', 'add-machine', '-e', 'steve'), x * 2 + 1)
 
1423
        with patch('subprocess.check_output', return_value=status):
1753
1424
            self.assertEqual({'test_id': 'ensure-machines', 'result': True},
1754
1425
                             deploy_iter.next())
1755
1426
        self.assertEqual({'test_id': 'deploy-many'}, deploy_iter.next())
1756
1427
        with patch('subprocess.check_call') as mock_cc:
1757
1428
            self.assertEqual({'test_id': 'deploy-many'}, deploy_iter.next())
1758
 
        calls = self.predict_add_machine_calls(deploy_many, LXD_MACHINE)
 
1429
        calls = self.predict_add_machine_calls(deploy_many)
1759
1430
        for num, args in enumerate(calls):
1760
1431
            assert_juju_call(self, mock_cc, client, args, num)
1761
1432
 
1762
 
    def get_wait_until_removed_timeout(self, container_type):
1763
 
        deploy_many = DeployManyAttempt()
1764
 
        client = fake_juju_client()
1765
 
        client.bootstrap()
1766
 
        deploy_iter = iter_steps_validate_info(self, deploy_many, client)
1767
 
        with patch('industrial_test.wait_until_removed') as wur_mock:
1768
 
            with patch.object(client, 'preferred_container',
1769
 
                              return_value=container_type):
1770
 
                list(deploy_iter)
1771
 
        return wur_mock.mock_calls[0][2]['timeout']
1772
 
 
1773
 
    def test_wait_until_removed_timeout_lxd(self):
1774
 
        self.assertEqual(900, self.get_wait_until_removed_timeout(LXD_MACHINE))
1775
 
 
1776
 
    def test_wait_until_removed_timeout_lxc(self):
1777
 
        self.assertEqual(30, self.get_wait_until_removed_timeout(LXC_MACHINE))
1778
 
 
1779
 
    def test_wait_until_removed_timeout_azure(self):
1780
 
        deploy_many = DeployManyAttempt(host_count=4, container_count=0)
1781
 
        client = fake_juju_client()
1782
 
        client.env.config['type'] = 'azure'
1783
 
        client.env.config['location'] = 'us-west-1'
1784
 
        client.bootstrap()
1785
 
        deploy_iter = iter_steps_validate_info(self, deploy_many, client)
1786
 
        with patch('industrial_test.wait_until_removed') as wur_mock:
1787
 
            list(deploy_iter)
1788
 
        remove_timeout = wur_mock.mock_calls[3][2]['timeout']
1789
 
        self.assertEqual(2400, remove_timeout)
1790
 
 
1791
 
    def test_wait_until_removed_timeout_not_azure(self):
1792
 
        deploy_many = DeployManyAttempt(host_count=4, container_count=0)
1793
 
        client = fake_juju_client()
1794
 
        client.env.config['type'] = 'aws'
1795
 
        client.bootstrap()
1796
 
        deploy_iter = iter_steps_validate_info(self, deploy_many, client)
1797
 
        with patch('industrial_test.wait_until_removed') as wur_mock:
1798
 
            list(deploy_iter)
1799
 
        remove_timeout = wur_mock.mock_calls[3][2]['timeout']
1800
 
        self.assertEqual(300, remove_timeout)
1801
 
 
1802
 
 
1803
 
class TestBackupRestoreAttempt(JujuPyTestCase):
 
1433
 
 
1434
class TestBackupRestoreAttempt(TestCase):
1804
1435
 
1805
1436
    def test_get_test_info(self):
1806
1437
        self.assertEqual(
1810
1441
    def test_iter_steps(self):
1811
1442
        br_attempt = BackupRestoreAttempt()
1812
1443
        client = FakeEnvJujuClient()
1813
 
        aws_env = get_aws_env()
1814
 
        client.env.environment = aws_env.environment
1815
 
        client.env.config = aws_env.config
1816
 
        client.env.juju_home = aws_env.juju_home
1817
 
        client.env.credentials = {'credentials': {'aws': {'creds': {}}}}
1818
 
        controller_client = client.get_controller_client()
 
1444
        client.env = get_aws_env()
1819
1445
        environ = dict(os.environ)
1820
1446
        environ.update(get_euca_env(client.env.config))
1821
1447
 
1822
1448
        def check_output(*args, **kwargs):
1823
 
            if args == ((
1824
 
                    'juju', '--show-log', 'create-backup', '-m',
1825
 
                    'steve:{}'.format(controller_client.env.environment),),):
1826
 
                return FakePopen('juju-backup-24.tgz', '', 0)
 
1449
            if args == (['juju', 'backup'],):
 
1450
                return 'juju-backup-24.tgz'
 
1451
            if args == (('juju', '--show-log', 'status', '-e', 'baz'),):
 
1452
                return yaml.safe_dump({
 
1453
                    'machines': {'0': {
 
1454
                        'instance-id': 'asdf',
 
1455
                        'dns-name': '128.100.100.128',
 
1456
                        }}
 
1457
                    })
1827
1458
            self.assertEqual([], args)
1828
 
        initial_status = {
1829
 
            'machines': {'0': {
1830
 
                'instance-id': 'asdf',
1831
 
                'dns-name': '128.100.100.128',
1832
 
                }}
1833
 
        }
1834
1459
        iterator = iter_steps_validate_info(self, br_attempt, client)
1835
1460
        self.assertEqual(iterator.next(), {'test_id': 'back-up-restore'})
1836
 
        with patch_status(controller_client, initial_status) as gs_mock:
1837
 
            with patch('subprocess.Popen',
1838
 
                       side_effect=check_output) as co_mock:
1839
 
                with patch('subprocess.check_call') as cc_mock:
1840
 
                    with patch.object(client, 'get_controller_client',
1841
 
                                      return_value=controller_client,
1842
 
                                      autospec=True):
1843
 
                        with patch('sys.stdout'):
1844
 
                            self.assertEqual(
1845
 
                                iterator.next(),
1846
 
                                {'test_id': 'back-up-restore'})
1847
 
        assert_juju_call(
1848
 
            self,
1849
 
            co_mock,
1850
 
            client,
1851
 
            ('juju', '--show-log', 'create-backup',
1852
 
             '-m', 'steve:{}'.format(controller_client.env.environment)),
1853
 
            0)
 
1461
        with patch('subprocess.check_output',
 
1462
                   side_effect=check_output) as co_mock:
 
1463
            with patch('subprocess.check_call') as cc_mock:
 
1464
                with patch('sys.stdout'):
 
1465
                    self.assertEqual(
 
1466
                        iterator.next(),
 
1467
                        {'test_id': 'back-up-restore'})
 
1468
        assert_juju_call(self, co_mock, client, ['juju', 'backup'], 0)
1854
1469
        self.assertEqual(
1855
1470
            cc_mock.mock_calls[0],
1856
1471
            call(['euca-terminate-instances', 'asdf'], env=environ))
1857
1472
        with patch('deploy_stack.wait_for_port'):
1858
 
            with patch('deploy_stack.print_now', autospec=True) as pn_mock:
1859
 
                self.assertEqual(iterator.next(),
1860
 
                                 {'test_id': 'back-up-restore'})
1861
 
        pn_mock.assert_called_with('Closed.')
1862
 
        with patch.object(controller_client, 'restore_backup') as rb_mock:
1863
1473
            self.assertEqual(iterator.next(), {'test_id': 'back-up-restore'})
1864
 
        rb_mock.assert_called_once_with(
1865
 
            os.path.abspath('juju-backup-24.tgz'))
 
1474
        with patch('subprocess.Popen') as po_mock:
 
1475
            with patch('sys.stdout'):
 
1476
                    self.assertEqual(iterator.next(),
 
1477
                                     {'test_id': 'back-up-restore'})
 
1478
        assert_juju_call(
 
1479
            self, po_mock, client, (
 
1480
                'juju', '--show-log', 'restore', '-e', 'baz',
 
1481
                os.path.abspath('juju-backup-24.tgz')))
 
1482
        po_mock.return_value.wait.return_value = 0
1866
1483
        with patch('os.unlink') as ul_mock:
1867
1484
            self.assertEqual(iterator.next(),
1868
1485
                             {'test_id': 'back-up-restore'})
1869
1486
        ul_mock.assert_called_once_with(os.path.abspath('juju-backup-24.tgz'))
1870
 
        final_status = {
 
1487
        output = yaml.safe_dump({
1871
1488
            'machines': {
1872
1489
                '0': {'agent-state': 'started'},
1873
1490
                },
1874
 
            'applications': {},
1875
 
        }
1876
 
        with patch_status(controller_client, final_status) as gs_mock:
 
1491
            'services': {},
 
1492
            })
 
1493
        with patch('subprocess.check_output', return_value=output) as co_mock:
1877
1494
            self.assertEqual(iterator.next(),
1878
1495
                             {'test_id': 'back-up-restore', 'result': True})
1879
 
        gs_mock.assert_called_once_with()
1880
 
 
1881
 
    def test_iter_steps_azure(self):
1882
 
        test_id = {'test_id': 'back-up-restore'}
1883
 
        br_attempt = BackupRestoreAttempt()
1884
 
        azure_env = SimpleEnvironment('steve', get_azure_config())
1885
 
        client = FakeEnvJujuClient()
1886
 
        client.env.environment = azure_env.environment
1887
 
        client.env.config = azure_env.config
1888
 
        client.env.credentials = {'credentials': {'azure': {'creds': {}}}}
1889
 
        controller_client = client.get_controller_client()
1890
 
        # First yield.
1891
 
        iterator = iter_steps_validate_info(self, br_attempt, client)
1892
 
        self.assertEqual(iterator.next(), test_id)
1893
 
        # Second yield.
1894
 
        initial_status = {
1895
 
            'machines': {'0': {
1896
 
                'instance-id': 'not-id',
1897
 
                'dns-name': '128.100.100.128',
1898
 
                }}
1899
 
        }
1900
 
        # The azure-provider does does not provide sane ids, so the instance-id
1901
 
        # is used to search for the actual azure instance.
1902
 
        substrate = AzureARMAccount(None)
1903
 
        with patch(
1904
 
                'industrial_test.make_substrate_manager'
1905
 
                ) as msm_mock:
1906
 
            msm_mock.return_value.__enter__.return_value = substrate
1907
 
            with patch.object(substrate, 'convert_to_azure_ids',
1908
 
                              autospec=True, return_value=['id']) as ca_mock:
1909
 
                with patch.object(client, 'get_controller_client',
1910
 
                                  autospec=True,
1911
 
                                  return_value=controller_client):
1912
 
                    with patch.object(controller_client, 'backup',
1913
 
                                      autospec=True,
1914
 
                                      return_value='juju-backup.tgz'
1915
 
                                      ) as b_mock:
1916
 
                        with patch('industrial_test.terminate_instances',
1917
 
                                   autospec=True):
1918
 
                            with patch_status(controller_client,
1919
 
                                              initial_status):
1920
 
                                self.assertEqual(iterator.next(), test_id)
1921
 
        b_mock.assert_called_once_with()
1922
 
        ca_mock.assert_called_once_with(controller_client, ['not-id'])
1923
 
 
1924
 
 
1925
 
class TestPrepareUpgradeJujuAttempt(JujuPyTestCase):
 
1496
        assert_juju_call(self, co_mock, client, (
 
1497
            'juju', '--show-log', 'status', '-e', 'baz'), assign_stderr=True)
 
1498
 
 
1499
 
 
1500
class TestUpgradeJujuAttempt(TestCase):
1926
1501
 
1927
1502
    def test_factory(self):
1928
 
        uj_attempt = PrepareUpgradeJujuAttempt.factory(
1929
 
            ['a', 'b', 'c'], None)
1930
 
        self.assertIs(type(uj_attempt), PrepareUpgradeJujuAttempt)
 
1503
        uj_attempt = UpgradeJujuAttempt.factory(['a', 'b', 'c'])
 
1504
        self.assertIs(type(uj_attempt), UpgradeJujuAttempt)
1931
1505
        self.assertEqual(uj_attempt.bootstrap_paths, {'b': 'a', 'c': 'b'})
1932
1506
 
1933
1507
    def test_factory_empty(self):
1934
1508
        with self.assertRaisesRegexp(
1935
1509
                ValueError, 'Not enough paths for upgrade.'):
1936
 
            PrepareUpgradeJujuAttempt.factory(['a'], None)
 
1510
            UpgradeJujuAttempt.factory(['a'])
1937
1511
        with self.assertRaisesRegexp(
1938
1512
                ValueError, 'Not enough paths for upgrade.'):
1939
 
            PrepareUpgradeJujuAttempt.factory([], None)
1940
 
 
1941
 
    def test_get_bootstrap_client(self):
1942
 
        client = fake_juju_client(full_path='c', debug=True)
1943
 
        puj_attempt = PrepareUpgradeJujuAttempt.factory(['a', 'b', 'c'], None)
1944
 
 
1945
 
        def by_version(path):
1946
 
            return fake_juju_client(client.env, path, client.debug)
1947
 
 
1948
 
        with patch.object(client, 'clone_path_cls', by_version):
1949
 
            bootstrap_client = puj_attempt.get_bootstrap_client(client)
1950
 
 
1951
 
        self.assertIsNot(bootstrap_client, client)
1952
 
        self.assertIs(client.debug, bootstrap_client.debug)
1953
 
        self.assertIs(client.env, bootstrap_client.env)
1954
 
        self.assertEqual('b', bootstrap_client.full_path)
 
1513
            UpgradeJujuAttempt.factory([])
1955
1514
 
1956
1515
    def test_iter_steps(self):
1957
 
        future_client = FakeEnvJujuClient(full_path='/future/juju')
1958
 
        present_client = FakeEnvJujuClient(full_path='/present/juju')
1959
 
        puj_attempt = PrepareUpgradeJujuAttempt(
 
1516
        future_client = FakeEnvJujuClient()
 
1517
        future_client.full_path = '/future/juju'
 
1518
        present_client = FakeEnvJujuClient()
 
1519
        present_client.full_path = '/present/juju'
 
1520
        uj_attempt = UpgradeJujuAttempt(
1960
1521
            {future_client.full_path: present_client.full_path})
1961
 
        puj_iterator = iter_steps_validate_info(self, puj_attempt,
1962
 
                                                future_client)
1963
 
        with patch('subprocess.check_output', return_value='2.0-alpha3-a-b'):
1964
 
            with patch('industrial_test.client_from_config',
1965
 
                       return_value=future_client):
1966
 
                self.assertEqual({'test_id': 'prepare-upgrade-juju'},
1967
 
                                 puj_iterator.next())
1968
 
        with observable_temp_file() as config_file:
1969
 
            with patch('subprocess.Popen') as po_mock:
1970
 
                self.assertEqual({'test_id': 'prepare-upgrade-juju'},
1971
 
                                 puj_iterator.next())
1972
 
            assert_juju_call(self, po_mock, future_client, (
1973
 
                'juju', '--show-log', 'bootstrap', '--constraints', 'mem=2G',
1974
 
                'steve', 'fake/regionx', '--config', config_file.name,
1975
 
                '--agent-version', '2.0-alpha3'))
1976
 
            po_mock.return_value.wait.return_value = 0
1977
 
            self.assertEqual(puj_iterator.next(),
1978
 
                             {'test_id': 'prepare-upgrade-juju'})
1979
 
        b_status = {
 
1522
        uj_iterator = iter_steps_validate_info(self, uj_attempt, future_client)
 
1523
        with patch('subprocess.check_output', return_value='foo'):
 
1524
            self.assertEqual({'test_id': 'prepare-upgrade-juju'},
 
1525
                             uj_iterator.next())
 
1526
        with patch('subprocess.Popen') as po_mock:
 
1527
            self.assertEqual({'test_id': 'prepare-upgrade-juju'},
 
1528
                             uj_iterator.next())
 
1529
        assert_juju_call(self, po_mock, present_client, (
 
1530
            'juju', '--show-log', 'bootstrap', '-e', 'steve', '--constraints',
 
1531
            'mem=2G'))
 
1532
        po_mock.return_value.wait.return_value = 0
 
1533
        self.assertEqual(uj_iterator.next(),
 
1534
                         {'test_id': 'prepare-upgrade-juju'})
 
1535
        b_status = yaml.safe_dump({
1980
1536
            'machines': {'0': {'agent-state': 'started'}},
1981
 
            'applications': {},
1982
 
        }
1983
 
        with patch_status(None, b_status):
 
1537
            'services': {},
 
1538
            })
 
1539
        with patch('subprocess.check_output', return_value=b_status):
1984
1540
            self.assertEqual(
1985
 
                puj_iterator.next(),
 
1541
                uj_iterator.next(),
1986
1542
                {'test_id': 'prepare-upgrade-juju', 'result': True})
 
1543
        self.assertEqual(uj_iterator.next(), {'test_id': 'upgrade-juju'})
 
1544
        with patch('subprocess.check_call') as cc_mock:
 
1545
            self.assertEqual({'test_id': 'upgrade-juju'}, uj_iterator.next())
 
1546
        assert_juju_call(self, cc_mock, future_client, (
 
1547
            'juju', '--show-log', 'upgrade-juju', '-e', 'steve', '--version',
 
1548
            future_client.get_matching_agent_version()))
 
1549
        version_status = yaml.safe_dump({
 
1550
            'machines': {'0': {
 
1551
                'agent-version': future_client.get_matching_agent_version()}},
 
1552
            'services': {},
 
1553
            })
 
1554
        with patch('subprocess.check_output', return_value=version_status):
 
1555
            self.assertEqual({'test_id': 'upgrade-juju', 'result': True},
 
1556
                             uj_iterator.next())
1987
1557
 
1988
1558
    def test_iter_steps_no_previous_client(self):
1989
 
        uj_attempt = PrepareUpgradeJujuAttempt({})
1990
 
        client = FakeEnvJujuClient(full_path='/present/juju')
 
1559
        uj_attempt = UpgradeJujuAttempt({})
 
1560
        client = FakeEnvJujuClient()
 
1561
        client.full_path = '/present/juju'
1991
1562
        uj_iterator = uj_attempt.iter_steps(client)
1992
1563
        with self.assertRaises(CannotUpgradeToClient) as exc_context:
1993
1564
            uj_iterator.next()
1994
1565
        self.assertIs(exc_context.exception.client, client)
1995
1566
 
1996
1567
 
1997
 
class TestUpgradeJujuAttempt(JujuPyTestCase):
1998
 
 
1999
 
    def test_iter_steps(self):
2000
 
        future_client = FakeEnvJujuClient(full_path='/future/juju')
2001
 
        uj_attempt = UpgradeJujuAttempt()
2002
 
        uj_iterator = iter_steps_validate_info(self, uj_attempt, future_client)
2003
 
        self.assertEqual(uj_iterator.next(), {'test_id': 'upgrade-juju'})
2004
 
        with patch('subprocess.check_call') as cc_mock:
2005
 
            self.assertEqual({'test_id': 'upgrade-juju'}, uj_iterator.next())
2006
 
        assert_juju_call(
2007
 
            self,
2008
 
            cc_mock,
2009
 
            future_client,
2010
 
            ('juju', '--show-log', 'upgrade-juju',
2011
 
             '-m', 'steve:steve', '--agent-version',
2012
 
             future_client.get_matching_agent_version()))
2013
 
        version_status = {
2014
 
            'machines': {'0': {
2015
 
                'agent-version': future_client.get_matching_agent_version()}},
2016
 
            'applications': {},
2017
 
        }
2018
 
        with patch_status(None, version_status):
2019
 
            self.assertEqual({'test_id': 'upgrade-juju', 'result': True},
2020
 
                             uj_iterator.next())
2021
 
 
2022
 
 
2023
 
class TestUpgradeCharmAttempt(JujuPyTestCase):
 
1568
class TestUpgradeCharmAttempt(TestCase):
2024
1569
 
2025
1570
    def assert_hook(self, hook_path, content):
2026
1571
        with open(hook_path) as hook_file:
2029
1574
        self.assertEqual(0o755, mode & 0o777)
2030
1575
 
2031
1576
    def test_iter_steps(self):
2032
 
        client = FakeEnvJujuClient(version='2.0.0', full_path='/future/juju')
2033
 
        self._iter_steps(client)
2034
 
 
2035
 
    def test_iter_steps_juju_1x(self):
2036
 
        client = FakeEnvJujuClient1X(version='1.25.0',
2037
 
                                     full_path='/future/juju')
2038
 
        self._iter_steps(client)
2039
 
 
2040
 
    def _iter_steps(self, client):
2041
 
        self.assertEqual(client.full_path, '/future/juju')
 
1577
        client = FakeEnvJujuClient()
 
1578
        client.full_path = '/future/juju'
2042
1579
        uc_attempt = UpgradeCharmAttempt()
2043
1580
        uc_iterator = iter_steps_validate_info(self, uc_attempt, client)
2044
1581
        self.assertEqual(uc_iterator.next(),
2055
1592
        self.assertEqual(metadata['name'], 'mycharm')
2056
1593
        self.assertIn('summary', metadata)
2057
1594
        self.assertIn('description', metadata)
2058
 
        self.assertEqual(['trusty'], metadata['series'])
2059
 
        if client.version.startswith('1.'):
2060
 
            charm_path = os.path.join('local:trusty', 'mycharm')
2061
 
            assert_juju_call(self, cc_mock, client, (
2062
 
                'juju', '--show-log', 'deploy', '-e', 'steve', charm_path,
2063
 
                '--repository', temp_repository))
2064
 
            option = '-e'
2065
 
        else:
2066
 
            charm_path = os.path.join(temp_repository, 'trusty', 'mycharm')
2067
 
            assert_juju_call(self, cc_mock, client, (
2068
 
                'juju', '--show-log', 'deploy',
2069
 
                '-m', 'steve:steve', charm_path))
2070
 
            option = '-m'
2071
 
        self.assertNotIn('min-juju-version', metadata)
2072
 
        status = {
 
1595
        assert_juju_call(self, cc_mock, client, (
 
1596
            'juju', '--show-log', 'deploy', '-e', 'steve',
 
1597
            'local:trusty/mycharm', '--repository', temp_repository))
 
1598
        status = yaml.safe_dump({
2073
1599
            'machines': {'0': {'agent-state': 'started'}},
2074
 
            'applications': {},
2075
 
        }
2076
 
        with patch_status(client, status):
 
1600
            'services': {},
 
1601
            })
 
1602
        with patch('subprocess.check_output', return_value=status):
2077
1603
            self.assertEqual(uc_iterator.next(),
2078
1604
                             {'test_id': 'prepare-upgrade-charm'})
2079
1605
        hooks_path = os.path.join(temp_repository, 'trusty', 'mycharm',
2096
1622
        self.assertEqual(uc_iterator.next(), {'test_id': 'upgrade-charm'})
2097
1623
        with patch('subprocess.check_call') as cc_mock:
2098
1624
            self.assertEqual(uc_iterator.next(), {'test_id': 'upgrade-charm'})
2099
 
        if client.version.startswith('1.'):
2100
 
            assert_juju_call(self, cc_mock, client, (
2101
 
                'juju', '--show-log', 'upgrade-charm', option, 'steve',
2102
 
                'mycharm', '--repository', temp_repository))
2103
 
        else:
2104
 
            assert_juju_call(self, cc_mock, client, (
2105
 
                'juju', '--show-log', 'upgrade-charm', option, 'steve:steve',
2106
 
                'mycharm', '--path', os.path.join(temp_repository, 'trusty',
2107
 
                                                  'mycharm')))
2108
 
        status = {
 
1625
        assert_juju_call(self, cc_mock, client, (
 
1626
            'juju', '--show-log', 'upgrade-charm', '-e', 'steve',
 
1627
            'mycharm', '--repository', temp_repository))
 
1628
        status = yaml.safe_dump({
2109
1629
            'machines': {'0': {'agent-state': 'started'}},
2110
 
            'applications': {'mycharm': {'units': {'mycharm/0': {
 
1630
            'services': {'mycharm': {'units': {'mycharm/0': {
2111
1631
                'open-ports': ['42/tcp', '34/tcp'],
2112
1632
                }}}},
2113
 
        }
2114
 
        with patch_status(client, status):
 
1633
            })
 
1634
        with patch('subprocess.check_output', return_value=status):
2115
1635
            self.assertEqual(
2116
1636
                uc_iterator.next(),
2117
1637
                {'test_id': 'upgrade-charm', 'result': True})
2140
1660
            self.assertEqual(temp_file.read(), expected)
2141
1661
 
2142
1662
 
2143
 
class TestMakeSubstrate(JujuPyTestCase):
 
1663
class TestMakeSubstrate(TestCase):
2144
1664
 
2145
1665
    def test_make_substrate_manager_no_support(self):
2146
 
        client = EnvJujuClient(JujuData('foo', {'type': 'foo'}),
 
1666
        client = EnvJujuClient(SimpleEnvironment('foo', {'type': 'foo'}),
2147
1667
                               '', '')
2148
 
        client.env.credentials = {'credentials': {'foo': {'creds': {}}}}
2149
1668
        with make_substrate_manager(client, []) as substrate:
2150
1669
            self.assertIs(substrate, None)
2151
1670
 
2152
1671
    def test_make_substrate_no_requirements(self):
2153
 
        client = EnvJujuClient(get_aws_juju_data(), '', '')
2154
 
        client.env.credentials = {'credentials': {'aws': {'creds': {}}}}
 
1672
        client = EnvJujuClient(get_aws_env(), '', '')
2155
1673
        with make_substrate_manager(client, []) as substrate:
2156
1674
            self.assertIs(type(substrate), AWSAccount)
2157
1675
 
2158
1676
    def test_make_substrate_manager_unsatisifed_requirements(self):
2159
 
        client = EnvJujuClient(get_aws_juju_data(), '', '')
2160
 
        client.env.credentials = {'credentials': {'aws': {'creds': {}}}}
 
1677
        client = EnvJujuClient(get_aws_env(), '', '')
2161
1678
        with make_substrate_manager(client, ['foo']) as substrate:
2162
1679
            self.assertIs(substrate, None)
2163
1680
        with make_substrate_manager(
2165
1682
            self.assertIs(substrate, None)
2166
1683
 
2167
1684
    def test_make_substrate_satisfied_requirements(self):
2168
 
        client = EnvJujuClient(get_aws_juju_data(), '', '')
2169
 
        client.env.credentials = {'credentials': {'aws': {'creds': {}}}}
 
1685
        client = EnvJujuClient(get_aws_env(), '', '')
2170
1686
        with make_substrate_manager(
2171
1687
                client, ['iter_security_groups']) as substrate:
2172
1688
            self.assertIs(type(substrate), AWSAccount)
2205
1721
                         {'test_id': 'foo-id', 'result': True})
2206
1722
        self.assertEqual(si.as_result(False),
2207
1723
                         {'test_id': 'foo-id', 'result': False})
2208
 
 
2209
 
 
2210
 
class TestAttemptSuiteFactory(TestCase):
2211
 
 
2212
 
    def test_factory(self):
2213
 
        fake_bootstrap = FakeAttemptClass('bootstrap')
2214
 
        factory = AttemptSuiteFactory([],
2215
 
                                      bootstrap_attempt=fake_bootstrap)
2216
 
        attempt_suite = factory.factory(['1', '2'], 'log-1', 'foo-stream')
2217
 
        self.assertEqual(factory, attempt_suite.attempt_list)
2218
 
        self.assertEqual(['1', '2'], attempt_suite.upgrade_sequence)
2219
 
        self.assertEqual('log-1', attempt_suite.log_dir)
2220
 
        self.assertEqual('foo-stream', attempt_suite.agent_stream)
2221
 
 
2222
 
    def test_get_test_info(self):
2223
 
        fake_bootstrap = FakeAttemptClass('fake-bootstrap')
2224
 
        fake_1 = FakeAttemptClass('fake-1')
2225
 
        fake_2 = FakeAttemptClass('fake-2')
2226
 
        factory = AttemptSuiteFactory([fake_1, fake_2],
2227
 
                                      bootstrap_attempt=fake_bootstrap)
2228
 
        self.assertEqual(OrderedDict([
2229
 
            ('fake-bootstrap-id', {'title': 'fake-bootstrap'}),
2230
 
            ('prepare-suite', {'title': 'Prepare suite tests',
2231
 
                               'report_on': False}),
2232
 
            ('fake-1-id', {'title': 'fake-1'}),
2233
 
            ('fake-2-id', {'title': 'fake-2'}),
2234
 
            ('destroy-env', {'title': 'destroy environment',
2235
 
                             'report_on': True}),
2236
 
            ('substrate-clean', {'title': 'check substrate clean',
2237
 
                                 'report_on': True}),
2238
 
            ]), factory.get_test_info())
2239
 
 
2240
 
 
2241
 
class TestAttemptSuite(TestCase):
2242
 
 
2243
 
    def test_get_test_info(self):
2244
 
        fake_bootstrap = FakeAttemptClass('fake-bootstrap')
2245
 
        fake_1 = FakeAttemptClass('fake-1')
2246
 
        fake_2 = FakeAttemptClass('fake-2')
2247
 
        factory = AttemptSuiteFactory([fake_1, fake_2],
2248
 
                                      bootstrap_attempt=fake_bootstrap)
2249
 
        attempt_suite = AttemptSuite(factory, None, None, None)
2250
 
        self.assertEqual(OrderedDict([
2251
 
            ('fake-bootstrap-id', {'title': 'fake-bootstrap'}),
2252
 
            ('prepare-suite', {'title': 'Prepare suite tests',
2253
 
                               'report_on': False}),
2254
 
            ('fake-1-id', {'title': 'fake-1'}),
2255
 
            ('fake-2-id', {'title': 'fake-2'}),
2256
 
            ('destroy-env', {'title': 'destroy environment',
2257
 
                             'report_on': True}),
2258
 
            ('substrate-clean', {'title': 'check substrate clean',
2259
 
                                 'report_on': True}),
2260
 
            ]), attempt_suite.get_test_info())
2261
 
 
2262
 
    @contextmanager
2263
 
    def iter_steps_cxt(self, attempt_suite):
2264
 
        with patch('industrial_test.BootstrapManager') as mock_bm:
2265
 
            with patch.object(attempt_suite,
2266
 
                              '_iter_bs_manager_steps') as mock_ibms:
2267
 
                with patch('industrial_test.make_log_dir',
2268
 
                           return_value='qux-1'):
2269
 
                    yield (mock_ibms, mock_bm)
2270
 
 
2271
 
    def test_iter_steps(self):
2272
 
        fake_bootstrap = FakeAttemptClass('fake-bootstrap', '1', '2')
2273
 
        factory = AttemptSuiteFactory([], bootstrap_attempt=fake_bootstrap)
2274
 
        attempt_suite = AttemptSuite(factory, None, 'asdf', None)
2275
 
        with self.iter_steps_cxt(attempt_suite) as (mock_ibms, mock_bm):
2276
 
            client = fake_juju_client()
2277
 
            attempt_suite.iter_steps(client)
2278
 
        mock_bm.assert_called_once_with(
2279
 
            'name', client, client, agent_stream=None, agent_url=None,
2280
 
            bootstrap_host=None, jes_enabled=True, keep_env=True,
2281
 
            log_dir='qux-1', machines=[], permanent=True,
2282
 
            region=None, series=None)
2283
 
 
2284
 
    def test_iter_steps_agent_stream(self):
2285
 
        fake_bootstrap = FakeAttemptClass('fake-bootstrap', '1', '2')
2286
 
        factory = AttemptSuiteFactory([], bootstrap_attempt=fake_bootstrap)
2287
 
        attempt_suite = AttemptSuite(factory, None, 'asdf', 'bar-stream')
2288
 
        with self.iter_steps_cxt(attempt_suite) as (mock_ibms, mock_bm):
2289
 
            client = fake_juju_client()
2290
 
            iterator = attempt_suite.iter_steps(client)
2291
 
        self.assertEqual(iterator, mock_ibms.return_value)
2292
 
        mock_bm.assert_called_once_with(
2293
 
            'name', client, client, agent_stream='bar-stream', agent_url=None,
2294
 
            bootstrap_host=None, jes_enabled=True, keep_env=True,
2295
 
            log_dir='qux-1', machines=[], permanent=True,
2296
 
            region=None, series=None)
2297
 
 
2298
 
    def test__iter_bs_manager_steps(self):
2299
 
        fake_bootstrap = FakeAttemptClass('fake-bootstrap', '1', '2')
2300
 
        fake_1 = FakeAttemptClass('fake-1', '1', '2')
2301
 
        fake_2 = FakeAttemptClass('fake-2', '1', '2')
2302
 
        factory = AttemptSuiteFactory([fake_1, fake_2],
2303
 
                                      bootstrap_attempt=fake_bootstrap)
2304
 
        attempt_suite = AttemptSuite(factory, None, None, None)
2305
 
        client = fake_juju_client()
2306
 
        bs_manager = FakeBootstrapManager(client)
2307
 
        steps = list(attempt_suite._iter_bs_manager_steps(
2308
 
            bs_manager, client, fake_bootstrap(), True))
2309
 
        self.assertEqual([
2310
 
            {'test_id': 'fake-bootstrap-id'},
2311
 
            {'test_id': 'fake-bootstrap-id', 'result': '1'},
2312
 
            {'test_id': 'prepare-suite'},
2313
 
            {'test_id': 'prepare-suite', 'result': True},
2314
 
            {'test_id': 'fake-1-id'},
2315
 
            {'test_id': 'fake-1-id', 'result': '1'},
2316
 
            {'test_id': 'fake-2-id'},
2317
 
            {'test_id': 'fake-2-id', 'result': '1'},
2318
 
            {'test_id': 'destroy-env'},
2319
 
            {'test_id': 'destroy-env', 'result': True},
2320
 
            {'test_id': 'substrate-clean'},
2321
 
            {'test_id': 'substrate-clean', 'result': True},
2322
 
            ], steps)
2323
 
 
2324
 
    def test__iter_bs_manager_steps_teardown_in_runtime(self):
2325
 
        fake_bootstrap = FakeAttemptClass('fake-bootstrap', '1', '2')
2326
 
        fake_1 = FakeAttemptClass('fake-1', Exception('fake exception'), '2')
2327
 
        factory = AttemptSuiteFactory([fake_1],
2328
 
                                      bootstrap_attempt=fake_bootstrap)
2329
 
        attempt_suite = AttemptSuite(factory, None, None, None)
2330
 
        client = fake_juju_client()
2331
 
        bs_manager = FakeBootstrapManager(client, keep_env=True)
2332
 
        with self.assertRaisesRegexp(Exception, 'fake exception'):
2333
 
            list(attempt_suite._iter_bs_manager_steps(
2334
 
                bs_manager, client, fake_bootstrap(), True))
2335
 
        self.assertIs(True, bs_manager.torn_down)