Skip to content

Commit

Permalink
Add a tail_file context manager to the Host class
Browse files Browse the repository at this point in the history
This is a pretty common request and should be useful in a number of
scenarios. Usage is pretty simple:

    with my_host.session.tail_file("/var/log/messages") as res:
        # do something that creates new messages
    print(res.stdout)
  • Loading branch information
JacobCallahan committed May 11, 2023
1 parent 5a807bf commit 74e9d6b
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 2 deletions.
2 changes: 0 additions & 2 deletions broker/hosts.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
# from functools import cached_property
from logzero import logger
from broker.exceptions import NotImplementedError, HostError
from broker.session import ContainerSession, Session
from broker.settings import settings


class Host:

default_timeout = 0 # timeout in ms, 0 is infinite

def __init__(self, hostname=None, name=None, from_dict=False, **kwargs):
Expand Down
27 changes: 27 additions & 0 deletions broker/session.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from contextlib import contextmanager
import os
import socket
import tempfile
Expand Down Expand Up @@ -77,6 +78,23 @@ def shell(self, pty=False):
channel = self.session.open_session()
return InteractiveShell(channel, pty)

@contextmanager
def tail_file(self, filename):
"""Simulate tailing a file on the remote host
example:
with my_host.session.tail_file("/var/log/messages") as res:
# do something that creates new messages
print(res.stdout)
returns a Result object with stdout, stderr, and status
"""
initial_size = int(self.run(f"stat -c %s {filename}").stdout.strip())
yield (res := helpers.Result())
# get the contents of the file from the initial size to the end
result = self.run(f"tail -c +{initial_size} {filename}")
res.__dict__.update(result.__dict__)

def sftp_read(self, source, destination=None, return_data=False):
"""read a remote file into a local destination or return a bytes object if return_data is True"""
if not return_data:
Expand Down Expand Up @@ -236,6 +254,15 @@ def disconnect(self):
"""Needed for simple compatability with Session"""
pass

@contextmanager
def tail_file(self, filename):
"""Simulate tailing a file on the remote host"""
initial_size = int(self.run(f"stat -c %s {filename}").stdout.strip())
yield (res := helpers.Result())
# get the contents of the file from the initial size to the end
result = self.run(f"tail -c +{initial_size} {filename}")
res.__dict__.update(result.__dict__)

def sftp_write(self, source, destination=None, ensure_dir=True):
"""Add one of more files to the container"""
# ensure source is a list of Path objects
Expand Down
7 changes: 7 additions & 0 deletions tests/functional/test_containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@ def test_container_e2e():
loc_settings_path.read_bytes() == data
), "Local file is different from the received one (return_data=True)"
assert data == Path(tmp.file.name).read_bytes(), "Received files do not match"
# test the tail_file context manager
tailed_file = f"{remote_dir}/tail_me.txt"
c_host.execute(f"echo 'hello world' > {tailed_file}")
with c_host.session.tail_file(tailed_file) as tf:
c_host.execute(f"echo 'this is a new line' >> {tailed_file}")
assert 'this is a new line' in tf.stdout
assert 'hello world' not in tf.stdout


def test_container_e2e_mp():
Expand Down
7 changes: 7 additions & 0 deletions tests/functional/test_satlab.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ def test_tower_host():
loc_settings_path.read_bytes() == data
), "Local file is different from the received one (return_data=True)"
assert data == Path(tmp.file.name).read_bytes(), "Received files do not match"
# test the tail_file context manager
tailed_file = f"{remote_dir}/tail_me.txt"
r_host.execute(f"echo 'hello world' > {tailed_file}")
with r_host.session.tail_file(tailed_file) as tf:
r_host.execute(f"echo 'this is a new line' >> {tailed_file}")
assert 'this is a new line' in tf.stdout
assert 'hello world' not in tf.stdout


def test_tower_host_mp():
Expand Down

0 comments on commit 74e9d6b

Please sign in to comment.