-
Notifications
You must be signed in to change notification settings - Fork 136
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a generic log retrieval client to Armada Python Client (#3892)
* Add a JobLogClient class for retrieving logs Signed-off-by: Clif Houck <[email protected]> --------- Signed-off-by: Clif Houck <[email protected]> Co-authored-by: Dejan Zele Pejchev <[email protected]>
- Loading branch information
Showing
9 changed files
with
298 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. | ||
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
from typing import Optional | ||
|
||
import grpc | ||
|
||
from armada_client.armada import ( | ||
binoculars_pb2, | ||
binoculars_pb2_grpc, | ||
) | ||
|
||
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. | ||
: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, | ||
since_time: str, | ||
pod_namespace: Optional[str] = "default", | ||
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, | ||
pod_namespace=pod_namespace, | ||
since_time=since_time, | ||
log_options=log_options, | ||
) | ||
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
"""Example script utiltizing JobLogClient.""" | ||
|
||
import os | ||
|
||
from armada_client.log_client import JobLogClient | ||
|
||
|
||
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") | ||
|
||
|
||
def main(): | ||
"""Demonstrate basic use of JobLogClient.""" | ||
url = f"{HOST}:{PORT}" | ||
client = JobLogClient(url, JOB_ID, DISABLE_SSL) | ||
|
||
log_lines = client.logs() | ||
|
||
for line in log_lines: | ||
print(line.line) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
from concurrent import futures | ||
|
||
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.internal.binoculars_client import BinocularsClient | ||
from armada_client.log_client import JobLogClient, LogLine | ||
|
||
|
||
@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", "") | ||
assert len(resp.log) == 3 | ||
|
||
|
||
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters