~hduran-8/juju-ci-tools/add_storage_ci_tests

19.1.30 by Aaron Bentley
Add tests to jujupy
1
__metaclass__ = type
2
966.1.12 by John George
Verify subordinate units are started in wait_for_subordinate_units and add the reporter.
3
from collections import defaultdict
372.1.3 by Aaron Bentley
until_timeout yields remaining seconds
4
from contextlib import contextmanager
751.1.1 by Aaron Bentley
Give equal time to old and new clients in DeployManyAttempt
5
from datetime import (
6
    datetime,
7
    timedelta,
953.3.9 by Nate Finch
more code review changes
8
)
112.1.4 by Aaron Bentley
Read config, use it to determine whether provider is local.
9
import os
10
import shutil
751.3.1 by Martin Packman
Use dots rather than repeated lines when waiting for status changes
11
import StringIO
139.1.2 by Aaron Bentley
Write stderr to a temp file.
12
import subprocess
112.1.4 by Aaron Bentley
Read config, use it to determine whether provider is local.
13
import tempfile
34.1.1 by Aaron Bentley
Flesh out JujuClientDevel tests.
14
from textwrap import dedent
19.1.30 by Aaron Bentley
Add tests to jujupy
15
from unittest import TestCase
16
112.1.5 by Aaron Bentley
Use config to determine whether upgrades are local.
17
from mock import (
663.1.2 by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests.
18
    call,
112.1.5 by Aaron Bentley
Use config to determine whether upgrades are local.
19
    MagicMock,
20
    patch,
21
)
112.1.4 by Aaron Bentley
Read config, use it to determine whether provider is local.
22
import yaml
23
663.1.2 by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests.
24
from jujuconfig import (
25
    get_environments_path,
26
    get_jenv_path,
796.4.1 by Aaron Bentley
Handle missing environments more cleanly.
27
    NoSuchEnvironment,
953.3.9 by Nate Finch
more code review changes
28
)
19.1.32 by Aaron Bentley
Test until_timeout, making it a class to enable patching.
29
from jujupy import (
195 by Curtis Hovey
Fix tests. update client1.17 rules to try sudo for lower revnos.
30
    CannotConnectEnv,
19.1.33 by Aaron Bentley
Add JujuClient tests.
31
    Environment,
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
32
    EnvJujuClient,
953.3.9 by Nate Finch
more code review changes
33
    EnvJujuClient22,
928.2.2 by Aaron Bentley
Auto-enable support for cloudsigma in 1.24.
34
    EnvJujuClient24,
957.2.1 by Aaron Bentley
Treat cloudsigma as provisional in 2.5
35
    EnvJujuClient25,
19.1.32 by Aaron Bentley
Test until_timeout, making it a class to enable patching.
36
    ErroredUnit,
751.3.1 by Martin Packman
Use dots rather than repeated lines when waiting for status changes
37
    GroupReporter,
663.1.2 by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests.
38
    get_local_root,
19.1.33 by Aaron Bentley
Add JujuClient tests.
39
    JujuClientDevel,
928.2.2 by Aaron Bentley
Auto-enable support for cloudsigma in 1.24.
40
    JUJU_DEV_FEATURE_FLAGS,
657.1.3 by Aaron Bentley
Extract SimpleEnvironment from Environment.
41
    SimpleEnvironment,
34.1.3 by Aaron Bentley
Add status and environment tests.
42
    Status,
663.1.8 by Aaron Bentley
Extract temp_bootstrap_env from bootstrap_from_env.
43
    temp_bootstrap_env,
925.1.3 by Aaron Bentley
Add tests for boot_context.
44
    _temp_env as temp_env,
696.1.10 by Aaron Bentley
Move uniquify_local to jujupy.
45
    uniquify_local,
953.3.7 by Nate Finch
update for code review comments
46
    make_client,
953.3.8 by Nate Finch
more review changes
47
    parse_new_state_server_from_error,
19.1.32 by Aaron Bentley
Test until_timeout, making it a class to enable patching.
48
)
663.1.2 by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests.
49
from utility import (
50
    scoped_environ,
51
    temp_dir,
953.3.9 by Nate Finch
more code review changes
52
)
19.1.30 by Aaron Bentley
Add tests to jujupy
53
54
717.2.1 by Aaron Bentley
Extract EnvJujuClient.backup from assess_recovery.
55
def assert_juju_call(test_case, mock_method, client, expected_args,
717.2.3 by Aaron Bentley
Add backup/restore to industrial test.
56
                     call_index=None, assign_stderr=False):
717.2.1 by Aaron Bentley
Extract EnvJujuClient.backup from assess_recovery.
57
    if call_index is None:
58
        test_case.assertEqual(len(mock_method.mock_calls), 1)
59
        call_index = 0
60
    empty, args, kwargs = mock_method.mock_calls[call_index]
61
    test_case.assertEqual(args, (expected_args,))
717.2.3 by Aaron Bentley
Add backup/restore to industrial test.
62
    kwarg_keys = ['env']
63
    if assign_stderr:
954.1.11 by Curtis Hovey
Hush py3 lint.
64
        with tempfile.TemporaryFile() as example:
65
            # 'example' is a pragmatic way of checking file types in py2 and 3.
66
            kwarg_keys = ['stderr'] + kwarg_keys
67
            test_case.assertIsInstance(kwargs['stderr'], type(example))
880.1.17 by Aaron Bentley
Fake merge of trunk into no-environment.
68
    test_case.assertItemsEqual(kwargs.keys(), kwarg_keys)
717.2.1 by Aaron Bentley
Extract EnvJujuClient.backup from assess_recovery.
69
    bin_dir = os.path.dirname(client.full_path)
70
    test_case.assertRegexpMatches(kwargs['env']['PATH'],
71
                                  r'^{}\:'.format(bin_dir))
72
73
19.1.30 by Aaron Bentley
Add tests to jujupy
74
class TestErroredUnit(TestCase):
75
76
    def test_output(self):
301.1.3 by Aaron Bentley
Remove environment name from log messages and errors.
77
        e = ErroredUnit('bar', 'baz')
78
        self.assertEqual('bar is in state baz', str(e))
19.1.30 by Aaron Bentley
Add tests to jujupy
79
80
928.2.2 by Aaron Bentley
Auto-enable support for cloudsigma in 1.24.
81
class ClientTest(TestCase):
19.1.33 by Aaron Bentley
Add JujuClient tests.
82
763.1.2 by Curtis Hovey
Added puase to sleep after ha, but remove it for tests.
83
    def setUp(self):
928.2.2 by Aaron Bentley
Auto-enable support for cloudsigma in 1.24.
84
        super(ClientTest, self).setUp()
763.1.2 by Curtis Hovey
Added puase to sleep after ha, but remove it for tests.
85
        patcher = patch('jujupy.pause')
86
        self.addCleanup(patcher.stop)
87
        self.pause_mock = patcher.start()
88
928.2.2 by Aaron Bentley
Auto-enable support for cloudsigma in 1.24.
89
957.2.1 by Aaron Bentley
Treat cloudsigma as provisional in 2.5
90
class TestEnvJujuClient25(ClientTest):
91
92
    client_class = EnvJujuClient25
928.2.2 by Aaron Bentley
Auto-enable support for cloudsigma in 1.24.
93
94
    def test__shell_environ_not_cloudsigma(self):
957.2.1 by Aaron Bentley
Treat cloudsigma as provisional in 2.5
95
        client = self.client_class(
96
            SimpleEnvironment('baz', {'type': 'ec2'}), '1.25-foobar', 'path')
928.2.2 by Aaron Bentley
Auto-enable support for cloudsigma in 1.24.
97
        env = client._shell_environ()
98
        self.assertEqual(env.get(JUJU_DEV_FEATURE_FLAGS, ''), '')
99
100
    def test__shell_environ_cloudsigma(self):
957.2.1 by Aaron Bentley
Treat cloudsigma as provisional in 2.5
101
        client = self.client_class(
928.2.2 by Aaron Bentley
Auto-enable support for cloudsigma in 1.24.
102
            SimpleEnvironment('baz', {'type': 'cloudsigma'}),
957.2.1 by Aaron Bentley
Treat cloudsigma as provisional in 2.5
103
            '1.25-foobar', 'path')
928.2.2 by Aaron Bentley
Auto-enable support for cloudsigma in 1.24.
104
        env = client._shell_environ()
953.3.9 by Nate Finch
more code review changes
105
        "".split()
106
        self.assertTrue('cloudsigma' in env[JUJU_DEV_FEATURE_FLAGS].split(","))
928.2.2 by Aaron Bentley
Auto-enable support for cloudsigma in 1.24.
107
932.2.1 by Aaron Bentley
Fix bug #1449123 by passing juju_home.
108
    def test__shell_environ_juju_home(self):
953.3.13 by Nate Finch
more code review changes
109
        client = self.client_class(
957.2.1 by Aaron Bentley
Treat cloudsigma as provisional in 2.5
110
            SimpleEnvironment('baz', {'type': 'ec2'}), '1.25-foobar', 'path')
932.2.1 by Aaron Bentley
Fix bug #1449123 by passing juju_home.
111
        env = client._shell_environ(juju_home='asdf')
112
        self.assertEqual(env['JUJU_HOME'], 'asdf')
113
928.2.2 by Aaron Bentley
Auto-enable support for cloudsigma in 1.24.
114
953.3.9 by Nate Finch
more code review changes
115
class TestEnvJujuClient22(ClientTest):
116
953.3.14 by Nate Finch
one more fix to TestEnvJujuClient22
117
    client_class = EnvJujuClient22
118
953.3.9 by Nate Finch
more code review changes
119
    def test__shell_environ(self):
953.3.14 by Nate Finch
one more fix to TestEnvJujuClient22
120
        client = self.client_class(
121
            SimpleEnvironment('baz', {'type': 'ec2'}), '1.22-foobar', 'path')
953.3.9 by Nate Finch
more code review changes
122
        env = client._shell_environ()
123
        self.assertEqual(env.get(JUJU_DEV_FEATURE_FLAGS), 'actions')
124
125
    def test__shell_environ_juju_home(self):
953.3.14 by Nate Finch
one more fix to TestEnvJujuClient22
126
        client = self.client_class(
953.3.10 by Nate Finch
merge
127
            SimpleEnvironment('baz', {'type': 'ec2'}), '1.22-foobar', 'path')
953.3.9 by Nate Finch
more code review changes
128
        env = client._shell_environ(juju_home='asdf')
129
        self.assertEqual(env['JUJU_HOME'], 'asdf')
130
131
957.2.1 by Aaron Bentley
Treat cloudsigma as provisional in 2.5
132
class TestEnvJujuClient24(TestEnvJujuClient25):
133
134
    client_class = EnvJujuClient25
135
136
928.2.2 by Aaron Bentley
Auto-enable support for cloudsigma in 1.24.
137
class TestEnvJujuClient(ClientTest):
138
19.1.33 by Aaron Bentley
Add JujuClient tests.
139
    def test_get_version(self):
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
140
        value = ' 5.6 \n'
141
        with patch('subprocess.check_output', return_value=value) as vsn:
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
142
            version = EnvJujuClient.get_version()
19.1.33 by Aaron Bentley
Add JujuClient tests.
143
        self.assertEqual('5.6', version)
107.1.2 by Aaron Bentley
Use full path when running under sudo.
144
        vsn.assert_called_with(('juju', '--version'))
145
650.1.1 by Aaron Bentley
Add juju_path to from_config
146
    def test_get_version_path(self):
147
        with patch('subprocess.check_output', return_value=' 4.3') as vsn:
650.1.12 by Aaron Bentley
Rename to assess-foreign, update to use EnvJujuClient.
148
            EnvJujuClient.get_version('foo/bar/baz')
650.1.1 by Aaron Bentley
Add juju_path to from_config
149
        vsn.assert_called_once_with(('foo/bar/baz', '--version'))
150
657.1.4 by Aaron Bentley
Move upgrade functionality to EnvJujuClient.
151
    def test_get_matching_agent_version(self):
152
        client = EnvJujuClient(SimpleEnvironment(None, {'type': 'local'}),
153
                               '1.23-series-arch', None)
154
        self.assertEqual('1.23.1', client.get_matching_agent_version())
155
        self.assertEqual('1.23', client.get_matching_agent_version(
156
                         no_build=True))
157
        client.version = '1.20-beta1-series-arch'
158
        self.assertEqual('1.20-beta1.1', client.get_matching_agent_version())
159
160
    def test_upgrade_juju_nonlocal(self):
161
        client = EnvJujuClient(
162
            SimpleEnvironment('foo', {'type': 'nonlocal'}), '1.234-76', None)
163
        with patch.object(client, 'juju') as juju_mock:
164
            client.upgrade_juju()
165
        juju_mock.assert_called_with(
659 by Aaron Bentley
Fix upgrade's juju invocation.
166
            'upgrade-juju', ('--version', '1.234'))
657.1.4 by Aaron Bentley
Move upgrade functionality to EnvJujuClient.
167
168
    def test_upgrade_juju_local(self):
169
        client = EnvJujuClient(
170
            SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None)
171
        with patch.object(client, 'juju') as juju_mock:
172
            client.upgrade_juju()
173
        juju_mock.assert_called_with(
659 by Aaron Bentley
Fix upgrade's juju invocation.
174
            'upgrade-juju', ('--version', '1.234', '--upload-tools',))
650.1.1 by Aaron Bentley
Add juju_path to from_config
175
650.1.15 by Aaron Bentley
Add tests for upgrade-juju force_version.
176
    def test_upgrade_juju_no_force_version(self):
177
        client = EnvJujuClient(
178
            SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None)
179
        with patch.object(client, 'juju') as juju_mock:
180
            client.upgrade_juju(force_version=False)
181
        juju_mock.assert_called_with(
182
            'upgrade-juju', ('--upload-tools',))
183
19.1.33 by Aaron Bentley
Add JujuClient tests.
184
    def test_by_version(self):
185
        def juju_cmd_iterator():
186
            yield '1.17'
187
            yield '1.16'
188
            yield '1.16.1'
189
            yield '1.15'
953.3.13 by Nate Finch
more code review changes
190
            yield '1.22.1'
928.2.2 by Aaron Bentley
Auto-enable support for cloudsigma in 1.24.
191
            yield '1.24-alpha1'
192
            yield '1.24.7'
193
            yield '1.25.1'
957.2.1 by Aaron Bentley
Treat cloudsigma as provisional in 2.5
194
            yield '1.26.1'
19.1.33 by Aaron Bentley
Add JujuClient tests.
195
146 by Curtis Hovey
Hush lint.
196
        context = patch.object(
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
197
            EnvJujuClient, 'get_version',
650.1.1 by Aaron Bentley
Add juju_path to from_config
198
            side_effect=juju_cmd_iterator().send)
107.1.2 by Aaron Bentley
Use full path when running under sudo.
199
        with context:
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
200
            self.assertIs(EnvJujuClient,
201
                          type(EnvJujuClient.by_version(None)))
372.1.2 by Aaron Bentley
Support supplying timeout to calls.
202
            with self.assertRaisesRegexp(Exception, 'Unsupported juju: 1.16'):
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
203
                EnvJujuClient.by_version(None)
372.1.2 by Aaron Bentley
Support supplying timeout to calls.
204
            with self.assertRaisesRegexp(Exception,
205
                                         'Unsupported juju: 1.16.1'):
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
206
                EnvJujuClient.by_version(None)
207
            client = EnvJujuClient.by_version(None)
208
            self.assertIs(EnvJujuClient, type(client))
107.1.2 by Aaron Bentley
Use full path when running under sudo.
209
            self.assertEqual('1.15', client.version)
928.2.2 by Aaron Bentley
Auto-enable support for cloudsigma in 1.24.
210
            client = EnvJujuClient.by_version(None)
953.3.13 by Nate Finch
more code review changes
211
            self.assertIs(type(client), EnvJujuClient22)
212
            client = EnvJujuClient.by_version(None)
928.2.2 by Aaron Bentley
Auto-enable support for cloudsigma in 1.24.
213
            self.assertIs(type(client), EnvJujuClient24)
214
            self.assertEqual(client.version, '1.24-alpha1')
215
            client = EnvJujuClient.by_version(None)
216
            self.assertIs(type(client), EnvJujuClient24)
217
            self.assertEqual(client.version, '1.24.7')
218
            client = EnvJujuClient.by_version(None)
957.2.1 by Aaron Bentley
Treat cloudsigma as provisional in 2.5
219
            self.assertIs(type(client), EnvJujuClient25)
220
            self.assertEqual(client.version, '1.25.1')
221
            client = EnvJujuClient.by_version(None)
928.2.2 by Aaron Bentley
Auto-enable support for cloudsigma in 1.24.
222
            self.assertIs(type(client), EnvJujuClient)
957.2.1 by Aaron Bentley
Treat cloudsigma as provisional in 2.5
223
            self.assertEqual(client.version, '1.26.1')
19.1.33 by Aaron Bentley
Add JujuClient tests.
224
650.1.1 by Aaron Bentley
Add juju_path to from_config
225
    def test_by_version_path(self):
226
        with patch('subprocess.check_output', return_value=' 4.3') as vsn:
650.1.14 by Aaron Bentley
Remove now-unused support for JujuClientDevel path.
227
            client = EnvJujuClient.by_version(None, 'foo/bar/qux')
650.1.1 by Aaron Bentley
Add juju_path to from_config
228
        vsn.assert_called_once_with(('foo/bar/qux', '--version'))
229
        self.assertNotEqual(client.full_path, 'foo/bar/qux')
230
        self.assertEqual(client.full_path, os.path.abspath('foo/bar/qux'))
231
19.1.33 by Aaron Bentley
Add JujuClient tests.
232
    def test_full_args(self):
34.1.3 by Aaron Bentley
Add status and environment tests.
233
        env = Environment('foo', '')
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
234
        client = EnvJujuClient(env, None, 'my/juju/bin')
235
        full = client._full_args('bar', False, ('baz', 'qux'))
112.1.2 by Aaron Bentley
Add --show-log to all juju commands.
236
        self.assertEqual(('juju', '--show-log', 'bar', '-e', 'foo', 'baz',
237
                          'qux'), full)
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
238
        full = client._full_args('bar', True, ('baz', 'qux'))
112.1.2 by Aaron Bentley
Add --show-log to all juju commands.
239
        self.assertEqual((
195 by Curtis Hovey
Fix tests. update client1.17 rules to try sudo for lower revnos.
240
            'juju', '--show-log', 'bar', '-e', 'foo',
241
            'baz', 'qux'), full)
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
242
        client.env = None
243
        full = client._full_args('bar', False, ('baz', 'qux'))
112.1.2 by Aaron Bentley
Add --show-log to all juju commands.
244
        self.assertEqual(('juju', '--show-log', 'bar', 'baz', 'qux'), full)
34.1.1 by Aaron Bentley
Flesh out JujuClientDevel tests.
245
471.1.3 by Aaron Bentley
Support debug flag.
246
    def test_full_args_debug(self):
247
        env = Environment('foo', '')
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
248
        client = EnvJujuClient(env, None, 'my/juju/bin')
471.1.3 by Aaron Bentley
Support debug flag.
249
        client.debug = True
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
250
        full = client._full_args('bar', False, ('baz', 'qux'))
471.1.3 by Aaron Bentley
Support debug flag.
251
        self.assertEqual((
252
            'juju', '--debug', 'bar', '-e', 'foo', 'baz', 'qux'), full)
253
966.1.5 by John George
Add support for running actions through jujupy and support for listing subordinates when jujupy.agent_items is called.
254
    def test_full_args_action(self):
255
        env = Environment('foo', '')
256
        client = EnvJujuClient(env, None, 'my/juju/bin')
257
        full = client._full_args('action bar', False, ('baz', 'qux'))
258
        self.assertEqual((
953.3.15 by Aaron Bentley
Fix lint.
259
            'juju', '--show-log', 'action', 'bar', '-e', 'foo', 'baz', 'qux'),
966.1.5 by John George
Add support for running actions through jujupy and support for listing subordinates when jujupy.agent_items is called.
260
            full)
261
139 by Curtis Hovey
Added hpcloud attribute based on hp auth-url. hpcloud uses 4G to ensure mysql starts.
262
    def test_bootstrap_hpcloud(self):
263
        env = Environment('hp', '')
264
        with patch.object(env, 'hpcloud', lambda: True):
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
265
            with patch.object(EnvJujuClient, 'juju') as mock:
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
266
                EnvJujuClient(env, None, None).bootstrap()
267
            mock.assert_called_with(
807.1.1 by Aaron Bentley
Avoid creating temp juju homes inside temp juju homes.
268
                'bootstrap', ('--constraints', 'mem=2G'), False,
269
                juju_home=None)
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
270
667.1.1 by John George
Add amd64 contraint, when bootstrapping the maas test environment.
271
    def test_bootstrap_maas(self):
272
        env = Environment('maas', '')
273
        with patch.object(EnvJujuClient, 'juju') as mock:
274
            client = EnvJujuClient(env, None, None)
275
            with patch.object(client.env, 'maas', lambda: True):
276
                client.bootstrap()
277
            mock.assert_called_with(
807.1.1 by Aaron Bentley
Avoid creating temp juju homes inside temp juju homes.
278
                'bootstrap', ('--constraints', 'mem=2G arch=amd64'), False,
279
                juju_home=None)
667.1.1 by John George
Add amd64 contraint, when bootstrapping the maas test environment.
280
940.2.1 by Martin Packman
Only use kvm packages on joyent provider
281
    def test_bootstrap_joyent(self):
282
        env = Environment('joyent', '')
283
        with patch.object(EnvJujuClient, 'juju', autospec=True) as mock:
284
            client = EnvJujuClient(env, None, None)
285
            with patch.object(client.env, 'joyent', lambda: True):
286
                client.bootstrap()
953.2.2 by John George
Drive-by lint.
287
            mock.assert_called_once_with(
288
                client, 'bootstrap', ('--constraints', 'mem=2G cpu-cores=1'),
289
                False, juju_home=None)
940.2.1 by Martin Packman
Only use kvm packages on joyent provider
290
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
291
    def test_bootstrap_non_sudo(self):
292
        env = Environment('foo', '')
657.1.3 by Aaron Bentley
Extract SimpleEnvironment from Environment.
293
        with patch.object(EnvJujuClient, 'juju') as mock:
294
            client = EnvJujuClient(env, None, None)
295
            with patch.object(client.env, 'needs_sudo', lambda: False):
296
                client.bootstrap()
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
297
            mock.assert_called_with(
807.1.1 by Aaron Bentley
Avoid creating temp juju homes inside temp juju homes.
298
                'bootstrap', ('--constraints', 'mem=2G'), False,
299
                juju_home=None)
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
300
301
    def test_bootstrap_sudo(self):
302
        env = Environment('foo', '')
303
        client = EnvJujuClient(env, None, None)
657.1.3 by Aaron Bentley
Extract SimpleEnvironment from Environment.
304
        with patch.object(client.env, 'needs_sudo', lambda: True):
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
305
            with patch.object(client, 'juju') as mock:
306
                client.bootstrap()
307
            mock.assert_called_with(
807.1.1 by Aaron Bentley
Avoid creating temp juju homes inside temp juju homes.
308
                'bootstrap', ('--constraints', 'mem=2G'), True, juju_home=None)
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
309
663.1.6 by Aaron Bentley
Base support for upload-tools
310
    def test_bootstrap_upload_tools(self):
311
        env = Environment('foo', '')
312
        client = EnvJujuClient(env, None, None)
313
        with patch.object(client.env, 'needs_sudo', lambda: True):
314
            with patch.object(client, 'juju') as mock:
315
                client.bootstrap(upload_tools=True)
316
            mock.assert_called_with(
317
                'bootstrap', ('--upload-tools', '--constraints', 'mem=2G'),
807.1.1 by Aaron Bentley
Avoid creating temp juju homes inside temp juju homes.
318
                True, juju_home=None)
319
320
    def test_bootstrap_juju_home(self):
321
        env = Environment('foo', '')
322
        client = EnvJujuClient(env, None, None)
323
        with patch.object(client.env, 'needs_sudo', lambda: True):
324
            with patch.object(client, 'juju', autospec=True) as mock:
325
                client.bootstrap(upload_tools=True, juju_home='temp-home')
326
            mock.assert_called_with(
327
                'bootstrap', ('--upload-tools', '--constraints', 'mem=2G'),
328
                True, juju_home='temp-home')
663.1.6 by Aaron Bentley
Base support for upload-tools
329
807.1.5 by Aaron Bentley
Test asynchronous bootstrap.
330
    def test_bootstrap_async(self):
331
        env = Environment('foo', '')
332
        with patch.object(EnvJujuClient, 'juju_async', autospec=True) as mock:
333
            client = EnvJujuClient(env, None, None)
807.1.9 by Aaron Bentley
Fix lint.
334
            with client.bootstrap_async(juju_home='foo'):
807.1.5 by Aaron Bentley
Test asynchronous bootstrap.
335
                mock.assert_called_once_with(
336
                    client, 'bootstrap', ('--constraints', 'mem=2G'),
337
                    juju_home='foo')
338
339
    def test_bootstrap_async_upload_tools(self):
340
        env = Environment('foo', '')
341
        with patch.object(EnvJujuClient, 'juju_async', autospec=True) as mock:
342
            client = EnvJujuClient(env, None, None)
807.1.9 by Aaron Bentley
Fix lint.
343
            with client.bootstrap_async(upload_tools=True):
807.1.5 by Aaron Bentley
Test asynchronous bootstrap.
344
                mock.assert_called_with(
345
                    client, 'bootstrap', ('--upload-tools', '--constraints',
346
                                          'mem=2G'), juju_home=None)
347
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
348
    def test_destroy_environment_non_sudo(self):
349
        env = Environment('foo', '')
350
        client = EnvJujuClient(env, None, None)
657.1.3 by Aaron Bentley
Extract SimpleEnvironment from Environment.
351
        with patch.object(client.env, 'needs_sudo', lambda: False):
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
352
            with patch.object(client, 'juju') as mock:
353
                client.destroy_environment()
354
            mock.assert_called_with(
355
                'destroy-environment', ('foo', '--force', '-y'),
768.1.1 by Aaron Bentley
Add timeout to EnvJujuClient.destroy_environment.
356
                False, check=False, include_e=False, timeout=600.0)
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
357
358
    def test_destroy_environment_sudo(self):
359
        env = Environment('foo', '')
360
        client = EnvJujuClient(env, None, None)
657.1.3 by Aaron Bentley
Extract SimpleEnvironment from Environment.
361
        with patch.object(client.env, 'needs_sudo', lambda: True):
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
362
            with patch.object(client, 'juju') as mock:
363
                client.destroy_environment()
364
            mock.assert_called_with(
365
                'destroy-environment', ('foo', '--force', '-y'),
768.1.1 by Aaron Bentley
Add timeout to EnvJujuClient.destroy_environment.
366
                True, check=False, include_e=False, timeout=600.0)
367
368
    def test_destroy_environment_no_force(self):
369
        env = Environment('foo', '')
370
        client = EnvJujuClient(env, None, None)
371
        with patch.object(client, 'juju') as mock:
372
            client.destroy_environment(force=False)
373
            mock.assert_called_with(
374
                'destroy-environment', ('foo', '-y'),
375
                False, check=False, include_e=False, timeout=600.0)
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
376
777.2.1 by Aaron Bentley
Always delete jenv in industrial test.
377
    def test_destroy_environment_delete_jenv(self):
378
        env = Environment('foo', '')
379
        client = EnvJujuClient(env, None, None)
380
        with patch.object(client, 'juju'):
925.1.3 by Aaron Bentley
Add tests for boot_context.
381
            with temp_env({}) as juju_home:
777.2.1 by Aaron Bentley
Always delete jenv in industrial test.
382
                jenv_path = get_jenv_path(juju_home, 'foo')
383
                os.makedirs(os.path.dirname(jenv_path))
384
                open(jenv_path, 'w')
385
                self.assertTrue(os.path.exists(jenv_path))
386
                client.destroy_environment(delete_jenv=True)
387
                self.assertFalse(os.path.exists(jenv_path))
388
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
389
    def test_get_juju_output(self):
390
        env = Environment('foo', '')
391
        asdf = lambda x, stderr, env: 'asdf'
392
        client = EnvJujuClient(env, None, None)
393
        with patch('subprocess.check_output', side_effect=asdf) as mock:
394
            result = client.get_juju_output('bar')
395
        self.assertEqual('asdf', result)
396
        self.assertEqual((('juju', '--show-log', 'bar', '-e', 'foo'),),
397
                         mock.call_args[0])
398
399
    def test_get_juju_output_accepts_varargs(self):
400
        env = Environment('foo', '')
401
        asdf = lambda x, stderr, env: 'asdf'
402
        client = EnvJujuClient(env, None, None)
403
        with patch('subprocess.check_output', side_effect=asdf) as mock:
404
            result = client.get_juju_output('bar', 'baz', '--qux')
405
        self.assertEqual('asdf', result)
406
        self.assertEqual((('juju', '--show-log', 'bar', '-e', 'foo', 'baz',
407
                           '--qux'),), mock.call_args[0])
408
409
    def test_get_juju_output_stderr(self):
410
        def raise_without_stderr(args, stderr, env):
411
            stderr.write('Hello!')
412
            raise subprocess.CalledProcessError('a', 'b')
413
        env = Environment('foo', '')
414
        client = EnvJujuClient(env, None, None)
415
        with self.assertRaises(subprocess.CalledProcessError) as exc:
416
            with patch('subprocess.check_output', raise_without_stderr):
417
                client.get_juju_output('bar')
418
        self.assertEqual(exc.exception.stderr, 'Hello!')
419
420
    def test_get_juju_output_accepts_timeout(self):
421
        env = Environment('foo', '')
422
        client = EnvJujuClient(env, None, None)
423
        with patch('subprocess.check_output') as sco_mock:
424
            client.get_juju_output('bar', timeout=5)
706.1.1 by Aaron Bentley
Switch to flake8 and fix lint.
425
        self.assertEqual(
426
            sco_mock.call_args[0][0],
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
427
            ('timeout', '5.00s', 'juju', '--show-log', 'bar', '-e', 'foo'))
428
928.2.2 by Aaron Bentley
Auto-enable support for cloudsigma in 1.24.
429
    def test__shell_environ_cloudsigma(self):
430
        client = EnvJujuClient(
431
            SimpleEnvironment('baz', {'type': 'cloudsigma'}),
432
            '1.24-foobar', 'path')
433
        env = client._shell_environ()
434
        self.assertEqual(env.get(JUJU_DEV_FEATURE_FLAGS, ''), '')
435
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
436
    def test_juju_output_supplies_path(self):
437
        env = Environment('foo', '')
438
        client = EnvJujuClient(env, None, '/foobar/bar')
439
        with patch('subprocess.check_output') as sco_mock:
966.1.5 by John George
Add support for running actions through jujupy and support for listing subordinates when jujupy.agent_items is called.
440
            client.get_juju_output('cmd', 'baz')
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
441
        self.assertRegexpMatches(sco_mock.call_args[1]['env']['PATH'],
442
                                 r'/foobar\:')
443
444
    def test_get_status(self):
445
        output_text = yield dedent("""\
446
                - a
447
                - b
448
                - c
449
                """)
450
        env = Environment('foo', '')
451
        client = EnvJujuClient(env, None, None)
452
        with patch.object(client, 'get_juju_output',
453
                          return_value=output_text):
454
            result = client.get_status()
455
        self.assertEqual(Status, type(result))
456
        self.assertEqual(['a', 'b', 'c'], result.status)
457
458
    def test_get_status_retries_on_error(self):
459
        env = Environment('foo', '')
460
        client = EnvJujuClient(env, None, None)
461
        client.attempt = 0
706.1.1 by Aaron Bentley
Switch to flake8 and fix lint.
462
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
463
        def get_juju_output(command, *args):
464
            if client.attempt == 1:
465
                return '"hello"'
466
            client.attempt += 1
467
            raise subprocess.CalledProcessError(1, command)
468
469
        with patch.object(client, 'get_juju_output', get_juju_output):
470
            client.get_status()
471
472
    def test_get_status_raises_on_timeout_1(self):
473
        env = Environment('foo', '')
474
        client = EnvJujuClient(env, None, None)
706.1.1 by Aaron Bentley
Switch to flake8 and fix lint.
475
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
476
        def get_juju_output(command):
477
            raise subprocess.CalledProcessError(1, command)
478
479
        with patch.object(client, 'get_juju_output',
480
                          side_effect=get_juju_output):
481
            with patch('jujupy.until_timeout', lambda x: iter([None, None])):
482
                with self.assertRaisesRegexp(
483
                        Exception, 'Timed out waiting for juju status'):
484
                    client.get_status()
485
486
    def test_get_status_raises_on_timeout_2(self):
657.1.6 by Aaron Bentley
Move wait_for_version to EnvJujuClient.
487
        env = SimpleEnvironment('foo')
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
488
        client = EnvJujuClient(env, None, None)
489
        with patch('jujupy.until_timeout', return_value=iter([1])) as mock_ut:
490
            with patch.object(client, 'get_juju_output',
491
                              side_effect=StopIteration):
492
                with self.assertRaises(StopIteration):
493
                    client.get_status(500)
494
        mock_ut.assert_called_with(500)
495
657.1.5 by Aaron Bentley
Move wait_for_started to EnvJujuClient.
496
    @staticmethod
497
    def make_status_yaml(key, machine_value, unit_value):
498
        return dedent("""\
499
            machines:
500
              "0":
501
                {0}: {1}
502
            services:
503
              jenkins:
504
                units:
505
                  jenkins/0:
506
                    {0}: {2}
507
        """.format(key, machine_value, unit_value))
508
650.1.12 by Aaron Bentley
Rename to assess-foreign, update to use EnvJujuClient.
509
    def test_deploy_non_joyent(self):
510
        env = EnvJujuClient(
511
            SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None)
512
        with patch.object(env, 'juju') as mock_juju:
513
            env.deploy('mondogb')
514
        mock_juju.assert_called_with('deploy', ('mondogb',))
515
516
    def test_deploy_joyent(self):
517
        env = EnvJujuClient(
518
            SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None)
519
        with patch.object(env, 'juju') as mock_juju:
520
            env.deploy('mondogb')
521
        mock_juju.assert_called_with('deploy', ('mondogb',))
522
874.2.7 by Aaron Bentley
Add unit tests for jujupy newness.
523
    def test_deploy_repository(self):
524
        env = EnvJujuClient(
525
            SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None)
526
        with patch.object(env, 'juju') as mock_juju:
527
            env.deploy('mondogb', '/home/jrandom/repo')
528
        mock_juju.assert_called_with(
529
            'deploy', ('mondogb', '--repository', '/home/jrandom/repo'))
530
531
    def test_status_until_always_runs_once(self):
532
        client = EnvJujuClient(
533
            SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None)
534
        status_txt = self.make_status_yaml('agent-state', 'started', 'started')
535
        with patch.object(client, 'get_juju_output', return_value=status_txt):
536
            result = list(client.status_until(-1))
537
        self.assertEqual(
538
            [r.status for r in result], [Status.from_text(status_txt).status])
539
540
    def test_status_until_timeout(self):
541
        client = EnvJujuClient(
542
            SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None)
543
        status_txt = self.make_status_yaml('agent-state', 'started', 'started')
544
        status_yaml = yaml.safe_load(status_txt)
545
546
        def until_timeout_stub(timeout, start=None):
