983.1.1
by Martin Packman
Add new Remote class for accessing juju machines |
1 |
"""Tests for remote access to juju machines."""
|
2 |
||
3 |
import logging |
|
4 |
from mock import patch |
|
994.4.1
by Martin Packman
Implement copy function for winrm remote |
5 |
import os |
983.1.1
by Martin Packman
Add new Remote class for accessing juju machines |
6 |
from StringIO import StringIO |
7 |
import subprocess |
|
8 |
import unittest |
|
9 |
||
994.3.1
by Martin Packman
Make Remote an abstract class and implement winrm based version |
10 |
import winrm |
11 |
||
983.1.1
by Martin Packman
Add new Remote class for accessing juju machines |
12 |
from jujupy import ( |
13 |
EnvJujuClient, |
|
14 |
SimpleEnvironment, |
|
15 |
Status, |
|
16 |
)
|
|
17 |
from remote import ( |
|
994.3.1
by Martin Packman
Make Remote an abstract class and implement winrm based version |
18 |
remote_from_address, |
19 |
remote_from_unit, |
|
994.4.1
by Martin Packman
Implement copy function for winrm remote |
20 |
WinRmRemote, |
21 |
)
|
|
22 |
from utility import ( |
|
23 |
temp_dir, |
|
983.1.1
by Martin Packman
Add new Remote class for accessing juju machines |
24 |
)
|
25 |
||
26 |
||
27 |
class TestRemote(unittest.TestCase): |
|
28 |
||
994.3.1
by Martin Packman
Make Remote an abstract class and implement winrm based version |
29 |
precise_status_output = """\ |
30 |
machines:
|
|
31 |
"1":
|
|
32 |
series: precise
|
|
33 |
services:
|
|
34 |
a-service:
|
|
35 |
units:
|
|
36 |
a-service/0:
|
|
37 |
machine: "1"
|
|
38 |
public-address: 10.55.60.1
|
|
39 |
"""
|
|
40 |
||
41 |
win2012hvr2_status_output = """\ |
|
42 |
machines:
|
|
43 |
"2":
|
|
44 |
series: win2012hvr2
|
|
45 |
services:
|
|
46 |
a-service:
|
|
47 |
units:
|
|
48 |
a-service/0:
|
|
49 |
machine: "2"
|
|
50 |
public-address: 10.55.60.2
|
|
51 |
"""
|
|
52 |
||
983.1.1
by Martin Packman
Add new Remote class for accessing juju machines |
53 |
def setUp(self): |
54 |
log = logging.getLogger() |
|
55 |
self.addCleanup(setattr, log, "handlers", log.handlers) |
|
56 |
log.handlers = [] |
|
57 |
self.log_stream = StringIO() |
|
58 |
handler = logging.StreamHandler(self.log_stream) |
|
59 |
handler.setFormatter(logging.Formatter("%(levelname)s %(message)s")) |
|
60 |
log.addHandler(handler) |
|
61 |
||
994.3.1
by Martin Packman
Make Remote an abstract class and implement winrm based version |
62 |
def test_remote_from_unit(self): |
63 |
env = SimpleEnvironment("an-env", {"type": "nonlocal"}) |
|
64 |
client = EnvJujuClient(env, None, None) |
|
65 |
unit = "a-service/0" |
|
66 |
with patch.object(client, "get_status", autospec=True) as st: |
|
67 |
st.return_value = Status.from_text(self.precise_status_output) |
|
68 |
remote = remote_from_unit(client, unit) |
|
69 |
self.assertEqual( |
|
70 |
repr(remote), |
|
71 |
"<SSHRemote env='an-env' unit='a-service/0'>") |
|
72 |
self.assertIs(False, remote.is_windows()) |
|
73 |
||
74 |
def test_remote_from_unit_with_series(self): |
|
75 |
env = SimpleEnvironment("an-env", {"type": "nonlocal"}) |
|
76 |
client = EnvJujuClient(env, None, None) |
|
77 |
unit = "a-service/0" |
|
78 |
remote = remote_from_unit(client, unit, series="trusty") |
|
79 |
self.assertEqual( |
|
80 |
repr(remote), |
|
81 |
"<SSHRemote env='an-env' unit='a-service/0'>") |
|
82 |
self.assertIs(False, remote.is_windows()) |
|
83 |
||
84 |
def test_remote_from_unit_with_status(self): |
|
85 |
env = SimpleEnvironment("an-env", {"type": "nonlocal"}) |
|
86 |
client = EnvJujuClient(env, None, None) |
|
87 |
unit = "a-service/0" |
|
88 |
status = Status.from_text(self.win2012hvr2_status_output) |
|
89 |
remote = remote_from_unit(client, unit, status=status) |
|
90 |
self.assertEqual( |
|
91 |
repr(remote), |
|
994.4.4
by Martin Packman
Update with extra tests and addresses review comments by sinzui |
92 |
"<WinRmRemote env='an-env' unit='a-service/0' addr='10.55.60.2'>") |
994.3.1
by Martin Packman
Make Remote an abstract class and implement winrm based version |
93 |
self.assertIs(True, remote.is_windows()) |
94 |
||
95 |
def test_remote_from_address(self): |
|
96 |
remote = remote_from_address("10.55.60.1") |
|
97 |
self.assertEqual(repr(remote), "<SSHRemote addr='10.55.60.1'>") |
|
98 |
self.assertIs(None, remote.is_windows()) |
|
99 |
||
100 |
def test_remote_from_address_and_series(self): |
|
101 |
remote = remote_from_address("10.55.60.2", series="trusty") |
|
102 |
self.assertEqual(repr(remote), "<SSHRemote addr='10.55.60.2'>") |
|
103 |
self.assertIs(False, remote.is_windows()) |
|
104 |
||
105 |
def test_remote_from_address_and_win_series(self): |
|
106 |
remote = remote_from_address("10.55.60.3", series="win2012hvr2") |
|
107 |
self.assertEqual(repr(remote), "<WinRmRemote addr='10.55.60.3'>") |
|
108 |
self.assertIs(True, remote.is_windows()) |
|
983.1.1
by Martin Packman
Add new Remote class for accessing juju machines |
109 |
|
110 |
def test_run_with_unit(self): |
|
111 |
env = SimpleEnvironment("an-env", {"type": "nonlocal"}) |
|
112 |
client = EnvJujuClient(env, None, None) |
|
113 |
unit = "a-service/0" |
|
994.3.1
by Martin Packman
Make Remote an abstract class and implement winrm based version |
114 |
remote = remote_from_unit(client, unit, series="trusty") |
983.1.1
by Martin Packman
Add new Remote class for accessing juju machines |
115 |
with patch.object(client, "get_juju_output") as mock_cmd: |
116 |
mock_cmd.return_value = "contents of /a/file" |
|
117 |
output = remote.run("cat /a/file") |
|
118 |
self.assertEqual(output, "contents of /a/file") |
|
119 |
mock_cmd.assert_called_once_with("ssh", unit, "cat /a/file") |
|
120 |
||
121 |
def test_run_with_unit_fallback(self): |
|
122 |
env = SimpleEnvironment("an-env", {"type": "nonlocal"}) |
|
123 |
client = EnvJujuClient(env, None, None) |
|
124 |
unit = "a-service/0" |
|
994.3.1
by Martin Packman
Make Remote an abstract class and implement winrm based version |
125 |
with patch.object(client, "get_status") as st: |
126 |
st.return_value = Status.from_text(self.precise_status_output) |
|
127 |
remote = remote_from_unit(client, unit) |
|
128 |
with patch.object(client, "get_juju_output") as mock_cmd: |
|
129 |
mock_cmd.side_effect = subprocess.CalledProcessError(1, "ssh") |
|
983.1.1
by Martin Packman
Add new Remote class for accessing juju machines |
130 |
with patch.object(remote, "_run_subprocess") as mock_run: |
131 |
mock_run.return_value = "contents of /a/file" |
|
132 |
output = remote.run("cat /a/file") |
|
133 |
self.assertEqual(output, "contents of /a/file") |
|
134 |
mock_cmd.assert_called_once_with("ssh", unit, "cat /a/file") |
|
135 |
mock_run.assert_called_once_with([ |
|
136 |
"ssh", |
|
137 |
"-o", "User ubuntu", |
|
138 |
"-o", "UserKnownHostsFile /dev/null", |
|
139 |
"-o", "StrictHostKeyChecking no", |
|
994.3.1
by Martin Packman
Make Remote an abstract class and implement winrm based version |
140 |
"10.55.60.1", |
983.1.1
by Martin Packman
Add new Remote class for accessing juju machines |
141 |
"cat /a/file", |
142 |
])
|
|
143 |
self.assertRegexpMatches( |
|
144 |
self.log_stream.getvalue(), |
|
145 |
"(?m)^WARNING juju ssh to 'a-service/0' failed: .*") |
|
146 |
||
147 |
def test_run_with_address(self): |
|
994.3.1
by Martin Packman
Make Remote an abstract class and implement winrm based version |
148 |
remote = remote_from_address("10.55.60.1") |
983.1.1
by Martin Packman
Add new Remote class for accessing juju machines |
149 |
with patch.object(remote, "_run_subprocess") as mock_run: |
150 |
mock_run.return_value = "contents of /a/file" |
|
151 |
output = remote.run("cat /a/file") |
|
152 |
self.assertEqual(output, "contents of /a/file") |
|
153 |
mock_run.assert_called_once_with([ |
|
154 |
"ssh", |
|
155 |
"-o", "User ubuntu", |
|
156 |
"-o", "UserKnownHostsFile /dev/null", |
|
157 |
"-o", "StrictHostKeyChecking no", |
|
158 |
"10.55.60.1", |
|
159 |
"cat /a/file", |
|
160 |
])
|
|
161 |
||
994.3.1
by Martin Packman
Make Remote an abstract class and implement winrm based version |
162 |
def test_cat(self): |
163 |
remote = remote_from_address("10.55.60.1") |
|
164 |
with patch.object(remote, "_run_subprocess") as mock_run: |
|
165 |
remote.cat("/a/file") |
|
166 |
mock_run.assert_called_once_with([ |
|
167 |
"ssh", |
|
168 |
"-o", "User ubuntu", |
|
169 |
"-o", "UserKnownHostsFile /dev/null", |
|
170 |
"-o", "StrictHostKeyChecking no", |
|
171 |
"10.55.60.1", |
|
172 |
"cat /a/file", |
|
173 |
])
|
|
174 |
||
175 |
def test_cat_on_windows(self): |
|
176 |
env = SimpleEnvironment("an-env", {"type": "nonlocal"}) |
|
177 |
client = EnvJujuClient(env, None, None) |
|
178 |
unit = "a-service/0" |
|
179 |
with patch.object(client, "get_status", autospec=True) as st: |
|
180 |
st.return_value = Status.from_text(self.win2012hvr2_status_output) |
|
181 |
response = winrm.Response(("contents of /a/file", "", 0)) |
|
182 |
remote = remote_from_unit(client, unit) |
|
183 |
with patch.object(remote.session, "run_cmd", autospec=True, |
|
184 |
return_value=response) as mock_run: |
|
185 |
output = remote.cat("/a/file") |
|
186 |
self.assertEqual(output, "contents of /a/file") |
|
187 |
st.assert_called_once_with() |
|
188 |
mock_run.assert_called_once_with("type", ["/a/file"]) |
|
189 |
||
983.1.1
by Martin Packman
Add new Remote class for accessing juju machines |
190 |
def test_copy(self): |
994.3.1
by Martin Packman
Make Remote an abstract class and implement winrm based version |
191 |
remote = remote_from_address("10.55.60.1") |
983.1.1
by Martin Packman
Add new Remote class for accessing juju machines |
192 |
dest = "/local/path" |
193 |
with patch.object(remote, "_run_subprocess") as mock_run: |
|
194 |
remote.copy(dest, ["/var/log/*", "~/.config"]) |
|
195 |
mock_run.assert_called_once_with([ |
|
196 |
"scp", |
|
197 |
"-C", |
|
198 |
"-o", "User ubuntu", |
|
199 |
"-o", "UserKnownHostsFile /dev/null", |
|
200 |
"-o", "StrictHostKeyChecking no", |
|
201 |
"10.55.60.1:/var/log/*", |
|
202 |
"10.55.60.1:~/.config", |
|
203 |
"/local/path", |
|
204 |
])
|
|
994.3.1
by Martin Packman
Make Remote an abstract class and implement winrm based version |
205 |
|
994.4.4
by Martin Packman
Update with extra tests and addresses review comments by sinzui |
206 |
def test_copy_on_windows(self): |
207 |
env = SimpleEnvironment("an-env", {"type": "nonlocal"}) |
|
208 |
client = EnvJujuClient(env, None, None) |
|
209 |
unit = "a-service/0" |
|
210 |
dest = "/local/path" |
|
211 |
with patch.object(client, "get_status", autospec=True) as st: |
|
212 |
st.return_value = Status.from_text(self.win2012hvr2_status_output) |
|
213 |
response = winrm.Response(("fake output", "", 0)) |
|
214 |
remote = remote_from_unit(client, unit) |
|
215 |
with patch.object(remote.session, "run_ps", autospec=True, |
|
216 |
return_value=response) as mock_run: |
|
217 |
with patch.object(remote, "_encoded_copy_to_dir", |
|
218 |
autospec=True) as mock_cpdir: |
|
219 |
remote.copy(dest, ["C:\\logs\\*", "%APPDATA%\\*.log"]) |
|
220 |
mock_cpdir.assert_called_once_with(dest, "fake output") |
|
221 |
st.assert_called_once_with() |
|
222 |
self.assertEquals(mock_run.call_count, 1) |
|
223 |
self.assertRegexpMatches( |
|
224 |
mock_run.call_args[0][0], |
|
225 |
r'.*"C:\\logs\\[*]","%APPDATA%\\[*].log".*') |
|
226 |
||
994.3.1
by Martin Packman
Make Remote an abstract class and implement winrm based version |
227 |
def test_run_cmd(self): |
228 |
env = SimpleEnvironment("an-env", {"type": "nonlocal"}) |
|
229 |
client = EnvJujuClient(env, None, None) |
|
230 |
unit = "a-service/0" |
|
231 |
with patch.object(client, "get_status", autospec=True) as st: |
|
232 |
st.return_value = Status.from_text(self.win2012hvr2_status_output) |
|
233 |
response = winrm.Response(("some out", "some err", 0)) |
|
234 |
remote = remote_from_unit(client, unit) |
|
235 |
with patch.object(remote.session, "run_cmd", autospec=True, |
|
236 |
return_value=response) as mock_run: |
|
237 |
output = remote.run_cmd( |
|
238 |
["C:\\Program Files\\bin.exe", "/IN", "Bob's Stuff"]) |
|
239 |
self.assertEqual(output, response) |
|
240 |
st.assert_called_once_with() |
|
241 |
mock_run.assert_called_once_with( |
|
242 |
'"C:\\Program Files\\bin.exe"', ['/IN "Bob\'s Stuff"']) |
|
994.4.1
by Martin Packman
Implement copy function for winrm remote |
243 |
|
244 |
def test_encoded_copy_to_dir_one(self): |
|
245 |
output = "testfile|K0ktLuECAA==\r\n" |
|
246 |
with temp_dir() as dest: |
|
247 |
WinRmRemote._encoded_copy_to_dir(dest, output) |
|
248 |
with open(os.path.join(dest, "testfile")) as f: |
|
249 |
self.assertEqual(f.read(), "test\n") |
|
250 |
||
251 |
def test_encoded_copy_to_dir_many(self): |
|
252 |
output = "test one|K0ktLuECAA==\r\ntest two|K0ktLuECAA==\r\n\r\n" |
|
253 |
with temp_dir() as dest: |
|
254 |
WinRmRemote._encoded_copy_to_dir(dest, output) |
|
994.4.2
by Martin Packman
Use winrm copy implementation to get logs from windows machines |
255 |
for name in ("test one", "test two"): |
256 |
with open(os.path.join(dest, name)) as f: |
|
257 |
self.assertEqual(f.read(), "test\n") |
|
994.4.4
by Martin Packman
Update with extra tests and addresses review comments by sinzui |
258 |
|
259 |
def test_encoded_copy_traversal_guard(self): |
|
260 |
output = "../../../etc/passwd|K0ktLuECAA==\r\n" |
|
261 |
with temp_dir() as dest: |
|
262 |
with self.assertRaises(ValueError): |
|
263 |
WinRmRemote._encoded_copy_to_dir(dest, output) |