flyte 0.2.0b1__py3-none-any.whl → 2.0.0b46__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flyte/__init__.py +83 -30
- flyte/_bin/connect.py +61 -0
- flyte/_bin/debug.py +38 -0
- flyte/_bin/runtime.py +87 -19
- flyte/_bin/serve.py +351 -0
- flyte/_build.py +3 -2
- flyte/_cache/cache.py +6 -5
- flyte/_cache/local_cache.py +216 -0
- flyte/_code_bundle/_ignore.py +31 -5
- flyte/_code_bundle/_packaging.py +42 -11
- flyte/_code_bundle/_utils.py +57 -34
- flyte/_code_bundle/bundle.py +130 -27
- flyte/_constants.py +1 -0
- flyte/_context.py +21 -5
- flyte/_custom_context.py +73 -0
- flyte/_debug/constants.py +37 -0
- flyte/_debug/utils.py +17 -0
- flyte/_debug/vscode.py +315 -0
- flyte/_deploy.py +396 -75
- flyte/_deployer.py +109 -0
- flyte/_environment.py +94 -11
- flyte/_excepthook.py +37 -0
- flyte/_group.py +2 -1
- flyte/_hash.py +1 -16
- flyte/_image.py +544 -231
- flyte/_initialize.py +456 -316
- flyte/_interface.py +40 -5
- flyte/_internal/controllers/__init__.py +22 -8
- flyte/_internal/controllers/_local_controller.py +159 -35
- flyte/_internal/controllers/_trace.py +18 -10
- flyte/_internal/controllers/remote/__init__.py +38 -9
- flyte/_internal/controllers/remote/_action.py +82 -12
- flyte/_internal/controllers/remote/_client.py +6 -2
- flyte/_internal/controllers/remote/_controller.py +290 -64
- flyte/_internal/controllers/remote/_core.py +155 -95
- flyte/_internal/controllers/remote/_informer.py +40 -20
- flyte/_internal/controllers/remote/_service_protocol.py +2 -2
- flyte/_internal/imagebuild/__init__.py +2 -10
- flyte/_internal/imagebuild/docker_builder.py +391 -84
- flyte/_internal/imagebuild/image_builder.py +111 -55
- flyte/_internal/imagebuild/remote_builder.py +409 -0
- flyte/_internal/imagebuild/utils.py +79 -0
- flyte/_internal/resolvers/_app_env_module.py +92 -0
- flyte/_internal/resolvers/_task_module.py +5 -38
- flyte/_internal/resolvers/app_env.py +26 -0
- flyte/_internal/resolvers/common.py +8 -1
- flyte/_internal/resolvers/default.py +2 -2
- flyte/_internal/runtime/convert.py +319 -36
- flyte/_internal/runtime/entrypoints.py +106 -18
- flyte/_internal/runtime/io.py +71 -23
- flyte/_internal/runtime/resources_serde.py +21 -7
- flyte/_internal/runtime/reuse.py +125 -0
- flyte/_internal/runtime/rusty.py +196 -0
- flyte/_internal/runtime/task_serde.py +239 -66
- flyte/_internal/runtime/taskrunner.py +48 -8
- flyte/_internal/runtime/trigger_serde.py +162 -0
- flyte/_internal/runtime/types_serde.py +7 -16
- flyte/_keyring/file.py +115 -0
- flyte/_link.py +30 -0
- flyte/_logging.py +241 -42
- flyte/_map.py +312 -0
- flyte/_metrics.py +59 -0
- flyte/_module.py +74 -0
- flyte/_pod.py +30 -0
- flyte/_resources.py +296 -33
- flyte/_retry.py +1 -7
- flyte/_reusable_environment.py +72 -7
- flyte/_run.py +462 -132
- flyte/_secret.py +47 -11
- flyte/_serve.py +333 -0
- flyte/_task.py +245 -56
- flyte/_task_environment.py +219 -97
- flyte/_task_plugins.py +47 -0
- flyte/_tools.py +8 -8
- flyte/_trace.py +15 -24
- flyte/_trigger.py +1027 -0
- flyte/_utils/__init__.py +12 -1
- flyte/_utils/asyn.py +3 -1
- flyte/_utils/async_cache.py +139 -0
- flyte/_utils/coro_management.py +5 -4
- flyte/_utils/description_parser.py +19 -0
- flyte/_utils/docker_credentials.py +173 -0
- flyte/_utils/helpers.py +45 -19
- flyte/_utils/module_loader.py +123 -0
- flyte/_utils/org_discovery.py +57 -0
- flyte/_utils/uv_script_parser.py +8 -1
- flyte/_version.py +16 -3
- flyte/app/__init__.py +27 -0
- flyte/app/_app_environment.py +362 -0
- flyte/app/_connector_environment.py +40 -0
- flyte/app/_deploy.py +130 -0
- flyte/app/_parameter.py +343 -0
- flyte/app/_runtime/__init__.py +3 -0
- flyte/app/_runtime/app_serde.py +383 -0
- flyte/app/_types.py +113 -0
- flyte/app/extras/__init__.py +9 -0
- flyte/app/extras/_auth_middleware.py +217 -0
- flyte/app/extras/_fastapi.py +93 -0
- flyte/app/extras/_model_loader/__init__.py +3 -0
- flyte/app/extras/_model_loader/config.py +7 -0
- flyte/app/extras/_model_loader/loader.py +288 -0
- flyte/cli/__init__.py +12 -0
- flyte/cli/_abort.py +28 -0
- flyte/cli/_build.py +114 -0
- flyte/cli/_common.py +493 -0
- flyte/cli/_create.py +371 -0
- flyte/cli/_delete.py +45 -0
- flyte/cli/_deploy.py +401 -0
- flyte/cli/_gen.py +316 -0
- flyte/cli/_get.py +446 -0
- flyte/cli/_option.py +33 -0
- flyte/{_cli → cli}/_params.py +57 -17
- flyte/cli/_plugins.py +209 -0
- flyte/cli/_prefetch.py +292 -0
- flyte/cli/_run.py +690 -0
- flyte/cli/_serve.py +338 -0
- flyte/cli/_update.py +86 -0
- flyte/cli/_user.py +20 -0
- flyte/cli/main.py +246 -0
- flyte/config/__init__.py +2 -167
- flyte/config/_config.py +215 -163
- flyte/config/_internal.py +10 -1
- flyte/config/_reader.py +225 -0
- flyte/connectors/__init__.py +11 -0
- flyte/connectors/_connector.py +330 -0
- flyte/connectors/_server.py +194 -0
- flyte/connectors/utils.py +159 -0
- flyte/errors.py +134 -2
- flyte/extend.py +24 -0
- flyte/extras/_container.py +69 -56
- flyte/git/__init__.py +3 -0
- flyte/git/_config.py +279 -0
- flyte/io/__init__.py +8 -1
- flyte/io/{structured_dataset → _dataframe}/__init__.py +32 -30
- flyte/io/{structured_dataset → _dataframe}/basic_dfs.py +75 -68
- flyte/io/{structured_dataset/structured_dataset.py → _dataframe/dataframe.py} +207 -242
- flyte/io/_dir.py +575 -113
- flyte/io/_file.py +587 -141
- flyte/io/_hashing_io.py +342 -0
- flyte/io/extend.py +7 -0
- flyte/models.py +635 -0
- flyte/prefetch/__init__.py +22 -0
- flyte/prefetch/_hf_model.py +563 -0
- flyte/remote/__init__.py +14 -3
- flyte/remote/_action.py +879 -0
- flyte/remote/_app.py +346 -0
- flyte/remote/_auth_metadata.py +42 -0
- flyte/remote/_client/_protocols.py +62 -4
- flyte/remote/_client/auth/_auth_utils.py +19 -0
- flyte/remote/_client/auth/_authenticators/base.py +8 -2
- flyte/remote/_client/auth/_authenticators/device_code.py +4 -5
- flyte/remote/_client/auth/_authenticators/factory.py +4 -0
- flyte/remote/_client/auth/_authenticators/passthrough.py +79 -0
- flyte/remote/_client/auth/_authenticators/pkce.py +17 -18
- flyte/remote/_client/auth/_channel.py +47 -18
- flyte/remote/_client/auth/_client_config.py +5 -3
- flyte/remote/_client/auth/_keyring.py +15 -2
- flyte/remote/_client/auth/_token_client.py +3 -3
- flyte/remote/_client/controlplane.py +206 -18
- flyte/remote/_common.py +66 -0
- flyte/remote/_data.py +107 -22
- flyte/remote/_logs.py +116 -33
- flyte/remote/_project.py +21 -19
- flyte/remote/_run.py +164 -631
- flyte/remote/_secret.py +72 -29
- flyte/remote/_task.py +387 -46
- flyte/remote/_trigger.py +368 -0
- flyte/remote/_user.py +43 -0
- flyte/report/_report.py +10 -6
- flyte/storage/__init__.py +13 -1
- flyte/storage/_config.py +237 -0
- flyte/storage/_parallel_reader.py +289 -0
- flyte/storage/_storage.py +268 -59
- flyte/syncify/__init__.py +56 -0
- flyte/syncify/_api.py +414 -0
- flyte/types/__init__.py +39 -0
- flyte/types/_interface.py +22 -7
- flyte/{io/pickle/transformer.py → types/_pickle.py} +37 -9
- flyte/types/_string_literals.py +8 -9
- flyte/types/_type_engine.py +226 -126
- flyte/types/_utils.py +1 -1
- flyte-2.0.0b46.data/scripts/debug.py +38 -0
- flyte-2.0.0b46.data/scripts/runtime.py +194 -0
- flyte-2.0.0b46.dist-info/METADATA +352 -0
- flyte-2.0.0b46.dist-info/RECORD +221 -0
- flyte-2.0.0b46.dist-info/entry_points.txt +8 -0
- flyte-2.0.0b46.dist-info/licenses/LICENSE +201 -0
- flyte/_api_commons.py +0 -3
- flyte/_cli/_common.py +0 -299
- flyte/_cli/_create.py +0 -42
- flyte/_cli/_delete.py +0 -23
- flyte/_cli/_deploy.py +0 -140
- flyte/_cli/_get.py +0 -235
- flyte/_cli/_run.py +0 -174
- flyte/_cli/main.py +0 -98
- flyte/_datastructures.py +0 -342
- flyte/_internal/controllers/pbhash.py +0 -39
- flyte/_protos/common/authorization_pb2.py +0 -66
- flyte/_protos/common/authorization_pb2.pyi +0 -108
- flyte/_protos/common/authorization_pb2_grpc.py +0 -4
- flyte/_protos/common/identifier_pb2.py +0 -71
- flyte/_protos/common/identifier_pb2.pyi +0 -82
- flyte/_protos/common/identifier_pb2_grpc.py +0 -4
- flyte/_protos/common/identity_pb2.py +0 -48
- flyte/_protos/common/identity_pb2.pyi +0 -72
- flyte/_protos/common/identity_pb2_grpc.py +0 -4
- flyte/_protos/common/list_pb2.py +0 -36
- flyte/_protos/common/list_pb2.pyi +0 -69
- flyte/_protos/common/list_pb2_grpc.py +0 -4
- flyte/_protos/common/policy_pb2.py +0 -37
- flyte/_protos/common/policy_pb2.pyi +0 -27
- flyte/_protos/common/policy_pb2_grpc.py +0 -4
- flyte/_protos/common/role_pb2.py +0 -37
- flyte/_protos/common/role_pb2.pyi +0 -53
- flyte/_protos/common/role_pb2_grpc.py +0 -4
- flyte/_protos/common/runtime_version_pb2.py +0 -28
- flyte/_protos/common/runtime_version_pb2.pyi +0 -24
- flyte/_protos/common/runtime_version_pb2_grpc.py +0 -4
- flyte/_protos/logs/dataplane/payload_pb2.py +0 -96
- flyte/_protos/logs/dataplane/payload_pb2.pyi +0 -168
- flyte/_protos/logs/dataplane/payload_pb2_grpc.py +0 -4
- flyte/_protos/secret/definition_pb2.py +0 -49
- flyte/_protos/secret/definition_pb2.pyi +0 -93
- flyte/_protos/secret/definition_pb2_grpc.py +0 -4
- flyte/_protos/secret/payload_pb2.py +0 -62
- flyte/_protos/secret/payload_pb2.pyi +0 -94
- flyte/_protos/secret/payload_pb2_grpc.py +0 -4
- flyte/_protos/secret/secret_pb2.py +0 -38
- flyte/_protos/secret/secret_pb2.pyi +0 -6
- flyte/_protos/secret/secret_pb2_grpc.py +0 -198
- flyte/_protos/secret/secret_pb2_grpc_grpc.py +0 -198
- flyte/_protos/validate/validate/validate_pb2.py +0 -76
- flyte/_protos/workflow/node_execution_service_pb2.py +0 -26
- flyte/_protos/workflow/node_execution_service_pb2.pyi +0 -4
- flyte/_protos/workflow/node_execution_service_pb2_grpc.py +0 -32
- flyte/_protos/workflow/queue_service_pb2.py +0 -106
- flyte/_protos/workflow/queue_service_pb2.pyi +0 -141
- flyte/_protos/workflow/queue_service_pb2_grpc.py +0 -172
- flyte/_protos/workflow/run_definition_pb2.py +0 -128
- flyte/_protos/workflow/run_definition_pb2.pyi +0 -310
- flyte/_protos/workflow/run_definition_pb2_grpc.py +0 -4
- flyte/_protos/workflow/run_logs_service_pb2.py +0 -41
- flyte/_protos/workflow/run_logs_service_pb2.pyi +0 -28
- flyte/_protos/workflow/run_logs_service_pb2_grpc.py +0 -69
- flyte/_protos/workflow/run_service_pb2.py +0 -133
- flyte/_protos/workflow/run_service_pb2.pyi +0 -175
- flyte/_protos/workflow/run_service_pb2_grpc.py +0 -412
- flyte/_protos/workflow/state_service_pb2.py +0 -58
- flyte/_protos/workflow/state_service_pb2.pyi +0 -71
- flyte/_protos/workflow/state_service_pb2_grpc.py +0 -138
- flyte/_protos/workflow/task_definition_pb2.py +0 -72
- flyte/_protos/workflow/task_definition_pb2.pyi +0 -65
- flyte/_protos/workflow/task_definition_pb2_grpc.py +0 -4
- flyte/_protos/workflow/task_service_pb2.py +0 -44
- flyte/_protos/workflow/task_service_pb2.pyi +0 -31
- flyte/_protos/workflow/task_service_pb2_grpc.py +0 -104
- flyte/io/_dataframe.py +0 -0
- flyte/io/pickle/__init__.py +0 -0
- flyte/remote/_console.py +0 -18
- flyte-0.2.0b1.dist-info/METADATA +0 -179
- flyte-0.2.0b1.dist-info/RECORD +0 -204
- flyte-0.2.0b1.dist-info/entry_points.txt +0 -3
- /flyte/{_cli → _debug}/__init__.py +0 -0
- /flyte/{_protos → _keyring}/__init__.py +0 -0
- {flyte-0.2.0b1.dist-info → flyte-2.0.0b46.dist-info}/WHEEL +0 -0
- {flyte-0.2.0b1.dist-info → flyte-2.0.0b46.dist-info}/top_level.txt +0 -0
flyte/remote/_app.py
ADDED
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import AsyncIterator, Literal, Mapping, Tuple, cast
|
|
4
|
+
|
|
5
|
+
import grpc
|
|
6
|
+
import rich.repr
|
|
7
|
+
from flyteidl2.app import app_definition_pb2, app_payload_pb2
|
|
8
|
+
from flyteidl2.common import identifier_pb2, list_pb2
|
|
9
|
+
|
|
10
|
+
from flyte._initialize import ensure_client, get_client, get_init_config
|
|
11
|
+
from flyte._logging import logger
|
|
12
|
+
from flyte.syncify import syncify
|
|
13
|
+
|
|
14
|
+
from ._common import ToJSONMixin, filtering, sorting
|
|
15
|
+
|
|
16
|
+
WaitFor = Literal["activated", "deactivated"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _is_active(state: app_definition_pb2.Status.DeploymentStatus) -> bool:
|
|
20
|
+
return state in [
|
|
21
|
+
app_definition_pb2.Status.DeploymentStatus.DEPLOYMENT_STATUS_ACTIVE,
|
|
22
|
+
app_definition_pb2.Status.DeploymentStatus.DEPLOYMENT_STATUS_STARTED,
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _is_deactivated(state: app_definition_pb2.Status.DeploymentStatus) -> bool:
|
|
27
|
+
return state in [
|
|
28
|
+
app_definition_pb2.Status.DeploymentStatus.DEPLOYMENT_STATUS_UNASSIGNED,
|
|
29
|
+
app_definition_pb2.Status.DeploymentStatus.DEPLOYMENT_STATUS_STOPPED,
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class App(ToJSONMixin):
|
|
34
|
+
pb2: app_definition_pb2.App
|
|
35
|
+
|
|
36
|
+
def __init__(self, pb2: app_definition_pb2.App):
|
|
37
|
+
self.pb2 = pb2
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def name(self) -> str:
|
|
41
|
+
"""
|
|
42
|
+
Get the name of the app.
|
|
43
|
+
"""
|
|
44
|
+
return self.pb2.metadata.id.name
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def revision(self) -> int:
|
|
48
|
+
"""
|
|
49
|
+
Get the revision number of the app.
|
|
50
|
+
"""
|
|
51
|
+
return self.pb2.metadata.revision
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def endpoint(self) -> str:
|
|
55
|
+
"""
|
|
56
|
+
Get the public endpoint URL of the app.
|
|
57
|
+
"""
|
|
58
|
+
return self.pb2.status.ingress.public_url
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def deployment_status(self) -> app_definition_pb2.Status.DeploymentStatus:
|
|
62
|
+
"""
|
|
63
|
+
Get the deployment status of the app
|
|
64
|
+
Returns:
|
|
65
|
+
|
|
66
|
+
"""
|
|
67
|
+
if len(self.pb2.status.conditions) > 0:
|
|
68
|
+
return self.pb2.status.conditions[-1].deployment_status
|
|
69
|
+
else:
|
|
70
|
+
return app_definition_pb2.Status.DeploymentStatus.DEPLOYMENT_STATUS_UNSPECIFIED
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def desired_state(self) -> app_definition_pb2.Spec.DesiredState:
|
|
74
|
+
"""
|
|
75
|
+
Get the desired state of the app.
|
|
76
|
+
"""
|
|
77
|
+
return self.pb2.spec.desired_state
|
|
78
|
+
|
|
79
|
+
def is_active(self) -> bool:
|
|
80
|
+
"""
|
|
81
|
+
Check if the app is currently active or started.
|
|
82
|
+
"""
|
|
83
|
+
return _is_active(self.deployment_status)
|
|
84
|
+
|
|
85
|
+
def is_deactivated(self) -> bool:
|
|
86
|
+
"""
|
|
87
|
+
Check if the app is currently deactivated or stopped.
|
|
88
|
+
"""
|
|
89
|
+
return _is_deactivated(self.deployment_status)
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def url(self) -> str:
|
|
93
|
+
"""
|
|
94
|
+
Get the console URL for viewing the app.
|
|
95
|
+
"""
|
|
96
|
+
client = get_client()
|
|
97
|
+
return client.console.app_url(
|
|
98
|
+
project=self.pb2.metadata.id.project,
|
|
99
|
+
domain=self.pb2.metadata.id.domain,
|
|
100
|
+
app_name=self.name,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
@syncify
|
|
104
|
+
async def watch(self, wait_for: WaitFor = "activated") -> App:
|
|
105
|
+
"""
|
|
106
|
+
Watch for the app to reach activated or deactivated state.
|
|
107
|
+
:param wait_for: ["activated", "deactivated"]
|
|
108
|
+
|
|
109
|
+
Returns: The app in the desired state.
|
|
110
|
+
Raises: RuntimeError if the app did not reach desired state and failed!
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
if wait_for == "activated" and self.is_active():
|
|
114
|
+
return self
|
|
115
|
+
elif wait_for == "deactivated" and self.is_deactivated():
|
|
116
|
+
return self
|
|
117
|
+
|
|
118
|
+
call = cast(
|
|
119
|
+
AsyncIterator[app_payload_pb2.WatchResponse],
|
|
120
|
+
get_client().app_service.Watch(
|
|
121
|
+
request=app_payload_pb2.WatchRequest(
|
|
122
|
+
app_id=self.pb2.metadata.id,
|
|
123
|
+
)
|
|
124
|
+
),
|
|
125
|
+
)
|
|
126
|
+
async for resp in call:
|
|
127
|
+
if resp.update_event:
|
|
128
|
+
updated_app = resp.update_event.updated_app
|
|
129
|
+
current_status = updated_app.status.conditions[-1].deployment_status
|
|
130
|
+
if current_status == app_definition_pb2.Status.DeploymentStatus.DEPLOYMENT_STATUS_FAILED:
|
|
131
|
+
raise RuntimeError(f"App deployment for app {self.name} has failed!")
|
|
132
|
+
if wait_for == "activated":
|
|
133
|
+
if _is_active(current_status):
|
|
134
|
+
return App(updated_app)
|
|
135
|
+
elif _is_deactivated(current_status):
|
|
136
|
+
raise RuntimeError(f"App deployment for app {self.name} has failed!")
|
|
137
|
+
elif wait_for == "deactivated":
|
|
138
|
+
if _is_deactivated(current_status):
|
|
139
|
+
return App(updated_app)
|
|
140
|
+
raise RuntimeError(f"App deployment for app {self.name} stalled!")
|
|
141
|
+
|
|
142
|
+
async def _update(
|
|
143
|
+
self, desired_state: app_definition_pb2.Spec.DesiredState, reason: str, wait_for: WaitFor | None = None
|
|
144
|
+
) -> App:
|
|
145
|
+
"""
|
|
146
|
+
Internal method to update the app's desired state.
|
|
147
|
+
|
|
148
|
+
:param desired_state: The new desired state for the app.
|
|
149
|
+
:param reason: Reason for the update.
|
|
150
|
+
:param wait_for: Optional state to wait for after update.
|
|
151
|
+
:return: The updated app.
|
|
152
|
+
"""
|
|
153
|
+
new_pb2 = app_definition_pb2.App()
|
|
154
|
+
new_pb2.CopyFrom(self.pb2)
|
|
155
|
+
new_pb2.spec.desired_state = desired_state
|
|
156
|
+
updated_app = await App.update.aio(new_pb2, reason=reason)
|
|
157
|
+
if wait_for:
|
|
158
|
+
await updated_app.watch.aio(wait_for)
|
|
159
|
+
return updated_app
|
|
160
|
+
|
|
161
|
+
@syncify
|
|
162
|
+
async def activate(self, wait: bool = False) -> App:
|
|
163
|
+
"""
|
|
164
|
+
Start the app
|
|
165
|
+
:param wait: Wait for the app to reach started state
|
|
166
|
+
|
|
167
|
+
"""
|
|
168
|
+
if self.is_active():
|
|
169
|
+
return self
|
|
170
|
+
return await self._update(
|
|
171
|
+
app_definition_pb2.Spec.DESIRED_STATE_STARTED,
|
|
172
|
+
"User requested to activate app from flyte-sdk",
|
|
173
|
+
"activated" if wait else None,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
@syncify
|
|
177
|
+
async def deactivate(self, wait: bool = False):
|
|
178
|
+
"""
|
|
179
|
+
Stop the app
|
|
180
|
+
:param wait: Wait for the app to reach the deactivated state
|
|
181
|
+
"""
|
|
182
|
+
if self.is_deactivated():
|
|
183
|
+
return
|
|
184
|
+
return await self._update(
|
|
185
|
+
app_definition_pb2.Spec.DESIRED_STATE_STOPPED,
|
|
186
|
+
"User requested to deactivate app from flyte-sdk",
|
|
187
|
+
"deactivated" if wait else None,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
def __rich_repr__(self) -> rich.repr.Result:
|
|
191
|
+
"""
|
|
192
|
+
Rich representation of the App object for pretty printing.
|
|
193
|
+
"""
|
|
194
|
+
yield "name", self.name
|
|
195
|
+
yield "revision", self.revision
|
|
196
|
+
yield "endpoint", self.endpoint
|
|
197
|
+
yield (
|
|
198
|
+
"deployment_status",
|
|
199
|
+
app_definition_pb2.Status.DeploymentStatus.Name(self.deployment_status)[len("DEPLOYMENT_STATUS_") :],
|
|
200
|
+
)
|
|
201
|
+
yield "desired_state", app_definition_pb2.Spec.DesiredState.Name(self.desired_state)[len("DESIRED_STATE_") :]
|
|
202
|
+
|
|
203
|
+
@syncify
|
|
204
|
+
@classmethod
|
|
205
|
+
async def update(cls, updated_app_proto: app_definition_pb2.App, reason: str) -> App:
|
|
206
|
+
ensure_client()
|
|
207
|
+
resp = await get_client().app_service.Update(
|
|
208
|
+
request=app_payload_pb2.UpdateRequest(
|
|
209
|
+
app=updated_app_proto,
|
|
210
|
+
reason=reason,
|
|
211
|
+
)
|
|
212
|
+
)
|
|
213
|
+
return App(pb2=resp.app)
|
|
214
|
+
|
|
215
|
+
@syncify
|
|
216
|
+
@classmethod
|
|
217
|
+
async def replace(
|
|
218
|
+
cls,
|
|
219
|
+
name: str,
|
|
220
|
+
updated_app_spec: app_definition_pb2.Spec,
|
|
221
|
+
reason: str,
|
|
222
|
+
labels: Mapping[str, str] | None = None,
|
|
223
|
+
project: str | None = None,
|
|
224
|
+
domain: str | None = None,
|
|
225
|
+
) -> App:
|
|
226
|
+
"""
|
|
227
|
+
Replace an existing app's that matches the given name, with a new spec and optionally labels.
|
|
228
|
+
:param name: Name of the new app
|
|
229
|
+
:param updated_app_spec: Updated app spec
|
|
230
|
+
:param labels: Optional labels for the new app
|
|
231
|
+
:param project: Optional project for the new app
|
|
232
|
+
:param domain: Optional domain for the new app
|
|
233
|
+
:return: A new app
|
|
234
|
+
"""
|
|
235
|
+
ensure_client()
|
|
236
|
+
app = await cls.get.aio(name=name, project=project, domain=domain)
|
|
237
|
+
updated_app_spec.creator.CopyFrom(app.pb2.spec.creator)
|
|
238
|
+
new_app = app_definition_pb2.App(
|
|
239
|
+
metadata=app_definition_pb2.Meta(
|
|
240
|
+
id=app.pb2.metadata.id,
|
|
241
|
+
revision=app.revision,
|
|
242
|
+
labels=labels if labels else app.pb2.metadata.labels,
|
|
243
|
+
),
|
|
244
|
+
spec=updated_app_spec,
|
|
245
|
+
status=app.pb2.status,
|
|
246
|
+
)
|
|
247
|
+
return await cls.update.aio(new_app, reason=reason)
|
|
248
|
+
|
|
249
|
+
@syncify
|
|
250
|
+
@classmethod
|
|
251
|
+
async def get(
|
|
252
|
+
cls,
|
|
253
|
+
name: str,
|
|
254
|
+
project: str | None = None,
|
|
255
|
+
domain: str | None = None,
|
|
256
|
+
) -> App:
|
|
257
|
+
"""
|
|
258
|
+
Get an app by name.
|
|
259
|
+
|
|
260
|
+
:param name: The name of the app.
|
|
261
|
+
:param project: The project of the app.
|
|
262
|
+
:param domain: The domain of the app.
|
|
263
|
+
:return: The app remote object.
|
|
264
|
+
"""
|
|
265
|
+
ensure_client()
|
|
266
|
+
cfg = get_init_config()
|
|
267
|
+
resp = await get_client().app_service.Get(
|
|
268
|
+
request=app_payload_pb2.GetRequest(
|
|
269
|
+
app_id=app_definition_pb2.Identifier(
|
|
270
|
+
org=cfg.org,
|
|
271
|
+
project=project or cfg.project,
|
|
272
|
+
domain=domain or cfg.domain,
|
|
273
|
+
name=name,
|
|
274
|
+
),
|
|
275
|
+
)
|
|
276
|
+
)
|
|
277
|
+
return cls(pb2=resp.app)
|
|
278
|
+
|
|
279
|
+
@syncify
|
|
280
|
+
@classmethod
|
|
281
|
+
async def listall(
|
|
282
|
+
cls,
|
|
283
|
+
created_by_subject: str | None = None,
|
|
284
|
+
sort_by: Tuple[str, Literal["asc", "desc"]] | None = None,
|
|
285
|
+
limit: int = 100,
|
|
286
|
+
) -> AsyncIterator[App]:
|
|
287
|
+
ensure_client()
|
|
288
|
+
cfg = get_init_config()
|
|
289
|
+
i = 0
|
|
290
|
+
token = None
|
|
291
|
+
sort_pb2 = sorting(sort_by)
|
|
292
|
+
filters = filtering(created_by_subject)
|
|
293
|
+
project = None
|
|
294
|
+
if cfg.project:
|
|
295
|
+
project = identifier_pb2.ProjectIdentifier(
|
|
296
|
+
organization=cfg.org,
|
|
297
|
+
name=cfg.project,
|
|
298
|
+
domain=cfg.domain,
|
|
299
|
+
)
|
|
300
|
+
while True:
|
|
301
|
+
req = app_payload_pb2.ListRequest(
|
|
302
|
+
request=list_pb2.ListRequest(
|
|
303
|
+
limit=min(100, limit),
|
|
304
|
+
token=token,
|
|
305
|
+
sort_by=sort_pb2,
|
|
306
|
+
filters=filters,
|
|
307
|
+
),
|
|
308
|
+
org=cfg.org,
|
|
309
|
+
project=project,
|
|
310
|
+
)
|
|
311
|
+
resp = await get_client().app_service.List(
|
|
312
|
+
request=req,
|
|
313
|
+
)
|
|
314
|
+
token = resp.token
|
|
315
|
+
for a in resp.apps:
|
|
316
|
+
i += 1
|
|
317
|
+
if i > limit:
|
|
318
|
+
return
|
|
319
|
+
yield cls(a)
|
|
320
|
+
if not token:
|
|
321
|
+
break
|
|
322
|
+
|
|
323
|
+
@syncify
|
|
324
|
+
@classmethod
|
|
325
|
+
async def create(cls, app: app_definition_pb2.App) -> App:
|
|
326
|
+
ensure_client()
|
|
327
|
+
try:
|
|
328
|
+
resp = await get_client().app_service.Create(app_payload_pb2.CreateRequest(app=app))
|
|
329
|
+
created_app = cls(resp.app)
|
|
330
|
+
logger.info(f"Deployed app {created_app.name} with revision {created_app.revision}")
|
|
331
|
+
return created_app
|
|
332
|
+
except grpc.aio.AioRpcError as e:
|
|
333
|
+
if e.code() in [grpc.StatusCode.ABORTED, grpc.StatusCode.ALREADY_EXISTS]:
|
|
334
|
+
if e.code() == grpc.StatusCode.ALREADY_EXISTS:
|
|
335
|
+
logger.warning(f"App {app.metadata.id.name} already exists, updating...")
|
|
336
|
+
elif e.code() == grpc.StatusCode.ABORTED:
|
|
337
|
+
logger.warning(f"Create App {app.metadata.id.name} was aborted on server, check state!")
|
|
338
|
+
return await App.replace.aio(
|
|
339
|
+
name=app.metadata.id.name,
|
|
340
|
+
labels=app.metadata.labels,
|
|
341
|
+
updated_app_spec=app.spec,
|
|
342
|
+
reason="User requested serve from sdk",
|
|
343
|
+
project=app.metadata.id.project,
|
|
344
|
+
domain=app.metadata.id.domain,
|
|
345
|
+
)
|
|
346
|
+
raise
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from contextlib import contextmanager
|
|
2
|
+
from typing import Tuple
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@contextmanager
|
|
6
|
+
def auth_metadata(*kv: Tuple[str, str]):
|
|
7
|
+
"""
|
|
8
|
+
This context manager allows you to pass contextualized auth metadata downstream to the Flyte authentication system.
|
|
9
|
+
|
|
10
|
+
This is only useful if flyte.init_passthrough() has been called.
|
|
11
|
+
|
|
12
|
+
Example:
|
|
13
|
+
```python
|
|
14
|
+
|
|
15
|
+
flyte.init_passthrough("my-endpoint")
|
|
16
|
+
|
|
17
|
+
...
|
|
18
|
+
|
|
19
|
+
with auth_metadata((key1, value1), (key2, value2)):
|
|
20
|
+
...
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
*kv: Tuple of auth metadata key/value pairs.
|
|
25
|
+
"""
|
|
26
|
+
if not kv:
|
|
27
|
+
raise ValueError("No auth metadata provided.")
|
|
28
|
+
from flyte._context import internal_ctx
|
|
29
|
+
|
|
30
|
+
ctx = internal_ctx()
|
|
31
|
+
with ctx.new_metadata(kv):
|
|
32
|
+
yield
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def get_auth_metadata() -> Tuple[Tuple[str, str], ...]:
|
|
36
|
+
"""
|
|
37
|
+
Returns auth metadata as tuple.
|
|
38
|
+
"""
|
|
39
|
+
from flyte._context import internal_ctx
|
|
40
|
+
|
|
41
|
+
ctx = internal_ctx()
|
|
42
|
+
return ctx.data.metadata or ()
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
from typing import AsyncIterator, Protocol
|
|
2
2
|
|
|
3
3
|
from flyteidl.admin import project_attributes_pb2, project_pb2, version_pb2
|
|
4
|
-
from flyteidl.service import dataproxy_pb2
|
|
4
|
+
from flyteidl.service import dataproxy_pb2, identity_pb2
|
|
5
|
+
from flyteidl2.app import app_payload_pb2
|
|
6
|
+
from flyteidl2.secret import payload_pb2
|
|
7
|
+
from flyteidl2.task import task_service_pb2
|
|
8
|
+
from flyteidl2.trigger import trigger_service_pb2
|
|
9
|
+
from flyteidl2.workflow import run_logs_service_pb2, run_service_pb2
|
|
5
10
|
from grpc.aio import UnaryStreamCall
|
|
6
11
|
from grpc.aio._typing import RequestType
|
|
7
12
|
|
|
8
|
-
from flyte._protos.secret import payload_pb2
|
|
9
|
-
from flyte._protos.workflow import run_logs_service_pb2, run_service_pb2, task_service_pb2
|
|
10
|
-
|
|
11
13
|
|
|
12
14
|
class MetadataServiceProtocol(Protocol):
|
|
13
15
|
async def GetVersion(self, request: version_pb2.GetVersionRequest) -> version_pb2.GetVersionResponse: ...
|
|
@@ -58,6 +60,28 @@ class TaskService(Protocol):
|
|
|
58
60
|
self, request: task_service_pb2.GetTaskDetailsRequest
|
|
59
61
|
) -> task_service_pb2.GetTaskDetailsResponse: ...
|
|
60
62
|
|
|
63
|
+
async def ListTasks(self, request: task_service_pb2.ListTasksRequest) -> task_service_pb2.ListTasksResponse: ...
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class AppService(Protocol):
|
|
67
|
+
async def Create(self, request: app_payload_pb2.CreateRequest) -> app_payload_pb2.CreateResponse: ...
|
|
68
|
+
|
|
69
|
+
async def Get(self, request: app_payload_pb2.GetRequest) -> app_payload_pb2.GetResponse: ...
|
|
70
|
+
|
|
71
|
+
async def Update(self, request: app_payload_pb2.UpdateRequest) -> app_payload_pb2.UpdateResponse: ...
|
|
72
|
+
|
|
73
|
+
async def UpdateStatus(
|
|
74
|
+
self, request: app_payload_pb2.UpdateStatusRequest
|
|
75
|
+
) -> app_payload_pb2.UpdateStatusResponse: ...
|
|
76
|
+
|
|
77
|
+
async def Delete(self, request: app_payload_pb2.DeleteRequest) -> app_payload_pb2.DeleteResponse: ...
|
|
78
|
+
|
|
79
|
+
async def List(self, request: app_payload_pb2.ListRequest) -> app_payload_pb2.ListResponse: ...
|
|
80
|
+
|
|
81
|
+
async def Watch(self, request: app_payload_pb2.WatchRequest) -> app_payload_pb2.WatchResponse: ...
|
|
82
|
+
|
|
83
|
+
async def Lease(self, request: app_payload_pb2.LeaseRequest) -> app_payload_pb2.LeaseResponse: ...
|
|
84
|
+
|
|
61
85
|
|
|
62
86
|
class RunService(Protocol):
|
|
63
87
|
async def CreateRun(self, request: run_service_pb2.CreateRunRequest) -> run_service_pb2.CreateRunResponse: ...
|
|
@@ -129,3 +153,37 @@ class SecretService(Protocol):
|
|
|
129
153
|
async def ListSecrets(self, request: payload_pb2.ListSecretsRequest) -> payload_pb2.ListSecretsResponse: ...
|
|
130
154
|
|
|
131
155
|
async def DeleteSecret(self, request: payload_pb2.DeleteSecretRequest) -> payload_pb2.DeleteSecretResponse: ...
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class IdentityService(Protocol):
|
|
159
|
+
async def UserInfo(self, request: identity_pb2.UserInfoRequest) -> identity_pb2.UserInfoResponse: ...
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class TriggerService(Protocol):
|
|
163
|
+
async def DeployTrigger(
|
|
164
|
+
self, request: trigger_service_pb2.DeployTriggerRequest
|
|
165
|
+
) -> trigger_service_pb2.DeployTriggerResponse: ...
|
|
166
|
+
|
|
167
|
+
async def GetTriggerDetails(
|
|
168
|
+
self, request: trigger_service_pb2.GetTriggerDetailsRequest
|
|
169
|
+
) -> trigger_service_pb2.GetTriggerDetailsResponse: ...
|
|
170
|
+
|
|
171
|
+
async def GetTriggerRevisionDetails(
|
|
172
|
+
self, request: trigger_service_pb2.GetTriggerRevisionDetailsRequest
|
|
173
|
+
) -> trigger_service_pb2.GetTriggerRevisionDetailsResponse: ...
|
|
174
|
+
|
|
175
|
+
async def ListTriggers(
|
|
176
|
+
self, request: trigger_service_pb2.ListTriggersRequest
|
|
177
|
+
) -> trigger_service_pb2.ListTriggersResponse: ...
|
|
178
|
+
|
|
179
|
+
async def GetTriggerRevisionHistory(
|
|
180
|
+
self, request: trigger_service_pb2.GetTriggerRevisionHistoryRequest
|
|
181
|
+
) -> trigger_service_pb2.GetTriggerRevisionHistoryResponse: ...
|
|
182
|
+
|
|
183
|
+
async def UpdateTriggers(
|
|
184
|
+
self, request: trigger_service_pb2.UpdateTriggersRequest
|
|
185
|
+
) -> trigger_service_pb2.UpdateTriggersResponse: ...
|
|
186
|
+
|
|
187
|
+
async def DeleteTriggers(
|
|
188
|
+
self, request: trigger_service_pb2.DeleteTriggersRequest
|
|
189
|
+
) -> trigger_service_pb2.DeleteTriggersResponse: ...
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
from typing import Literal
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def decode_api_key(encoded_str: str) -> tuple[str, str, str, str | Literal["None"]]:
|
|
8
|
+
"""Decode encoded base64 string into app credentials. endpoint, client_id, client_secret, org"""
|
|
9
|
+
# Split with maxsplit=3 to handle endpoints with colons (e.g., dns:///endpoint.com)
|
|
10
|
+
parts = base64.b64decode(encoded_str.encode("utf-8")).decode("utf-8").split(":", 3)
|
|
11
|
+
if len(parts) != 4:
|
|
12
|
+
raise ValueError(f"Invalid API key format. Expected 4 parts separated by ':', got {len(parts)}")
|
|
13
|
+
|
|
14
|
+
endpoint, client_id, client_secret, org = parts
|
|
15
|
+
# For consistency, let's make sure org is always a non-empty string
|
|
16
|
+
if not org:
|
|
17
|
+
org = "None"
|
|
18
|
+
|
|
19
|
+
return endpoint, client_id, client_secret, org
|
|
@@ -34,6 +34,7 @@ class Authenticator(object):
|
|
|
34
34
|
http_proxy_url: typing.Optional[str] = None,
|
|
35
35
|
verify: bool = True,
|
|
36
36
|
ca_cert_path: typing.Optional[str] = None,
|
|
37
|
+
default_header_key: str = "authorization",
|
|
37
38
|
**kwargs,
|
|
38
39
|
):
|
|
39
40
|
"""
|
|
@@ -80,6 +81,7 @@ class Authenticator(object):
|
|
|
80
81
|
self._http_session = http_session or get_async_session(**kwargs)
|
|
81
82
|
# Id for tracking credential refresh state
|
|
82
83
|
self._creds_id = self._creds.id if self._creds else None
|
|
84
|
+
self._default_header_key = default_header_key
|
|
83
85
|
|
|
84
86
|
async def _resolve_config(self) -> ClientConfig:
|
|
85
87
|
"""
|
|
@@ -131,10 +133,14 @@ class Authenticator(object):
|
|
|
131
133
|
"""
|
|
132
134
|
creds = self.get_credentials()
|
|
133
135
|
if creds:
|
|
134
|
-
|
|
136
|
+
header_key = self._default_header_key
|
|
137
|
+
if self._resolved_config is not None:
|
|
138
|
+
# We only resolve the config during authentication flow, to avoid unnecessary network calls
|
|
139
|
+
# and usually the header_key is consistent.
|
|
140
|
+
header_key = self._resolved_config.header_key
|
|
135
141
|
return GrpcAuthMetadata(
|
|
136
142
|
creds_id=creds.id,
|
|
137
|
-
pairs=Metadata((
|
|
143
|
+
pairs=Metadata((header_key, f"Bearer {creds.access_token}")),
|
|
138
144
|
)
|
|
139
145
|
return None
|
|
140
146
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
from rich import print as rich_print
|
|
2
2
|
|
|
3
3
|
from flyte._logging import logger
|
|
4
4
|
from flyte.remote._client.auth import _token_client as token_client
|
|
@@ -81,7 +81,7 @@ class DeviceCodeAuthenticator(Authenticator):
|
|
|
81
81
|
for_endpoint=self._endpoint,
|
|
82
82
|
)
|
|
83
83
|
except (AuthenticationError, AuthenticationPending):
|
|
84
|
-
logger.warning("
|
|
84
|
+
logger.warning("Logging in...")
|
|
85
85
|
|
|
86
86
|
"""Fall back to device flow"""
|
|
87
87
|
resp = await token_client.get_device_code(
|
|
@@ -94,10 +94,9 @@ class DeviceCodeAuthenticator(Authenticator):
|
|
|
94
94
|
|
|
95
95
|
full_uri = f"{resp.verification_uri}?user_code={resp.user_code}"
|
|
96
96
|
text = (
|
|
97
|
-
f"To Authenticate, navigate in a browser to the following URL: "
|
|
98
|
-
f"{click.style(full_uri, fg='blue', underline=True)}"
|
|
97
|
+
f"To Authenticate, navigate in a browser to the following URL: [blue link={full_uri}]{full_uri}[/blue link]"
|
|
99
98
|
)
|
|
100
|
-
|
|
99
|
+
rich_print(text)
|
|
101
100
|
try:
|
|
102
101
|
token, refresh_token, expires_in = await token_client.poll_token_endpoint(
|
|
103
102
|
resp,
|
|
@@ -169,6 +169,10 @@ def get_async_authenticator(
|
|
|
169
169
|
from flyte.remote._client.auth._authenticators.device_code import DeviceCodeAuthenticator
|
|
170
170
|
|
|
171
171
|
return DeviceCodeAuthenticator(endpoint=endpoint, cfg_store=cfg_store, verify=verify, **kwargs)
|
|
172
|
+
case "Passthrough":
|
|
173
|
+
from flyte.remote._client.auth._authenticators.passthrough import PassthroughAuthenticator
|
|
174
|
+
|
|
175
|
+
return PassthroughAuthenticator(endpoint=endpoint, **kwargs)
|
|
172
176
|
case _:
|
|
173
177
|
raise ValueError(
|
|
174
178
|
f"Invalid auth mode [{auth_type}] specified. Please update the creds config to use a valid value"
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from grpc.aio import Metadata
|
|
4
|
+
|
|
5
|
+
from flyte.remote._client.auth._authenticators.base import Authenticator, GrpcAuthMetadata
|
|
6
|
+
from flyte.remote._client.auth._keyring import Credentials
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PassthroughAuthenticator(Authenticator):
|
|
10
|
+
"""
|
|
11
|
+
Passthrough authenticator that extracts headers from the context and passes them
|
|
12
|
+
to the gRPC calls without performing any authentication flow.
|
|
13
|
+
|
|
14
|
+
This authenticator is used when you want to pass custom authentication metadata
|
|
15
|
+
using the flyte.remote.auth_metadata() context manager.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, endpoint: str, **kwargs):
|
|
19
|
+
"""
|
|
20
|
+
Initialize the passthrough authenticator.
|
|
21
|
+
|
|
22
|
+
We knowingly skip calling super, as that initializes a bunch of things that are not needed.!
|
|
23
|
+
|
|
24
|
+
:param endpoint: The endpoint URL
|
|
25
|
+
:param kwargs: Additional arguments (ignored for passthrough auth)
|
|
26
|
+
"""
|
|
27
|
+
# Don't call parent __init__ to avoid unnecessary setup for passthrough auth
|
|
28
|
+
self._endpoint = endpoint
|
|
29
|
+
# We don't need credentials, config store, or HTTP session for passthrough
|
|
30
|
+
# We will create dummy creds
|
|
31
|
+
self._creds = Credentials(
|
|
32
|
+
access_token="passthrough",
|
|
33
|
+
for_endpoint=self._endpoint,
|
|
34
|
+
)
|
|
35
|
+
self._creds_id: str = "passthrough"
|
|
36
|
+
|
|
37
|
+
def refresh_credentials(self, creds_id: str | None = None):
|
|
38
|
+
return
|
|
39
|
+
|
|
40
|
+
def get_credentials(self) -> typing.Optional[Credentials]:
|
|
41
|
+
"""
|
|
42
|
+
Passthrough authenticator doesn't use traditional credentials.
|
|
43
|
+
Returns a dummy credential to signal that metadata is available.
|
|
44
|
+
"""
|
|
45
|
+
# Return a dummy credential so the interceptor knows to call get_grpc_call_auth_metadata
|
|
46
|
+
return self._creds
|
|
47
|
+
|
|
48
|
+
async def get_grpc_call_auth_metadata(self) -> typing.Optional[GrpcAuthMetadata]:
|
|
49
|
+
"""
|
|
50
|
+
Fetch the authentication metadata from the context.
|
|
51
|
+
|
|
52
|
+
:return: GrpcAuthMetadata with the metadata from the context, or None if no metadata is available
|
|
53
|
+
"""
|
|
54
|
+
# Lazy import to avoid circular dependencies
|
|
55
|
+
from flyte.remote._auth_metadata import get_auth_metadata
|
|
56
|
+
|
|
57
|
+
# Get metadata from context
|
|
58
|
+
metadata_tuples = get_auth_metadata()
|
|
59
|
+
|
|
60
|
+
if not metadata_tuples:
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
return GrpcAuthMetadata(
|
|
64
|
+
creds_id=self._creds_id,
|
|
65
|
+
pairs=Metadata(*metadata_tuples),
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
async def _do_refresh_credentials(self) -> Credentials:
|
|
69
|
+
"""
|
|
70
|
+
Passthrough authenticator doesn't need to refresh credentials.
|
|
71
|
+
This method should never be called in practice.
|
|
72
|
+
"""
|
|
73
|
+
if self._creds is None:
|
|
74
|
+
# Just to satisfy mypy
|
|
75
|
+
return Credentials(
|
|
76
|
+
access_token="passthrough",
|
|
77
|
+
for_endpoint=self._endpoint,
|
|
78
|
+
)
|
|
79
|
+
return self._creds
|