Skip to content

Commit af10ed0

Browse files
authored
Merge pull request #393 from ChrisMacNaughton/feature/ssh
#393 Fixes #79
2 parents 5d8b1ba + e49d771 commit af10ed0

3 files changed

Lines changed: 47 additions & 3 deletions

File tree

juju/machine.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ async def _scp(self, source, destination, scp_opts):
194194
if process.returncode != 0:
195195
raise JujuError("command failed: %s" % cmd)
196196

197-
def ssh(
197+
async def ssh(
198198
self, command, user=None, proxy=False, ssh_opts=None):
199199
"""Execute a command over SSH on this machine.
200200
@@ -204,7 +204,24 @@ def ssh(
204204
:param str ssh_opts: Additional options to the `ssh` command
205205
206206
"""
207-
raise NotImplementedError()
207+
if proxy:
208+
raise NotImplementedError('proxy option is not implemented')
209+
address = self.dns_name
210+
destination = "{}@{}".format(user, address)
211+
cmd = [
212+
'ssh',
213+
'-i', os.path.expanduser('~/.local/share/juju/ssh/juju_id_rsa'),
214+
'-o', 'StrictHostKeyChecking=no',
215+
'-q',
216+
destination
217+
]
218+
cmd.extend(ssh_opts.split() if isinstance(ssh_opts, str) else ssh_opts)
219+
cmd.extend([command])
220+
loop = self.model.loop
221+
process = await asyncio.create_subprocess_exec(*cmd, loop=loop)
222+
await process.wait()
223+
if process.returncode != 0:
224+
raise JujuError("command failed: %s" % cmd)
208225

209226
def status_history(self, num=20, utc=False):
210227
"""Get status history for this machine.

juju/unit.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ def ssh(
232232
:param str ssh_opts: Additional options to the `ssh` command
233233
234234
"""
235-
raise NotImplementedError()
235+
self.machine.ssh(command, user, proxy, ssh_opts)
236236

237237
def status_history(self, num=20, utc=False):
238238
"""Get status history for this unit.

tests/integration/test_unit.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,33 @@ async def test_scp(event_loop):
9999
assert f.read() == b'testcontents'
100100

101101

102+
@base.bootstrapped
103+
@pytest.mark.asyncio
104+
async def test_ssh(event_loop):
105+
# ensure that asyncio.subprocess will work;
106+
try:
107+
asyncio.get_child_watcher().attach_loop(event_loop)
108+
except RuntimeError:
109+
pytest.skip('test_ssh will always fail outside of MainThread')
110+
async with base.CleanModel() as model:
111+
app = await model.deploy('ubuntu')
112+
113+
await asyncio.wait_for(
114+
model.block_until(lambda: app.units),
115+
timeout=60)
116+
unit = app.units[0]
117+
await asyncio.wait_for(
118+
model.block_until(lambda: unit.machine),
119+
timeout=60)
120+
machine = unit.machine
121+
await asyncio.wait_for(
122+
model.block_until(lambda: (machine.status == 'running' and
123+
machine.agent_status == 'started')),
124+
timeout=480)
125+
output = await unit.ssh("echo test")
126+
assert(output == "test")
127+
128+
102129
@base.bootstrapped
103130
@pytest.mark.asyncio
104131
async def test_resolve(event_loop):

0 commit comments

Comments
 (0)