~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-01-19 15:32:33 UTC
  • mto: This revision was merged to the branch mainline in revision 804.
  • Revision ID: aaron.bentley@canonical.com-20150119153233-jjcvikwiw1dx2lak
Print error on missing environment.

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
 
from collections import OrderedDict
4
 
from contextlib import (
5
 
    closing,
6
 
    contextmanager,
7
 
    )
8
 
import json
 
4
from contextlib import contextmanager
9
5
import os
10
 
from tempfile import (
11
 
    mkdtemp,
12
 
    NamedTemporaryFile,
13
 
    )
 
6
from tempfile import NamedTemporaryFile
14
7
from textwrap import dedent
 
8
from unittest import TestCase
15
9
 
16
10
from boto.ec2.securitygroup import SecurityGroup
17
11
from mock import (
21
15
    )
22
16
import yaml
23
17
 
24
 
from fakejuju import (
25
 
    fake_juju_client,
26
 
    )
27
18
from industrial_test import (
28
 
    AttemptSuite,
29
 
    AttemptSuiteFactory,
30
19
    BACKUP,
31
20
    BackupRestoreAttempt,
32
21
    BootstrapAttempt,
33
 
    CannotUpgradeToClient,
34
 
    CannotUpgradeToOldClient,
35
22
    DENSITY,
36
23
    DeployManyAttempt,
 
24
    DeployManyFactory,
37
25
    DestroyEnvironmentAttempt,
38
26
    EnsureAvailabilityAttempt,
39
27
    FULL,
40
28
    IndustrialTest,
41
 
    make_substrate_manager,
 
29
    make_substrate,
42
30
    maybe_write_json,
43
31
    MultiIndustrialTest,
44
32
    parse_args,
45
 
    PrepareUpgradeJujuAttempt,
46
33
    QUICK,
47
 
    StageInfo,
 
34
    StageAttempt,
48
35
    SteppedStageAttempt,
49
 
    UpgradeCharmAttempt,
50
 
    UpgradeJujuAttempt,
51
36
    )
52
37
from jujuconfig import get_euca_env
53
38
from jujupy import (
54
39
    EnvJujuClient,
55
 
    EnvJujuClient1X,
56
 
    get_timeout_prefix,
57
 
    JujuData,
58
 
    KVM_MACHINE,
59
 
    LXC_MACHINE,
60
 
    LXD_MACHINE,
61
40
    SimpleEnvironment,
62
 
    Status,
63
41
    _temp_env,
64
42
    )
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 (
 
43
from substrate import AWSAccount
 
44
from test_jujupy import assert_juju_call
 
45
from test_substrate import (
82
46
    get_aws_env,
83
 
    get_azure_config,
84
47
    get_os_config,
85
48
    make_os_security_group_instance,
86
49
    make_os_security_groups,
87
50
    )
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()
 
51
from test_utility import parse_error
117
52
 
118
53
 
119
54
def iter_steps_validate_info(test, stage, client):
139
74
        unexpected, 'Unexpected test_id: {}'.format(result['test_id']))
140
75
 
141
76
 
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
77
class TestParseArgs(TestCase):
166
78
 
167
79
    def test_parse_args(self):
177
89
            args = parse_args(['rai', 'new-juju'])
178
90
        self.assertRegexpMatches(
179
91
            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'])
 
92
        args = parse_args(['rai', 'new-juju', QUICK])
185
93
        self.assertEqual(args.env, 'rai')
186
94
        self.assertEqual(args.new_juju_path, 'new-juju')
187
 
        self.assertEqual(args.log_dir, 'log-dir')
188
 
        self.assertEqual(args.suite, [QUICK])
189
 
        self.assertIs(args.agent_stream, None)
 
95
        self.assertEqual(args.suite, QUICK)
190
96
 
191
97
    def test_parse_args_attempts(self):
192
 
        args = parse_args(['rai', 'new-juju', QUICK, 'log-dir'])
 
98
        args = parse_args(['rai', 'new-juju', QUICK])
193
99
        self.assertEqual(args.attempts, 2)
194
 
        args = parse_args(['rai', 'new-juju', '--attempts', '3', QUICK,
195
 
                           'log-dir'])
 
100
        args = parse_args(['rai', 'new-juju', '--attempts', '3', QUICK])
196
101
        self.assertEqual(args.attempts, 3)
197
102
 
198
103
    def test_parse_args_json_file(self):
199
 
        args = parse_args(['rai', 'new-juju', QUICK, 'log-dir'])
 
104
        args = parse_args(['rai', 'new-juju', QUICK])
200
105
        self.assertIs(args.json_file, None)
201
 
        args = parse_args(['rai', 'new-juju', '--json-file', 'foobar', QUICK,
202
 
                           'log-dir'])
 
106
        args = parse_args(['rai', 'new-juju', '--json-file', 'foobar', QUICK])
203
107
        self.assertEqual(args.json_file, 'foobar')
204
108
 
205
109
    def test_parse_args_suite(self):
206
 
        args = parse_args(['rai', 'new-juju', 'full', 'log-dir'])
207
 
        self.assertEqual(args.suite, [FULL])
208
 
        args = parse_args(['rai', 'new-juju', QUICK, 'log-dir'])
209
 
        self.assertEqual(args.suite, [QUICK])
210
 
        args = parse_args(['rai', 'new-juju', DENSITY, 'log-dir'])
211
 
        self.assertEqual(args.suite, [DENSITY])
212
 
        args = parse_args(['rai', 'new-juju', BACKUP, 'log-dir'])
213
 
        self.assertEqual(args.suite, [BACKUP])
214
 
        with parse_error(self) as stderr:
215
 
            args = parse_args(['rai', 'new-juju', 'foo', 'log-dir'])
216
 
        self.assertRegexpMatches(
217
 
            stderr.getvalue(), ".*argument suite: invalid choice: 'foo'.*")
218
 
 
219
 
    def test_parse_args_multi_suite(self):
220
 
        args = parse_args(['rai', 'new-juju', 'full,quick', 'log-dir'])
221
 
        self.assertEqual(args.suite, [FULL, QUICK])
222
 
        with parse_error(self) as stderr:
223
 
            args = parse_args(['rai', 'new-juju', 'full,foo', 'log-dir'])
 
110
        args = parse_args(['rai', 'new-juju', 'full'])
 
111
        self.assertEqual(args.suite, FULL)
 
112
        args = parse_args(['rai', 'new-juju', QUICK])
 
113
        self.assertEqual(args.suite, QUICK)
 
114
        args = parse_args(['rai', 'new-juju', DENSITY])
 
115
        self.assertEqual(args.suite, DENSITY)
 
116
        args = parse_args(['rai', 'new-juju', BACKUP])
 
117
        self.assertEqual(args.suite, BACKUP)
 
118
        with parse_error(self) as stderr:
 
119
            args = parse_args(['rai', 'new-juju', 'foo'])
224
120
        self.assertRegexpMatches(
225
121
            stderr.getvalue(), ".*argument suite: invalid choice: 'foo'.*")
226
122
 
227
123
    def test_parse_args_agent_url(self):
228
 
        args = parse_args(['rai', 'new-juju', QUICK, 'log-dir'])
 
124
        args = parse_args(['rai', 'new-juju', QUICK])
229
125
        self.assertEqual(args.new_agent_url, None)
230
126
        args = parse_args(['rai', 'new-juju', '--new-agent-url',
231
 
                           'http://example.org', QUICK, 'log-dir'])
 
127
                           'http://example.org', QUICK])
232
128
        self.assertEqual(args.new_agent_url, 'http://example.org')
233
129
 
234
 
    def test_parse_args_debug(self):
235
 
        args = parse_args(['rai', 'new-juju', QUICK, 'log-dir'])
236
 
        self.assertEqual(args.debug, False)
237
 
        args = parse_args(['rai', 'new-juju', '--debug', QUICK, 'log-dir'])
238
 
        self.assertEqual(args.debug, True)
239
 
 
240
 
    def test_parse_args_old_stable(self):
241
 
        args = parse_args(['rai', 'new-juju', QUICK, 'log-dir',
242
 
                           '--old-stable', 'asdf'])
243
 
        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'])
252
 
        self.assertIs(args.old_stable, None)
253
 
 
254
130
 
255
131
class FakeStepAttempt:
256
132
 
257
 
    def __init__(self, result, new_path=None):
 
133
    def __init__(self, result):
258
134
        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
135
 
280
136
    def iter_test_results(self, old, new):
281
137
        return iter(self.result)
282
138
 
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)
 
139
 
 
140
class FakeAttempt(FakeStepAttempt):
 
141
 
 
142
    def __init__(self, old_result, new_result, test_id='foo-id'):
 
143
        super(FakeAttempt, self).__init__([(test_id, old_result, new_result)])
 
144
 
 
145
    def do_stage(self, old_client, new_client):
 
146
        return self.result[0]
292
147
 
293
148
 
294
149
class FakeAttemptClass:
295
 
    """Instances of this class behave like classes, not instances.
296
 
 
297
 
    Methods like factory, that would be classmethods on a normal class, are
298
 
    normal methods on FakeAttemptClass.
299
 
    """
300
 
 
301
 
    def factory(self, upgrade_sequence, attempt_stream):
302
 
        return self()
303
 
 
304
 
    def __init__(self, title, *result, **kwargs):
 
150
 
 
151
    def __init__(self, title, *result):
305
152
        self.title = title
306
153
        self.test_id = '{}-id'.format(title)
307
154
        self.result = result
308
 
        self.new_path = kwargs.get('new_path')
309
155
 
310
156
    def get_test_info(self):
311
157
        return {self.test_id: {'title': self.title}}
312
158
 
313
159
    def __call__(self):
314
 
        return FakeStepAttempt.from_result(*self.result, test_id=self.test_id,
315
 
                                           new_path=self.new_path)
 
160
        return FakeAttempt(*self.result, test_id=self.test_id)
 
161
 
 
162
 
 
163
class StubJujuClient:
 
164
 
 
165
    def destroy_environment(self, delete_jenv=False):
 
166
        pass
316
167
 
317
168
 
318
169
@contextmanager
324
175
        yield
325
176
 
326
177
 
327
 
def fake_bootstrap_manager(self, temp_env_name, client, *args, **kwargs):
328
 
    return FakeBootstrapManager(client)
329
 
 
330
 
 
331
178
class TestMultiIndustrialTest(TestCase):
332
179
 
333
180
    def test_from_args(self):
334
 
        args = Namespace(
335
 
            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)
 
181
        args = Namespace(env='foo', new_juju_path='new-path', attempts=7,
 
182
                         suite=QUICK, new_agent_url=None)
338
183
        with temp_env('foo'):
339
 
            mit = MultiIndustrialTest.from_args(args, QUICK)
 
184
            mit = MultiIndustrialTest.from_args(args)
340
185
        self.assertEqual(mit.env, 'foo')
341
186
        self.assertEqual(mit.new_juju_path, 'new-path')
342
187
        self.assertEqual(mit.attempt_count, 7)
343
188
        self.assertEqual(mit.max_attempts, 14)
344
 
        self.assertEqual(mit.log_parent_dir, 'log-dir')
345
 
        self.assertIs(mit.agent_stream, None)
346
189
        self.assertEqual(
347
 
            mit.stages, AttemptSuiteFactory([]))
348
 
        args = Namespace(
349
 
            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)
 
190
            mit.stages, [BootstrapAttempt, DestroyEnvironmentAttempt])
 
191
        args = Namespace(env='bar', new_juju_path='new-path2', attempts=6,
 
192
                         suite=FULL, new_agent_url=None)
352
193
        with temp_env('bar'):
353
 
            mit = MultiIndustrialTest.from_args(args, FULL)
 
194
            mit = MultiIndustrialTest.from_args(args)
354
195
        self.assertEqual(mit.env, 'bar')