547
            return iter([None, None])
548
549
        with patch.object(client, 'get_juju_output', return_value=status_txt):
550
            with patch('jujupy.until_timeout',
551
                       side_effect=until_timeout_stub) as ut_mock:
552
                result = list(client.status_until(30, 70))
553
        self.assertEqual(
554
            [r.status for r in result], [status_yaml] * 3)
555
        # until_timeout is called by status as well as status_until.
556
        self.assertEqual(ut_mock.mock_calls,
557
                         [call(60), call(30, start=70), call(60), call(60)])
558
657.1.5 by Aaron Bentley
Move wait_for_started to EnvJujuClient.
559
    def test_wait_for_started(self):
560
        value = self.make_status_yaml('agent-state', 'started', 'started')
561
        client = EnvJujuClient(SimpleEnvironment('local'), None, None)
562
        with patch.object(client, 'get_juju_output', return_value=value):
563
            client.wait_for_started()
564
565
    def test_wait_for_started_timeout(self):
566
        value = self.make_status_yaml('agent-state', 'pending', 'started')
567
        client = EnvJujuClient(SimpleEnvironment('local'), None, None)
751.1.1 by Aaron Bentley
Give equal time to old and new clients in DeployManyAttempt
568
        with patch('jujupy.until_timeout', lambda x, start=None: range(1)):
657.1.5 by Aaron Bentley
Move wait_for_started to EnvJujuClient.
569
            with patch.object(client, 'get_juju_output', return_value=value):
570
                with self.assertRaisesRegexp(
571
                        Exception,
572
                        'Timed out waiting for agents to start in local'):
754.1.1 by Aaron Bentley
Emit status text for agent timeout.
573
                    with patch('logging.error'):
574
                        client.wait_for_started()
657.1.5 by Aaron Bentley
Move wait_for_started to EnvJujuClient.
575
751.1.1 by Aaron Bentley
Give equal time to old and new clients in DeployManyAttempt
576
    def test_wait_for_started_start(self):
577
        value = self.make_status_yaml('agent-state', 'started', 'pending')
578
        client = EnvJujuClient(SimpleEnvironment('local'), None, None)
579
        now = datetime.now() + timedelta(days=1)
580
        with patch('utility.until_timeout.now', return_value=now):
581
            with patch.object(client, 'get_juju_output', return_value=value):
582
                with self.assertRaisesRegexp(
583
                        Exception,
584
                        'Timed out waiting for agents to start in local'):
754.1.1 by Aaron Bentley
Emit status text for agent timeout.
585
                    with patch('logging.error'):
586
                        client.wait_for_started(start=now - timedelta(1200))
587
588
    def test_wait_for_started_logs_status(self):
589
        value = self.make_status_yaml('agent-state', 'pending', 'started')
590
        client = EnvJujuClient(SimpleEnvironment('local'), None, None)
591
        with patch.object(client, 'get_juju_output', return_value=value):
592
            with self.assertRaisesRegexp(
593
                    Exception,
594
                    'Timed out waiting for agents to start in local'):
595
                with patch('logging.error') as le_mock:
596
                    client.wait_for_started(0)
597
        le_mock.assert_called_once_with(value)
751.1.1 by Aaron Bentley
Give equal time to old and new clients in DeployManyAttempt
598
966.1.10 by John George
Check that subordinate unit count matches service unit count in wait_for_subordinate_units.
599
    def test_wait_for_subordinate_units(self):
966.1.9 by John George
Add wait_for_subordinate_unit to jujupy.py.
600
        value = dedent("""\
601
            machines:
602
              "0":
603
                agent-state: started
604
            services:
605
              jenkins:
606
                units:
607
                  jenkins/0:
608
                    subordinates:
966.1.11 by John George
Include checking for a forward slash at the end of the prefix in wait_for_subordinate_units.
609
                      sub1/0:
966.1.9 by John George
Add wait_for_subordinate_unit to jujupy.py.
610
                        agent-state: started
966.1.12 by John George
Verify subordinate units are started in wait_for_subordinate_units and add the reporter.
611
              ubuntu:
612
                units:
613
                  ubuntu/0:
614
                    subordinates:
615
                      sub2/0:
616
                        agent-state: started
617
                      sub3/0:
618
                        agent-state: started
619
        """)
620
        client = EnvJujuClient(SimpleEnvironment('local'), None, None)
621
        now = datetime.now() + timedelta(days=1)
622
        with patch('utility.until_timeout.now', return_value=now):
623
            with patch.object(client, 'get_juju_output', return_value=value):
624
                with patch('jujupy.GroupReporter.update') as update_mock:
625
                    with patch('jujupy.GroupReporter.finish') as finish_mock:
626
                        client.wait_for_subordinate_units(
627
                            'jenkins', 'sub1', start=now - timedelta(1200))
628
        expected_states = defaultdict(list)
629
        expected_states['started'].append('sub1/0')
630
        update_mock.assert_called_once_with(expected_states)
631
        finish_mock.assert_called_once_with()
632
633
    def test_wait_for_multiple_subordinate_units(self):
634
        value = dedent("""\
635
            machines:
636
              "0":
637
                agent-state: started
638
            services:
639
              ubuntu:
640
                units:
641
                  ubuntu/0:
642
                    subordinates:
643
                      sub/0:
644
                        agent-state: started
645
                  ubuntu/1:
646
                    subordinates:
647
                      sub/1:
648
                        agent-state: started
649
        """)
650
        client = EnvJujuClient(SimpleEnvironment('local'), None, None)
651
        now = datetime.now() + timedelta(days=1)
652
        with patch('utility.until_timeout.now', return_value=now):
653
            with patch.object(client, 'get_juju_output', return_value=value):
654
                with patch('jujupy.GroupReporter.update') as update_mock:
655
                    with patch('jujupy.GroupReporter.finish') as finish_mock:
656
                        client.wait_for_subordinate_units(
657
                            'ubuntu', 'sub', start=now - timedelta(1200))
658
        expected_states = defaultdict(list)
659
        expected_states['started'].append('sub/0')
660
        expected_states['started'].append('sub/1')
661
        update_mock.assert_called_once_with(expected_states)
662
        finish_mock.assert_called_once_with()
966.1.9 by John George
Add wait_for_subordinate_unit to jujupy.py.
663
966.1.11 by John George
Include checking for a forward slash at the end of the prefix in wait_for_subordinate_units.
664
    def test_wait_for_subordinate_units_checks_slash_in_unit_name(self):
665
        value = dedent("""\
666
            machines:
667
              "0":
668
                agent-state: started
669
            services:
670
              jenkins:
671
                units:
672
                  jenkins/0:
673
                    subordinates:
674
                      sub1:
675
                        agent-state: started
676
        """)
677
        client = EnvJujuClient(SimpleEnvironment('local'), None, None)
678
        now = datetime.now() + timedelta(days=1)
679
        with patch('utility.until_timeout.now', return_value=now):
680
            with patch.object(client, 'get_juju_output', return_value=value):
681
                with self.assertRaisesRegexp(
682
                        Exception,
683
                        'Timed out waiting for agents to start in local'):
684
                    with patch('logging.error'):
685
                        client.wait_for_subordinate_units(
686
                            'jenkins', 'sub1', start=now - timedelta(1200))
687
966.1.10 by John George
Check that subordinate unit count matches service unit count in wait_for_subordinate_units.
688
    def test_wait_for_subordinate_units_no_subordinate(self):
966.1.9 by John George
Add wait_for_subordinate_unit to jujupy.py.
689
        value = dedent("""\
690
            machines:
691
              "0":
692
                agent-state: started
693
            services:
694
              jenkins:
695
                units:
696
                  jenkins/0:
697
                    agent-state: started
698
        """)
699
        client = EnvJujuClient(SimpleEnvironment('local'), None, None)
700
        now = datetime.now() + timedelta(days=1)
701
        with patch('utility.until_timeout.now', return_value=now):
702
            with patch.object(client, 'get_juju_output', return_value=value):
703
                with self.assertRaisesRegexp(
704
                        Exception,
705
                        'Timed out waiting for agents to start in local'):
706
                    with patch('logging.error'):
966.1.10 by John George
Check that subordinate unit count matches service unit count in wait_for_subordinate_units.
707
                        client.wait_for_subordinate_units(
966.1.9 by John George
Add wait_for_subordinate_unit to jujupy.py.
708
                            'jenkins', 'sub1', start=now - timedelta(1200))
709
703.1.1 by Aaron Bentley
Add ensure-availability stage to industrial_test.
710
    def test_wait_for_ha(self):
711
        value = yaml.safe_dump({
712
            'machines': {
713
                '0': {'state-server-member-status': 'has-vote'},
714
                '1': {'state-server-member-status': 'has-vote'},
715
                '2': {'state-server-member-status': 'has-vote'},
953.3.9 by Nate Finch
more code review changes
716
            },
703.1.1 by Aaron Bentley
Add ensure-availability stage to industrial_test.
717
            'services': {},
953.3.9 by Nate Finch
more code review changes
718
        })
703.1.1 by Aaron Bentley
Add ensure-availability stage to industrial_test.
719
        client = EnvJujuClient(SimpleEnvironment('local'), None, None)
720
        with patch.object(client, 'get_juju_output', return_value=value):
721
            client.wait_for_ha()
722
723
    def test_wait_for_ha_no_has_vote(self):
724
        value = yaml.safe_dump({
725
            'machines': {
726
                '0': {'state-server-member-status': 'no-vote'},
727
                '1': {'state-server-member-status': 'no-vote'},
728
                '2': {'state-server-member-status': 'no-vote'},
953.3.9 by Nate Finch
more code review changes
729
            },
703.1.1 by Aaron Bentley
Add ensure-availability stage to industrial_test.
730
            'services': {},
953.3.9 by Nate Finch
more code review changes
731
        })
703.1.1 by Aaron Bentley
Add ensure-availability stage to industrial_test.
732
        client = EnvJujuClient(SimpleEnvironment('local'), None, None)
703.1.2 by Aaron Bentley
Fix lint.
733
        with patch('sys.stdout'):
703.1.1 by Aaron Bentley
Add ensure-availability stage to industrial_test.
734
            with patch.object(client, 'get_juju_output', return_value=value):
735
                with self.assertRaisesRegexp(
736
                        Exception,
737
                        'Timed out waiting for voting to be enabled.'):
738
                    client.wait_for_ha(0.01)
739
740
    def test_wait_for_ha_timeout(self):
741
        value = yaml.safe_dump({
742
            'machines': {
743
                '0': {'state-server-member-status': 'has-vote'},
744
                '1': {'state-server-member-status': 'has-vote'},
953.3.9 by Nate Finch
more code review changes
745
            },
703.1.1 by Aaron Bentley
Add ensure-availability stage to industrial_test.
746
            'services': {},
953.3.9 by Nate Finch
more code review changes
747
        })
703.1.1 by Aaron Bentley
Add ensure-availability stage to industrial_test.
748
        client = EnvJujuClient(SimpleEnvironment('local'), None, None)
749
        with patch('jujupy.until_timeout', lambda x: range(0)):
750
            with patch.object(client, 'get_juju_output', return_value=value):
751
                with self.assertRaisesRegexp(
752
                        Exception,
753
                        'Timed out waiting for voting to be enabled.'):
754
                    client.wait_for_ha()
755
796.2.1 by John George
Add quickstart_deploy.py
756
    def test_wait_for_deploy_started(self):
757
        value = yaml.safe_dump({
758
            'machines': {
759
                '0': {'agent-state': 'started'},
953.3.9 by Nate Finch
more code review changes
760
            },
796.2.1 by John George
Add quickstart_deploy.py
761
            'services': {
762
                'jenkins': {
763
                    'units': {
764
                        'jenkins/1': {'baz': 'qux'}
765
                    }
766
                }
767
            }
768
        })
769
        client = EnvJujuClient(SimpleEnvironment('local'), None, None)
770
        with patch.object(client, 'get_juju_output', return_value=value):
771
            client.wait_for_deploy_started()
772
773
    def test_wait_for_deploy_started_timeout(self):
774
        value = yaml.safe_dump({
775
            'machines': {
776
                '0': {'agent-state': 'started'},
953.3.9 by Nate Finch
more code review changes
777
            },
796.2.1 by John George
Add quickstart_deploy.py
778
            'services': {},
953.3.9 by Nate Finch
more code review changes
779
        })
796.2.1 by John George
Add quickstart_deploy.py
780
        client = EnvJujuClient(SimpleEnvironment('local'), None, None)
781
        with patch('jujupy.until_timeout', lambda x: range(0)):
782
            with patch.object(client, 'get_juju_output', return_value=value):
783
                with self.assertRaisesRegexp(
784
                        Exception,
785
                        'Timed out waiting for services to start.'):
786
                    client.wait_for_deploy_started()
787
657.1.6 by Aaron Bentley
Move wait_for_version to EnvJujuClient.
788
    def test_wait_for_version(self):
789
        value = self.make_status_yaml('agent-version', '1.17.2', '1.17.2')
790
        client = EnvJujuClient(SimpleEnvironment('local'), None, None)
791
        with patch.object(client, 'get_juju_output', return_value=value):
792
            client.wait_for_version('1.17.2')
793
794
    def test_wait_for_version_timeout(self):
795
        value = self.make_status_yaml('agent-version', '1.17.2', '1.17.1')
796
        client = EnvJujuClient(SimpleEnvironment('local'), None, None)
797
        with patch('jujupy.until_timeout', lambda x: range(0)):
798
            with patch.object(client, 'get_juju_output', return_value=value):
799
                with self.assertRaisesRegexp(
800
                        Exception, 'Some versions did not update'):
801
                    client.wait_for_version('1.17.2')
802
803
    def test_wait_for_version_handles_connection_error(self):
804
        err = subprocess.CalledProcessError(2, 'foo')
805
        err.stderr = 'Unable to connect to environment'
806
        err = CannotConnectEnv(err)
807
        status = self.make_status_yaml('agent-version', '1.17.2', '1.17.2')
808
        actions = [err, status]
809
810
        def get_juju_output_fake(*args):
811
            action = actions.pop(0)
812
            if isinstance(action, Exception):
813
                raise action
814
            else:
815
                return action
816
817
        client = EnvJujuClient(SimpleEnvironment('local'), None, None)
818
        output_real = 'test_jujupy.EnvJujuClient.get_juju_output'
819
        devnull = open(os.devnull, 'w')
820
        with patch('sys.stdout', devnull):
821
            with patch(output_real, get_juju_output_fake):
822
                client.wait_for_version('1.17.2')
823
824
    def test_wait_for_version_raises_non_connection_error(self):
825
        err = Exception('foo')
826
        status = self.make_status_yaml('agent-version', '1.17.2', '1.17.2')
827
        actions = [err, status]
828
829
        def get_juju_output_fake(*args):
830
            action = actions.pop(0)
831
            if isinstance(action, Exception):
832
                raise action
833
            else:
834
                return action
835
836
        client = EnvJujuClient(SimpleEnvironment('local'), None, None)
837
        output_real = 'test_jujupy.EnvJujuClient.get_juju_output'
838
        devnull = open(os.devnull, 'w')
839
        with patch('sys.stdout', devnull):
840
            with patch(output_real, get_juju_output_fake):
841
                with self.assertRaisesRegexp(Exception, 'foo'):
842
                    client.wait_for_version('1.17.2')
843
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
844
    def test_get_env_option(self):
845
        env = Environment('foo', '')
846
        client = EnvJujuClient(env, None, None)
847
        with patch('subprocess.check_output') as mock:
848
            mock.return_value = 'https://example.org/juju/tools'
849
            result = client.get_env_option('tools-metadata-url')
850
        self.assertEqual(
851
            mock.call_args[0][0],
852
            ('juju', '--show-log', 'get-env', '-e', 'foo',
853
             'tools-metadata-url'))
854
        self.assertEqual('https://example.org/juju/tools', result)
855
856
    def test_set_env_option(self):
857
        env = Environment('foo', '')
858
        client = EnvJujuClient(env, None, None)
859
        with patch('subprocess.check_call') as mock:
860
            client.set_env_option(
861
                'tools-metadata-url', 'https://example.org/juju/tools')
862
        mock.assert_called_with(
863
            ('juju', '--show-log', 'set-env', '-e', 'foo',
864
             'tools-metadata-url=https://example.org/juju/tools'),
865
            env=os.environ)
866
880.1.17 by Aaron Bentley
Fake merge of trunk into no-environment.
867
    def test_set_testing_tools_metadata_url(self):
868
        env = SimpleEnvironment(None, {'type': 'foo'})
869
        client = EnvJujuClient(env, None, None)
870
        with patch.object(client, 'get_env_option') as mock_get:
871
            mock_get.return_value = 'https://example.org/juju/tools'
872
            with patch.object(client, 'set_env_option') as mock_set:
873
                client.set_testing_tools_metadata_url()
874
        mock_get.assert_called_with('tools-metadata-url')
875
        mock_set.assert_called_with(
876
            'tools-metadata-url',
877
            'https://example.org/juju/testing/tools')
878
879
    def test_set_testing_tools_metadata_url_noop(self):
880
        env = SimpleEnvironment(None, {'type': 'foo'})
881
        client = EnvJujuClient(env, None, None)
882
        with patch.object(client, 'get_env_option') as mock_get:
883
            mock_get.return_value = 'https://example.org/juju/testing/tools'
884
            with patch.object(client, 'set_env_option') as mock_set:
885
                client.set_testing_tools_metadata_url()
886
        mock_get.assert_called_with('tools-metadata-url')
887
        self.assertEqual(0, mock_set.call_count)
888
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
889
    def test_juju(self):
890
        env = Environment('qux', '')
891
        client = EnvJujuClient(env, None, None)
892
        with patch('sys.stdout') as stdout_mock:
893
            with patch('subprocess.check_call') as mock:
894
                client.juju('foo', ('bar', 'baz'))
