|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
3 | | -from dataclasses import dataclass, field |
| 3 | +from collections import OrderedDict |
| 4 | +from dataclasses import InitVar, dataclass, field |
4 | 5 | from datetime import datetime, timedelta |
5 | 6 |
|
6 | 7 | import yaml |
7 | 8 | from google.protobuf import duration_pb2, field_mask_pb2, json_format |
| 9 | +from grpc import ChannelConnectivity |
8 | 10 | from grpc.aio import Channel |
9 | | -from jumpstarter_protocol import client_pb2, client_pb2_grpc, kubernetes_pb2 |
| 11 | +from jumpstarter_protocol import client_pb2, client_pb2_grpc, jumpstarter_pb2_grpc, kubernetes_pb2 |
10 | 12 | from pydantic import BaseModel, ConfigDict, Field, field_serializer |
11 | 13 |
|
12 | 14 | from jumpstarter.common.grpc import translate_grpc_exceptions |
@@ -250,3 +252,25 @@ async def DeleteLease(self, *, name: str): |
250 | 252 | name="namespaces/{}/leases/{}".format(self.namespace, name), |
251 | 253 | ) |
252 | 254 | ) |
| 255 | + |
| 256 | + |
| 257 | +@dataclass(frozen=True, slots=True) |
| 258 | +class SmartExporterServiceStub: |
| 259 | + channels: InitVar[list[Channel]] |
| 260 | + |
| 261 | + __stubs: dict[Channel, jumpstarter_pb2_grpc.ExporterServiceStub] = field( |
| 262 | + init=False, |
| 263 | + default_factory=OrderedDict, |
| 264 | + ) |
| 265 | + |
| 266 | + def __post_init__(self, channels): |
| 267 | + for channel in channels: |
| 268 | + self.__stubs[channel] = jumpstarter_pb2_grpc.ExporterServiceStub(channel) |
| 269 | + |
| 270 | + def __getattr__(self, name): |
| 271 | + for channel, stub in self.__stubs.items(): |
| 272 | + # find the first channel that's ready |
| 273 | + if channel.get_state(try_to_connect=True) == ChannelConnectivity.READY: |
| 274 | + return getattr(stub, name) |
| 275 | + # or fallback to the last channel (via router) |
| 276 | + return getattr(next(reversed(self.__stubs.values())), name) |
0 commit comments