355
196
        self.assertEqual(mit.new_juju_path, 'new-path2')
356
197
        self.assertEqual(mit.attempt_count, 6)
357
198
        self.assertEqual(mit.max_attempts, 12)
358
 
        self.assertEqual(mit.log_parent_dir, 'log-dir2')
359
 
        self.assertIs(mit.agent_stream, None)
360
199
        self.assertEqual(
361
 
            mit.stages, AttemptSuiteFactory([
362
 
                UpgradeCharmAttempt, DeployManyAttempt,
363
 
                BackupRestoreAttempt, EnsureAvailabilityAttempt]))
 
200
            mit.stages, [
 
201
                BootstrapAttempt, DeployManyAttempt, BackupRestoreAttempt,
 
202
                EnsureAvailabilityAttempt, DestroyEnvironmentAttempt])
364
203
 
365
204
    def test_from_args_maas(self):
366
 
        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)
370
 
        with temp_env('foo', {'type': 'maas'}):
371
 
            mit = MultiIndustrialTest.from_args(args, DENSITY)
372
 
        self.assertEqual(
373
 
            mit.stages, AttemptSuiteFactory([DeployManyAttempt]))
374
 
 
375
 
    def test_from_args_debug(self):
376
 
        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)
380
 
        with temp_env('foo', {'type': 'maas'}):
381
 
            mit = MultiIndustrialTest.from_args(args, DENSITY)
382
 
            self.assertEqual(mit.debug, False)
383
 
            args.debug = True
384
 
            mit = MultiIndustrialTest.from_args(args, DENSITY)
385
 
            self.assertEqual(mit.debug, True)
386
 
 
387
 
    def test_from_args_really_old_path(self):
388
 
        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)
392
 
        with temp_env('foo'):
393
 
            mit = MultiIndustrialTest.from_args(args, FULL)
394
 
        self.assertEqual(mit.really_old_path, 'really-old-path')
395
 
        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)
399
 
        with temp_env('bar'):
400
 
            mit = MultiIndustrialTest.from_args(args, FULL)
401
 
        self.assertIs(mit.really_old_path, None)
402
 
 
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')
 
205
        args = Namespace(env='foo', new_juju_path='new-path', attempts=7,
 
206
                         suite=DENSITY, new_agent_url=None)
 
207
        with temp_env('foo', {'type': 'maas'}):
 
208
            mit = MultiIndustrialTest.from_args(args)
 
209
        self.assertEqual(
 
210
            mit.stages, [
 
211
                BootstrapAttempt, DeployManyFactory(2, 2),
 
212
                DestroyEnvironmentAttempt])
 
213
 
 
214
    def test_get_stages(self):
 
215
        self.assertEqual(
 
216
            MultiIndustrialTest.get_stages(QUICK, {'type': 'foo'}),
 
217
            [BootstrapAttempt, DestroyEnvironmentAttempt])
 
218
 
 
219
        self.assertEqual(
 
220
            MultiIndustrialTest.get_stages(FULL, {'type': 'foo'}), [
 
221
                BootstrapAttempt, DeployManyAttempt, BackupRestoreAttempt,
 
222
                EnsureAvailabilityAttempt, DestroyEnvironmentAttempt])
 
223
        self.assertEqual(
 
224
            MultiIndustrialTest.get_stages(DENSITY, {'type': 'foo'}), [
 
225
                BootstrapAttempt, DeployManyAttempt,
 
226
                DestroyEnvironmentAttempt])
 
227
        self.assertEqual(
 
228
            MultiIndustrialTest.get_stages(BACKUP, {'type': 'foo'}), [
 
229
                BootstrapAttempt, BackupRestoreAttempt,
 
230
                DestroyEnvironmentAttempt])
 
231
 
 
232
    def test_get_stages_maas(self):
 
233
        self.assertEqual(
 
234
            MultiIndustrialTest.get_stages(QUICK, {'type': 'maas'}),
 
235
            [BootstrapAttempt, DestroyEnvironmentAttempt])
 
236
        self.assertEqual(
 
237
            MultiIndustrialTest.get_stages(FULL, {'type': 'maas'}), [
 
238
                BootstrapAttempt, DeployManyFactory(2, 2),
 
239
                BackupRestoreAttempt, EnsureAvailabilityAttempt,
 
240
                DestroyEnvironmentAttempt])
 
241
        self.assertEqual(
 
242
            MultiIndustrialTest.get_stages(DENSITY, {'type': 'maas'}), [
 
243
                BootstrapAttempt, DeployManyFactory(2, 2),
 
244
                DestroyEnvironmentAttempt])
414
245
 
415
246
    def test_density_suite(self):
416
 
        args = Namespace(
417
 
            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)
 
247
        args = Namespace(env='foo', new_juju_path='new-path', attempts=7,
 
248
                         suite=DENSITY, new_agent_url=None)
420
249
        with temp_env('foo'):
421
 
            mit = MultiIndustrialTest.from_args(args, DENSITY)
 
250
            mit = MultiIndustrialTest.from_args(args)
422
251
        self.assertEqual(
423
 
            mit.stages, AttemptSuiteFactory([DeployManyAttempt]))
 
252
            mit.stages, [BootstrapAttempt, DeployManyAttempt,
 
253
                         DestroyEnvironmentAttempt])
424
254
 
425
255
    def test_backup_suite(self):
426
 
        args = Namespace(
427
 
            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)
 
256
        args = Namespace(env='foo', new_juju_path='new-path', attempts=7,
 
257
                         suite=BACKUP, new_agent_url=None)
430
258
        with temp_env('foo'):
431
 
            mit = MultiIndustrialTest.from_args(args, BACKUP)
 
259
            mit = MultiIndustrialTest.from_args(args)
432
260
        self.assertEqual(
433
 
            mit.stages, AttemptSuiteFactory([BackupRestoreAttempt]))
 
261
            mit.stages, [BootstrapAttempt, BackupRestoreAttempt,
 
262
                         DestroyEnvironmentAttempt])
434
263
 
435
264
    def test_from_args_new_agent_url(self):
436
 
        args = Namespace(
437
 
            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)
 
265
        args = Namespace(env='foo', new_juju_path='new-path', attempts=7,
 
266
                         suite=QUICK, new_agent_url='http://example.net')
440
267
        with temp_env('foo'):
441
 
            mit = MultiIndustrialTest.from_args(args, suite=QUICK)
 
268
            mit = MultiIndustrialTest.from_args(args)
442
269
        self.assertEqual(mit.new_agent_url, 'http://example.net')
443
270
 
444
271
    def test_init(self):
445
272
        mit = MultiIndustrialTest('foo-env', 'bar-path', [
446
 
            DestroyEnvironmentAttempt, BootstrapAttempt], 'log-dir', 5)
 
273
            DestroyEnvironmentAttempt, BootstrapAttempt], 5)
447
274
        self.assertEqual(mit.env, 'foo-env')
448
275
        self.assertEqual(mit.new_juju_path, 'bar-path')
449
276
        self.assertEqual(mit.stages, [DestroyEnvironmentAttempt,
450
277
                                      BootstrapAttempt])
451
278
        self.assertEqual(mit.attempt_count, 5)
452
 
        self.assertEqual(mit.log_parent_dir, 'log-dir')
453
279
 
454
280
    def test_make_results(self):
455
 
        mit = MultiIndustrialTest('foo-env', 'bar-path', AttemptSuiteFactory([
456
 
            DestroyEnvironmentAttempt]), 5)
457
 
        results = mit.make_results()
458
 
        self.assertEqual(results, {'results': [
459
 
            {'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
 
             'title': 'destroy environment', 'test_id': 'destroy-env',
466
 
             'report_on': True},
467
 
            {'attempts': 0, 'old_failures': 0, 'new_failures': 0,
468
 
             'title': 'check substrate clean', 'test_id': 'substrate-clean',
469
 
             'report_on': True},
470
 
        ]})
471
 
 
472
 
    def test_make_results_report_on(self):
473
 
        class NoReportOn:
474
 
 
475
 
            @staticmethod
476
 
            def get_test_info():
477
 
                return {'no-report': {
478
 
                    'title': 'No report', 'report_on': False}}
479
 
 
480
 
        mit = MultiIndustrialTest('foo-env', 'bar-path', AttemptSuiteFactory([
481
 
            NoReportOn]), 5)
482
 
        results = mit.make_results()
483
 
        self.assertEqual(results, {'results': [
484
 
            {
485
 
                'test_id': 'bootstrap',
486
 
                'title': 'bootstrap',
487
 
                'report_on': True,
488
 
                'attempts': 0,
489
 
                'old_failures': 0,
490
 
                'new_failures': 0,
491
 
            },
492
 
            {
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
 
                'test_id': 'no-report',
502
 
                'title': 'No report',
503
 
                'report_on': False,
504
 
                'attempts': 0,
505
 
                'old_failures': 0,
506
 
                'new_failures': 0,
507
 
            },
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
 
        ]})
525
 
 
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
 
281
        mit = MultiIndustrialTest('foo-env', 'bar-path', [
 
282
            DestroyEnvironmentAttempt, BootstrapAttempt], 5)
 
283
        results = mit.make_results()
 
284
        self.assertEqual(results, {'results': [
 
285
            {'attempts': 0, 'old_failures': 0, 'new_failures': 0,
 
286
             'title': 'destroy environment', 'test_id': 'destroy-env'},
 
287
            {'attempts': 0, 'old_failures': 0, 'new_failures': 0,
 
288
             'title': 'check substrate clean', 'test_id': 'substrate-clean'},
 
289
            {'attempts': 0, 'old_failures': 0, 'new_failures': 0,
 
290
             'title': 'bootstrap', 'test_id': 'bootstrap'},
 
291
        ]})
536
292
 
537
293
    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])
 
294
        mit = MultiIndustrialTest('foo-env', 'bar-path', [
 
295
            DestroyEnvironmentAttempt, BootstrapAttempt], 5)
 
296
        side_effect = lambda x, y=None: (x, y)
 
297
        with patch('jujupy.EnvJujuClient.by_version', side_effect=side_effect):
 
298
            with patch('jujupy.SimpleEnvironment.from_config',
 
299
                       side_effect=lambda x: SimpleEnvironment(x, {})):
 
300
                industrial = mit.make_industrial_test()
 
301
        self.assertEqual(industrial.old_client,
 
302
                         (SimpleEnvironment('foo-env-old', {}), None))
 
303
        self.assertEqual(industrial.new_client,
 
304
                         (SimpleEnvironment('foo-env-new', {}), 'bar-path'))
 
305
        self.assertEqual(len(industrial.stage_attempts), 2)
 
306
        for stage, attempt in zip(mit.stages, industrial.stage_attempts):
 
307
            self.assertIs(type(attempt), stage)
555
308
 
556
309
    def test_make_industrial_test_new_agent_url(self):
557
 
        mit = MultiIndustrialTest('foo-env', 'bar-path',
558
 
                                  AttemptSuiteFactory([]), 'log-dir',
 
310
        mit = MultiIndustrialTest('foo-env', 'bar-path', [],
559
311
                                  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()
 
312
        side_effect = lambda x, y=None: (x, y)
 
313
        with patch('jujupy.EnvJujuClient.by_version', side_effect=side_effect):
 
314
            with patch('jujupy.SimpleEnvironment.from_config',
 
315
                       side_effect=lambda x: SimpleEnvironment(x, {})):
 
316
                industrial = mit.make_industrial_test()
563
317
        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'),
 
318
            industrial.new_client, (
 
319
                SimpleEnvironment('foo-env-new', {
 
320
                    'tools-metadata-url': 'http://example.com'}),
572
321
                'bar-path')
573
322
            )
574
323
 
575
 
    def test_make_industrial_test_debug(self):
576
 
        mit = MultiIndustrialTest('foo-env', 'bar-path',
577
 
                                  AttemptSuiteFactory([]), 'log-dir',
578
 
                                  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)
593
 
 
594
324
    def test_update_results(self):
595
 
        mit = MultiIndustrialTest('foo-env', 'bar-path',
596
 
                                  AttemptSuiteFactory([]), 2)
 
325
        mit = MultiIndustrialTest('foo-env', 'bar-path', [
 
326
            DestroyEnvironmentAttempt, BootstrapAttempt], 2)
597
327
        results = mit.make_results()
598
 
        mit.update_results([('bootstrap', True, False)], results)
 
328
        mit.update_results([('destroy-env', True, False)], results)
599
329
        expected = {'results': [
600
 
            {'title': 'bootstrap', 'test_id': 'bootstrap',
601
 
             'attempts': 1, 'new_failures': 1, 'old_failures': 0,
602
 
             '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
330
            {'title': 'destroy environment', 'test_id': 'destroy-env',
607
 
             'attempts': 0,
608
 
             'new_failures': 0, 'old_failures': 0, 'report_on': True},
 
331
             'attempts': 1, 'new_failures': 1, 'old_failures': 0},
609
332
            {'title': 'check substrate clean', 'test_id': 'substrate-clean',
610
 
             'attempts': 0, 'new_failures': 0, 'old_failures': 0,
611
 
             'report_on': True},
 
333
             'attempts': 0, 'new_failures': 0, 'old_failures': 0},
 
334
            {'title': 'bootstrap', 'test_id': 'bootstrap', 'attempts': 0,
 
335
             'new_failures': 0, 'old_failures': 0},
612
336
            ]}
613
337
        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)
 
338
        mit.update_results(
 
339
            [('destroy-env', True, True), ('substrate-clean', True, True),
 
340
             ('bootstrap', False, True)],
 
341
            results)
618
342
        self.assertEqual(results, {'results': [
619
 
            {'title': 'bootstrap', 'test_id': 'bootstrap',
620
 
             'attempts': 2, 'new_failures': 1, 'old_failures': 0,
621
 
             '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
343
            {'title': 'destroy environment', 'test_id': 'destroy-env',
626
 
             'attempts': 1, 'new_failures': 0, 'old_failures': 1, 'report_on':
627
 
             True},
 
344
             'attempts': 2, 'new_failures': 1, 'old_failures': 0},
628
345
            {'title': 'check substrate clean', 'test_id': 'substrate-clean',
629
 
             'attempts': 1, 'new_failures': 0, 'old_failures': 0,
630
 
             'report_on': True},
 
346
             'attempts': 1, 'new_failures': 0, 'old_failures': 0},
 
347
            {'title': 'bootstrap', 'test_id': 'bootstrap', 'attempts': 1,
 
348
             'new_failures': 0, 'old_failures': 1},
631
349
            ]})
632
350
        mit.update_results(
633
 
            [('bootstrap', False, False), ('prepare-suite', True, True),
634
 
             ('destroy-env', False, False), ('substrate-clean', True, True)],
 
351
            [('destroy-env', False, False), ('substrate-clean', True, True),
 
352
             ('bootstrap', False, False)],
635
353
            results)
636
354
        expected = {'results': [
637
 
            {'title': 'bootstrap', 'test_id': 'bootstrap',
638
 
             'attempts': 2, 'new_failures': 1, 'old_failures': 0,
639
 
             '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
355
            {'title': 'destroy environment', 'test_id': 'destroy-env',
644
 
             'attempts': 2, 'new_failures': 1, 'old_failures': 2,
645
 
             'report_on': True},
 
356
             'attempts': 2, 'new_failures': 1, 'old_failures': 0},
646
357
            {'title': 'check substrate clean', 'test_id': 'substrate-clean',
647
 
             'attempts': 2, 'new_failures': 0, 'old_failures': 0,
648
 
             'report_on': True},
 
358
             'attempts': 2, 'new_failures': 0, 'old_failures': 0},
 
359
            {'title': 'bootstrap', 'test_id': 'bootstrap', 'attempts': 2,
 
360
             'new_failures': 1, 'old_failures': 2},
649
361
            ]}
650
362
        self.assertEqual(results, expected)
651
363
 
652
364
    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):
 
365
        mit = MultiIndustrialTest('foo-env', 'bar-path', [
 
366
            FakeAttemptClass('foo', True, True),
 
367
            FakeAttemptClass('bar', True, False),
 
368
            ], 5, 10)
 
369
        side_effect = lambda x, y=None: StubJujuClient()
 
370
        with patch('jujupy.EnvJujuClient.by_version', side_effect=side_effect):
 
371
            with patch('jujupy.SimpleEnvironment.from_config',
 
372
                       side_effect=lambda x: SimpleEnvironment(x, {})):
665
373
                results = mit.run_tests()
666
374
        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
375
            {'title': 'foo', 'test_id': 'foo-id', 'attempts': 5,
673
 
             'old_failures': 0, 'new_failures': 0, 'report_on': True},
 
376
             'old_failures': 0, 'new_failures': 0},
674
377
            {'title': 'bar', 'test_id': 'bar-id', 'attempts': 5,
675
 
             '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},
 
378
             'old_failures': 0, 'new_failures': 5},
682
379
            ]})
683
380
 
684
381
    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):
 
382
        mit = MultiIndustrialTest('foo-env', 'bar-path', [
 
383
            FakeAttemptClass('foo', True, False),
 
384
            FakeAttemptClass('bar', True, False),
 
385
            ], 5, 6)
 
386
        side_effect = lambda x, y=None: StubJujuClient()
 
387
        with patch('jujupy.EnvJujuClient.by_version', side_effect=side_effect):
 
388
            with patch('jujupy.SimpleEnvironment.from_config',
 
389
                       side_effect=lambda x: SimpleEnvironment(x, {})):
697
390
                results = mit.run_tests()
698
391
        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
392
            {'title': 'foo', 'test_id': 'foo-id', 'attempts': 5,
705
 
             'old_failures': 0, 'new_failures': 5, 'report_on': True},
 
393
             'old_failures': 0, 'new_failures': 5},
706
394
            {'title': 'bar', 'test_id': 'bar-id', 'attempts': 0,
707
 
             '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},
 
395
             'old_failures': 0, 'new_failures': 0},
714
396
            ]})
715
397
 
716
398
    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):
 
399
        mit = MultiIndustrialTest('foo-env', 'bar-path', [
 
400
            FakeAttemptClass('foo', True, False),
 
401
            FakeAttemptClass('bar', True, False),
 
402
            ], 5, 4)
 
403
        side_effect = lambda x, y=None: StubJujuClient()
 
404
        with patch('jujupy.EnvJujuClient.by_version', side_effect=side_effect):
 
405
            with patch('jujupy.SimpleEnvironment.from_config',
 
406
                       side_effect=lambda x: SimpleEnvironment(x, {})):
731
407
                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},
 