895
        mock.assert_called_with(('juju', '--show-log', 'foo', '-e', 'qux',
896
                                 'bar', 'baz'), env=os.environ)
897
        stdout_mock.flush.assert_called_with()
898
899
    def test_juju_env(self):
900
        env = Environment('qux', '')
901
        client = EnvJujuClient(env, None, '/foobar/baz')
902
        with patch('subprocess.check_call') as cc_mock:
903
            client.juju('foo', ('bar', 'baz'))
904
        self.assertRegexpMatches(cc_mock.call_args[1]['env']['PATH'],
905
                                 r'/foobar\:')
906
907
    def test_juju_no_check(self):
908
        env = Environment('qux', '')
909
        client = EnvJujuClient(env, None, None)
910
        with patch('sys.stdout') as stdout_mock:
911
            with patch('subprocess.call') as mock:
912
                client.juju('foo', ('bar', 'baz'), check=False)
913
        mock.assert_called_with(('juju', '--show-log', 'foo', '-e', 'qux',
914
                                 'bar', 'baz'), env=os.environ)
915
        stdout_mock.flush.assert_called_with()
916
917
    def test_juju_no_check_env(self):
918
        env = Environment('qux', '')
919
        client = EnvJujuClient(env, None, '/foobar/baz')
920
        with patch('subprocess.call') as call_mock:
921
            client.juju('foo', ('bar', 'baz'), check=False)
922
        self.assertRegexpMatches(call_mock.call_args[1]['env']['PATH'],
923
                                 r'/foobar\:')
924
768.1.1 by Aaron Bentley
Add timeout to EnvJujuClient.destroy_environment.
925
    def test_juju_timeout(self):
926
        env = Environment('qux', '')
927
        client = EnvJujuClient(env, None, '/foobar/baz')
928
        with patch('subprocess.check_call') as cc_mock:
929
            client.juju('foo', ('bar', 'baz'), timeout=58)
930
        self.assertEqual(cc_mock.call_args[0][0], (
931
            'timeout', '58.00s', 'juju', '--show-log', 'foo', '-e', 'qux',
932
            'bar', 'baz'))
933
807.1.1 by Aaron Bentley
Avoid creating temp juju homes inside temp juju homes.
934
    def test_juju_juju_home(self):
935
        env = SimpleEnvironment('qux')
936
        client = EnvJujuClient(env, None, '/foobar/baz')
937
        with scoped_environ():
938
            os.environ['JUJU_HOME'] = 'foo'
939
            with patch('subprocess.check_call') as cc_mock:
940
                client.juju('foo', ('bar', 'baz'))
941
                self.assertEqual(cc_mock.mock_calls[0][2]['env']['JUJU_HOME'],
942
                                 'foo')
943
                client.juju('foo', ('bar', 'baz'), juju_home='asdf')
944
                self.assertEqual(cc_mock.mock_calls[1][2]['env']['JUJU_HOME'],
945
                                 'asdf')
946
966.2.1 by Curtis Hovey
Added extra_env, but tests fail.
947
    def test_juju_extra_env(self):
948
        env = Environment('qux', '')
949
        client = EnvJujuClient(env, None, None)
950
        extra_env = {'JUJU': '/juju'}
951
        with patch('sys.stdout'):
952
            with patch('subprocess.check_call') as mock:
966.2.2 by Curtis Hovey
pass extra_env={'JUJU': None} with the quickstart command.
953
                client.juju('quickstart', ('bar', 'baz'), extra_env=extra_env)
966.2.1 by Curtis Hovey
Added extra_env, but tests fail.
954
        env = dict(os.environ)
955
        env.update(extra_env)
956
        mock.assert_called_with(
966.2.2 by Curtis Hovey
pass extra_env={'JUJU': None} with the quickstart command.
957
            ('juju', '--show-log', 'quickstart', '-e', 'qux', 'bar', 'baz'),
966.2.1 by Curtis Hovey
Added extra_env, but tests fail.
958
            env=env)
966.2.2 by Curtis Hovey
pass extra_env={'JUJU': None} with the quickstart command.
959
        self.assertEqual('/juju', mock.call_args[1]['env']['JUJU'])
966.2.1 by Curtis Hovey
Added extra_env, but tests fail.
960
757.1.2 by Curtis Hovey
Updated backup file test.
961
    def test_juju_backup_with_tgz(self):
717.2.1 by Aaron Bentley
Extract EnvJujuClient.backup from assess_recovery.
962
        env = SimpleEnvironment('qux')
963
        client = EnvJujuClient(env, None, '/foobar/baz')
964
        with patch('subprocess.check_output',
965
                   return_value='foojuju-backup-24.tgzz') as co_mock:
966
            with patch('sys.stdout'):
967
                backup_file = client.backup()
968
        self.assertEqual(backup_file, os.path.abspath('juju-backup-24.tgz'))
969
        assert_juju_call(self, co_mock, client, ['juju', 'backup'])
970
        self.assertEqual(co_mock.mock_calls[0][2]['env']['JUJU_ENV'], 'qux')
971
757.1.2 by Curtis Hovey
Updated backup file test.
972
    def test_juju_backup_with_tar_gz(self):
973
        env = SimpleEnvironment('qux')
974
        client = EnvJujuClient(env, None, '/foobar/baz')
975
        with patch('subprocess.check_output',
754.2.13 by Aaron Bentley
Fix lint.
976
                   return_value='foojuju-backup-123-456.tar.gzbar'):
757.1.2 by Curtis Hovey
Updated backup file test.
977
            with patch('sys.stdout'):
978
                backup_file = client.backup()
979
        self.assertEqual(
980
            backup_file, os.path.abspath('juju-backup-123-456.tar.gz'))
981
717.2.1 by Aaron Bentley
Extract EnvJujuClient.backup from assess_recovery.
982
    def test_juju_backup_no_file(self):
983
        env = SimpleEnvironment('qux')
984
        client = EnvJujuClient(env, None, '/foobar/baz')
985
        with patch('subprocess.check_output', return_value=''):
986
            with self.assertRaisesRegexp(
987
                    Exception, 'The backup file was not found in output'):
988
                with patch('sys.stdout'):
989
                    client.backup()
990
991
    def test_juju_backup_wrong_file(self):
992
        env = SimpleEnvironment('qux')
993
        client = EnvJujuClient(env, None, '/foobar/baz')
994
        with patch('subprocess.check_output',
995
                   return_value='mumu-backup-24.tgz'):
996
            with self.assertRaisesRegexp(
997
                    Exception, 'The backup file was not found in output'):
998
                with patch('sys.stdout'):
999
                    client.backup()
1000
807.1.3 by Aaron Bentley
Get juju_async under test.
1001
    def test_juju_async(self):
1002
        env = SimpleEnvironment('qux')
1003
        client = EnvJujuClient(env, None, '/foobar/baz')
1004
        with patch('subprocess.Popen') as popen_class_mock:
1005
            with client.juju_async('foo', ('bar', 'baz')) as proc:
1006
                assert_juju_call(self, popen_class_mock, client, (
1007
                    'juju', '--show-log', 'foo', '-e', 'qux', 'bar', 'baz'))
1008
                self.assertIs(proc, popen_class_mock.return_value)
1009
                self.assertEqual(proc.wait.call_count, 0)
1010
                proc.wait.return_value = 0
1011
        proc.wait.assert_called_once_with()
1012
1013
    def test_juju_async_failure(self):
1014
        env = SimpleEnvironment('qux')
1015
        client = EnvJujuClient(env, None, '/foobar/baz')
1016
        with patch('subprocess.Popen') as popen_class_mock:
1017
            with self.assertRaises(subprocess.CalledProcessError) as err_cxt:
1018
                with client.juju_async('foo', ('bar', 'baz')):
1019
                    proc_mock = popen_class_mock.return_value
1020
                    proc_mock.wait.return_value = 23
1021
        self.assertEqual(err_cxt.exception.returncode, 23)
1022
        self.assertEqual(err_cxt.exception.cmd, (
1023
            'juju', '--show-log', 'foo', '-e', 'qux', 'bar', 'baz'))
1024
696.1.10 by Aaron Bentley
Move uniquify_local to jujupy.
1025
1026
class TestUniquifyLocal(TestCase):
1027
1028
    def test_uniquify_local_empty(self):
1029
        env = SimpleEnvironment('foo', {'type': 'local'})
1030
        uniquify_local(env)
1031
        self.assertEqual(env.config, {
1032
            'type': 'local',
1033
            'api-port': 17071,
1034
            'state-port': 37018,
1035
            'storage-port': 8041,
1036
            'syslog-port': 6515,
1037
        })
1038
1039
    def test_uniquify_local_preset(self):
1040
        env = SimpleEnvironment('foo', {
1041
            'type': 'local',
1042
            'api-port': 17071,
1043
            'state-port': 37018,
1044
            'storage-port': 8041,
1045
            'syslog-port': 6515,
1046
        })
1047
        uniquify_local(env)
1048
        self.assertEqual(env.config, {
1049
            'type': 'local',
1050
            'api-port': 17072,
1051
            'state-port': 37019,
1052
            'storage-port': 8042,
1053
            'syslog-port': 6516,
1054
        })
1055
1056
    def test_uniquify_nonlocal(self):
1057
        env = SimpleEnvironment('foo', {
1058
            'type': 'nonlocal',
1059
            'api-port': 17071,
1060
            'state-port': 37018,
1061
            'storage-port': 8041,
1062
            'syslog-port': 6515,
1063
        })
1064
        uniquify_local(env)
1065
        self.assertEqual(env.config, {
1066
            'type': 'nonlocal',
1067
            'api-port': 17071,
1068
            'state-port': 37018,
1069
            'storage-port': 8041,
1070
            'syslog-port': 6515,
1071
        })
1072
1073
663.1.2 by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests.
1074
@contextmanager
1075
def bootstrap_context(client):
663.1.8 by Aaron Bentley
Extract temp_bootstrap_env from bootstrap_from_env.
1076
    # Avoid unnecessary syscalls.
1077
    with patch('jujupy.check_free_disk_space'):
1078
        with scoped_environ():
1079
            with temp_dir() as fake_home:
1080
                os.environ['JUJU_HOME'] = fake_home
1081
                yield fake_home
663.1.2 by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests.
1082
1083
663.1.7 by Aaron Bentley
Add --upload-tools to assess-heterogeneous-control.
1084
def stub_bootstrap(upload_tools=False):
663.1.2 by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests.
1085
    jenv_path = get_jenv_path(os.environ['JUJU_HOME'], 'qux')
1086
    os.mkdir(os.path.dirname(jenv_path))
1087
    with open(jenv_path, 'w') as f:
1088
        f.write('Bogus jenv')
1089
1090
663.1.8 by Aaron Bentley
Extract temp_bootstrap_env from bootstrap_from_env.
1091
class TestTempJujuEnv(TestCase):
663.1.2 by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests.
1092
1093
    def test_no_config_mangling_side_effect(self):
1094
        env = SimpleEnvironment('qux', {'type': 'local'})
1095
        client = EnvJujuClient.by_version(env)
1096
        with bootstrap_context(client) as fake_home:
663.1.8 by Aaron Bentley
Extract temp_bootstrap_env from bootstrap_from_env.
1097
            with temp_bootstrap_env(fake_home, client):
1098
                stub_bootstrap()
663.1.2 by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests.
1099
        self.assertEqual(env.config, {'type': 'local'})
1100
663.1.8 by Aaron Bentley
Extract temp_bootstrap_env from bootstrap_from_env.
1101
    def test_temp_bootstrap_env_environment(self):
663.1.2 by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests.
1102
        env = SimpleEnvironment('qux', {'type': 'local'})
1103
        client = EnvJujuClient.by_version(env)
1104
        agent_version = client.get_matching_agent_version()
1105
        with bootstrap_context(client) as fake_home:
663.1.8 by Aaron Bentley
Extract temp_bootstrap_env from bootstrap_from_env.
1106
            with temp_bootstrap_env(fake_home, client):
663.1.2 by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests.
1107
                temp_home = os.environ['JUJU_HOME']
1108
                self.assertNotEqual(temp_home, fake_home)
1109
                symlink_path = get_jenv_path(fake_home, 'qux')
1110
                symlink_target = os.path.realpath(symlink_path)
977.1.2 by Curtis Hovey
Use realpath to ensure real symlink matches expected.
1111
                expected_target = os.path.realpath(
1112
                    get_jenv_path(temp_home, 'qux'))
663.1.2 by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests.
1113
                self.assertEqual(symlink_target, expected_target)
1114
                config = yaml.safe_load(
1115
                    open(get_environments_path(temp_home)))
1116
                self.assertEqual(config, {'environments': {'qux': {
1117
                    'type': 'local',
1118
                    'root-dir': get_local_root(fake_home, client.env),
1119
                    'agent-version': agent_version,
709.1.2 by Aaron Bentley
Comma.
1120
                    'test-mode': True,
953.3.9 by Nate Finch
more code review changes
1121
                }}})
663.1.2 by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests.
1122
                stub_bootstrap()
1123
807.1.1 by Aaron Bentley
Avoid creating temp juju homes inside temp juju homes.
1124
    def test_temp_bootstrap_env_provides_dir(self):
1125
        env = SimpleEnvironment('qux', {'type': 'local'})
1126
        client = EnvJujuClient.by_version(env)
1127
        with temp_dir() as fake_home:
1128
            juju_home = os.path.join(fake_home, 'asdf')
1129
1130
            def side_effect(*args, **kwargs):
1131
                os.mkdir(juju_home)
1132
                return juju_home
1133
1134
            with patch('utility.mkdtemp', side_effect=side_effect):
1135
                with temp_bootstrap_env(fake_home, client) as temp_home:
1136
                    pass
1137
        self.assertEqual(temp_home, juju_home)
1138
1139
    def test_temp_bootstrap_env_no_set_home(self):
1140
        env = SimpleEnvironment('qux', {'type': 'local'})
1141
        client = EnvJujuClient.by_version(env)
1142
        with temp_dir() as fake_home:
1143
            with scoped_environ():
1144
                os.environ['JUJU_HOME'] = 'foo'
1145
                with temp_bootstrap_env(fake_home, client, set_home=False):
1146
                    self.assertEqual(os.environ['JUJU_HOME'], 'foo')
1147
663.1.2 by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests.
1148
    def test_output(self):
1149
        env = SimpleEnvironment('qux', {'type': 'local'})
1150
        client = EnvJujuClient.by_version(env)
1151
        with bootstrap_context(client) as fake_home:
663.1.8 by Aaron Bentley
Extract temp_bootstrap_env from bootstrap_from_env.
1152
            with temp_bootstrap_env(fake_home, client):
1153
                stub_bootstrap()
663.1.2 by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests.
1154
            jenv_path = get_jenv_path(fake_home, 'qux')
1155
            self.assertFalse(os.path.islink(jenv_path))
1156
            self.assertEqual(open(jenv_path).read(), 'Bogus jenv')
1157
1158
    def test_rename_on_exception(self):
1159
        env = SimpleEnvironment('qux', {'type': 'local'})
1160
        client = EnvJujuClient.by_version(env)
1161
        with bootstrap_context(client) as fake_home:
663.1.8 by Aaron Bentley
Extract temp_bootstrap_env from bootstrap_from_env.
1162
            with self.assertRaisesRegexp(Exception, 'test-rename'):
1163
                with temp_bootstrap_env(fake_home, client):
1164
                    stub_bootstrap()
1165
                    raise Exception('test-rename')
663.1.2 by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests.
1166
            jenv_path = get_jenv_path(os.environ['JUJU_HOME'], 'qux')
1167
            self.assertFalse(os.path.islink(jenv_path))
1168
            self.assertEqual(open(jenv_path).read(), 'Bogus jenv')
1169
1170
    def test_exception_no_jenv(self):
1171
        env = SimpleEnvironment('qux', {'type': 'local'})
1172
        client = EnvJujuClient.by_version(env)
1173
        with bootstrap_context(client) as fake_home:
663.1.8 by Aaron Bentley
Extract temp_bootstrap_env from bootstrap_from_env.
1174
            with self.assertRaisesRegexp(Exception, 'test-rename'):
1175
                with temp_bootstrap_env(fake_home, client):
1176
                    jenv_path = get_jenv_path(os.environ['JUJU_HOME'], 'qux')
1177
                    os.mkdir(os.path.dirname(jenv_path))
1178
                    raise Exception('test-rename')
663.1.2 by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests.
1179
            jenv_path = get_jenv_path(os.environ['JUJU_HOME'], 'qux')
1180
            self.assertFalse(os.path.lexists(jenv_path))
1181
1182
    def test_check_space_local_lxc(self):
1183
        env = SimpleEnvironment('qux', {'type': 'local'})
1184
        client = EnvJujuClient.by_version(env)
1185
        with bootstrap_context(client) as fake_home:
1186
            with patch('jujupy.check_free_disk_space') as mock_cfds:
663.1.8 by Aaron Bentley
Extract temp_bootstrap_env from bootstrap_from_env.
1187
                with temp_bootstrap_env(fake_home, client):
1188
                    stub_bootstrap()
663.1.2 by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests.
1189
        self.assertEqual(mock_cfds.mock_calls, [
1190
            call(os.path.join(fake_home, 'qux'), 8000000, 'MongoDB files'),
1191
            call('/var/lib/lxc', 2000000, 'LXC containers'),
953.3.9 by Nate Finch
more code review changes
1192
        ])
663.1.2 by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests.
1193
1194
    def test_check_space_local_kvm(self):
1195
        env = SimpleEnvironment('qux', {'type': 'local', 'container': 'kvm'})
1196
        client = EnvJujuClient.by_version(env)
1197
        with bootstrap_context(client) as fake_home:
1198
            with patch('jujupy.check_free_disk_space') as mock_cfds:
663.1.8 by Aaron Bentley
Extract temp_bootstrap_env from bootstrap_from_env.
1199
                with temp_bootstrap_env(fake_home, client):
1200
                    stub_bootstrap()
663.1.2 by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests.
1201
        self.assertEqual(mock_cfds.mock_calls, [
1202
            call(os.path.join(fake_home, 'qux'), 8000000, 'MongoDB files'),
1203
            call('/var/lib/uvtool/libvirt/images', 2000000, 'KVM disk files'),
953.3.9 by Nate Finch
more code review changes
1204
        ])
663.1.2 by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests.
1205
1206
    def test_error_on_jenv(self):
1207
        env = SimpleEnvironment('qux', {'type': 'local'})
1208
        client = EnvJujuClient.by_version(env)
1209
        with bootstrap_context(client) as fake_home:
1210
            jenv_path = get_jenv_path(fake_home, 'qux')
1211
            os.mkdir(os.path.dirname(jenv_path))
1212
            with open(jenv_path, 'w') as f:
1213
                f.write('In the way')
1214
            with self.assertRaisesRegexp(Exception, '.* already exists!'):
663.1.8 by Aaron Bentley
Extract temp_bootstrap_env from bootstrap_from_env.
1215
                with temp_bootstrap_env(fake_home, client):
1216
                    stub_bootstrap()
663.1.7 by Aaron Bentley
Add --upload-tools to assess-heterogeneous-control.
1217
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
1218
1219
class TestJujuClientDevel(TestCase):
1220
1221
    def test_get_version(self):
1222
        value = ' 5.6 \n'
1223
        with patch('subprocess.check_output', return_value=value) as vsn:
1224
            version = JujuClientDevel.get_version()
1225
        self.assertEqual('5.6', version)
1226
        vsn.assert_called_with(('juju', '--version'))
1227
1228
    def test_by_version(self):
1229
        def juju_cmd_iterator():
1230
            yield '1.17'
1231
            yield '1.16'
1232
            yield '1.16.1'
1233
            yield '1.15'
1234
1235
        context = patch.object(
1236
            JujuClientDevel, 'get_version',
650.1.14 by Aaron Bentley
Remove now-unused support for JujuClientDevel path.
1237
            side_effect=juju_cmd_iterator().next)
657.1.2 by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel.
1238
        with context:
1239
            self.assertIs(JujuClientDevel,
1240
                          type(JujuClientDevel.by_version()))
1241
            with self.assertRaisesRegexp(Exception, 'Unsupported juju: 1.16'):
1242
                JujuClientDevel.by_version()
1243
            with self.assertRaisesRegexp(Exception,
1244
                                         'Unsupported juju: 1.16.1'):
1245
                JujuClientDevel.by_version()
1246
            client = JujuClientDevel.by_version()
1247
            self.assertIs(JujuClientDevel, type(client))
1248
            self.assertEqual('1.15', client.version)
1249
1250
    def test_bootstrap_hpcloud(self):
1251
        env = Environment('hp', '')
1252
        with patch.object(env, 'hpcloud', lambda: True):
1253
            with patch.object(EnvJujuClient, 'juju') as mock:
139 by Curtis Hovey
Added hpcloud attribute based on hp auth-url. hpcloud uses 4G to ensure mysql starts.
1254
                JujuClientDevel(None, None).bootstrap(env)
1255
            mock.assert_called_with(
807.1.1 by Aaron Bentley
Avoid creating temp juju homes inside temp juju homes.
1256
                'bootstrap', ('--constraints', 'mem=2G'), False,
1257
                juju_home=None)
139 by Curtis Hovey
Added hpcloud attribute based on hp auth-url. hpcloud uses 4G to ensure mysql starts.
1258
34.1.1 by Aaron Bentley
Flesh out JujuClientDevel tests.
1259
    def test_bootstrap_non_sudo(self):
34.1.3 by Aaron Bentley
Add status and environment tests.
1260
        env = Environment('foo', '')
657.1.3 by Aaron Bentley
Extract SimpleEnvironment from Environment.
1261
        with patch.object(SimpleEnvironment, 'needs_sudo', return_value=False):
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
1262
            with patch.object(EnvJujuClient, 'juju') as mock:
107.1.2 by Aaron Bentley
Use full path when running under sudo.
1263
                JujuClientDevel(None, None).bootstrap(env)
34.1.1 by Aaron Bentley
Flesh out JujuClientDevel tests.
1264
            mock.assert_called_with(
807.1.1 by Aaron Bentley
Avoid creating temp juju homes inside temp juju homes.
1265
                'bootstrap', ('--constraints', 'mem=2G'), False,
1266
                juju_home=None)
34.1.1 by Aaron Bentley
Flesh out JujuClientDevel tests.
1267
1268
    def test_bootstrap_sudo(self):
34.1.3 by Aaron Bentley
Add status and environment tests.
1269
        env = Environment('foo', '')
107.1.2 by Aaron Bentley
Use full path when running under sudo.
1270
        client = JujuClientDevel(None, None)
657.1.3 by Aaron Bentley
Extract SimpleEnvironment from Environment.
1271
        with patch.object(SimpleEnvironment, 'needs_sudo', return_value=True):
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
1272
            with patch.object(EnvJujuClient, 'juju') as mock:
107.1.2 by Aaron Bentley
Use full path when running under sudo.
1273
                client.bootstrap(env)
34.1.1 by Aaron Bentley
Flesh out JujuClientDevel tests.
1274
            mock.assert_called_with(
807.1.1 by Aaron Bentley
Avoid creating temp juju homes inside temp juju homes.
1275
                'bootstrap', ('--constraints', 'mem=2G'), True, juju_home=None)
34.1.1 by Aaron Bentley
Flesh out JujuClientDevel tests.
1276
1277
    def test_destroy_environment_non_sudo(self):
34.1.3 by Aaron Bentley
Add status and environment tests.
1278
        env = Environment('foo', '')
107.1.2 by Aaron Bentley
Use full path when running under sudo.
1279
        client = JujuClientDevel(None, None)
657.1.3 by Aaron Bentley
Extract SimpleEnvironment from Environment.
1280
        with patch.object(SimpleEnvironment, 'needs_sudo', return_value=False):
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
1281
            with patch.object(EnvJujuClient, 'juju') as mock:
107.1.2 by Aaron Bentley
Use full path when running under sudo.
1282
                client.destroy_environment(env)
34.1.1 by Aaron Bentley
Flesh out JujuClientDevel tests.
1283
            mock.assert_called_with(
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
1284
                'destroy-environment', ('foo', '--force', '-y'),
768.1.1 by Aaron Bentley
Add timeout to EnvJujuClient.destroy_environment.
1285
                False, check=False, include_e=False, timeout=600)
34.1.1 by Aaron Bentley
Flesh out JujuClientDevel tests.
1286
34.1.2 by Aaron Bentley
Get Client16 under test.
1287
    def test_destroy_environment_sudo(self):
34.1.3 by Aaron Bentley
Add status and environment tests.
1288
        env = Environment('foo', '')
107.1.2 by Aaron Bentley
Use full path when running under sudo.
1289
        client = JujuClientDevel(None, None)
657.1.3 by Aaron Bentley
Extract SimpleEnvironment from Environment.
1290
        with patch.object(SimpleEnvironment, 'needs_sudo', return_value=True):
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
1291
            with patch.object(EnvJujuClient, 'juju') as mock:
107.1.2 by Aaron Bentley
Use full path when running under sudo.
1292
                client.destroy_environment(env)
34.1.1 by Aaron Bentley
Flesh out JujuClientDevel tests.
1293
            mock.assert_called_with(
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
1294
                'destroy-environment', ('foo', '--force', '-y'),
768.1.1 by Aaron Bentley
Add timeout to EnvJujuClient.destroy_environment.
1295
                True, check=False, include_e=False, timeout=600.0)
34.1.1 by Aaron Bentley
Flesh out JujuClientDevel tests.
1296
1297
    def test_get_juju_output(self):
34.1.3 by Aaron Bentley
Add status and environment tests.
1298
        env = Environment('foo', '')
652 by Aaron Bentley
Insert path-to-juju in PATH, fix test_assess_recovery
1299
        asdf = lambda x, stderr, env: 'asdf'
107.1.2 by Aaron Bentley
Use full path when running under sudo.
1300
        client = JujuClientDevel(None, None)
34.1.1 by Aaron Bentley
Flesh out JujuClientDevel tests.
1301
        with patch('subprocess.check_output', side_effect=asdf) as mock:
107.1.2 by Aaron Bentley
Use full path when running under sudo.
1302
            result = client.get_juju_output(env, 'bar')
34.1.1 by Aaron Bentley
Flesh out JujuClientDevel tests.
1303
        self.assertEqual('asdf', result)
139.1.2 by Aaron Bentley
Write stderr to a temp file.
1304
        self.assertEqual((('juju', '--show-log', 'bar', '-e', 'foo'),),
1305
                         mock.call_args[0])
1306
357.1.1 by Aaron Bentley
Add option to deploy a dummy stack.
1307
    def test_get_juju_output_accepts_varargs(self):
1308
        env = Environment('foo', '')
652 by Aaron Bentley
Insert path-to-juju in PATH, fix test_assess_recovery
1309
        asdf = lambda x, stderr, env: 'asdf'
357.1.1 by Aaron Bentley
Add option to deploy a dummy stack.
1310
        client = JujuClientDevel(None, None)
1311
        with patch('subprocess.check_output', side_effect=asdf) as mock:
1312
            result = client.get_juju_output(env, 'bar', 'baz', '--qux')
1313
        self.assertEqual('asdf', result)
1314
        self.assertEqual((('juju', '--show-log', 'bar', '-e', 'foo', 'baz',
1315
                           '--qux'),), mock.call_args[0])
1316
139.1.2 by Aaron Bentley
Write stderr to a temp file.
1317
    def test_get_juju_output_stderr(self):
652 by Aaron Bentley
Insert path-to-juju in PATH, fix test_assess_recovery
1318
        def raise_without_stderr(args, stderr, env):
139.1.2 by Aaron Bentley
Write stderr to a temp file.
1319
            stderr.write('Hello!')
1320
            raise subprocess.CalledProcessError('a', 'b')
1321
        env = Environment('foo', '')
1322
        client = JujuClientDevel(None, None)
1323
        with self.assertRaises(subprocess.CalledProcessError) as exc:
1324
            with patch('subprocess.check_output', raise_without_stderr):
146 by Curtis Hovey
Hush lint.
1325
                client.get_juju_output(env, 'bar')
139.1.2 by Aaron Bentley
Write stderr to a temp file.
1326
        self.assertEqual(exc.exception.stderr, 'Hello!')
34.1.1 by Aaron Bentley
Flesh out JujuClientDevel tests.
1327
372.1.2 by Aaron Bentley
Support supplying timeout to calls.
1328
    def test_get_juju_output_accepts_timeout(self):
1329
        env = Environment('foo', '')
1330
        client = JujuClientDevel(None, None)
1331
        with patch('subprocess.check_output') as sco_mock:
1332
            client.get_juju_output(env, 'bar', timeout=5)
706.1.1 by Aaron Bentley
Switch to flake8 and fix lint.
1333
        self.assertEqual(
1334
            sco_mock.call_args[0][0],
372.1.2 by Aaron Bentley
Support supplying timeout to calls.
1335
            ('timeout', '5.00s', 'juju', '--show-log', 'bar', '-e', 'foo'))
1336
652 by Aaron Bentley
Insert path-to-juju in PATH, fix test_assess_recovery
1337
    def test_juju_output_supplies_path(self):
1338
        env = Environment('foo', '')
1339
        client = JujuClientDevel(None, '/foobar/bar')
1340
        with patch('subprocess.check_output') as sco_mock:
1341
            client.get_juju_output(env, 'baz')
1342
        self.assertRegexpMatches(sco_mock.call_args[1]['env']['PATH'],
1343
                                 r'/foobar\:')
1344
34.1.1 by Aaron Bentley
Flesh out JujuClientDevel tests.
1345
    def test_get_status(self):
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
1346
        output_text = yield dedent("""\
34.1.1 by Aaron Bentley
Flesh out JujuClientDevel tests.
1347
                - a
1348
                - b
1349
                - c
1350
                """)
657.1.7 by Aaron Bentley
Clean-up.
1351
        client = JujuClientDevel(None, None)
34.1.3 by Aaron Bentley
Add status and environment tests.
1352
        env = Environment('foo', '')
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
1353
        with patch.object(EnvJujuClient, 'get_juju_output',
1354
                          return_value=output_text):
1355
            result = client.get_status(env)
34.1.3 by Aaron Bentley
Add status and environment tests.
1356
        self.assertEqual(Status, type(result))
1357
        self.assertEqual(['a', 'b', 'c'], result.status)
34.1.1 by Aaron Bentley
Flesh out JujuClientDevel tests.
1358
331.1.1 by Aaron Bentley
juju status retries on error for 30 seconds.
1359
    def test_get_status_retries_on_error(self):
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
1360
        client = JujuClientDevel(None, None)
331.1.1 by Aaron Bentley
juju status retries on error for 30 seconds.
1361
        client.attempt = 0
706.1.1 by Aaron Bentley
Switch to flake8 and fix lint.
1362
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
1363
        def get_juju_output(command, *args):
331.1.1 by Aaron Bentley
juju status retries on error for 30 seconds.
1364
            if client.attempt == 1:
1365
                return '"hello"'
1366
            client.attempt += 1
1367
            raise subprocess.CalledProcessError(1, command)
1368
1369
        env = Environment('foo', '')
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
1370
        with patch.object(EnvJujuClient, 'get_juju_output', get_juju_output):
353 by Curtis Hovey
Added get_env_option().
1371
            client.get_status(env)
331.1.1 by Aaron Bentley
juju status retries on error for 30 seconds.
1372
500 by Curtis Hovey
Only strip the series and arch from the build juju version.
1373
    def test_get_status_raises_on_timeout_1(self):
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
1374
        client = JujuClientDevel(None, None)
706.1.1 by Aaron Bentley
Switch to flake8 and fix lint.
1375
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
1376
        def get_juju_output(command):
331.1.1 by Aaron Bentley
juju status retries on error for 30 seconds.
1377
            raise subprocess.CalledProcessError(1, command)
1378
1379
        env = Environment('foo', '')
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
1380
        with patch.object(EnvJujuClient, 'get_juju_output',
1381
                          side_effect=get_juju_output):
331.1.1 by Aaron Bentley
juju status retries on error for 30 seconds.
1382
            with patch('jujupy.until_timeout', lambda x: iter([None, None])):
1383
                with self.assertRaisesRegexp(
1384
                        Exception, 'Timed out waiting for juju status'):
353 by Curtis Hovey
Added get_env_option().
1385
                    client.get_status(env)
1386
500 by Curtis Hovey
Only strip the series and arch from the build juju version.
1387
    def test_get_status_raises_on_timeout_2(self):
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
1388
        client = JujuClientDevel(None, None)
419.1.13 by Aaron Bentley
Allow 10 minutes for status to start working again.
1389
        env = Environment('foo', '')
1390
        with patch('jujupy.until_timeout', return_value=iter([1])) as mock_ut:
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
1391
            with patch.object(EnvJujuClient, 'get_juju_output',
1392
                              side_effect=StopIteration):
1393
                with self.assertRaises(StopIteration):
1394
                    client.get_status(env, 500)
419.1.13 by Aaron Bentley
Allow 10 minutes for status to start working again.
1395
        mock_ut.assert_called_with(500)
1396
353 by Curtis Hovey
Added get_env_option().
1397
    def test_get_env_option(self):
361 by Curtis Hovey
Use get_juju_output() to get the env option.
1398
        client = JujuClientDevel(None, None)
353 by Curtis Hovey
Added get_env_option().
1399
        env = Environment('foo', '')
361 by Curtis Hovey
Use get_juju_output() to get the env option.
1400
        with patch('subprocess.check_output') as mock:
353 by Curtis Hovey
Added get_env_option().
1401
            mock.return_value = 'https://example.org/juju/tools'
1402
            result = client.get_env_option(env, 'tools-metadata-url')
361 by Curtis Hovey
Use get_juju_output() to get the env option.
1403
        self.assertEqual(
1404
            mock.call_args[0][0],
353 by Curtis Hovey
Added get_env_option().
1405
            ('juju', '--show-log', 'get-env', '-e', 'foo',
1406
             'tools-metadata-url'))
1407
        self.assertEqual('https://example.org/juju/tools', result)
331.1.1 by Aaron Bentley
juju status retries on error for 30 seconds.
1408
354 by Curtis Hovey
Added set_env_option().
1409
    def test_set_env_option(self):
362 by Curtis Hovey
Use the real client in the tests. Check the call_count()
1410
        client = JujuClientDevel(None, None)
354 by Curtis Hovey
Added set_env_option().
1411
        env = Environment('foo', '')
1412
        with patch('subprocess.check_call') as mock:
1413
            client.set_env_option(
357 by Curtis Hovey
Separate option from values. The callee doesn't need to
1414
                env, 'tools-metadata-url', 'https://example.org/juju/tools')
354 by Curtis Hovey
Added set_env_option().
1415
        mock.assert_called_with(
1416
            ('juju', '--show-log', 'set-env', '-e', 'foo',
652 by Aaron Bentley
Insert path-to-juju in PATH, fix test_assess_recovery
1417
             'tools-metadata-url=https://example.org/juju/tools'),
1418
            env=os.environ)
354 by Curtis Hovey
Added set_env_option().
1419
34.1.1 by Aaron Bentley
Flesh out JujuClientDevel tests.
1420
    def test_juju(self):
34.1.3 by Aaron Bentley
Add status and environment tests.
1421
        env = Environment('qux', '')
107.1.2 by Aaron Bentley
Use full path when running under sudo.
1422
        client = JujuClientDevel(None, None)
