Skip to content

Commit 18c310b

Browse files
committed
coordinator: Serve env file via new GetEnvironment RPC
The lab admin currently has to distribute the env file to every client out of band - via a shared filesystem, git checkout, scp etc. This is awkward duplication for casual clients who just want to talk to a few boards from a remote machine. It also makes it harder to scale to larger labs with a lot of clients. For some labs, particularly smaller ones, the environment is very tied to the client, with each client having its own special file. For other labs, such as larger labs where changes are few, the environment is the same for each client. Add a new GetEnvironment RPC that returns the env file's text content, and a coordinator --environment flag pointing at the file to serve. The file is read fresh on each request, so admins can edit it in place and clients pick up the new version on their next invocation. The default (no --environment) is unchanged: GetEnvironment returns an empty string and clients keep loading env from a local file as before. Signed-off-by: Simon Glass <sjg@chromium.org>
1 parent db1cba3 commit 18c310b

7 files changed

Lines changed: 113 additions & 7 deletions

File tree

doc/man/coordinator.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,23 @@ OPTIONS
2525
display command line help
2626
-l ADDRESS, --listen ADDRESS
2727
make coordinator listen on host and port
28+
-e FILE, --environment FILE
29+
serve the given YAML env file to clients via the ``GetEnvironment`` RPC.
30+
Clients opt in by setting ``LG_ENV=coordinator:`` (see
31+
``labgrid-client``\(1))
2832
-d, --debug
2933
enable debug mode
3034

35+
-e / --environment
36+
~~~~~~~~~~~~~~~~~~
37+
When this option is set the coordinator reads the file fresh on each
38+
``GetEnvironment`` request and returns its contents verbatim to the client.
39+
This means a remote user no longer needs a local copy of the env file - they
40+
only need network access to the coordinator.
41+
42+
The default (no ``--environment``) is unchanged: ``GetEnvironment`` returns an
43+
empty string and clients keep loading env from a local file as before.
44+
3145
SEE ALSO
3246
--------
3347

labgrid/remote/coordinator.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,11 +209,12 @@ class ExporterError(Exception):
209209

210210

211211
class Coordinator(labgrid_coordinator_pb2_grpc.CoordinatorServicer):
212-
def __init__(self) -> None:
212+
def __init__(self, environment_file: str | None = None) -> None:
213213
self.places: dict[str, Place] = {}
214214
self.reservations = {}
215215
self.poll_tasks = []
216216
self.save_scheduled = False
217+
self.environment_file = environment_file
217218