408
        self.assertEqual(results, {'results': [
738
409
            {'title': 'foo', 'test_id': 'foo-id', 'attempts': 4,
739
 
             'old_failures': 0, 'new_failures': 4, 'report_on': True},
 
410
             'old_failures': 0, 'new_failures': 4},
740
411
            {'title': 'bar', 'test_id': 'bar-id', 'attempts': 0,
741
 
             '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})
750
 
 
751
 
    @staticmethod
752
 
    def get_results_1():
753
 
        return {
754
 
            'results': [
755
 
                {'title': 'foo', 'attempts': 5, 'old_failures': 1,
756
 
                 'new_failures': 2, 'test_id': 'foo-id'},
757
 
                {'title': 'bar', 'attempts': 5, 'old_failures': 3,
758
 
                 'new_failures': 4, 'report_on': True, 'test_id': 'bar-id'},
759
 
                {'title': 'baz', 'attempts': 5, 'old_failures': 3,
760
 
                 'new_failures': 4, 'report_on': False, 'test_id': 'baz-id'},
761
 
                ]}
762
 
 
763
 
    def test_combine_results_noop(self):
764
 
        new_results = MultiIndustrialTest.combine_results([
765
 
            self.get_results_1()])
766
 
        self.assertEqual(new_results, self.get_results_1())
767
 
 
768
 
    def test_combine_results_append(self):
769
 
        results_2 = {'results': [
770
 
            {'title': 'qux', 'test_id': 'quxx-id', 'attempts': 2,
771
 
             'old_failures': 2, 'new_failures': 1}]}
772
 
        new_results = MultiIndustrialTest.combine_results(
773
 
            [self.get_results_1(), results_2])
774
 
        self.assertEqual(new_results['results'][:3],
775
 
                         self.get_results_1()['results'])
776
 
        self.assertEqual(new_results['results'][3:], results_2['results'])
777
 
 
778
 
    def test_combine_results_add(self):
779
 
        results_2 = {'results': [
780
 
            {'test_id': 'foo-id', 'title': 'foo6', 'attempts': 3,
781
 
             'old_failures': 2, 'new_failures': 1}]}
782
 
        new_results = MultiIndustrialTest.combine_results(
783
 
            [self.get_results_1(), results_2])
784
 
        self.assertEqual(new_results, {'results': [
785
 
            {'title': 'foo', 'attempts': 8, 'old_failures': 3,
786
 
             'new_failures': 3, 'test_id': 'foo-id', 'report_on': True},
787
 
            {'title': 'bar', 'attempts': 5, 'old_failures': 3,
788
 
             'new_failures': 4, 'report_on': True, 'test_id': 'bar-id'},
789
 
            {'title': 'baz', 'attempts': 5, 'old_failures': 3,
790
 
             'new_failures': 4, 'report_on': False, 'test_id': 'baz-id'},
 
412
             'old_failures': 0, 'new_failures': 0},
791
413
            ]})
792
414
 
793
 
    def test_combine_results_report_on(self):
794
 
        results_1 = {'results': [
795
 
            {'test_id': 'foo-id', 'title': 'foo6', 'attempts': 3,
796
 
             'old_failures': 2, 'new_failures': 1},
797
 
            {'test_id': 'bar-id', 'title': 'bar6', 'attempts': 3,
798
 
             'old_failures': 2, 'new_failures': 1},
799
 
            {'test_id': 'baz-id', 'title': 'baz', 'attempts': 3,
800
 
             'old_failures': 2, 'new_failures': 1, 'report_on': False},
801
 
            {'test_id': 'qux-id', 'title': 'qux', 'attempts': 3,
802
 
             'old_failures': 2, 'new_failures': 1, 'report_on': False},
803
 
            ]}
804
 
        results_2 = {'results': [
805
 
            {'test_id': 'foo-id', 'title': 'foo6', 'attempts': 3,
806
 
             'old_failures': 2, 'new_failures': 1, 'report_on': True},
807
 
            {'test_id': 'bar-id', 'title': 'foo6', 'attempts': 3,
808
 
             'old_failures': 2, 'new_failures': 1, 'report_on': False},
809
 
            {'test_id': 'baz-id', 'title': 'foo6', 'attempts': 3,
810
 
             'old_failures': 2, 'new_failures': 1},
811
 
            {'test_id': 'qux-id', 'title': 'qux6', 'attempts': 3,
812
 
             'old_failures': 2, 'new_failures': 1, 'report_on': False},
813
 
            ]}
814
 
        new_results = MultiIndustrialTest.combine_results(
815
 
            [results_1, results_2])
816
 
        self.assertEqual(new_results['results'][0].get('report_on', True),
817
 
                         True)
818
 
        self.assertEqual(new_results['results'][1].get('report_on', True),
819
 
                         True)
820
 
        self.assertEqual(new_results['results'][2].get('report_on', True),
821
 
                         True)
822
 
        self.assertEqual(new_results['results'][3].get('report_on', False),
823
 
                         False)
824
 
 
825
415
    def test_results_table(self):
826
416
        results = [
827
417
            {'title': 'foo', 'attempts': 5, 'old_failures': 1,
828
418
             'new_failures': 2},
829
419
            {'title': 'bar', 'attempts': 5, 'old_failures': 3,
830
 
             'new_failures': 4, 'report_on': True},
831
 
            {'title': 'baz', 'attempts': 5, 'old_failures': 3,
832
 
             'new_failures': 4, 'report_on': False},
 
420
             'new_failures': 4},
833
421
            ]
834
422
        self.assertEqual(
835
423
            ''.join(MultiIndustrialTest.results_table(results)),
840
428
            """))
841
429
 
842
430
 
843
 
class TestIndustrialTest(JujuPyTestCase):
 
431
class TestIndustrialTest(TestCase):
844
432
 
845
433
    def test_init(self):
846
434
        old_client = object()
852
440
        self.assertIs(attempt_list, industrial.stage_attempts)
853
441
 
854
442
    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', [])
 
443
        side_effect = lambda x, y=None: (x, y)
 
444
        with patch('jujupy.EnvJujuClient.by_version', side_effect=side_effect):
 
445
            with patch('jujupy.SimpleEnvironment.from_config',
 
446
                       side_effect=lambda x: SimpleEnvironment(x, {})):
 
447
                industrial = IndustrialTest.from_args(
 
448
                    'foo', 'new-juju-path', [])
861
449
        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)
871
 
 
872
 
    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):
877
 
            with patch('jujupy.SimpleEnvironment.from_config'):
878
 
                industrial = IndustrialTest.from_args(
879
 
                    'foo', 'new-juju-path', [], debug=False)
880
 
                self.assertEqual(industrial.old_client.debug, False)
881
 
                self.assertEqual(industrial.new_client.debug, False)
882
 
                industrial = IndustrialTest.from_args(
883
 
                    'foo', 'new-juju-path', [], debug=True)
884
 
                self.assertEqual(industrial.old_client.debug, True)
885
 
                self.assertEqual(industrial.new_client.debug, True)
 
450
        self.assertEqual(industrial.old_client,
 
451
                         (SimpleEnvironment('foo-old', {}), None))
 
452
        self.assertEqual(industrial.new_client,
 
453
                         (SimpleEnvironment('foo-new', {}), 'new-juju-path'))
 
454
        self.assertNotEqual(industrial.old_client[0].environment,
 
455
                            industrial.new_client[0].environment)
886
456
 
887
457
    def test_run_stages(self):
888
458
        old_client = FakeEnvJujuClient('old')
889
459
        new_client = FakeEnvJujuClient('new')
890
460
        industrial = IndustrialTest(old_client, new_client, [
891
 
            FakeStepAttempt.from_result(True, True),
892
 
            FakeStepAttempt.from_result(True, True)])
 
461
            FakeAttempt(True, True), FakeAttempt(True, True)])
893
462
        with patch('subprocess.call') as cc_mock:
894
463
            result = industrial.run_stages()
895
464
            self.assertItemsEqual(result, [('foo-id', True, True),
897
466
        self.assertEqual(len(cc_mock.mock_calls), 0)
898
467
 
899
468
    def test_run_stages_old_fail(self):
900
 
        old_client = fake_juju_client()
901
 
        new_client = fake_juju_client(full_path='bar-path')
 
469
        old_client = FakeEnvJujuClient('old')
 
470
        new_client = FakeEnvJujuClient('new')
902
471
        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):
 
472
            FakeAttempt(False, True), FakeAttempt(True, True)])
 
473
        with patch('subprocess.call') as cc_mock:
913
474
            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)
 
475
            self.assertItemsEqual(result, [('foo-id', False, True)])
 
476
        assert_juju_call(self, cc_mock, old_client,
 
477
                         ('timeout', '600.00s', 'juju', '--show-log',
 
478
                          'destroy-environment', 'old', '--force', '-y'), 0)
 
479
        assert_juju_call(self, cc_mock, new_client,
 
480
                         ('timeout', '600.00s', 'juju', '--show-log',
 
481
                          'destroy-environment', 'new', '--force', '-y'), 1)
922
482
 
923
483
    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):
 
484
        old_client = FakeEnvJujuClient('old')
 
485
        new_client = FakeEnvJujuClient('new')
 
486
        industrial = IndustrialTest(old_client, new_client, [
 
487
            FakeAttempt(True, False), FakeAttempt(True, True)])
 
488
        with patch('subprocess.call') as cc_mock:
934
489
            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)
 
490
            self.assertItemsEqual(result, [('foo-id', True, False)])
 
491
        assert_juju_call(self, cc_mock, old_client,
 
492
                         ('timeout', '600.00s', 'juju', '--show-log',
 
493
                          'destroy-environment', 'old', '--force', '-y'), 0)
 
494
        assert_juju_call(self, cc_mock, new_client,
 
495
                         ('timeout', '600.00s', 'juju', '--show-log',
 
496
                          'destroy-environment', 'new', '--force', '-y'), 1)
943
497
 
944
498
    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):
 
499
        old_client = FakeEnvJujuClient('old')
 
500
        new_client = FakeEnvJujuClient('new')
 
501
        industrial = IndustrialTest(old_client, new_client, [
 
502
            FakeAttempt(False, False), FakeAttempt(True, True)])
 
503
        with patch('subprocess.call') as cc_mock:
955
504
            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)
 
505
            self.assertItemsEqual(result, [('foo-id', False, False)])
 
506
        assert_juju_call(self, cc_mock, old_client,
 
507
                         ('timeout', '600.00s', 'juju', '--show-log',
 
508
                          'destroy-environment', 'old', '--force', '-y'), 0)
 
509
        assert_juju_call(self, cc_mock, new_client,
 
510
                         ('timeout', '600.00s', 'juju', '--show-log',
 
511
                          'destroy-environment', 'new', '--force', '-y'), 1)
964
512
 
965
513
    def test_run_stages_recover_failure(self):
966
 
        old_client = fake_juju_client()
967
 
        new_client = fake_juju_client()
 
514
        old_client = FakeEnvJujuClient('old')
 
515
        new_client = FakeEnvJujuClient('new')
968
516
        fsa = FakeStepAttempt([('foo', True, False), ('bar', True, True)])
969
517
        industrial = IndustrialTest(old_client, new_client, [
970
 
            fsa, FakeStepAttempt.from_result(True, True)])
 
518
            fsa, FakeAttempt(True, True)])
971
519
        self.assertEqual(list(industrial.run_stages()), [
972
520
            ('foo', True, False), ('bar', True, True), ('foo-id', True, True)])
973
521
 
976
524
        new_client = FakeEnvJujuClient('new')
977
525
        fsa = FakeStepAttempt([('foo', True, True), ('bar', False, True)])
978
526
        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'):
 
527
            fsa, FakeAttempt(True, True)])
 
528
        with patch.object(old_client, 'destroy_environment'):
 
529
            with patch.object(new_client, 'destroy_environment'):
982
530
                self.assertEqual(list(industrial.run_stages()), [
983
531
                    ('foo', True, True), ('bar', False, True)])
984
532
 
985
 
    def test_run_stages_raises_cannot_upgrade_to_old_client(self):
986
 
        old = FakeEnvJujuClient()
987
 
        new = FakeEnvJujuClient()
988
 
        industrial = IndustrialTest(old, new, [PrepareUpgradeJujuAttempt({})])
989
 
        with self.assertRaises(CannotUpgradeToOldClient):
990
 
            list(industrial.run_stages())
 
533
    def test_destroy_both_even_with_exception(self):
 
534
        old_client = FakeEnvJujuClient('old')
 
535
        new_client = FakeEnvJujuClient('new')
 
536
        industrial = IndustrialTest(old_client, new_client, [
 
537
            FakeAttempt(False, False), FakeAttempt(True, True)])
 
538
        with patch.object(old_client, 'destroy_environment',
 
539
                          side_effect=Exception) as oc_mock:
 
540
            with patch.object(new_client, 'destroy_environment',
 
541
                              side_effect=Exception) as nc_mock:
 
542
                with self.assertRaises(Exception):
 
543
                    industrial.destroy_both()
 
544
        oc_mock.assert_called_once_with(delete_jenv=True)
 
545
        nc_mock.assert_called_once_with(delete_jenv=True)
991
546
 
992
547
    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])
 
548
        old_client = FakeEnvJujuClient('old')
 
549
        new_client = FakeEnvJujuClient('new')
 
550
        attempt = FakeAttempt(True, True)
 
551
        industrial = IndustrialTest(old_client, new_client, [attempt])
1000
552
 
1001
 
        def iter_test_results(old, new):
 
553
        def iter_test_results():
1002
554
            raise Exception
1003
555
            yield
1004
556
 
1005
 
        with patch.object(attempt, 'iter_test_results',
1006
 
                          iter_test_results):
 
557
        with patch.object(attempt, 'iter_test_results', iter_test_results):
1007
558
            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):
 
559
                with patch.object(industrial, 'destroy_both') as db_mock:
 
560
                    with self.assertRaises(SystemExit):
 
561
                        industrial.run_attempt()
 
562
        le_mock.assert_called_once()
 
563
        self.assertEqual(db_mock.mock_calls, [call(), call()])
 
564
 
 
565
 
 
566
class TestStageAttempt(TestCase):
 
567
 
 
568
    def test_do_stage(self):
 
569
 
 
570
        class StubSA(StageAttempt):
 
571
 
 
572
            def __init__(self):
 
573
                super(StageAttempt, self).__init__()
 
574
                self.did_op = []
 
575
 
 
576
            def do_operation(self, client):
 
577
                self.did_op.append(client)
 
578
 
 
579
            def get_result(self, client):
 
580
                return self.did_op.index(client)
 
581
 
 
582
        attempt = StubSA()
 
583
        old = object()
 
584
        new = object()
 
585
        result = attempt.do_stage(old, new)
 
586
        self.assertEqual([old, new], attempt.did_op)
 
587
        self.assertEqual(result, (0, 1))
 
588
 
 
589
    def test_get_test_info(self):
 
590
 
 
591
        class StubSA(StageAttempt):
 
592
 
 
593
            test_id = 'foo-bar'
 
594
 
 
595
            title = 'baz'
 
596
 
 
597
        self.assertEqual(StubSA.get_test_info(), {'foo-bar': {'title': 'baz'}})
 
598
 
 
599
    def test_iter_test_results(self):
 
600
 
 
601
        did_operation = [False]
 
602
 
 
603
        class StubSA(StageAttempt):
 
604
 
 
605
            test_id = 'foo-id'
 
606
 
 
607
            def do_stage(self, old, new):
 
608
 
 
609
                did_operation[0] = True
 
610
                return True, True
 
611
 
 
612
        for result in StubSA().iter_test_results(None, None):
 
613
            self.assertEqual(result, ('foo-id', True, True))
 
614
 
 
615
 
 
616
class TestSteppedStageAttempt(TestCase):
1019
617
 
1020
618
    def test__iter_for_result_premature_results(self):
1021
 
        iterator = (x for x in [{'test_id': 'foo-id', 'result': True}])
 
619
        iterator = iter([{'test_id': 'foo-id', 'result': True}])
1022
620
        with self.assertRaisesRegexp(ValueError, 'Result before declaration.'):
1023
621
            list(SteppedStageAttempt._iter_for_result(iterator))
1024
622
 
1025
623
    def test__iter_for_result_many(self):
1026
 
        iterator = (x for x in [
 
624
        iterator = iter([
1027
625
            {'test_id': 'foo-id'},
1028
626
            {'test_id': 'foo-id', 'result': True},
1029
627
            {'test_id': 'bar-id'},
1048
646
        le_mock.assert_called_once_with(error)
1049
647
 
1050
648
    def test_iter_for_result_id_change(self):
1051
 
        iterator = (x for x in [
 
649
        iterator = iter([
1052
650
            {'test_id': 'foo-id'}, {'test_id': 'bar-id'}])
1053
651
        with self.assertRaisesRegexp(ValueError, 'ID changed without result.'):
1054
652
            list(SteppedStageAttempt._iter_for_result(iterator))
1065
663
            list(SteppedStageAttempt._iter_for_result(iterator()))
1066
664
 
1067
665
    def test_iter_for_result_id_change_result(self):
1068
 
        iterator = (x for x in [
 
666
        iterator = iter([
1069
667
            {'test_id': 'foo-id'}, {'test_id': 'bar-id', 'result': True}])
1070
668
        with self.assertRaisesRegexp(ValueError, 'ID changed without result.'):
1071
669
            list(SteppedStageAttempt._iter_for_result(iterator))
1072
670
 
1073
671
    def test__iter_test_results_success(self):
1074
 
        old_iter = (x for x in [
 
672
        old_iter = iter([
1075
673
            None, {'test_id': 'foo-id', 'result': True}])
1076
 
        new_iter = (x for x in [
 
674
        new_iter = iter([
1077
675
            None, {'test_id': 'foo-id', 'result': False}])
1078
676
 
1079
677
        class StubSA(SteppedStageAttempt):
1083
681
                return {'foo-id': {'title': 'foo-id'}}
1084
682
 
1085
683
        self.assertItemsEqual(
1086
 
            StubSA()._iter_test_results(old_iter, new_iter),
 
684
            StubSA._iter_test_results(old_iter, new_iter),
1087
685
            [('foo-id', True, False)])
1088
686
 
1089
687
    def test__iter_test_results_interleaved(self):
1090
688
        # Using a single iterator for both proves that they are interleaved.
1091
689
        # Otherwise, we'd get Result before declaration.
1092
 
        both_iter = (x for x in [
 
690
        both_iter = iter([
1093
691
            None, None,
1094
692
            {'test_id': 'foo-id', 'result': True},
1095
693
            {'test_id': 'foo-id', 'result': False},
1102
700
                return {'foo-id': {'title': 'foo-id'}}
1103
701
 
1104
702
        self.assertItemsEqual(
1105
 
            StubSA()._iter_test_results(both_iter, both_iter),
 
703
            StubSA._iter_test_results(both_iter, both_iter),
1106
704
            [('foo-id', True, False)])
1107
705
 
1108
706
    def test__iter_test_results_id_mismatch(self):
1109
 
        old_iter = (x for x in [
 
707
        old_iter = iter([
1110
708
            None, {'test_id': 'foo-id', 'result': True}])
1111
 
        new_iter = (x for x in [
 
709
        new_iter = iter([
1112
710
            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)
 
711
        with self.assertRaisesRegexp(ValueError, 'Test id mismatch.'):
 
712
            list(SteppedStageAttempt._iter_test_results(old_iter, new_iter))
1117
713
 
1118
714
    def test__iter_test_results_many(self):
1119
 
        old_iter = (x for x in [
 
715
        old_iter = iter([
1120
716
            None, {'test_id': 'foo-id', 'result': True},
1121
717
            None, {'test_id': 'bar-id', 'result': False},
1122
718
            ])
1123
 
        new_iter = (x for x in [
 
719
        new_iter = iter([
1124
720
            None, {'test_id': 'foo-id', 'result': False},
1125
721
            None, {'test_id': 'bar-id', 'result': False},
1126
722
            ])
1134
730
                    'bar-id': {'title': 'bar-id'},
1135
731
                }
1136
732
        self.assertItemsEqual(
1137
 
            StubSA()._iter_test_results(old_iter, new_iter),
 
733
            StubSA._iter_test_results(old_iter, new_iter),
1138
734
            [('foo-id', True, False), ('bar-id', False, False)])
1139
735
 
1140
736
    def test_iter_test_results(self):
1166
762
                [('test-1', True, False), ('test-2', False, True)])
1167
763
        le_mock.assert_called_once_with(error)
1168
764
 
1169
 
    def test_factory(self):
1170
 
 
1171
 
        class StubSA(SteppedStageAttempt):
1172
 
 
1173
 
            def __init__(self):
1174
 
                super(StubSA, self).__init__()
1175
 
 
1176
 
        self.assertIs(type(StubSA.factory(['a', 'b', 'c'], None)), StubSA)
1177
 
 
1178
 
    def test_get_test_info(self):
1179
 
 
1180
 
        class StubSA(SteppedStageAttempt):
1181
 
 
1182
 
            @staticmethod
1183
 
            def get_stage_info():
1184
 
                return [StageInfo('foo-id', 'Foo title'),
1185
 
                        StageInfo('bar-id', 'Bar title', report_on=False)]
1186
 
 
1187
 
        self.assertEqual(StubSA.get_test_info(), OrderedDict([
1188
 
            ('foo-id', {'title': 'Foo title', 'report_on': True}),
1189
 
            ('bar-id', {'title': 'Bar title', 'report_on': False})]))
1190
 
 
1191
 
 
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):
1207
 
 
1208
 
    def test_iter_steps(self):
1209
 
        client = FakeEnvJujuClient()
1210
 
        bootstrap = BootstrapAttempt()
1211
 
        boot_iter = iter_steps_validate_info(self, bootstrap, client)
1212
 
        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
1227
 
            self.assertEqual(boot_iter.next(), {'test_id': 'bootstrap'})
1228
 
        with patch_status(client, *statuses) as gs_mock:
1229
 
            self.assertEqual(boot_iter.next(),
1230
 
                             {'test_id': 'bootstrap', 'result': True})
1231
 
        self.assertEqual(2, gs_mock.call_count)
1232
 
 
1233
 
 
1234
 
class TestDestroyEnvironmentAttempt(JujuPyTestCase):
1235
 
 
1236
 
    def test_iter_steps(self):
1237
 
        client = FakeEnvJujuClient()
1238
 
        destroy_env = DestroyEnvironmentAttempt()
1239
 
        iterator = iter_steps_validate_info(self, destroy_env, client)
1240
 
        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'))
 
765
 
 
766
class FakeEnvJujuClient(EnvJujuClient):
 
767
 
 
768
    def __init__(self, name='steve'):
 
769
        super(FakeEnvJujuClient, self).__init__(
 
770
            SimpleEnvironment(name, {'type': 'fake'}), '1.2', '/jbin/juju')
 
771
 
 
772
    def wait_for_started(self, start=None):
 
773
        with patch('sys.stdout'):
 
774
            return super(FakeEnvJujuClient, self).wait_for_started(0.01,
 
775
                                                                   start=start)
 
776
 
 
777
    def wait_for_ha(self):
 
778
        with patch('sys.stdout'):
 
779
            return super(FakeEnvJujuClient, self).wait_for_ha(0.01)
 
780
 
 
781
    def juju(self, *args, **kwargs):
 
782
        # Suppress stdout for juju commands.
 
783
        with patch('sys.stdout'):
 
784
            return super(FakeEnvJujuClient, self).juju(*args, **kwargs)
 
785
 
 
786
 
 
787
class TestBootstrapAttempt(TestCase):
 
788
 
 
789
    def test_do_operation(self):
 
790
        client = FakeEnvJujuClient()
 
791
        bootstrap = BootstrapAttempt()
 
792
        with patch('subprocess.check_call') as mock_cc:
 
793
            bootstrap.do_operation(client)
 
794
        assert_juju_call(self, mock_cc, client, (
 
795
            'juju', '--show-log', 'bootstrap', '-e', 'steve',
 
796
            '--constraints', 'mem=2G'))
 
797
 
 
798
    def test_do_operation_exception(self):
 
799
        client = FakeEnvJujuClient()
 
800
        bootstrap = BootstrapAttempt()
 
801
        with patch('subprocess.check_call', side_effect=Exception
 
802
                   ) as mock_cc:
 
803
            with patch('logging.exception') as le_mock:
 
804
                bootstrap.do_operation(client)
 
805
        le_mock.assert_called_once()
 
806
        assert_juju_call(self, mock_cc, client, (
 
807
            'juju', '--show-log', 'bootstrap', '-e', 'steve',
 
808
            '--constraints', 'mem=2G'))
 
809
        output = yaml.safe_dump({
 
810
            'machines': {'0': {'agent-state': 'started'}},
 
811
            'services': {},
 
812
            })
 
813
        with patch('subprocess.check_output', return_value=output):
 
814
            with patch('logging.exception') as le_mock:
 
815
                self.assertFalse(bootstrap.get_result(client))
 
816
        le_mock.assert_called_once()
 
817
 
 
818
    def test_get_result_true(self):
 
819
        bootstrap = BootstrapAttempt()
 
820
        client = FakeEnvJujuClient()
 
821
        output = yaml.safe_dump({
 
822
            'machines': {'0': {'agent-state': 'started'}},
 
823
            'services': {},
 
824
            })
 
825
        with patch('subprocess.check_output', return_value=output):
 
826
            self.assertTrue(bootstrap.get_result(client))
 
827
 
 
828
    @patch('logging.error')
 
829
    def test_get_result_false(self, le_mock):
 
830
        bootstrap = BootstrapAttempt()
 
831
        client = FakeEnvJujuClient()
 
832
        output = yaml.safe_dump({
 
833
            'machines': {'0': {'agent-state': 'pending'}},
 
834
            'services': {},
 
835
            })
 
836
        with patch('subprocess.check_output', return_value=output):
 
837
            with patch('logging.exception') as le_mock:
 
838
                self.assertFalse(bootstrap.get_result(client))
 
839
        le_mock.assert_called_once()
 
840
 
 
841
 
 
842
class TestDestroyEnvironmentAttempt(TestCase):
 
843
 
 
844
    def test_iter_steps(self):
 
845
        client = FakeEnvJujuClient()
 
846
        destroy_env = DestroyEnvironmentAttempt()
 
847
        iterator = iter_steps_validate_info(self, destroy_env, client)
 
848
        self.assertEqual({'test_id': 'destroy-env'}, iterator.next())
 
849
        with patch('subprocess.call') as mock_cc:
 
850
            with patch.object(destroy_env, 'get_security_groups') as gsg_mock:
 
851
                self.assertEqual(iterator.next(), {
 
852
                    'test_id': 'destroy-env', 'result': True})
 
853
        gsg_mock.assert_called_once_with(client)
 
854
        assert_juju_call(self, mock_cc, client, (
 
855
            'timeout', '600.00s', 'juju', '--show-log', 'destroy-environment',
 
856
            'steve', '-y'))
1269
857
        self.assertEqual(iterator.next(), {'test_id': 'substrate-clean'})
1270
858
        with patch.object(destroy_env, 'check_security_groups') as csg_mock:
1271
859
            self.assertEqual(iterator.next(),
1275
863
    def test_iter_test_results(self):
1276
864
        client = FakeEnvJujuClient()
1277
865
        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))
 
866
        with patch('subprocess.check_call'):
 
867
            output = list(destroy_env.iter_test_results(client, client))
1282
868
        self.assertEqual(output, [
1283
869
            ('destroy-env', True, True), ('substrate-clean', True, True)])
1284
870
 
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
871
    @staticmethod
1335
872
    def get_aws_client():
1336
873
        client = FakeEnvJujuClient()
1340
877
    @staticmethod
1341
878
    def get_openstack_client():
1342
879
        client = FakeEnvJujuClient()
1343
 
        client.env.clouds = {'clouds': {'quxxx': {
1344
 
            'type': 'openstack', 'endpoint': 'qux',
1345
 
            }}}
1346
880
        client.env.config = get_os_config()
1347
 
        client.env.credentials = {'credentials': {'quxxx': {'creds': {
1348
 
            }}}}
1349
881
        return client
1350
882
 
1351
883
    def test_get_security_groups_aws(self):
1352
884
        client = self.get_aws_client()
1353
885
        destroy_env = DestroyEnvironmentAttempt()
1354
 
        status = {'machines': {
 
886
        yaml_instances = yaml.safe_dump({'machines': {
1355
887
            'foo': {'instance-id': 'foo-id'},
1356
 
        }}
 
888
            }})
1357
889
        aws_instances = [
1358
890
            MagicMock(instances=[MagicMock(groups=[
1359
891
                SecurityGroup(id='foo', name='bar'),
1363
895
                SecurityGroup(id='quxx-id', name='quxx'),
1364
896
                ])]),
1365
897
        ]
1366
 
        aws_client = MagicMock()
1367
 
        aws_client.get_all_instances.return_value = aws_instances
1368
898
        with patch(
1369
 
                'substrate.ec2.connect_to_region') as gec_mock:
1370
 
            with patch_status(client, status):
 
899
                'substrate.AWSAccount.get_ec2_connection') as gec_mock:
 
900
            with patch('subprocess.check_output', return_value=yaml_instances):
1371
901
                gai_mock = gec_mock.return_value.get_all_instances
1372
902
                gai_mock.return_value = aws_instances
1373
903
                self.assertEqual(destroy_env.get_security_groups(client), {
1374
904
                    'baz': 'qux', 'foo': 'bar', 'quxx-id': 'quxx'
1375
905
                    })
1376
 
        self.assert_ec2_connection_call(gec_mock)
 
906
        gec_mock.assert_called_once_with()
1377
907
        gai_mock.assert_called_once_with(instance_ids=['foo-id'])
1378
908
 
1379
909
    def test_get_security_groups_openstack(self):
1380
910
        client = self.get_openstack_client()
1381
911
        destroy_env = DestroyEnvironmentAttempt()
1382
 
        status = {'machines': {
 
912
        yaml_instances = yaml.safe_dump({'machines': {
1383
913
            'foo': {'instance-id': 'bar-id'},
1384
914
            'bar': {'instance-id': 'baz-qux-id'},
1385
 
        }}
 
915
            }})
1386
916
        os_instances = [
1387
917
            make_os_security_group_instance(['bar']),
1388
918
            make_os_security_group_instance(['baz', 'qux']),
1393
923
            os_client.servers.list.return_value = os_instances
1394
924
            security_groups = make_os_security_groups(['bar', 'baz', 'qux'])
1395
925
            os_client.security_groups.list.return_value = security_groups
1396
 
            with patch_status(client, status):
 
926
            with patch('subprocess.check_output', return_value=yaml_instances):
1397
927
                self.assertEqual(destroy_env.get_security_groups(client), {
1398
928
                    'baz-id': 'baz', 'bar-id': 'bar', 'qux-id': 'qux'
1399
929
                    })
1409
939
    def test_check_security_groups_match(self):
1410
940
        client = self.get_aws_client()
1411
941
        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:
 
942
        output = (
 
943
            'GROUP\tfoo-id\t\tfoo-group\n'
 
944
            'GROUP\tbaz-id\t\tbaz-group\n'
 
945
        )
 
946
        with patch('subprocess.check_output', return_value=output) as co_mock:
1417
947
            with self.assertRaisesRegexp(
1418
948
                Exception, (
1419
949
                    r'Security group\(s\) not cleaned up: foo-group.')):
1421
951
                               lambda x: iter([None])):
1422
952
                        destroy_env.check_security_groups(
1423
953
                            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
 
954
        env = AWSAccount.from_config(client.env.config).get_environ()
 
955
        co_mock.assert_called_once_with(
 
956
            ['euca-describe-groups', '--filter', 'description=juju group'],
 
957
            env=env)
1434
958
 
1435
959
    def test_check_security_groups_no_match(self):
1436
960
        client = self.get_aws_client()
1437
961
        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:
 
962
        output = (
 
963
            'GROUP\tfoo-id\t\tfoo-group\n'
 
964
            'GROUP\tbaz-id\t\tbaz-group\n'
 
965
        )
 
966
        with patch('subprocess.check_output', return_value=output) as co_mock:
1443
967
                destroy_env.check_security_groups(
1444
968
                    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')
 
969
        env = AWSAccount.from_config(client.env.config).get_environ()
 
970
        co_mock.assert_called_once_with(
 
971
            ['euca-describe-groups', '--filter', 'description=juju group'],
 
972
            env=env)
1453
973
 
1454
974
    def test_check_security_groups_non_aws(self):
1455
975
        client = FakeEnvJujuClient()
1460
980
        self.assertEqual(co_mock.call_count, 0)
1461
981
 
1462
982
 
1463
 
class TestEnsureAvailabilityAttempt(JujuPyTestCase):
1464
 
 
1465
 
    def test_iter_steps(self):
1466
 
        client = FakeEnvJujuClient()
1467
 
        controller_client = client.get_controller_client()
1468
 
        ensure_av = EnsureAvailabilityAttempt()
1469
 
        ensure_iter = iter_steps_validate_info(self, ensure_av, client)
1470
 
        self.assertEqual(ensure_iter.next(), {
1471
 
            'test_id': 'ensure-availability-n3'})
1472
 
        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 = {
1483
 
            'machines': {
1484
 
                '0': {'controller-member-status': 'has-vote'},
1485
 
                '1': {'controller-member-status': 'has-vote'},
1486
 
                '2': {'controller-member-status': 'has-vote'},
1487
 
                },
1488
 
            'applications': {},
1489
 
        }
1490
 
        with patch_status(controller_client, status) as gs_mock:
1491
 
            self.assertEqual(ensure_iter.next(), {
1492
 
                'test_id': 'ensure-availability-n3', 'result': True})
1493
 
        gs_mock.assert_called_once_with(controller=True)
1494
 
 
1495
 
    def test_iter_steps_failure(self):
1496
 
        client = FakeEnvJujuClient()
1497
 
        ensure_av = EnsureAvailabilityAttempt()
1498
 
        ensure_iter = iter_steps_validate_info(self, ensure_av, client)
1499
 
        ensure_iter.next()
1500
 
        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 = {
1506
 
            'machines': {
1507
 
                '0': {'state-server-member-status': 'has-vote'},
1508
 
                '1': {'state-server-member-status': 'has-vote'},
1509
 
                },
1510
 
            'applications': {},
1511
 
        }
1512
 
        with patch_status(controller_client, status) as gs_mock:
 
983
class TestEnsureAvailabilityAttempt(TestCase):
 
984
 
 
985
    def setUp(self):
 
986
        patcher = patch('jujupy.pause')
 
987
        self.addCleanup(patcher.stop)
 
988
        self.pause_mock = patcher.start()
 
989
 
 
990
    def test__operation(self):
 
991
        client = FakeEnvJujuClient()
 
992
        ensure_av = EnsureAvailabilityAttempt()
 
993
        with patch('subprocess.check_call') as mock_cc:
 
994
            ensure_av._operation(client)
 
995
        assert_juju_call(self, mock_cc, client, (
 
996
            'juju', '--show-log', 'ensure-availability', '-e', 'steve', '-n',
 
997
            '3'))
 
998
 
 
999
    def test__result_true(self):
 
1000
        ensure_av = EnsureAvailabilityAttempt()
 
1001
        client = FakeEnvJujuClient()
 
1002
        output = yaml.safe_dump({
 
1003
            'machines': {
 
1004
                '0': {'state-server-member-status': 'has-vote'},
 
1005
                '1': {'state-server-member-status': 'has-vote'},
 
1006
                '2': {'state-server-member-status': 'has-vote'},
 
1007
                },
 
1008
            'services': {},
 
1009
            })
 
1010
        with patch('subprocess.check_output', return_value=output):
 
1011
            self.assertTrue(ensure_av.get_result(client))
 
1012
 
 
1013
    def test__result_false(self):
 
1014
        ensure_av = EnsureAvailabilityAttempt()
 
1015
        client = FakeEnvJujuClient()
 
1016
        output = yaml.safe_dump({
 
1017
            'machines': {
 
1018
                '0': {'state-server-member-status': 'has-vote'},
 
1019
                '1': {'state-server-member-status': 'has-vote'},
 
1020
                },
 
1021
            'services': {},
 
1022
            })
 
1023
        with patch('subprocess.check_output', return_value=output):
1513
1024
            with self.assertRaisesRegexp(
1514
1025
                    Exception, 'Timed out waiting for voting to be enabled.'):
1515
 
                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):
 
1026
                ensure_av._result(client)
 
1027
 
 
1028
 
 
1029
class TestDeployManyAttempt(TestCase):
 
1030
 
 
1031
    def predict_add_machine_calls(self, deploy_many):
1522
1032
        for host in range(1, deploy_many.host_count + 1):
1523
1033
            for container in range(deploy_many.container_count):
1524
 
                target = '{}:{}'.format(machine_type, host)
 
1034
                target = 'lxc:{}'.format(host)
1525
1035
                service = 'ubuntu{}x{}'.format(host, container)
1526
 
                yield ('juju', '--show-log', 'deploy', '-m', 'steve:steve',
1527
 
                       'ubuntu', service, '--to', target, '--series', 'angsty')
1528
 
 
1529
 
    def predict_remove_machine_calls(self, deploy_many):
1530
 
        total_guests = deploy_many.host_count * deploy_many.container_count
1531
 
        for guest in range(100, total_guests + 100):
1532
 
            yield ('juju', '--show-log', 'remove-machine', '-m', 'steve:steve',
1533
 
                   '--force', str(guest))
 
1036
                yield ('juju', '--show-log', 'deploy', '-e', 'steve', '--to',
 
1037
                       target, 'ubuntu', service)
1534
1038
 
1535
1039
    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):
 
1040
        client = FakeEnvJujuClient()
1553
1041
        deploy_many = DeployManyAttempt(9, 11)
1554
1042
        deploy_iter = iter_steps_validate_info(self, deploy_many, client)
1555
1043
        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):
 
1044
        status = yaml.safe_dump({
 
1045
            'machines': {'0': {'agent-state': 'started'}},
 
1046
            'services': {},
 
1047
            })
 
1048
        with patch('subprocess.check_output', return_value=status):
1561
1049
            with patch('subprocess.check_call') as mock_cc:
1562
1050
                self.assertEqual(deploy_iter.next(),
1563
1051
                                 {'test_id': 'add-machine-many'})
1564
1052
        for index in range(deploy_many.host_count):
1565
1053
            assert_juju_call(self, mock_cc, client, (
1566
 
                'juju', '--show-log', 'add-machine',
1567
 
                '-m', 'steve:steve'), index)
 
1054
                'juju', '--show-log', 'add-machine', '-e', 'steve'), index)
1568
1055
 
1569
 
        status = {
1570
 
            'machines': dict((str(x), dict(machine_started))
 
1056
        status = yaml.safe_dump({
 
1057
            'machines': dict((str(x), {'agent-state': 'started'})
1571
1058
                             for x in range(deploy_many.host_count + 1)),
1572
 
            'applications': {},
1573
 
        }
1574
 
        with patch_status(client, status):
 
1059
            'services': {},
 
1060
            })
 
1061
        with patch('subprocess.check_output', return_value=status):
1575
1062
                self.assertEqual(
1576
1063
                    deploy_iter.next(),
1577
1064
                    {'test_id': 'add-machine-many', 'result': True})
1579
1066
                         {'test_id': 'ensure-machines'})
1580
1067
        self.assertEqual(deploy_iter.next(),
1581
1068
                         {'test_id': 'ensure-machines'})
1582
 
        with patch_status(client, status):
 
1069
        with patch('subprocess.check_output', return_value=status):
1583
1070
            self.assertEqual(deploy_iter.next(),
1584
1071
                             {'test_id': 'ensure-machines', 'result': True})
1585
1072
        self.assertEqual(deploy_iter.next(),
1588
1075
            self.assertEqual(deploy_iter.next(),
1589
1076
                             {'test_id': 'deploy-many'})
1590
1077
 
1591
 
        calls = self.predict_add_machine_calls(deploy_many, machine_type)
 
1078
        calls = self.predict_add_machine_calls(deploy_many)
1592
1079
        for num, args in enumerate(calls):
1593
1080
            assert_juju_call(self, mock_cc, client, args, num)
1594
 
        service_names = []
1595
 
        for host in range(1, deploy_many.host_count + 1):
1596
 
            for container in range(deploy_many.container_count):
1597
 
                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):
 
1081
        with patch('subprocess.check_output', return_value=status):
1611
1082
            self.assertEqual(deploy_iter.next(),
1612
1083
                             {'test_id': 'deploy-many', 'result': True})
1613
1084
 
1614
 
        self.assertEqual(deploy_iter.next(),
1615
 
                         {'test_id': 'remove-machine-many-container'})
1616
 
        with patch_status(client, status):
1617
 
            with patch('subprocess.check_call') as mock_cc:
1618
 
                self.assertEqual(
1619
 
                    deploy_iter.next(),
1620
 
                    {'test_id': 'remove-machine-many-container'})
1621
 
        calls = self.predict_remove_machine_calls(deploy_many)
1622
 
        for num, args in enumerate(calls):
1623
 
            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:
1629
 
            self.assertEqual(
1630
 
                deploy_iter.next(),
1631
 
                {'test_id': 'remove-machine-many-container', 'result': True})
1632
 
        self.assertEqual(2, status_mock.call_count)
1633
 
        self.assertEqual(deploy_iter.next(), {
1634
 
            'test_id': 'remove-machine-many-instance'})
1635
 
        with patch('subprocess.check_call') as mock_cc:
1636
 
            self.assertEqual(
1637
 
                deploy_iter.next(),
1638
 
                {'test_id': 'remove-machine-many-instance'})
1639
 
        for num in range(deploy_many.host_count):
1640
 
            assert_juju_call(self, mock_cc, client, (
1641
 
                'juju', '--show-log', 'remove-machine', '-m', 'steve:steve',
1642
 
                str(num + 1)), num)
1643
 
 
1644
 
        statuses = [
1645
 
            {'machines': {'1': dict(machine_started)}, 'applications': {}},
1646
 
            {'machines': {}, 'applications': {}},
1647
 
        ]
1648
 
        with patch_status(client, *statuses) as status_mock:
1649
 
            self.assertEqual(
1650
 
                deploy_iter.next(),
1651
 
                {'test_id': 'remove-machine-many-instance', 'result': True})
1652
 
        self.assertEqual(2, status_mock.call_count)
1653
 
 
1654
 
    def test_iter_step_failure(self):
 
1085
    @patch('logging.error')
 
1086
    def test_iter_step_failure(self, le_mock):
1655
1087
        deploy_many = DeployManyAttempt()
1656
1088
        client = FakeEnvJujuClient()
1657
 
        client.env.config['default-series'] = 'angsty'
1658
1089
        deploy_iter = iter_steps_validate_info(self, deploy_many, client)
1659
1090
        self.assertEqual(deploy_iter.next(), {'test_id': 'add-machine-many'})
1660
 
        status = {
 
1091
        status = yaml.safe_dump({
1661
1092
            'machines': {'0': {'agent-state': 'started'}},
1662
 
            'applications': {},
1663
 
        }
1664
 
        with patch_status(client, status):
 
1093
            'services': {},
 
1094
            })
 
1095
        with patch('subprocess.check_output', return_value=status):
1665
1096
            with patch('subprocess.check_call') as mock_cc:
1666
1097
                self.assertEqual(deploy_iter.next(),
1667
1098
                                 {'test_id': 'add-machine-many'})
1668
1099
        for index in range(deploy_many.host_count):
1669
1100
            assert_juju_call(self, mock_cc, client, (
1670
 
                'juju', '--show-log', 'add-machine',
1671
 
                '-m', 'steve:steve'), index)
 
1101
                'juju', '--show-log', 'add-machine', '-e', 'steve'), index)
1672
1102
 
1673
 
        status = {
 
1103
        status = yaml.safe_dump({
1674
1104
            'machines': dict((str(x), {'agent-state': 'started'})
1675
1105
                             for x in range(deploy_many.host_count + 1)),
1676
 
            'applications': {},
1677
 
        }
1678
 
        with patch_status(client, status):
 
1106
            'services': {},
 
1107
            })
 
1108
        with patch('subprocess.check_output', return_value=status):
1679
1109
                self.assertEqual(
1680
1110
                    deploy_iter.next(),
1681
1111
                    {'test_id': 'add-machine-many', 'result': True})
1683
1113
                         {'test_id': 'ensure-machines'})
1684
1114
        self.assertEqual(deploy_iter.next(),
1685
1115
                         {'test_id': 'ensure-machines'})
1686
 
        with patch_status(client, status):
 
1116
        with patch('subprocess.check_output', return_value=status):
1687
1117
            self.assertEqual(deploy_iter.next(),
1688
1118
                             {'test_id': 'ensure-machines', 'result': True})
1689
1119
        self.assertEqual(deploy_iter.next(),
1691
1121
        with patch('subprocess.check_call') as mock_cc:
1692
1122
            self.assertEqual(deploy_iter.next(),
1693
1123
                             {'test_id': 'deploy-many'})
1694
 
        status = {
 
1124
        output = yaml.safe_dump({
1695
1125
            'machines': {
1696
1126
                '0': {'agent-state': 'pending'},
1697
1127
                },
1698
 
            'applications': {},
1699
 
        }
1700
 
        with patch_status(client, status):
 
1128
            'services': {},
 
1129
            })
 
1130
        with patch('subprocess.check_output', return_value=output):
1701
1131
            with self.assertRaisesRegexp(
1702
1132
                    Exception,
1703
1133
                    'Timed out waiting for agents to start in steve.'):
1704
1134
                deploy_iter.next()
1705
1135
 
1706
 
    def test_iter_step_add_machine_failure(self):
 
1136
    @patch('logging.error')
 
1137
    def test_iter_step_add_machine_failure(self, le_mock):
1707
1138
        deploy_many = DeployManyAttempt()
1708
1139
        client = FakeEnvJujuClient()
1709
 
        client.env.config['default-series'] = 'angsty'
1710
1140
        deploy_iter = iter_steps_validate_info(self, deploy_many, client)
1711
1141
        self.assertEqual(deploy_iter.next(), {'test_id': 'add-machine-many'})
1712
 
        status = {
 
1142
        status = yaml.safe_dump({
1713
1143
            'machines': {'0': {'agent-state': 'started'}},
1714
 
            'applications': {},
1715
 
        }
1716
 
        with patch_status(client, status) as gs_mock:
 
1144
            'services': {},
 
1145
            })
 
1146
        with patch('subprocess.check_output', return_value=status):
1717
1147
            with patch('subprocess.check_call') as mock_cc:
1718
1148
                self.assertEqual(deploy_iter.next(),
1719
1149
                                 {'test_id': 'add-machine-many'})
1720
1150
        for index in range(deploy_many.host_count):
1721
1151
            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()
 
1152
                'juju', '--show-log', 'add-machine', '-e', 'steve'), index)
1725
1153
 
1726
 
        status = {
 
1154
        status = yaml.safe_dump({
1727
1155
            'machines': dict((str(x), {'agent-state': 'pending'})
1728
1156
                             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})
 
1157
            'services': {},
 
1158
            })
 
1159
        with patch('subprocess.check_output', return_value=status):
 
1160
                self.assertEqual(
 
1161
                    deploy_iter.next(),
 
1162
                    {'test_id': 'add-machine-many', 'result': False})
1734
1163
        self.assertEqual(deploy_iter.next(),
1735
1164
                         {'test_id': 'ensure-machines'})
 
1165
        status = yaml.safe_dump({
 
1166
            'machines': dict((str(x), {'agent-state': 'started'})
 
1167
                             for x in range(deploy_many.host_count + 1)),
 
1168
            'services': {},
 
1169
            })
1736
1170
        with patch('subprocess.check_call') as mock_cc:
1737
1171
            self.assertEqual({'test_id': 'ensure-machines'},
1738
1172
                             deploy_iter.next())
1739
1173
        for x in range(deploy_many.host_count):
1740
1174
            assert_juju_call(self, mock_cc, client, (
1741
 
                'juju', '--show-log', 'remove-machine', '-m', 'steve:steve',
 
1175
                'juju', '--show-log', 'destroy-machine', '-e', 'steve',
1742
1176
                '--force', str((x + 1))), x * 2)
1743
1177
            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:
 
1178
                'juju', '--show-log', 'add-machine', '-e', 'steve'), x * 2 + 1)
 
1179
        with patch('subprocess.check_output', return_value=status):
1753
1180
            self.assertEqual({'test_id': 'ensure-machines', 'result': True},
1754
1181
                             deploy_iter.next())
1755
1182
        self.assertEqual({'test_id': 'deploy-many'}, deploy_iter.next())
1756
1183
        with patch('subprocess.check_call') as mock_cc:
1757
1184
            self.assertEqual({'test_id': 'deploy-many'}, deploy_iter.next())
1758
 
        calls = self.predict_add_machine_calls(deploy_many, LXD_MACHINE)
 
1185
        calls = self.predict_add_machine_calls(deploy_many)
1759
1186
        for num, args in enumerate(calls):
1760
1187
            assert_juju_call(self, mock_cc, client, args, num)
1761
1188
 
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):
 
1189
 
 
1190
class TestDeployManyFactory(TestCase):
 
1191
 
 
1192
    def test_get_test_info(self):
 
1193
        self.assertEqual(DeployManyFactory(2, 2).get_test_info(),
 
1194
                         DeployManyAttempt.get_test_info())
 
1195
 
 
1196
    def test_call(self):
 
1197
        factory = DeployManyFactory(4, 2)
 
1198
        self.assertEqual(factory(), DeployManyAttempt(4, 2))
 
1199
 
 
1200
 
 
1201
class TestBackupRestoreAttempt(TestCase):
1804
1202
 
1805
1203
    def test_get_test_info(self):
1806
1204
        self.assertEqual(
1810
1208
    def test_iter_steps(self):
1811
1209
        br_attempt = BackupRestoreAttempt()
1812
1210
        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()
 
1211
        client.env = get_aws_env()
1819
1212
        environ = dict(os.environ)
1820
1213
        environ.update(get_euca_env(client.env.config))
1821
1214
 
1822
1215
        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)
 
1216
            if args == (['juju', 'backup'],):
 
1217
                return 'juju-backup-24.tgz'
 
1218
            if args == (('juju', '--show-log', 'status', '-e', 'baz'),):
 
1219
                return yaml.safe_dump({
 
1220
                    'machines': {'0': {
 
1221
                        'instance-id': 'asdf',
 
1222
                        'dns-name': '128.100.100.128',
 
1223
                        }}
 
1224
                    })
1827
1225
            self.assertEqual([], args)
1828
 
        initial_status = {
1829
 
            'machines': {'0': {
1830
 
                'instance-id': 'asdf',
1831
 
                'dns-name': '128.100.100.128',
1832
 
                }}
1833
 
        }
1834
1226
        iterator = iter_steps_validate_info(self, br_attempt, client)
1835
1227
        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)
 
1228
        with patch('subprocess.check_output',
 
1229
                   side_effect=check_output) as co_mock:
 
1230
            with patch('subprocess.check_call') as cc_mock:
 
1231
                with patch('sys.stdout'):
 
1232
                    self.assertEqual(
 
1233
                        iterator.next(),
 
1234
                        {'test_id': 'back-up-restore'})
 
1235
        assert_juju_call(self, co_mock, client, ['juju', 'backup'], 0)
1854
1236
        self.assertEqual(
1855
1237
            cc_mock.mock_calls[0],
1856
1238
            call(['euca-terminate-instances', 'asdf'], env=environ))
1857
 
        with patch('deploy_stack.wait_for_port'):
1858
 
            with patch('deploy_stack.print_now', autospec=True) as pn_mock:
 
1239
        self.assertEqual(iterator.next(), {'test_id': 'back-up-restore'})
 
1240
        with patch('subprocess.check_call') as cc_mock:
 
1241
            with patch('sys.stdout'):
1859
1242
                self.assertEqual(iterator.next(),
1860
1243
                                 {'test_id': 'back-up-restore'})
1861
 
        pn_mock.assert_called_with('Closed.')
1862
 
        with patch.object(controller_client, 'restore_backup') as rb_mock:
1863
 
            self.assertEqual(iterator.next(), {'test_id': 'back-up-restore'})
1864
 
        rb_mock.assert_called_once_with(
1865
 
            os.path.abspath('juju-backup-24.tgz'))
1866
 
        with patch('os.unlink') as ul_mock:
1867
 
            self.assertEqual(iterator.next(),
1868
 
                             {'test_id': 'back-up-restore'})
1869
 
        ul_mock.assert_called_once_with(os.path.abspath('juju-backup-24.tgz'))
1870
 
        final_status = {
 
1244
        assert_juju_call(
 
1245
            self, cc_mock, client, (
 
1246
                'juju', '--show-log', 'restore', '-e', 'baz',
 
1247
                os.path.abspath('juju-backup-24.tgz')))
 
1248
        output = yaml.safe_dump({
1871
1249
            'machines': {
1872
1250
                '0': {'agent-state': 'started'},
1873
1251
                },
1874
 
            'applications': {},
1875
 
        }
1876
 
        with patch_status(controller_client, final_status) as gs_mock:
 
1252
            'services': {},
 
1253
            })
 
1254
        with patch('subprocess.check_output', return_value=output) as co_mock:
1877
1255
            self.assertEqual(iterator.next(),
1878
1256
                             {'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):
1926
 
 
1927
 
    def test_factory(self):
1928
 
        uj_attempt = PrepareUpgradeJujuAttempt.factory(
1929
 
            ['a', 'b', 'c'], None)
1930
 
        self.assertIs(type(uj_attempt), PrepareUpgradeJujuAttempt)
1931
 
        self.assertEqual(uj_attempt.bootstrap_paths, {'b': 'a', 'c': 'b'})
1932
 
 
1933
 
    def test_factory_empty(self):
1934
 
        with self.assertRaisesRegexp(
1935
 
                ValueError, 'Not enough paths for upgrade.'):
1936
 
            PrepareUpgradeJujuAttempt.factory(['a'], None)
1937
 
        with self.assertRaisesRegexp(
1938
 
                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)
1955
 
 
1956
 
    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(
1960
 
            {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 = {
1980
 
            'machines': {'0': {'agent-state': 'started'}},
1981
 
            'applications': {},
1982
 
        }
1983
 
        with patch_status(None, b_status):
1984
 
            self.assertEqual(
1985
 
                puj_iterator.next(),
1986
 
                {'test_id': 'prepare-upgrade-juju', 'result': True})
1987
 
 
1988
 
    def test_iter_steps_no_previous_client(self):
1989
 
        uj_attempt = PrepareUpgradeJujuAttempt({})
1990
 
        client = FakeEnvJujuClient(full_path='/present/juju')
1991
 
        uj_iterator = uj_attempt.iter_steps(client)
1992
 
        with self.assertRaises(CannotUpgradeToClient) as exc_context:
1993
 
            uj_iterator.next()
1994
 
        self.assertIs(exc_context.exception.client, client)
1995
 
 
1996
 
 
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):
2024
 
 
2025
 
    def assert_hook(self, hook_path, content):
2026
 
        with open(hook_path) as hook_file:
2027
 
            self.assertEqual(hook_file.read(), content)
2028
 
            mode = os.fstat(hook_file.fileno()).st_mode
2029
 
        self.assertEqual(0o755, mode & 0o777)
2030
 
 
2031
 
    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')
2042
 
        uc_attempt = UpgradeCharmAttempt()
2043
 
        uc_iterator = iter_steps_validate_info(self, uc_attempt, client)
2044
 
        self.assertEqual(uc_iterator.next(),
2045
 
                         {'test_id': 'prepare-upgrade-charm'})
2046
 
        temp_repository = mkdtemp()
2047
 
        with patch('utility.mkdtemp', return_value=temp_repository):
2048
 
            with patch('subprocess.check_call') as cc_mock:
2049
 
                self.assertEqual(uc_iterator.next(),
2050
 
                                 {'test_id': 'prepare-upgrade-charm'})
2051
 
        metadata_path = os.path.join(
2052
 
            temp_repository, 'trusty', 'mycharm', 'metadata.yaml')
2053
 
        with open(metadata_path) as metadata_file:
2054
 
            metadata = yaml.safe_load(metadata_file)
2055
 
        self.assertEqual(metadata['name'], 'mycharm')
2056
 
        self.assertIn('summary', metadata)
2057
 
        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 = {
2073
 
            'machines': {'0': {'agent-state': 'started'}},
2074
 
            'applications': {},
2075
 
        }
2076
 
        with patch_status(client, status):
2077
 
            self.assertEqual(uc_iterator.next(),
2078
 
                             {'test_id': 'prepare-upgrade-charm'})
2079
 
        hooks_path = os.path.join(temp_repository, 'trusty', 'mycharm',
2080
 
                                  'hooks')
2081
 
        upgrade_path = os.path.join(hooks_path, 'upgrade-charm')
2082
 
        config_changed = os.path.join(hooks_path, 'config-changed')
2083
 
        self.assertFalse(os.path.exists(config_changed))
2084
 
        self.assertFalse(os.path.exists(upgrade_path))
2085
 
        self.assertEqual(
2086
 
            uc_iterator.next(),
2087
 
            {'test_id': 'prepare-upgrade-charm', 'result': True})
2088
 
        self.assert_hook(upgrade_path, dedent("""\
2089
 
            #!/bin/sh
2090
 
            open-port 42
2091
 
            """))
2092
 
        self.assert_hook(config_changed, dedent("""\
2093
 
            #!/bin/sh
2094
 
            open-port 34
2095
 
            """))
2096
 
        self.assertEqual(uc_iterator.next(), {'test_id': 'upgrade-charm'})
2097
 
        with patch('subprocess.check_call') as cc_mock:
2098
 
            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 = {
2109
 
            'machines': {'0': {'agent-state': 'started'}},
2110
 
            'applications': {'mycharm': {'units': {'mycharm/0': {
2111
 
                'open-ports': ['42/tcp', '34/tcp'],
2112
 
                }}}},
2113
 
        }
2114
 
        with patch_status(client, status):
2115
 
            self.assertEqual(
2116
 
                uc_iterator.next(),
2117
 
                {'test_id': 'upgrade-charm', 'result': True})
 
1257
        assert_juju_call(self, co_mock, client, (
 
1258
            'juju', '--show-log', 'status', '-e', 'baz'), assign_stderr=True)
2118
1259
 
2119
1260
 
2120
1261
class TestMaybeWriteJson(TestCase):
2140
1281
            self.assertEqual(temp_file.read(), expected)
2141
1282
 
2142
1283
 
2143
 
class TestMakeSubstrate(JujuPyTestCase):
 
1284
class TestMakeSubstrate(TestCase):
2144
1285
 
2145
 
    def test_make_substrate_manager_no_support(self):
2146
 
        client = EnvJujuClient(JujuData('foo', {'type': 'foo'}),
 
1286
    def test_make_substrate_no_support(self):
 
1287
        client = EnvJujuClient(SimpleEnvironment('foo', {'type': 'foo'}),
2147
1288
                               '', '')
2148
 
        client.env.credentials = {'credentials': {'foo': {'creds': {}}}}
2149
 
        with make_substrate_manager(client, []) as substrate:
2150
 
            self.assertIs(substrate, None)
 
1289
        self.assertIs(make_substrate(client, []), None)
2151
1290
 
2152
1291
    def test_make_substrate_no_requirements(self):
2153
 
        client = EnvJujuClient(get_aws_juju_data(), '', '')
2154
 
        client.env.credentials = {'credentials': {'aws': {'creds': {}}}}
2155
 
        with make_substrate_manager(client, []) as substrate:
2156
 
            self.assertIs(type(substrate), AWSAccount)
 
1292
        client = EnvJujuClient(get_aws_env(), '', '')
 
1293
        self.assertIs(type(make_substrate(client, [])), AWSAccount)
2157
1294
 
2158
 
    def test_make_substrate_manager_unsatisifed_requirements(self):
2159
 
        client = EnvJujuClient(get_aws_juju_data(), '', '')
2160
 
        client.env.credentials = {'credentials': {'aws': {'creds': {}}}}
2161
 
        with make_substrate_manager(client, ['foo']) as substrate:
2162
 
            self.assertIs(substrate, None)
2163
 
        with make_substrate_manager(
2164
 
                client, ['iter_security_groups', 'foo']) as substrate:
2165
 
            self.assertIs(substrate, None)
 
1295
    def test_make_substrate_unsatisifed_requirements(self):
 
1296
        client = EnvJujuClient(get_aws_env(), '', '')
 
1297
        self.assertIs(make_substrate(client, ['foo']), None)
 
1298
        self.assertIs(make_substrate(client, ['iter_security_groups', 'foo']),
 
1299
                      None)
2166
1300
 
2167
1301
    def test_make_substrate_satisfied_requirements(self):
2168
 
        client = EnvJujuClient(get_aws_juju_data(), '', '')
2169
 
        client.env.credentials = {'credentials': {'aws': {'creds': {}}}}
2170
 
        with make_substrate_manager(
2171
 
                client, ['iter_security_groups']) as substrate:
2172
 
            self.assertIs(type(substrate), AWSAccount)
2173
 
        with make_substrate_manager(
2174
 
                client, ['iter_security_groups',
2175
 
                         'iter_instance_security_groups']
2176
 
                ) as substrate:
2177
 
            self.assertIs(type(substrate), AWSAccount)
2178
 
 
2179
 
 
2180
 
class TestStageInfo(TestCase):
2181
 
 
2182
 
    def test_ctor(self):
2183
 
        si = StageInfo('foo-id', 'Foo title')
2184
 
        self.assertEqual(si.stage_id, 'foo-id')
2185
 
        self.assertEqual(si.title, 'Foo title')
2186
 
        self.assertEqual(si.report_on, True)
2187
 
 
2188
 
        si = StageInfo('foo-id', 'Foo title', False)
2189
 
        self.assertEqual(si.report_on, False)
2190
 
 
2191
 
    def test_as_tuple(self):
2192
 
        si = StageInfo('foo-id', 'Foo title')
2193
 
        self.assertEqual(
2194
 
            si.as_tuple(),
2195
 
            ('foo-id', {'title': 'Foo title', 'report_on': True}))
2196
 
        si = StageInfo('bar-id', 'Bar title', False)
2197
 
        self.assertEqual(
2198
 
            si.as_tuple(),
2199
 
            ('bar-id', {'title': 'Bar title', 'report_on': False}))
2200
 
 
2201
 
    def test_as_result(self):
2202
 
        si = StageInfo('foo-id', 'Foo title')
2203
 
        self.assertEqual(si.as_result(), {'test_id': 'foo-id'})
2204
 
        self.assertEqual(si.as_result(True),
2205
 
                         {'test_id': 'foo-id', 'result': True})
2206
 
        self.assertEqual(si.as_result(False),
2207
 
                         {'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)
 
1302
        client = EnvJujuClient(get_aws_env(), '', '')
 
1303
        self.assertIs(type(make_substrate(client, ['iter_security_groups'])),
 
1304
                      AWSAccount)
 
1305
        self.assertIs(type(make_substrate(client, [
 
1306
            'iter_security_groups', 'iter_instance_security_groups'])),
 
1307
            AWSAccount)