34.1.1 by Aaron Bentley
Flesh out JujuClientDevel tests.
1423
        with patch('sys.stdout') as stdout_mock:
1424
            with patch('subprocess.check_call') as mock:
107.1.2 by Aaron Bentley
Use full path when running under sudo.
1425
                client.juju(env, 'foo', ('bar', 'baz'))
112.1.2 by Aaron Bentley
Add --show-log to all juju commands.
1426
        mock.assert_called_with(('juju', '--show-log', 'foo', '-e', 'qux',
652 by Aaron Bentley
Insert path-to-juju in PATH, fix test_assess_recovery
1427
                                 'bar', 'baz'), env=os.environ)
34.1.1 by Aaron Bentley
Flesh out JujuClientDevel tests.
1428
        stdout_mock.flush.assert_called_with()
1429
652 by Aaron Bentley
Insert path-to-juju in PATH, fix test_assess_recovery
1430
    def test_juju_env(self):
1431
        env = Environment('qux', '')
1432
        client = JujuClientDevel(None, '/foobar/baz')
1433
        with patch('subprocess.check_call') as cc_mock:
1434
            client.juju(env, 'foo', ('bar', 'baz'))
1435
        self.assertRegexpMatches(cc_mock.call_args[1]['env']['PATH'],
1436
                                 r'/foobar\:')
1437
34.1.1 by Aaron Bentley
Flesh out JujuClientDevel tests.
1438
    def test_juju_no_check(self):
34.1.3 by Aaron Bentley
Add status and environment tests.
1439
        env = Environment('qux', '')
107.1.2 by Aaron Bentley
Use full path when running under sudo.
1440
        client = JujuClientDevel(None, None)
34.1.1 by Aaron Bentley
Flesh out JujuClientDevel tests.
1441
        with patch('sys.stdout') as stdout_mock:
1442
            with patch('subprocess.call') as mock:
107.1.2 by Aaron Bentley
Use full path when running under sudo.
1443
                client.juju(env, 'foo', ('bar', 'baz'), check=False)
112.1.2 by Aaron Bentley
Add --show-log to all juju commands.
1444
        mock.assert_called_with(('juju', '--show-log', 'foo', '-e', 'qux',
652 by Aaron Bentley
Insert path-to-juju in PATH, fix test_assess_recovery
1445
                                 'bar', 'baz'), env=os.environ)
34.1.1 by Aaron Bentley
Flesh out JujuClientDevel tests.
1446
        stdout_mock.flush.assert_called_with()
34.1.2 by Aaron Bentley
Get Client16 under test.
1447
652 by Aaron Bentley
Insert path-to-juju in PATH, fix test_assess_recovery
1448
    def test_juju_no_check_env(self):
1449
        env = Environment('qux', '')
1450
        client = JujuClientDevel(None, '/foobar/baz')
1451
        with patch('subprocess.call') as call_mock:
1452
            client.juju(env, 'foo', ('bar', 'baz'), check=False)
1453
        self.assertRegexpMatches(call_mock.call_args[1]['env']['PATH'],
1454
                                 r'/foobar\:')
34.1.2 by Aaron Bentley
Get Client16 under test.
1455
706.1.1 by Aaron Bentley
Switch to flake8 and fix lint.
1456
34.1.3 by Aaron Bentley
Add status and environment tests.
1457
class TestStatus(TestCase):
1458
805.1.3 by Aaron Bentley
Allow iterating through container machines.
1459
    def test_iter_machines_no_containers(self):
1460
        status = Status({
1461
            'machines': {
1462
                '1': {'foo': 'bar', 'containers': {'1/lxc/0': {'baz': 'qux'}}}
1463
            },
1464
            'services': {}}, '')
1465
        self.assertEqual(list(status.iter_machines()),
1466
                         [('1', status.status['machines']['1'])])
1467
1468
    def test_iter_machines_containers(self):
1469
        status = Status({
1470
            'machines': {
1471
                '1': {'foo': 'bar', 'containers': {'1/lxc/0': {'baz': 'qux'}}}
1472
            },
1473
            'services': {}}, '')
1474
        self.assertEqual(list(status.iter_machines(containers=True)), [
1475
            ('1', status.status['machines']['1']),
1476
            ('1/lxc/0', {'baz': 'qux'}),
953.3.9 by Nate Finch
more code review changes
1477
        ])
805.1.3 by Aaron Bentley
Allow iterating through container machines.
1478
34.1.3 by Aaron Bentley
Add status and environment tests.
1479
    def test_agent_items_empty(self):
754.1.1 by Aaron Bentley
Emit status text for agent timeout.
1480
        status = Status({'machines': {}, 'services': {}}, '')
34.1.3 by Aaron Bentley
Add status and environment tests.
1481
        self.assertItemsEqual([], status.agent_items())
1482
1483
    def test_agent_items(self):
1484
        status = Status({
1485
            'machines': {
1486
                '1': {'foo': 'bar'}
1487
            },
1488
            'services': {
1489
                'jenkins': {
1490
                    'units': {
966.1.5 by John George
Add support for running actions through jujupy and support for listing subordinates when jujupy.agent_items is called.
1491
                        'jenkins/1': {
1492
                            'subordinates': {
1493
                                'sub': {'baz': 'qux'}
1494
                            }
1495
                        }
34.1.3 by Aaron Bentley
Add status and environment tests.
1496
                    }
1497
                }
1498
            }
754.1.1 by Aaron Bentley
Emit status text for agent timeout.
1499
        }, '')
34.1.3 by Aaron Bentley
Add status and environment tests.
1500
        expected = [
966.1.5 by John George
Add support for running actions through jujupy and support for listing subordinates when jujupy.agent_items is called.
1501
            ('1', {'foo': 'bar'}),
1502
            ('jenkins/1', {'subordinates': {'sub': {'baz': 'qux'}}}),
1503
            ('sub', {'baz': 'qux'})]
34.1.3 by Aaron Bentley
Add status and environment tests.
1504
        self.assertItemsEqual(expected, status.agent_items())
1505
650.1.16 by Aaron Bentley
Test agent_items with containers.
1506
    def test_agent_items_containers(self):
1507
        status = Status({
1508
            'machines': {
1509
                '1': {'foo': 'bar', 'containers': {
1510
                    '2': {'qux': 'baz'},
1511
                }}
1512
            },
1513
            'services': {}
754.1.1 by Aaron Bentley
Emit status text for agent timeout.
1514
        }, '')
650.1.16 by Aaron Bentley
Test agent_items with containers.
1515
        expected = [
706.1.1 by Aaron Bentley
Switch to flake8 and fix lint.
1516
            ('1', {'foo': 'bar', 'containers': {'2': {'qux': 'baz'}}}),
1517
            ('2', {'qux': 'baz'})
650.1.16 by Aaron Bentley
Test agent_items with containers.
1518
        ]
1519
        self.assertItemsEqual(expected, status.agent_items())
1520
796.2.1 by John George
Add quickstart_deploy.py
1521
    def test_get_service_count_zero(self):
1522
        status = Status({
1523
            'machines': {
1524
                '1': {'agent-state': 'good'},
1525
                '2': {},
1526
            },
1527
        }, '')
1528
        self.assertEqual(0, status.get_service_count())
1529
1530
    def test_get_service_count(self):
1531
        status = Status({
1532
            'machines': {
1533
                '1': {'agent-state': 'good'},
1534
                '2': {},
1535
            },
1536
            'services': {
1537
                'jenkins': {
1538
                    'units': {
1539
                        'jenkins/1': {'agent-state': 'bad'},
1540
                    }
1541
                },
1542
                'dummy-sink': {
1543
                    'units': {
1544
                        'dummy-sink/0': {'agent-state': 'started'},
1545
                    }
1546
                },
1547
                'juju-reports': {
1548
                    'units': {
1549
                        'juju-reports/0': {'agent-state': 'pending'},
1550
                    }
1551
                }
1552
            }
1553
        }, '')
1554
        self.assertEqual(3, status.get_service_count())
1555
966.1.10 by John George
Check that subordinate unit count matches service unit count in wait_for_subordinate_units.
1556
    def test_get_service_unit_count_zero(self):
1557
        status = Status({
1558
            'machines': {
1559
                '1': {'agent-state': 'good'},
1560
                '2': {},
1561
            },
1562
        }, '')
1563
        self.assertEqual(0, status.get_service_unit_count('jenkins'))
1564
1565
    def test_get_service_unit_count(self):
1566
        status = Status({
1567
            'machines': {
1568
                '1': {'agent-state': 'good'},
1569
                '2': {},
1570
            },
1571
            'services': {
1572
                'jenkins': {
1573
                    'units': {
1574
                        'jenkins/1': {'agent-state': 'bad'},
1575
                        'jenkins/2': {'agent-state': 'bad'},
1576
                        'jenkins/3': {'agent-state': 'bad'},
1577
                    }
1578
                }
1579
            }
1580
        }, '')
1581
        self.assertEqual(3, status.get_service_unit_count('jenkins'))
1582
874.2.7 by Aaron Bentley
Add unit tests for jujupy newness.
1583
    def test_get_unit(self):
1584
        status = Status({
954.1.10 by Curtis Hovey
Hush lint.
1585
            'machines': {
1586
                '1': {},
1587
            },
874.2.7 by Aaron Bentley
Add unit tests for jujupy newness.
1588
            'services': {
1589
                'jenkins': {
1590
                    'units': {
1591
                        'jenkins/1': {'agent-state': 'bad'},
1592
                    }
1593
                },
1594
                'dummy-sink': {
1595
                    'units': {
1596
                        'jenkins/2': {'agent-state': 'started'},
1597
                    }
1598
                },
1599
            }
1600
        }, '')
1601
        self.assertEqual(
1602
            status.get_unit('jenkins/1'), {'agent-state': 'bad'})
1603
        self.assertEqual(
1604
            status.get_unit('jenkins/2'), {'agent-state': 'started'})
1605
        with self.assertRaisesRegexp(KeyError, 'jenkins/3'):
1606
            status.get_unit('jenkins/3')
1607
966.1.9 by John George
Add wait_for_subordinate_unit to jujupy.py.
1608
    def test_service_subordinate_units(self):
966.1.1 by John George
Add get_subordinate_units() to jujupy.Status.
1609
        status = Status({
1610
            'machines': {
1611
                '1': {},
1612
            },
1613
            'services': {
1614
                'ubuntu': {},
1615
                'jenkins': {
1616
                    'units': {
1617
                        'jenkins/1': {
1618
                            'subordinates': {
1619
                                'chaos-monkey/0': {'agent-state': 'started'},
1620
                            }
1621
                        }
1622
                    }
1623
                },
1624
                'dummy-sink': {
1625
                    'units': {
1626
                        'jenkins/2': {
1627
                            'subordinates': {
1628
                                'chaos-monkey/1': {'agent-state': 'started'}
1629
                            }
1630
                        },
1631
                        'jenkins/3': {
1632
                            'subordinates': {
1633
                                'chaos-monkey/2': {'agent-state': 'started'}
1634
                            }
1635
                        }
1636
                    }
1637
                }
1638
            }
1639
        }, '')
966.1.9 by John George
Add wait_for_subordinate_unit to jujupy.py.
1640
        self.assertItemsEqual(
1641
            status.service_subordinate_units('ubuntu'),
966.1.1 by John George
Add get_subordinate_units() to jujupy.Status.
1642
            [])
966.1.9 by John George
Add wait_for_subordinate_unit to jujupy.py.
1643
        self.assertItemsEqual(
1644
            status.service_subordinate_units('jenkins'),
1645
            [('chaos-monkey/0', {'agent-state': 'started'},)])
1646
        self.assertItemsEqual(
1647
            status.service_subordinate_units('dummy-sink'), [
1648
                ('chaos-monkey/1', {'agent-state': 'started'}),
1649
                ('chaos-monkey/2', {'agent-state': 'started'})]
966.1.1 by John George
Add get_subordinate_units() to jujupy.Status.
1650
            )
1651
874.2.7 by Aaron Bentley
Add unit tests for jujupy newness.
1652
    def test_get_open_ports(self):
1653
        status = Status({
954.1.10 by Curtis Hovey
Hush lint.
1654
            'machines': {
1655
                '1': {},
1656
            },
874.2.7 by Aaron Bentley
Add unit tests for jujupy newness.
1657
            'services': {
1658
                'jenkins': {
1659
                    'units': {
1660
                        'jenkins/1': {'agent-state': 'bad'},
1661
                    }
1662
                },
1663
                'dummy-sink': {
1664
                    'units': {
1665
                        'jenkins/2': {'open-ports': ['42/tcp']},
1666
                    }
1667
                },
1668
            }
1669
        }, '')
1670
        self.assertEqual(status.get_open_ports('jenkins/1'), [])
1671
        self.assertEqual(status.get_open_ports('jenkins/2'), ['42/tcp'])
1672
34.1.3 by Aaron Bentley
Add status and environment tests.
1673
    def test_agent_states(self):
1674
        status = Status({
1675
            'machines': {
1676
                '1': {'agent-state': 'good'},
1677
                '2': {},
1678
            },
1679
            'services': {
1680
                'jenkins': {
1681
                    'units': {
1682
                        'jenkins/1': {'agent-state': 'bad'},
1683
                        'jenkins/2': {'agent-state': 'good'},
1684
                    }
1685
                }
1686
            }
754.1.1 by Aaron Bentley
Emit status text for agent timeout.
1687
        }, '')
34.1.3 by Aaron Bentley
Add status and environment tests.
1688
        expected = {
1689
            'good': ['1', 'jenkins/2'],
1690
            'bad': ['jenkins/1'],
1691
            'no-agent': ['2'],
1692
        }
1693
        self.assertEqual(expected, status.agent_states())
1694
1695
    def test_check_agents_started_not_started(self):
1696
        status = Status({
1697
            'machines': {
1698
                '1': {'agent-state': 'good'},
1699
                '2': {},
1700
            },
1701
            'services': {
1702
                'jenkins': {
1703
                    'units': {
1704
                        'jenkins/1': {'agent-state': 'bad'},
1705
                        'jenkins/2': {'agent-state': 'good'},
1706
                    }
1707
                }
1708
            }
754.1.1 by Aaron Bentley
Emit status text for agent timeout.
1709
        }, '')
34.1.3 by Aaron Bentley
Add status and environment tests.
1710
        self.assertEqual(status.agent_states(),
1711
                         status.check_agents_started('env1'))
1712
1713
    def test_check_agents_started_all_started(self):
1714
        status = Status({
1715
            'machines': {
1716
                '1': {'agent-state': 'started'},
1717
                '2': {'agent-state': 'started'},
1718
            },
1719
            'services': {
1720
                'jenkins': {
1721
                    'units': {
966.1.5 by John George
Add support for running actions through jujupy and support for listing subordinates when jujupy.agent_items is called.
1722
                        'jenkins/1': {
1723
                            'agent-state': 'started',
1724
                            'subordinates': {
1725
                                'sub1': {
1726
                                    'agent-state': 'started'
1727
                                }
1728
                            }
1729
                        },
34.1.3 by Aaron Bentley
Add status and environment tests.
1730
                        'jenkins/2': {'agent-state': 'started'},
1731
                    }
1732
                }
1733
            }
754.1.1 by Aaron Bentley
Emit status text for agent timeout.
1734
        }, '')
34.1.3 by Aaron Bentley
Add status and environment tests.
1735
        self.assertIs(None, status.check_agents_started('env1'))
1736
1737
    def test_check_agents_started_agent_error(self):
1738
        status = Status({
1739
            'machines': {
1740
                '1': {'agent-state': 'any-error'},
1741
            },
1742
            'services': {}
754.1.1 by Aaron Bentley
Emit status text for agent timeout.
1743
        }, '')
34.1.3 by Aaron Bentley
Add status and environment tests.
1744
        with self.assertRaisesRegexp(ErroredUnit,
301.1.3 by Aaron Bentley
Remove environment name from log messages and errors.
1745
                                     '1 is in state any-error'):
34.1.3 by Aaron Bentley
Add status and environment tests.
1746
            status.check_agents_started('env1')
1747
771.2.1 by Aaron Bentley
Increase set of agent-state-infos that indicate failure.
1748
    def do_check_agents_started_failure(self, failure):
1749
        status = Status({
1750
            'machines': {'0': {
1751
                'agent-state-info': failure}},
1752
            'services': {},
953.3.9 by Nate Finch
more code review changes
1753
        }, '')
771.2.1 by Aaron Bentley
Increase set of agent-state-infos that indicate failure.
1754
        with self.assertRaises(ErroredUnit) as e_cxt:
771.2.2 by Aaron Bentley
Remove unused var.
1755
            status.check_agents_started()
771.2.1 by Aaron Bentley
Increase set of agent-state-infos that indicate failure.
1756
        e = e_cxt.exception
1757
        self.assertEqual(
1758
            str(e), '0 is in state {}'.format(failure))
1759
        self.assertEqual(e.unit_name, '0')
1760
        self.assertEqual(e.state, failure)
1761
1762
    def test_check_agents_cannot_set_up_groups(self):
1763
        self.do_check_agents_started_failure('cannot set up groups foobar')
1764
1765
    def test_check_agents_error(self):
1766
        self.do_check_agents_started_failure('error executing "lxc-start"')
1767
1768
    def test_check_agents_cannot_run_instances(self):
1769
        self.do_check_agents_started_failure('cannot run instances')
1770
1771
    def test_check_agents_cannot_run_instance(self):
1772
        self.do_check_agents_started_failure('cannot run instance')
1773
34.1.3 by Aaron Bentley
Add status and environment tests.
1774
    def test_check_agents_started_agent_info_error(self):
1775
        # Sometimes the error is indicated in a special 'agent-state-info'
1776
        # field.
1777
        status = Status({
1778
            'machines': {
1779
                '1': {'agent-state-info': 'any-error'},
1780
            },
1781
            'services': {}
754.1.1 by Aaron Bentley
Emit status text for agent timeout.
1782
        }, '')