218219
self.lock = asyncio.Lock()
219220
self.exporters: dict[str, ExporterSession] = {}
@@ -1105,8 +1106,17 @@ async def GetReservations(self, request: labgrid_coordinator_pb2.GetReservations
11051106
reservations = [x.as_pb2() for x in self.reservations.values()]
11061107
return labgrid_coordinator_pb2.GetReservationsResponse(reservations=reservations)
11071108

1109+
async def GetEnvironment(self, request: labgrid_coordinator_pb2.GetEnvironmentRequest, context):
1110+
if not self.environment_file:
1111+
return labgrid_coordinator_pb2.GetEnvironmentResponse(config="")
1112+
try:
1113+
with open(self.environment_file, encoding="utf-8") as f:
1114+
return labgrid_coordinator_pb2.GetEnvironmentResponse(config=f.read())
1115+
except OSError as e:
1116+
await context.abort(grpc.StatusCode.FAILED_PRECONDITION, f"cannot read environment file: {e}")
1117+
11081118

1109-
async def serve(listen, cleanup) -> None:
1119+
async def serve(listen, cleanup, environment_file=None) -> None:
11101120
asyncio.current_task().set_name("coordinator-serve")
11111121
# It seems since https://github.com/grpc/grpc/pull/34647, the
11121122
# ping_timeout_ms default of 60 seconds overrides keepalive_timeout_ms,
@@ -1124,7 +1134,7 @@ async def serve(listen, cleanup) -> None:
11241134
server = grpc.aio.server(
11251135
options=channel_options,
11261136
)
1127-
coordinator = Coordinator()
1137+
coordinator = Coordinator(environment_file=environment_file)
11281138
labgrid_coordinator_pb2_grpc.add_CoordinatorServicer_to_server(coordinator, server)
11291139
# enable reflection for use with grpcurl
11301140
reflection.enable_server_reflection(
@@ -1172,6 +1182,15 @@ def main():
11721182
default="[::]:20408",
11731183
help="coordinator listening host and port",
11741184
)
1185+
parser.add_argument(
1186+
"-e",
1187+
"--environment",
1188+
metavar="FILE",
1189+
type=str,
1190+
default=None,
1191+
help="path to a YAML environment file to serve to clients via "
1192+
"GetEnvironment (LG_ENV=coordinator: on the client side)",
1193+
)
11751194
parser.add_argument("-d", "--debug", action="store_true", default=False, help="enable debug mode")
11761195
parser.add_argument("--pystuck", action="store_true", help="enable pystuck")
11771196
parser.add_argument(
@@ -1201,7 +1220,7 @@ def main():
12011220
cleanup = []
12021221
loop.set_debug(True)
12031222
try:
1204-
loop.run_until_complete(serve(args.listen, cleanup))
1223+
loop.run_until_complete(serve(args.listen, cleanup, environment_file=args.environment))
12051224
finally:
12061225
if cleanup:
12071226
loop.run_until_complete(*cleanup)

labgrid/remote/generated/labgrid_coordinator_pb2.py

Lines changed: 7 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

labgrid/remote/generated/labgrid_coordinator_pb2.pyi

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,3 +446,13 @@ class GetReservationsResponse(_message.Message):
446446
class GetReservationsRequest(_message.Message):
447447
__slots__ = ()
448448
def __init__(self) -> None: ...
449+
450+
class GetEnvironmentRequest(_message.Message):
451+
__slots__ = ()
452+
def __init__(self) -> None: ...
453+
454+
class GetEnvironmentResponse(_message.Message):
455+
__slots__ = ("config",)
456+
CONFIG_FIELD_NUMBER: _ClassVar[int]
457+
config: str
458+
def __init__(self, config: _Optional[str] = ...) -> None: ...

labgrid/remote/generated/labgrid_coordinator_pb2_grpc.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@ def __init__(self, channel):
104104
request_serializer=labgrid__coordinator__pb2.GetReservationsRequest.SerializeToString,
105105
response_deserializer=labgrid__coordinator__pb2.GetReservationsResponse.FromString,
106106
)
107+
self.GetEnvironment = channel.unary_unary(
108+
'/labgrid.Coordinator/GetEnvironment',
109+
request_serializer=labgrid__coordinator__pb2.GetEnvironmentRequest.SerializeToString,
110+
response_deserializer=labgrid__coordinator__pb2.GetEnvironmentResponse.FromString,
111+
)
107112

108113

109114
class CoordinatorServicer(object):
@@ -217,6 +222,12 @@ def GetReservations(self, request, context):
217222
context.set_details('Method not implemented!')
218223
raise NotImplementedError('Method not implemented!')
219224

225+
def GetEnvironment(self, request, context):
226+
"""Missing associated documentation comment in .proto file."""
227+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
228+
context.set_details('Method not implemented!')
229+
raise NotImplementedError('Method not implemented!')
230+
220231

221232
def add_CoordinatorServicer_to_server(servicer, server):
222233
rpc_method_handlers = {
@@ -310,6 +321,11 @@ def add_CoordinatorServicer_to_server(servicer, server):
310321
request_deserializer=labgrid__coordinator__pb2.GetReservationsRequest.FromString,
311322
response_serializer=labgrid__coordinator__pb2.GetReservationsResponse.SerializeToString,
312323
),
324+
'GetEnvironment': grpc.unary_unary_rpc_method_handler(
325+
servicer.GetEnvironment,
326+
request_deserializer=labgrid__coordinator__pb2.GetEnvironmentRequest.FromString,
327+
response_serializer=labgrid__coordinator__pb2.GetEnvironmentResponse.SerializeToString,
328+
),
313329
}
314330
generic_handler = grpc.method_handlers_generic_handler(
315331
'labgrid.Coordinator', rpc_method_handlers)
@@ -625,3 +641,20 @@ def GetReservations(request,
625641
labgrid__coordinator__pb2.GetReservationsResponse.FromString,
626642
options, channel_credentials,
627643
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
644+
645+
@staticmethod
646+
def GetEnvironment(request,
647+
target,
648+
options=(),
649+
channel_credentials=None,
650+
call_credentials=None,
651+
insecure=False,
652+
compression=None,
653+
wait_for_ready=None,
654+
timeout=None,
655+
metadata=None):
656+
return grpc.experimental.unary_unary(request, target, '/labgrid.Coordinator/GetEnvironment',
657+
labgrid__coordinator__pb2.GetEnvironmentRequest.SerializeToString,
658+
labgrid__coordinator__pb2.GetEnvironmentResponse.FromString,
659+
options, channel_credentials,
660+
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)

labgrid/remote/proto/labgrid-coordinator.proto

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ service Coordinator {
3838
rpc PollReservation(PollReservationRequest) returns (PollReservationResponse) {}
3939

4040
rpc GetReservations(GetReservationsRequest) returns (GetReservationsResponse) {}
41+
42+
rpc GetEnvironment(GetEnvironmentRequest) returns (GetEnvironmentResponse) {}
4143
}
4244

4345
message ClientInMessage {
@@ -295,3 +297,13 @@ message GetReservationsResponse {
295297

296298
message GetReservationsRequest {
297299
};
300+
301+
message GetEnvironmentRequest {
302+
};
303+
304+
message GetEnvironmentResponse {
305+
// Raw text of the lab environment file (typically YAML) curated by
306+
// the lab admin and served to clients. Empty if the coordinator was
307+
// started without a configured environment file.
308+
string config = 1;
309+
};

man/labgrid-coordinator.1

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,23 @@ display command line help
5151
.BI \-l \ ADDRESS\fR,\fB \ \-\-listen \ ADDRESS
5252
make coordinator listen on host and port
5353
.TP
54+
.BI \-e \ FILE\fR,\fB \ \-\-environment \ FILE
55+
serve the given YAML env file to clients via the \fBGetEnvironment\fP RPC.
56+
Clients opt in by setting \fBLG_ENV=coordinator:\fP (see
57+
\fBlabgrid\-client\fP(1))
58+
.TP
5459
.B \-d\fP,\fB \-\-debug
5560
enable debug mode
5661
.UNINDENT
62+
.SS \-e / \-\-environment
63+
.sp
64+
When this option is set the coordinator reads the file fresh on each
65+
\fBGetEnvironment\fP request and returns its contents verbatim to the client.
66+
This means a remote user no longer needs a local copy of the env file \- they
67+
only need network access to the coordinator.
68+
.sp
69+
The default (no \fB\-\-environment\fP) is unchanged: \fBGetEnvironment\fP returns an
70+
empty string and clients keep loading env from a local file as before.
5771
.SS SEE ALSO
5872
.sp
5973
\fBlabgrid\-client\fP(1), \fBlabgrid\-exporter\fP(1)

0 commit comments

Comments
 (0)