From 0e3effdec1785a0a85e7afa2c85365e9ab5da4df Mon Sep 17 00:00:00 2001 From: Clif Houck Date: Fri, 23 Aug 2024 12:49:59 -0500 Subject: [PATCH 1/7] Add support for binoculars to python client Signed-off-by: Clif Houck --- .../python/armada_client/binoculars_client.py | 45 +++++++++++++++++++ client/python/tests/unit/server_mock.py | 20 ++++++++- .../tests/unit/test_binoculars_client.py | 44 ++++++++++++++++++ scripts/build-python-client.sh | 4 +- 4 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 client/python/armada_client/binoculars_client.py create mode 100644 client/python/tests/unit/test_binoculars_client.py diff --git a/client/python/armada_client/binoculars_client.py b/client/python/armada_client/binoculars_client.py new file mode 100644 index 00000000000..f2b821b1f87 --- /dev/null +++ b/client/python/armada_client/binoculars_client.py @@ -0,0 +1,45 @@ +import datetime +from typing import Optional + + +from armada_client.armada import ( + binoculars_pb2, + binoculars_pb2_grpc, +) + +from armada_client.k8s.io.api.core.v1 import generated_pb2 as core_v1 + + +class BinocularsClient: + """ + Client for accessing Armada's Binoculars service over gRPC. + + :param channel: gRPC channel used for authentication. See + https://grpc.github.io/grpc/python/grpc.html + for more information. + :return: an Binoculars client instance + """ + + def __init__(self, channel): + self.binoculars_stub = binoculars_pb2_grpc.BinocularsStub(channel) + + def logs( + self, + job_id: str, + pod_namespace: str, + since_time: datetime.datetime, + pod_number: Optional[int] = 0, + log_options: Optional[core_v1.PodLogOptions] = None, + ): + log_request = binoculars_pb2.LogRequest( + job_id=job_id, + pod_number=pod_number, + pod_namespace=pod_namespace, + since_time=since_time.isoformat(), + log_options=log_options, + ) + return self.binoculars_stub.Logs(log_request) + + def cordon(self, node_name: str): + cordon_request = binoculars_pb2.CordonRequest(node_name=node_name) + return self.binoculars_stub.Cordon(cordon_request) diff --git a/client/python/tests/unit/server_mock.py b/client/python/tests/unit/server_mock.py index 8d19101203b..1bb94bcf053 100644 --- a/client/python/tests/unit/server_mock.py +++ b/client/python/tests/unit/server_mock.py @@ -1,13 +1,15 @@ from google.protobuf import empty_pb2 from armada_client.armada import ( - submit_pb2_grpc, - submit_pb2, + binoculars_pb2, + binoculars_pb2_grpc, event_pb2, event_pb2_grpc, health_pb2, job_pb2_grpc, job_pb2, + submit_pb2, + submit_pb2_grpc, ) from armada_client.armada.job_pb2 import JobRunState from armada_client.armada.submit_pb2 import JobState @@ -149,3 +151,17 @@ def GetJobRunDetails(self, request, context): for run in request.run_ids } ) + + +class BinocularsService(binoculars_pb2_grpc.BinocularsServicer): + def Logs(self, request, context): + return binoculars_pb2.LogResponse( + log=[ + binoculars_pb2.LogLine(timestamp="now", line="some log contents!"), + binoculars_pb2.LogLine(timestamp="now", line="some more log contents!"), + binoculars_pb2.LogLine(timestamp="now", line="even more log contents!"), + ], + ) + + def Cordon(self, request, context): + return empty_pb2.Empty() diff --git a/client/python/tests/unit/test_binoculars_client.py b/client/python/tests/unit/test_binoculars_client.py new file mode 100644 index 00000000000..cd9a3a5474a --- /dev/null +++ b/client/python/tests/unit/test_binoculars_client.py @@ -0,0 +1,44 @@ +from concurrent import futures +import datetime + +import grpc +import pytest + +from google.protobuf import empty_pb2 + +from server_mock import BinocularsService + +from armada_client.armada import binoculars_pb2_grpc +from armada_client.binoculars_client import BinocularsClient + + +@pytest.fixture(scope="session", autouse=True) +def binoculars_server_mock(): + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + binoculars_pb2_grpc.add_BinocularsServicer_to_server(BinocularsService(), server) + server.add_insecure_port("[::]:4000") + server.start() + + yield + server.stop(False) + + +channel = grpc.insecure_channel(target="127.0.0.1:4000") +tester = BinocularsClient( + grpc.insecure_channel( + target="127.0.0.1:4000", + options={ + "grpc.keepalive_time_ms": 30000, + }.items(), + ) +) + + +def test_logs(): + resp = tester.logs("fake-job-id", "fake-namespace", datetime.datetime.now()) + assert len(resp.log) == 3 + + +def test_cordon(): + result = tester.cordon("fake-node-name") + assert result == empty_pb2.Empty() diff --git a/scripts/build-python-client.sh b/scripts/build-python-client.sh index 9e2bece9a24..5fd23818146 100755 --- a/scripts/build-python-client.sh +++ b/scripts/build-python-client.sh @@ -3,7 +3,7 @@ # make the python package armada.client, not pkg.api mkdir -p proto/armada -cp pkg/api/event.proto pkg/api/submit.proto pkg/api/health.proto pkg/api/job.proto proto/armada +cp pkg/api/event.proto pkg/api/submit.proto pkg/api/health.proto pkg/api/job.proto pkg/api/binoculars/binoculars.proto proto/armada sed -i 's/\([^\/]\)pkg\/api/\1armada/g' proto/armada/*.proto # generate python stubs @@ -11,7 +11,7 @@ cd proto python3 -m grpc_tools.protoc -I. --plugin=protoc-gen-mypy=$(which protoc-gen-mypy) --python_out=../client/python/armada_client --grpc_python_out=../client/python/armada_client --mypy_out=../client/python/armada_client \ google/api/annotations.proto \ google/api/http.proto \ - armada/event.proto armada/submit.proto armada/health.proto armada/job.proto \ + armada/event.proto armada/submit.proto armada/health.proto armada/job.proto armada/binoculars.proto \ github.com/gogo/protobuf/gogoproto/gogo.proto \ k8s.io/api/core/v1/generated.proto \ k8s.io/apimachinery/pkg/api/resource/generated.proto \ From d6ff7cff30280230828f3d98fb681755b8005e49 Mon Sep 17 00:00:00 2001 From: Clif Houck Date: Wed, 4 Sep 2024 09:02:41 -0500 Subject: [PATCH 2/7] Fixes to python binoculars client Signed-off-by: Clif Houck --- .../python/armada_client/binoculars_client.py | 6 ++-- client/python/examples/binoculars.py | 36 +++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 client/python/examples/binoculars.py diff --git a/client/python/armada_client/binoculars_client.py b/client/python/armada_client/binoculars_client.py index f2b821b1f87..678e9228362 100644 --- a/client/python/armada_client/binoculars_client.py +++ b/client/python/armada_client/binoculars_client.py @@ -27,15 +27,15 @@ def logs( self, job_id: str, pod_namespace: str, - since_time: datetime.datetime, + since_time: str, pod_number: Optional[int] = 0, - log_options: Optional[core_v1.PodLogOptions] = None, + log_options: Optional[core_v1.PodLogOptions] = core_v1.PodLogOptions(), ): log_request = binoculars_pb2.LogRequest( job_id=job_id, pod_number=pod_number, pod_namespace=pod_namespace, - since_time=since_time.isoformat(), + since_time=since_time, log_options=log_options, ) return self.binoculars_stub.Logs(log_request) diff --git a/client/python/examples/binoculars.py b/client/python/examples/binoculars.py new file mode 100644 index 00000000000..92972b67fda --- /dev/null +++ b/client/python/examples/binoculars.py @@ -0,0 +1,36 @@ +import grpc +from armada_client.binoculars_client import BinocularsClient + +import datetime +import os + + +def main(): + if DISABLE_SSL: + channel = grpc.insecure_channel(f"{HOST}:{PORT}") + else: + channel_credentials = grpc.ssl_channel_credentials() + channel = grpc.secure_channel( + f"{HOST}:{PORT}", + channel_credentials, + ) + + client = BinocularsClient(channel) + + log_response = client.logs( + JOB_ID, + "default", + "", + ) + + for line in log_response.log: + print(line) + + +if __name__ == "__main__": + DISABLE_SSL = os.environ.get("DISABLE_SSL", True) + HOST = os.environ.get("BINOCULARS_SERVER", "localhost") + PORT = os.environ.get("BINOCULARS_PORT", "50053") + JOB_ID = os.environ.get("JOB_ID") + + main() From 31dc6c7cd62605520324dd70ab14e50f466d6cde Mon Sep 17 00:00:00 2001 From: Clif Houck Date: Wed, 4 Sep 2024 09:12:56 -0500 Subject: [PATCH 3/7] Remove unsued imports Signed-off-by: Clif Houck --- client/python/armada_client/binoculars_client.py | 1 - client/python/examples/binoculars.py | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/client/python/armada_client/binoculars_client.py b/client/python/armada_client/binoculars_client.py index 678e9228362..adc459b943b 100644 --- a/client/python/armada_client/binoculars_client.py +++ b/client/python/armada_client/binoculars_client.py @@ -1,4 +1,3 @@ -import datetime from typing import Optional diff --git a/client/python/examples/binoculars.py b/client/python/examples/binoculars.py index 92972b67fda..5ac8c6a79eb 100644 --- a/client/python/examples/binoculars.py +++ b/client/python/examples/binoculars.py @@ -1,9 +1,8 @@ +import os + import grpc from armada_client.binoculars_client import BinocularsClient -import datetime -import os - def main(): if DISABLE_SSL: From 620f296a92a00dc019affa0d316e4ea6af4d1b9f Mon Sep 17 00:00:00 2001 From: Clif Houck Date: Wed, 4 Sep 2024 09:27:45 -0500 Subject: [PATCH 4/7] Add docs to BinocularsClient Signed-off-by: Clif Houck --- .../python/armada_client/binoculars_client.py | 16 +++++ .../docs/source/python_armada_client.rst | 9 ++- docs/python_armada_client.md | 67 +++++++++++++++++++ 3 files changed, 91 insertions(+), 1 deletion(-) diff --git a/client/python/armada_client/binoculars_client.py b/client/python/armada_client/binoculars_client.py index adc459b943b..2b05a48b904 100644 --- a/client/python/armada_client/binoculars_client.py +++ b/client/python/armada_client/binoculars_client.py @@ -30,6 +30,17 @@ def logs( pod_number: Optional[int] = 0, log_options: Optional[core_v1.PodLogOptions] = core_v1.PodLogOptions(), ): + """Retrieve logs for a specific Armada job. + + :param job_id: The ID of the job for which to retreieve logs. + :param pod_namespace: The namespace of the pod/job. + :param since_time: If the empty string, retrieves all available logs. + Otherwise, retrieves logs emitted since given timestamp. + :param pod_number: The zero-indexed pod number from which to retrieve + logs. Defaults to zero. + :param log_options: An optional Kubernetes PodLogOptions object. + :return: A LogResponse object. + """ log_request = binoculars_pb2.LogRequest( job_id=job_id, pod_number=pod_number, @@ -40,5 +51,10 @@ def logs( return self.binoculars_stub.Logs(log_request) def cordon(self, node_name: str): + """Send a cordon request for a specific node. + + :param node_name: The name of the node. + :return: Empty grpc object. + """ cordon_request = binoculars_pb2.CordonRequest(node_name=node_name) return self.binoculars_stub.Cordon(cordon_request) diff --git a/client/python/docs/source/python_armada_client.rst b/client/python/docs/source/python_armada_client.rst index 3db43ad4c39..5b05695db73 100644 --- a/client/python/docs/source/python_armada_client.rst +++ b/client/python/docs/source/python_armada_client.rst @@ -20,4 +20,11 @@ armada\_client.permissions module --------------------------------- .. automodule:: armada_client.permissions - :members: \ No newline at end of file + :members: + + +armada\_client.binoculars_client module +--------------------------------------- + +.. automodule:: armada_client.binoculars_client + :members: diff --git a/docs/python_armada_client.md b/docs/python_armada_client.md index e2dc1228f89..91a4d6e0b25 100644 --- a/docs/python_armada_client.md +++ b/docs/python_armada_client.md @@ -654,3 +654,70 @@ Convert this Subject to a grpc Subject. * **Return type** armada.submit_pb2.Subject + + +## armada_client.binoculars_client module + + +### _class_ armada_client.binoculars_client.BinocularsClient(channel) +Client for accessing Armada’s Binoculars service over gRPC. + + +* **Parameters** + + **channel** – gRPC channel used for authentication. See + [https://grpc.github.io/grpc/python/grpc.html](https://grpc.github.io/grpc/python/grpc.html) + for more information. + + + +* **Returns** + + an Binoculars client instance + + + +#### cordon(node_name) +Send a cordon request for a specific node. + + +* **Parameters** + + **node_name** (*str*) – The name of the node. + + + +* **Returns** + + Empty grpc object. + + + +#### logs(job_id, pod_namespace, since_time, pod_number=0, log_options=) +Retrieve logs for a specific Armada job. + + +* **Parameters** + + + * **job_id** (*str*) – The ID of the job for which to retreieve logs. + + + * **pod_namespace** (*str*) – The namespace of the pod/job. + + + * **since_time** (*str*) – If the empty string, retrieves all available logs. + Otherwise, retrieves logs emitted since given timestamp. + + + * **pod_number** (*int** | **None*) – The zero-indexed pod number from which to retrieve + logs. Defaults to zero. + + + * **log_options** (*k8s.io.api.core.v1.generated_pb2.PodLogOptions** | **None*) – An optional Kubernetes PodLogOptions object. + + + +* **Returns** + + A LogResponse object. From 6af31585724893fc0c370d426a8a5f2dc919b6b8 Mon Sep 17 00:00:00 2001 From: Clif Houck Date: Wed, 4 Sep 2024 09:45:44 -0500 Subject: [PATCH 5/7] Fix binoculars unit test Signed-off-by: Clif Houck --- client/python/tests/unit/test_binoculars_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/python/tests/unit/test_binoculars_client.py b/client/python/tests/unit/test_binoculars_client.py index cd9a3a5474a..e5e3170408f 100644 --- a/client/python/tests/unit/test_binoculars_client.py +++ b/client/python/tests/unit/test_binoculars_client.py @@ -35,7 +35,7 @@ def binoculars_server_mock(): def test_logs(): - resp = tester.logs("fake-job-id", "fake-namespace", datetime.datetime.now()) + resp = tester.logs("fake-job-id", "fake-namespace", "") assert len(resp.log) == 3 From 53978b6f2382a44c11199e34c8032ab2cf869189 Mon Sep 17 00:00:00 2001 From: Clif Houck Date: Wed, 4 Sep 2024 09:48:46 -0500 Subject: [PATCH 6/7] Remove unused import Signed-off-by: Clif Houck --- client/python/tests/unit/test_binoculars_client.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/python/tests/unit/test_binoculars_client.py b/client/python/tests/unit/test_binoculars_client.py index e5e3170408f..d7257c0121c 100644 --- a/client/python/tests/unit/test_binoculars_client.py +++ b/client/python/tests/unit/test_binoculars_client.py @@ -1,5 +1,4 @@ from concurrent import futures -import datetime import grpc import pytest From 9290748bf616e61d34664a95a86833ff1b435aa2 Mon Sep 17 00:00:00 2001 From: Clif Houck Date: Wed, 25 Sep 2024 09:24:20 -0500 Subject: [PATCH 7/7] Add a JobLogClient class for retrieving logs Signed-off-by: Clif Houck --- .../python/armada_client/internal/__init__.py | 6 +++ .../{ => internal}/binoculars_client.py | 29 ++++++++++- client/python/armada_client/log_client.py | 42 +++++++++++++++ .../docs/source/python_armada_client.rst | 4 +- client/python/examples/binoculars.py | 39 ++++++-------- ...noculars_client.py => test_log_clients.py} | 12 ++++- docs/python_armada_client.md | 51 ++++++++----------- 7 files changed, 124 insertions(+), 59 deletions(-) create mode 100644 client/python/armada_client/internal/__init__.py rename client/python/armada_client/{ => internal}/binoculars_client.py (70%) create mode 100644 client/python/armada_client/log_client.py rename client/python/tests/unit/{test_binoculars_client.py => test_log_clients.py} (71%) diff --git a/client/python/armada_client/internal/__init__.py b/client/python/armada_client/internal/__init__.py new file mode 100644 index 00000000000..f97e6e79daf --- /dev/null +++ b/client/python/armada_client/internal/__init__.py @@ -0,0 +1,6 @@ +"""DO NOT USE - INTERNAL FACING ONLY - DO NOT USE + +Internal modules are *NOT* meant for public consumption. External users of the +armada_client should not use or call any code contained in these modules as +they are unsupported and could change or break at any time. +""" diff --git a/client/python/armada_client/binoculars_client.py b/client/python/armada_client/internal/binoculars_client.py similarity index 70% rename from client/python/armada_client/binoculars_client.py rename to client/python/armada_client/internal/binoculars_client.py index 2b05a48b904..72950c02f81 100644 --- a/client/python/armada_client/binoculars_client.py +++ b/client/python/armada_client/internal/binoculars_client.py @@ -1,5 +1,6 @@ from typing import Optional +import grpc from armada_client.armada import ( binoculars_pb2, @@ -9,6 +10,32 @@ from armada_client.k8s.io.api.core.v1 import generated_pb2 as core_v1 +def new_binoculars_client(url: str, disable_ssl: bool = False): + """Constructs and returns a new BinocularsClient object. + + :param url: A url specifying the gRPC binoculars endpoint in the format + "host:port". + + :return: A new BinocularsClient object. + """ + parts = url.split(":") + if len(parts) != 2: + raise ValueError(f"Could not parse url provided: {url}") + + host, port = parts[0], parts[1] + if disable_ssl: + channel = grpc.insecure_channel(f"{host}:{port}") + else: + channel_credentials = grpc.ssl_channel_credentials() + channel = grpc.secure_channel( + f"{host}:{port}", + channel_credentials, + ) + + client = BinocularsClient(channel) + return (channel, client) + + class BinocularsClient: """ Client for accessing Armada's Binoculars service over gRPC. @@ -25,8 +52,8 @@ def __init__(self, channel): def logs( self, job_id: str, - pod_namespace: str, since_time: str, + pod_namespace: Optional[str] = "default", pod_number: Optional[int] = 0, log_options: Optional[core_v1.PodLogOptions] = core_v1.PodLogOptions(), ): diff --git a/client/python/armada_client/log_client.py b/client/python/armada_client/log_client.py new file mode 100644 index 00000000000..2fe477aa1e6 --- /dev/null +++ b/client/python/armada_client/log_client.py @@ -0,0 +1,42 @@ +from dataclasses import dataclass +from typing import Optional + +from armada_client.internal.binoculars_client import new_binoculars_client + + +@dataclass +class LogLine: + """Represents a single line from a log.""" + + line: str + timestamp: str + + +class JobLogClient: + """ + Client for retrieving logs for a given job. + + :param url: The url to use for retreiving logs. + :param job_id: The ID of the job. + :return: A JobLogClient instance. + """ + + def __init__(self, url: str, job_id: str, disable_ssl: bool = False): + self.job_id = job_id + self.url = url + self._channel, self._concrete_client = new_binoculars_client( + self.url, disable_ssl + ) + + def logs(self, since_time: Optional[str] = ""): + """Retrieve logs for the job associated with this client. + + :param since_time: Logs will be retrieved starting at the time + specified in this str. Must conform to RFC3339 date time format. + + :return: A list of LogLine objects. + """ + return [ + LogLine(line.line, line.timestamp) + for line in self._concrete_client.logs(self.job_id, since_time).log + ] diff --git a/client/python/docs/source/python_armada_client.rst b/client/python/docs/source/python_armada_client.rst index 5b05695db73..73eca757176 100644 --- a/client/python/docs/source/python_armada_client.rst +++ b/client/python/docs/source/python_armada_client.rst @@ -23,8 +23,8 @@ armada\_client.permissions module :members: -armada\_client.binoculars_client module +armada\_client.log_client module --------------------------------------- -.. automodule:: armada_client.binoculars_client +.. automodule:: armada_client.log_client :members: diff --git a/client/python/examples/binoculars.py b/client/python/examples/binoculars.py index 5ac8c6a79eb..23ee2c6fcda 100644 --- a/client/python/examples/binoculars.py +++ b/client/python/examples/binoculars.py @@ -1,35 +1,26 @@ +"""Example script utiltizing JobLogClient.""" + import os -import grpc -from armada_client.binoculars_client import BinocularsClient +from armada_client.log_client import JobLogClient -def main(): - if DISABLE_SSL: - channel = grpc.insecure_channel(f"{HOST}:{PORT}") - else: - channel_credentials = grpc.ssl_channel_credentials() - channel = grpc.secure_channel( - f"{HOST}:{PORT}", - channel_credentials, - ) +DISABLE_SSL = os.environ.get("DISABLE_SSL", True) +HOST = os.environ.get("BINOCULARS_SERVER", "localhost") +PORT = os.environ.get("BINOCULARS_PORT", "50053") +JOB_ID = os.environ.get("JOB_ID") - client = BinocularsClient(channel) - log_response = client.logs( - JOB_ID, - "default", - "", - ) +def main(): + """Demonstrate basic use of JobLogClient.""" + url = f"{HOST}:{PORT}" + client = JobLogClient(url, JOB_ID, DISABLE_SSL) - for line in log_response.log: - print(line) + log_lines = client.logs() + for line in log_lines: + print(line.line) -if __name__ == "__main__": - DISABLE_SSL = os.environ.get("DISABLE_SSL", True) - HOST = os.environ.get("BINOCULARS_SERVER", "localhost") - PORT = os.environ.get("BINOCULARS_PORT", "50053") - JOB_ID = os.environ.get("JOB_ID") +if __name__ == "__main__": main() diff --git a/client/python/tests/unit/test_binoculars_client.py b/client/python/tests/unit/test_log_clients.py similarity index 71% rename from client/python/tests/unit/test_binoculars_client.py rename to client/python/tests/unit/test_log_clients.py index d7257c0121c..3f3babd626e 100644 --- a/client/python/tests/unit/test_binoculars_client.py +++ b/client/python/tests/unit/test_log_clients.py @@ -8,7 +8,8 @@ from server_mock import BinocularsService from armada_client.armada import binoculars_pb2_grpc -from armada_client.binoculars_client import BinocularsClient +from armada_client.internal.binoculars_client import BinocularsClient +from armada_client.log_client import JobLogClient, LogLine @pytest.fixture(scope="session", autouse=True) @@ -41,3 +42,12 @@ def test_logs(): def test_cordon(): result = tester.cordon("fake-node-name") assert result == empty_pb2.Empty() + + +def test_job_log_client(): + client = JobLogClient("127.0.0.1:4000", "fake-job-id", True) + log_lines = client.logs() + assert len(log_lines) == 3 + for line in log_lines: + assert isinstance(line, LogLine) + assert len(line.line) > 0 diff --git a/docs/python_armada_client.md b/docs/python_armada_client.md index 91a4d6e0b25..ce44f8741ab 100644 --- a/docs/python_armada_client.md +++ b/docs/python_armada_client.md @@ -656,68 +656,57 @@ Convert this Subject to a grpc Subject. armada.submit_pb2.Subject -## armada_client.binoculars_client module +## armada_client.log_client module -### _class_ armada_client.binoculars_client.BinocularsClient(channel) -Client for accessing Armada’s Binoculars service over gRPC. +### _class_ armada_client.log_client.JobLogClient(url, job_id, disable_ssl=False) +Client for retrieving logs for a given job. * **Parameters** - **channel** – gRPC channel used for authentication. See - [https://grpc.github.io/grpc/python/grpc.html](https://grpc.github.io/grpc/python/grpc.html) - for more information. - - - -* **Returns** - - an Binoculars client instance - - + + * **url** (*str*) – The url to use for retreiving logs. -#### cordon(node_name) -Send a cordon request for a specific node. + * **job_id** (*str*) – The ID of the job. -* **Parameters** - **node_name** (*str*) – The name of the node. + * **disable_ssl** (*bool*) – * **Returns** - Empty grpc object. + A JobLogClient instance. -#### logs(job_id, pod_namespace, since_time, pod_number=0, log_options=) -Retrieve logs for a specific Armada job. +#### logs(since_time='') +Retrieve logs for the job associated with this client. * **Parameters** - - * **job_id** (*str*) – The ID of the job for which to retreieve logs. + **since_time** (*str** | **None*) – Logs will be retrieved starting at the time + specified in this str. Must conform to RFC3339 date time format. - * **pod_namespace** (*str*) – The namespace of the pod/job. +* **Returns** - * **since_time** (*str*) – If the empty string, retrieves all available logs. - Otherwise, retrieves logs emitted since given timestamp. + A list of LogLine objects. - * **pod_number** (*int** | **None*) – The zero-indexed pod number from which to retrieve - logs. Defaults to zero. +### _class_ armada_client.log_client.LogLine(line, timestamp) +Represents a single line from a log. - * **log_options** (*k8s.io.api.core.v1.generated_pb2.PodLogOptions** | **None*) – An optional Kubernetes PodLogOptions object. +* **Parameters** + + * **line** (*str*) – -* **Returns** - A LogResponse object. + * **timestamp** (*str*) –