34.1.3 by Aaron Bentley
Add status and environment tests.
1783
        with self.assertRaisesRegexp(ErroredUnit,
301.1.3 by Aaron Bentley
Remove environment name from log messages and errors.
1784
                                     '1 is in state any-error'):
34.1.3 by Aaron Bentley
Add status and environment tests.
1785
            status.check_agents_started('env1')
1786
84.1.4 by Aaron Bentley
Move version-dicting to jujupy.
1787
    def test_get_agent_versions(self):
1788
        status = Status({
1789
            'machines': {
1790
                '1': {'agent-version': '1.6.2'},
1791
                '2': {'agent-version': '1.6.1'},
1792
            },
1793
            'services': {
1794
                'jenkins': {
1795
                    'units': {
146 by Curtis Hovey
Hush lint.
1796
                        'jenkins/0': {
1797
                            'agent-version': '1.6.1'},
84.1.4 by Aaron Bentley
Move version-dicting to jujupy.
1798
                        'jenkins/1': {},
1799
                    },
1800
                }
1801
            }
754.1.1 by Aaron Bentley
Emit status text for agent timeout.
1802
        }, '')
84.1.4 by Aaron Bentley
Move version-dicting to jujupy.
1803
        self.assertEqual({
1804
            '1.6.2': {'1'},
1805
            '1.6.1': {'jenkins/0', '2'},
1806
            'unknown': {'jenkins/1'},
1807
        }, status.get_agent_versions())
1808
703.1.3 by Aaron Bentley
Start implementing deploy-many stage.
1809
    def test_iter_new_machines(self):
1810
        old_status = Status({
1811
            'machines': {
1812
                'bar': 'bar_info',
953.3.9 by Nate Finch
more code review changes
1813
            }
1814
        }, '')
703.1.3 by Aaron Bentley
Start implementing deploy-many stage.
1815
        new_status = Status({
1816
            'machines': {
1817
                'foo': 'foo_info',
1818
                'bar': 'bar_info',
953.3.9 by Nate Finch
more code review changes
1819
            }
1820
        }, '')
703.1.3 by Aaron Bentley
Start implementing deploy-many stage.
1821
        self.assertItemsEqual(new_status.iter_new_machines(old_status),
1822
                              [('foo', 'foo_info')])
1823
717.2.2 by Aaron Bentley
Checkpoint with assess_recovery working.
1824
    def test_get_instance_id(self):
1825
        status = Status({
1826
            'machines': {
1827
                '0': {'instance-id': 'foo-bar'},
1828
                '1': {},
953.3.9 by Nate Finch
more code review changes
1829
            }
1830
        }, '')
717.2.2 by Aaron Bentley
Checkpoint with assess_recovery working.
1831
        self.assertEqual(status.get_instance_id('0'), 'foo-bar')
1832
        with self.assertRaises(KeyError):
1833
            status.get_instance_id('1')
1834
        with self.assertRaises(KeyError):
1835
            status.get_instance_id('2')
1836
754.1.1 by Aaron Bentley
Emit status text for agent timeout.
1837
    def test_from_text(self):
1838
        text = TestEnvJujuClient.make_status_yaml(
1839
            'agent-state', 'pending', 'horsefeathers')
1840
        status = Status.from_text(text)
1841
        self.assertEqual(status.status_text, text)
1842
        self.assertEqual(status.status, {
1843
            'machines': {'0': {'agent-state': 'pending'}},
1844
            'services': {'jenkins': {'units': {'jenkins/0': {
1845
                'agent-state': 'horsefeathers'}}}}
1846
        })
1847
34.1.3 by Aaron Bentley
Add status and environment tests.
1848
34.1.5 by Aaron Bentley
Add tests for check_wordpress.
1849
def fast_timeout(count):
1850
    if False:
1851
        yield
1852
706.1.1 by Aaron Bentley
Switch to flake8 and fix lint.
1853
650.1.1 by Aaron Bentley
Add juju_path to from_config
1854
@contextmanager
1855
def temp_config():
1856
    home = tempfile.mkdtemp()
1857
    try:
1858
        environments_path = os.path.join(home, 'environments.yaml')
1859
        old_home = os.environ.get('JUJU_HOME')
1860
        os.environ['JUJU_HOME'] = home
1861
        try:
1862
            with open(environments_path, 'w') as environments:
1863
                yaml.dump({'environments': {
1864
                    'foo': {'type': 'local'}
1865
                }}, environments)
1866
            yield
1867
        finally:
1868
            if old_home is None:
1869
                del os.environ['JUJU_HOME']
1870
            else:
1871
                os.environ['JUJU_HOME'] = old_home
1872
    finally:
1873
        shutil.rmtree(home)
34.1.5 by Aaron Bentley
Add tests for check_wordpress.
1874
1875
657.1.3 by Aaron Bentley
Extract SimpleEnvironment from Environment.
1876
class TestSimpleEnvironment(TestCase):
1877
1878
    def test_local_from_config(self):
1879
        env = SimpleEnvironment('local', {'type': 'openstack'})
1880
        self.assertFalse(env.local, 'Does not respect config type.')
1881
        env = SimpleEnvironment('local', {'type': 'local'})
1882
        self.assertTrue(env.local, 'Does not respect config type.')
1883
1884
    def test_kvm_from_config(self):
1885
        env = SimpleEnvironment('local', {'type': 'local'})
1886
        self.assertFalse(env.kvm, 'Does not respect config type.')
1887
        env = SimpleEnvironment('local',
1888
                                {'type': 'local', 'container': 'kvm'})
1889
        self.assertTrue(env.kvm, 'Does not respect config type.')
1890
1891
    def test_hpcloud_from_config(self):
1892
        env = SimpleEnvironment('cloud', {'auth-url': 'before.keystone.after'})
1893
        self.assertFalse(env.hpcloud, 'Does not respect config type.')
1894
        env = SimpleEnvironment('hp', {'auth-url': 'before.hpcloudsvc.after/'})
1895
        self.assertTrue(env.hpcloud, 'Does not respect config type.')
1896
1897
    def test_from_config(self):
650.1.1 by Aaron Bentley
Add juju_path to from_config
1898
        with temp_config():
650.1.9 by Aaron Bentley
Merged trunk into compatibility-test.
1899
            env = SimpleEnvironment.from_config('foo')
1900
            self.assertIs(SimpleEnvironment, type(env))
1901
            self.assertEqual({'type': 'local'}, env.config)
657.1.3 by Aaron Bentley
Extract SimpleEnvironment from Environment.
1902
796.4.1 by Aaron Bentley
Handle missing environments more cleanly.
1903
    def test_from_bogus_config(self):
1904
        with temp_config():
1905
            with self.assertRaises(NoSuchEnvironment):
796.4.3 by Aaron Bentley
Fix lint.
1906
                SimpleEnvironment.from_config('bar')
796.4.1 by Aaron Bentley
Handle missing environments more cleanly.
1907
657.1.3 by Aaron Bentley
Extract SimpleEnvironment from Environment.
1908
34.1.3 by Aaron Bentley
Add status and environment tests.
1909
class TestEnvironment(TestCase):
1910
149 by Curtis Hovey
Added a helper to make status yaml.
1911
    @staticmethod
1912
    def make_status_yaml(key, machine_value, unit_value):
1913
        return dedent("""\
1914
            machines:
1915
              "0":
1916
                {0}: {1}
1917
            services:
1918
              jenkins:
1919
                units:
1920
                  jenkins/0:
1921
                    {0}: {2}
1922
        """.format(key, machine_value, unit_value))
1923
151 by Curtis Hovey
updated wait_for_started tests to use make_status_yaml.
1924
    def test_wait_for_started(self):
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
1925
        value = self.make_status_yaml('agent-state', 'started', 'started')
1926
        env = Environment('local', JujuClientDevel(None, None))
1927
        with patch.object(EnvJujuClient, 'get_juju_output',
1928
                          return_value=value):
1929
            env.wait_for_started()
151 by Curtis Hovey
updated wait_for_started tests to use make_status_yaml.
1930
1931
    def test_wait_for_started_timeout(self):
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
1932
        value = self.make_status_yaml('agent-state', 'pending', 'started')
1933
        env = Environment('local', JujuClientDevel(None, None))
751.1.1 by Aaron Bentley
Give equal time to old and new clients in DeployManyAttempt
1934
        with patch('jujupy.until_timeout', lambda x, start=None: range(1)):
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
1935
            with patch.object(EnvJujuClient, 'get_juju_output',
1936
                              return_value=value):
1937
                with self.assertRaisesRegexp(
1938
                        Exception,
1939
                        'Timed out waiting for agents to start in local'):
754.1.1 by Aaron Bentley
Emit status text for agent timeout.
1940
                    with patch('logging.error'):
1941
                        env.wait_for_started()
151 by Curtis Hovey
updated wait_for_started tests to use make_status_yaml.
1942
143 by Curtis Hovey
Added test for wait_for_version.
1943
    def test_wait_for_version(self):
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
1944
        value = self.make_status_yaml('agent-version', '1.17.2', '1.17.2')
1945
        env = Environment('local', JujuClientDevel(None, None))
1946
        with patch.object(
706.1.1 by Aaron Bentley
Switch to flake8 and fix lint.
1947
                EnvJujuClient, 'get_juju_output', return_value=value):
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
1948
            env.wait_for_version('1.17.2')
143 by Curtis Hovey
Added test for wait_for_version.
1949
144 by Curtis Hovey
Added test for wait_for_version_timeout.
1950
    def test_wait_for_version_timeout(self):
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
1951
        value = self.make_status_yaml('agent-version', '1.17.2', '1.17.1')
1952
        env = Environment('local', JujuClientDevel(None, None))
144 by Curtis Hovey
Added test for wait_for_version_timeout.
1953
        with patch('jujupy.until_timeout', lambda x: range(0)):
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
1954
            with patch.object(EnvJujuClient, 'get_juju_output',
1955
                              return_value=value):
1956
                with self.assertRaisesRegexp(
1957
                        Exception, 'Some versions did not update'):
1958
                    env.wait_for_version('1.17.2')
144 by Curtis Hovey
Added test for wait_for_version_timeout.
1959
145 by Curtis Hovey
Added test for wait_for_version handling API connection errors.
1960
    def test_wait_for_version_handles_connection_error(self):
195 by Curtis Hovey
Fix tests. update client1.17 rules to try sudo for lower revnos.
1961
        err = subprocess.CalledProcessError(2, 'foo')
145 by Curtis Hovey
Added test for wait_for_version handling API connection errors.
1962
        err.stderr = 'Unable to connect to environment'
195 by Curtis Hovey
Fix tests. update client1.17 rules to try sudo for lower revnos.
1963
        err = CannotConnectEnv(err)
150 by Curtis Hovey
Updated wait_for_version tests to use make_status_yaml.
1964
        status = self.make_status_yaml('agent-version', '1.17.2', '1.17.2')
145 by Curtis Hovey
Added test for wait_for_version handling API connection errors.
1965
        actions = [err, status]
1966
146 by Curtis Hovey
Hush lint.
1967
        def get_juju_output_fake(*args):
145 by Curtis Hovey
Added test for wait_for_version handling API connection errors.
1968
            action = actions.pop(0)
1969
            if isinstance(action, Exception):
1970
                raise action
1971
            else:
1972
                return action
1973
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
1974
        env = Environment('local', JujuClientDevel(None, None))
1975
        output_real = 'test_jujupy.EnvJujuClient.get_juju_output'
147 by Curtis Hovey
Supress the print output from the testrunner.
1976
        devnull = open(os.devnull, 'w')
1977
        with patch('sys.stdout', devnull):
1978
            with patch(output_real, get_juju_output_fake):
1979
                env.wait_for_version('1.17.2')
145 by Curtis Hovey
Added test for wait_for_version handling API connection errors.
1980
148 by Curtis Hovey
Added test to verify errors are reraised.
1981
    def test_wait_for_version_raises_non_connection_error(self):
1982
        err = Exception('foo')
150 by Curtis Hovey
Updated wait_for_version tests to use make_status_yaml.
1983
        status = self.make_status_yaml('agent-version', '1.17.2', '1.17.2')
148 by Curtis Hovey
Added test to verify errors are reraised.
1984
        actions = [err, status]
1985
1986
        def get_juju_output_fake(*args):
1987
            action = actions.pop(0)
1988
            if isinstance(action, Exception):
1989
                raise action
1990
            else:
1991
                return action
1992
657.1.1 by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel.
1993
        env = Environment('local', JujuClientDevel(None, None))
1994
        output_real = 'test_jujupy.EnvJujuClient.get_juju_output'
148 by Curtis Hovey
Added test to verify errors are reraised.
1995
        devnull = open(os.devnull, 'w')
1996
        with patch('sys.stdout', devnull):
1997
            with patch(output_real, get_juju_output_fake):
1998
                with self.assertRaisesRegexp(Exception, 'foo'):
1999
                    env.wait_for_version('1.17.2')
2000
112.1.4 by Aaron Bentley
Read config, use it to determine whether provider is local.
2001
    def test_from_config(self):
650.1.9 by Aaron Bentley
Merged trunk into compatibility-test.
2002
        with temp_config():
650.1.1 by Aaron Bentley
Add juju_path to from_config
2003
            env = Environment.from_config('foo')
650.1.9 by Aaron Bentley
Merged trunk into compatibility-test.
2004
            self.assertIs(Environment, type(env))
2005
            self.assertEqual({'type': 'local'}, env.config)
650.1.1 by Aaron Bentley
Add juju_path to from_config
2006
112.1.5 by Aaron Bentley
Use config to determine whether upgrades are local.
2007
    def test_upgrade_juju_nonlocal(self):
657.1.4 by Aaron Bentley
Move upgrade functionality to EnvJujuClient.
2008
        client = JujuClientDevel('1.234-76', None)
2009
        env = Environment('foo', client, {'type': 'nonlocal'})
2010
        env_client = client.get_env_client(env)
2011
        with patch.object(client, 'get_env_client', return_value=env_client):
2012
            with patch.object(env_client, 'juju') as juju_mock:
2013
                env.upgrade_juju()
2014
        juju_mock.assert_called_with(
659 by Aaron Bentley
Fix upgrade's juju invocation.
2015
            'upgrade-juju', ('--version', '1.234'))
112.1.5 by Aaron Bentley
Use config to determine whether upgrades are local.
2016
112.1.7 by Aaron Bentley
Fix upgrade command.
2017
    def test_get_matching_agent_version(self):
657.1.4 by Aaron Bentley
Move upgrade functionality to EnvJujuClient.
2018
        client = JujuClientDevel('1.23-series-arch', None)
2019
        env = Environment('foo', client, {'type': 'local'})
500 by Curtis Hovey
Only strip the series and arch from the build juju version.
2020
        self.assertEqual('1.23.1', env.get_matching_agent_version())
2021
        self.assertEqual('1.23', env.get_matching_agent_version(
112.1.7 by Aaron Bentley
Fix upgrade command.
2022
                         no_build=True))
500 by Curtis Hovey
Only strip the series and arch from the build juju version.
2023
        env.client.version = '1.20-beta1-series-arch'
2024
        self.assertEqual('1.20-beta1.1', env.get_matching_agent_version())
112.1.7 by Aaron Bentley
Fix upgrade command.
2025
112.1.5 by Aaron Bentley
Use config to determine whether upgrades are local.
2026
    def test_upgrade_juju_local(self):
657.1.4 by Aaron Bentley
Move upgrade functionality to EnvJujuClient.
2027
        client = JujuClientDevel(None, None)
2028
        env = Environment('foo', client, {'type': 'local'})
112.1.5 by Aaron Bentley
Use config to determine whether upgrades are local.
2029
        env.client.version = '1.234-76'
657.1.4 by Aaron Bentley
Move upgrade functionality to EnvJujuClient.
2030
        env_client = client.get_env_client(env)
2031
        with patch.object(client, 'get_env_client', return_value=env_client):
2032
            with patch.object(env_client, 'juju') as juju_mock:
2033
                env.upgrade_juju()
2034
        juju_mock.assert_called_with(
659 by Aaron Bentley
Fix upgrade's juju invocation.
2035
            'upgrade-juju', ('--version', '1.234', '--upload-tools',))
112.1.5 by Aaron Bentley
Use config to determine whether upgrades are local.
2036
339.1.1 by Aaron Bentley
Use deploy method, tweak exception reporting.
2037
    def test_deploy_non_joyent(self):
2038
        env = Environment('foo', MagicMock(), {'type': 'local'})
2039
        env.client.version = '1.234-76'
2040
        env.deploy('mondogb')
2041
        env.client.juju.assert_called_with(env, 'deploy', ('mondogb',))
2042
2043
    def test_deploy_joyent(self):
2044
        env = Environment('foo', MagicMock(), {'type': 'joyent'})
2045
        env.client.version = '1.234-76'
2046
        env.deploy('mondogb')
2047
        env.client.juju.assert_called_with(
339.1.2 by Aaron Bentley
Don't force --to on joyent, now that we have sufficient machines.
2048
            env, 'deploy', ('mondogb',))
34.1.5 by Aaron Bentley
Add tests for check_wordpress.
2049
884 by John George
Add juju-deployer test support.
2050
    def test_deployer(self):
2051
        client = EnvJujuClient(SimpleEnvironment(None, {'type': 'local'}),
2052
                               '1.23-series-arch', None)
2053
        with patch.object(EnvJujuClient, 'juju') as mock:
2054
            client.deployer('bundle:~juju-qa/some-bundle')
2055
        mock.assert_called_with(
2056
            'deployer', ('--debug', '--deploy-delay', '10', '--config',
2057
                         'bundle:~juju-qa/some-bundle'), True
2058
        )
2059
953.2.1 by John George
Support taking a bundle name in addition to the bundle config file path.
2060
    def test_deployer_with_bundle_name(self):
2061
        client = EnvJujuClient(SimpleEnvironment(None, {'type': 'local'}),
2062
                               '1.23-series-arch', None)
2063
        with patch.object(EnvJujuClient, 'juju') as mock:
2064
            client.deployer('bundle:~juju-qa/some-bundle', 'name')
2065
        mock.assert_called_with(
2066
            'deployer', ('--debug', '--deploy-delay', '10', '--config',
2067
                         'bundle:~juju-qa/some-bundle', 'name'), True
2068
        )
2069
796.2.2 by John George
Call quickstart from EnvJujuClient, with constraints
2070
    def test_quickstart_maas(self):
2071
        client = EnvJujuClient(SimpleEnvironment(None, {'type': 'maas'}),
966.2.3 by Curtis Hovey
Be clear about the full path the juju used in the test.
2072
                               '1.23-series-arch', '/juju')
796.2.2 by John George
Call quickstart from EnvJujuClient, with constraints
2073
        with patch.object(EnvJujuClient, 'juju') as mock:
2074
            client.quickstart('bundle:~juju-qa/some-bundle')
2075
        mock.assert_called_with(
2076
            'quickstart',
2077
            ('--constraints', 'mem=2G arch=amd64', '--no-browser',
966.2.3 by Curtis Hovey
Be clear about the full path the juju used in the test.
2078
             'bundle:~juju-qa/some-bundle'), False, extra_env={'JUJU': '/juju'}
796.2.2 by John George
Call quickstart from EnvJujuClient, with constraints
2079
        )
2080
2081
    def test_quickstart_local(self):
2082
        client = EnvJujuClient(SimpleEnvironment(None, {'type': 'local'}),
966.2.3 by Curtis Hovey
Be clear about the full path the juju used in the test.
2083
                               '1.23-series-arch', '/juju')
796.2.2 by John George
Call quickstart from EnvJujuClient, with constraints
2084
        with patch.object(EnvJujuClient, 'juju') as mock:
2085
            client.quickstart('bundle:~juju-qa/some-bundle')
2086
        mock.assert_called_with(
2087
            'quickstart',
2088
            ('--constraints', 'mem=2G', '--no-browser',
966.2.3 by Curtis Hovey
Be clear about the full path the juju used in the test.
2089
             'bundle:~juju-qa/some-bundle'), True, extra_env={'JUJU': '/juju'}
796.2.2 by John George
Call quickstart from EnvJujuClient, with constraints
2090
        )
2091
2092
    def test_quickstart_nonlocal(self):
2093
        client = EnvJujuClient(SimpleEnvironment(None, {'type': 'nonlocal'}),
966.2.3 by Curtis Hovey
Be clear about the full path the juju used in the test.
2094
                               '1.23-series-arch', '/juju')
796.2.2 by John George
Call quickstart from EnvJujuClient, with constraints
2095
        with patch.object(EnvJujuClient, 'juju') as mock:
2096
            client.quickstart('bundle:~juju-qa/some-bundle')
2097
        mock.assert_called_with(
2098
            'quickstart',
2099
            ('--constraints', 'mem=2G', '--no-browser',
966.2.3 by Curtis Hovey
Be clear about the full path the juju used in the test.
2100
             'bundle:~juju-qa/some-bundle'), False, extra_env={'JUJU': '/juju'}
796.2.1 by John George
Add quickstart_deploy.py
2101
        )
2102
355 by Curtis Hovey
Added set_testing_tools_metadata_url().
2103
    def test_set_testing_tools_metadata_url(self):
362 by Curtis Hovey
Use the real client in the tests. Check the call_count()
2104
        client = JujuClientDevel(None, None)
355 by Curtis Hovey
Added set_testing_tools_metadata_url().
2105
        env = Environment('foo', client)
2106
        with patch.object(client, 'get_env_option') as mock_get:
2107
            mock_get.return_value = 'https://example.org/juju/tools'
2108
            with patch.object(client, 'set_env_option') as mock_set:
2109
                env.set_testing_tools_metadata_url()
2110
        mock_get.assert_called_with(env, 'tools-metadata-url')
2111
        mock_set.assert_called_with(
706.1.1 by Aaron Bentley
Switch to flake8 and fix lint.
2112
            env, 'tools-metadata-url',
2113
            'https://example.org/juju/testing/tools')
355 by Curtis Hovey
Added set_testing_tools_metadata_url().
2114
358 by Curtis Hovey
Do not change the tools-metadata-url if it already has 'testing'
2115
    def test_set_testing_tools_metadata_url_noop(self):
362 by Curtis Hovey
Use the real client in the tests. Check the call_count()
2116
        client = JujuClientDevel(None, None)
358 by Curtis Hovey
Do not change the tools-metadata-url if it already has 'testing'
2117
        env = Environment('foo', client)
2118
        with patch.object(client, 'get_env_option') as mock_get:
2119
            mock_get.return_value = 'https://example.org/juju/testing/tools'
2120
            with patch.object(client, 'set_env_option') as mock_set:
2121
                env.set_testing_tools_metadata_url()
2122
        mock_get.assert_called_with(env, 'tools-metadata-url')
362 by Curtis Hovey
Use the real client in the tests. Check the call_count()
2123
        self.assertEqual(0, mock_set.call_count)
2124
953.3.5 by Nate Finch
tests for new action stuff
2125
    def test_action_do(self):
2126
        client = EnvJujuClient(SimpleEnvironment(None, {'type': 'local'}),
2127
                               '1.23-series-arch', None)
2128
        with patch.object(EnvJujuClient, 'get_juju_output') as mock:
2129
            mock.return_value = \
2130
                "Action queued with id: 5a92ec93-d4be-4399-82dc-7431dbfd08f9"
2131
            id = client.action_do("foo/0", "myaction", "param=5")
2132
            self.assertEqual(id, "5a92ec93-d4be-4399-82dc-7431dbfd08f9")
953.3.9 by Nate Finch
more code review changes
2133
        mock.assert_called_once_with(
953.3.13 by Nate Finch
more code review changes
2134
            'action do', 'foo/0', 'myaction', "param=5"
953.3.5 by Nate Finch
tests for new action stuff
2135
        )
2136
2137
    def test_action_do_error(self):
2138
        client = EnvJujuClient(SimpleEnvironment(None, {'type': 'local'}),
2139
                               '1.23-series-arch', None)
2140
        with patch.object(EnvJujuClient, 'get_juju_output') as mock:
2141
            mock.return_value = "some bad text"
953.3.9 by Nate Finch
more code review changes
2142
            with self.assertRaisesRegexp(Exception,
2143
                                         "Action id not found in output"):
953.3.5 by Nate Finch
tests for new action stuff
2144
                client.action_do("foo/0", "myaction", "param=5")
2145
2146
    def test_action_fetch(self):
2147
        client = EnvJujuClient(SimpleEnvironment(None, {'type': 'local'}),
2148
                               '1.23-series-arch', None)
2149
        with patch.object(EnvJujuClient, 'get_juju_output') as mock:
2150
            ret = "status: completed\nfoo: bar"
2151
            mock.return_value = ret
2152
            out = client.action_fetch("123")
2153
            self.assertEqual(out, ret)
953.3.9 by Nate Finch
more code review changes
2154
        mock.assert_called_once_with(
953.3.13 by Nate Finch
more code review changes
2155
            'action fetch', '123', "--wait", "1m"
953.3.5 by Nate Finch
tests for new action stuff
2156
        )
2157
2158
    def test_action_fetch_timeout(self):
2159
        client = EnvJujuClient(SimpleEnvironment(None, {'type': 'local'}),
2160
                               '1.23-series-arch', None)
953.3.9 by Nate Finch
more code review changes
2161
        ret = "status: pending\nfoo: bar"
2162
        with patch.object(EnvJujuClient,
2163
                          'get_juju_output', return_value=ret):
2164
            with self.assertRaisesRegexp(Exception,
2165
                                         "timed out waiting for action"):
953.3.5 by Nate Finch
tests for new action stuff
2166
                client.action_fetch("123")
2167
2168
    def test_action_do_fetch(self):
2169
        client = EnvJujuClient(SimpleEnvironment(None, {'type': 'local'}),
2170
                               '1.23-series-arch', None)
2171
        with patch.object(EnvJujuClient, 'get_juju_output') as mock:
2172
            ret = "status: completed\nfoo: bar"
953.3.9 by Nate Finch
more code review changes
2173
            # setting side_effect to an iterable will return the next value
2174
            # from the list each time the function is called.
2175
            mock.side_effect = [
2176
                "Action queued with id: 5a92ec93-d4be-4399-82dc-7431dbfd08f9",
2177
                ret]
953.3.5 by Nate Finch
tests for new action stuff
2178
            out = client.action_do_fetch("foo/0", "myaction", "param=5")
2179
            self.assertEqual(out, ret)
2180
354 by Curtis Hovey
Added set_env_option().
2181
751.3.1 by Martin Packman
Use dots rather than repeated lines when waiting for status changes
2182
class TestGroupReporter(TestCase):
2183
2184
    def test_single(self):
2185
        sio = StringIO.StringIO()
2186
        reporter = GroupReporter(sio, "done")
2187
        self.assertEqual(sio.getvalue(), "")
2188
        reporter.update({"working": ["1"]})
2189
        self.assertEqual(sio.getvalue(), "working: 1")
2190
        reporter.update({"done": ["1"]})
2191
        self.assertEqual(sio.getvalue(), "working: 1\n")
2192
2193
    def test_single_ticks(self):
2194
        sio = StringIO.StringIO()
2195
        reporter = GroupReporter(sio, "done")
2196
        reporter.update({"working": ["1"]})
2197
        self.assertEqual(sio.getvalue(), "working: 1")
2198
        reporter.update({"working": ["1"]})
2199
        self.assertEqual(sio.getvalue(), "working: 1 .")
2200
        reporter.update({"working": ["1"]})
2201
        self.assertEqual(sio.getvalue(), "working: 1 ..")
2202
        reporter.update({"done": ["1"]})
2203
        self.assertEqual(sio.getvalue(), "working: 1 ..\n")
2204
2205
    def test_multiple_values(self):
2206
        sio = StringIO.StringIO()
2207
        reporter = GroupReporter(sio, "done")
2208
        reporter.update({"working": ["1", "2"]})
2209
        self.assertEqual(sio.getvalue(), "working: 1, 2")
2210
        reporter.update({"working": ["1"], "done": ["2"]})
2211
        self.assertEqual(sio.getvalue(), "working: 1, 2\nworking: 1")
2212
        reporter.update({"done": ["1", "2"]})
2213
        self.assertEqual(sio.getvalue(), "working: 1, 2\nworking: 1\n")
2214
2215
    def test_multiple_groups(self):
2216
        sio = StringIO.StringIO()
2217
        reporter = GroupReporter(sio, "done")
2218
        reporter.update({"working": ["1", "2"], "starting": ["3"]})
2219
        first = "starting: 3 | working: 1, 2"
2220
        self.assertEqual(sio.getvalue(), first)
2221
        reporter.update({"working": ["1", "3"], "done": ["2"]})
2222
        second = "working: 1, 3"
2223
        self.assertEqual(sio.getvalue(), "\n".join([first, second]))
2224
        reporter.update({"done": ["1", "2", "3"]})
2225
        self.assertEqual(sio.getvalue(), "\n".join([first, second, ""]))
769.1.1 by Martin Packman
Add finish method to GroupReporter to end dotties
2226
2227
    def test_finish(self):
2228
        sio = StringIO.StringIO()
2229
        reporter = GroupReporter(sio, "done")
2230
        self.assertEqual(sio.getvalue(), "")
2231
        reporter.update({"working": ["1"]})
2232
        self.assertEqual(sio.getvalue(), "working: 1")
2233
        reporter.finish()
2234
        self.assertEqual(sio.getvalue(), "working: 1\n")
2235
2236
    def test_finish_unchanged(self):
2237
        sio = StringIO.StringIO()
2238
        reporter = GroupReporter(sio, "done")
2239
        self.assertEqual(sio.getvalue(), "")
2240
        reporter.finish()
2241
        self.assertEqual(sio.getvalue(), "")
922.1.1 by Martin Packman
Add wrapping at 80 characters to group reporter
2242
2243
    def test_wrap_to_width(self):
2244
        sio = StringIO.StringIO()
2245
        reporter = GroupReporter(sio, "done")
922.1.2 by Martin Packman
Use width of 79 and add test suggested by abentley in review
2246
        self.assertEqual(sio.getvalue(), "")
2247
        for _ in range(150):
2248
            reporter.update({"working": ["1"]})
2249
        reporter.finish()
2250
        self.assertEqual(sio.getvalue(), """\
2251
working: 1 ....................................................................
2252
...............................................................................
2253
..
2254
""")
2255
2256
    def test_wrap_to_width_exact(self):
2257
        sio = StringIO.StringIO()
2258
        reporter = GroupReporter(sio, "done")
922.1.1 by Martin Packman
Add wrapping at 80 characters to group reporter
2259
        reporter.wrap_width = 12
2260
        self.assertEqual(sio.getvalue(), "")
2261
        changes = []
2262
        for _ in range(20):
2263
            reporter.update({"working": ["1"]})
2264
            changes.append(sio.getvalue())
2265
        self.assertEqual(changes[::4], [
2266
            "working: 1",
2267
            "working: 1 .\n...",
2268
            "working: 1 .\n.......",
2269
            "working: 1 .\n...........",
2270
            "working: 1 .\n............\n...",
2271
        ])
2272
        reporter.finish()
2273
        self.assertEqual(sio.getvalue(), changes[-1] + "\n")
2274
2275
    def test_wrap_to_width_overflow(self):
2276
        sio = StringIO.StringIO()
2277
        reporter = GroupReporter(sio, "done")
2278
        reporter.wrap_width = 8
2279
        self.assertEqual(sio.getvalue(), "")
2280
        changes = []
2281
        for _ in range(16):
2282
            reporter.update({"working": ["1"]})
2283
            changes.append(sio.getvalue())
2284
        self.assertEqual(changes[::4], [
2285
            "working: 1",
2286
            "working: 1\n....",
2287
            "working: 1\n........",
2288
            "working: 1\n........\n....",
2289
        ])
2290
        reporter.finish()
2291
        self.assertEqual(sio.getvalue(), changes[-1] + "\n")
930.1.1 by Martin Packman
Fix wrapping of dots when printing updated group status
2292
2293
    def test_wrap_to_width_multiple_groups(self):
2294
        sio = StringIO.StringIO()
2295
        reporter = GroupReporter(sio, "done")
2296
        reporter.wrap_width = 16
2297
        self.assertEqual(sio.getvalue(), "")
2298
        changes = []
2299
        for _ in range(6):
2300
            reporter.update({"working": ["1", "2"]})
2301
            changes.append(sio.getvalue())
2302
        for _ in range(10):
2303
            reporter.update({"working": ["1"], "done": ["2"]})
2304
            changes.append(sio.getvalue())
2305
        self.assertEqual(changes[::4], [
2306
            "working: 1, 2",
2307
            "working: 1, 2 ..\n..",
2308
            "working: 1, 2 ..\n...\n"
2309
            "working: 1 ..",
2310
            "working: 1, 2 ..\n...\n"
2311
            "working: 1 .....\n.",
2312
        ])
2313
        reporter.finish()
2314
        self.assertEqual(sio.getvalue(), changes[-1] + "\n")
953.3.7 by Nate Finch
update for code review comments
2315
2316
2317
class TestMakeClient(TestCase):
2318
2319
    @contextmanager
2320
    def make_client_cxt(self):
2321
        td = temp_dir()
2322
        te = temp_env({'environments': {'foo': {
2323
            'orig-name': 'foo', 'name': 'foo'}}})
2324
        with td as juju_path, te, patch('subprocess.Popen',
2325
                                        side_effect=ValueError):
2326
            with patch('subprocess.check_output') as co_mock:
2327
                co_mock.return_value = '1.18'
2328
                yield juju_path
2329
2330
    def test_make_client(self):
2331
        with self.make_client_cxt() as juju_path:
2332
            client = make_client(juju_path, False, 'foo', 'bar')
2333
        self.assertEqual(client.full_path, os.path.join(juju_path, 'juju'))
2334
        self.assertEqual(client.debug, False)
2335
        self.assertEqual(client.env.config['orig-name'], 'foo')
2336
        self.assertEqual(client.env.config['name'], 'bar')
2337
        self.assertEqual(client.env.environment, 'bar')
2338
2339
    def test_make_client_debug(self):
2340
        with self.make_client_cxt() as juju_path:
2341
            client = make_client(juju_path, True, 'foo', 'bar')
2342
        self.assertEqual(client.debug, True)
2343
2344
    def test_make_client_no_temp_env_name(self):
2345
        with self.make_client_cxt() as juju_path:
2346
            client = make_client(juju_path, False, 'foo', None)
2347
        self.assertEqual(client.full_path, os.path.join(juju_path, 'juju'))
2348
        self.assertEqual(client.env.config['orig-name'], 'foo')
2349
        self.assertEqual(client.env.config['name'], 'foo')
2350
        self.assertEqual(client.env.environment, 'foo')
953.3.8 by Nate Finch
more review changes
2351
953.3.9 by Nate Finch
more code review changes
2352
953.3.8 by Nate Finch
more review changes
2353
class AssessParseStateServerFromErrorTestCase(TestCase):
2354
2355
    def test_parse_new_state_server_from_error(self):
2356
        output = dedent("""
2357
            Waiting for address
2358
            Attempting to connect to 10.0.0.202:22
2359
            Attempting to connect to 1.2.3.4:22
2360
            The fingerprint for the ECDSA key sent by the remote host is
2361
            """)
2362
        error = subprocess.CalledProcessError(1, ['foo'], output)
2363
        address = parse_new_state_server_from_error(error)
2364
        self.assertEqual('1.2.3.4', address)