flyte 0.0.1b3__py3-none-any.whl → 0.2.0a0__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.
Potentially problematic release.
This version of flyte might be problematic. Click here for more details.
- flyte/__init__.py +20 -4
- flyte/_bin/runtime.py +33 -7
- flyte/_build.py +3 -2
- flyte/_cache/cache.py +1 -2
- flyte/_code_bundle/_packaging.py +1 -1
- flyte/_code_bundle/_utils.py +0 -16
- flyte/_code_bundle/bundle.py +43 -12
- flyte/_context.py +8 -2
- flyte/_deploy.py +56 -15
- flyte/_environment.py +45 -4
- flyte/_excepthook.py +37 -0
- flyte/_group.py +2 -1
- flyte/_image.py +8 -4
- flyte/_initialize.py +112 -254
- flyte/_interface.py +3 -3
- flyte/_internal/controllers/__init__.py +19 -6
- flyte/_internal/controllers/_local_controller.py +83 -8
- flyte/_internal/controllers/_trace.py +2 -1
- flyte/_internal/controllers/remote/__init__.py +27 -7
- flyte/_internal/controllers/remote/_action.py +7 -2
- flyte/_internal/controllers/remote/_client.py +5 -1
- flyte/_internal/controllers/remote/_controller.py +159 -26
- flyte/_internal/controllers/remote/_core.py +13 -5
- flyte/_internal/controllers/remote/_informer.py +4 -4
- flyte/_internal/controllers/remote/_service_protocol.py +6 -6
- flyte/_internal/imagebuild/docker_builder.py +12 -1
- flyte/_internal/imagebuild/image_builder.py +16 -11
- flyte/_internal/runtime/convert.py +164 -21
- flyte/_internal/runtime/entrypoints.py +1 -1
- flyte/_internal/runtime/io.py +3 -3
- flyte/_internal/runtime/task_serde.py +140 -20
- flyte/_internal/runtime/taskrunner.py +4 -3
- flyte/_internal/runtime/types_serde.py +1 -1
- flyte/_logging.py +12 -1
- flyte/_map.py +215 -0
- flyte/_pod.py +19 -0
- flyte/_protos/common/list_pb2.py +3 -3
- flyte/_protos/common/list_pb2.pyi +2 -0
- flyte/_protos/logs/dataplane/payload_pb2.py +28 -24
- flyte/_protos/logs/dataplane/payload_pb2.pyi +11 -2
- flyte/_protos/workflow/common_pb2.py +27 -0
- flyte/_protos/workflow/common_pb2.pyi +14 -0
- flyte/_protos/workflow/environment_pb2.py +29 -0
- flyte/_protos/workflow/environment_pb2.pyi +12 -0
- flyte/_protos/workflow/queue_service_pb2.py +40 -41
- flyte/_protos/workflow/queue_service_pb2.pyi +35 -30
- flyte/_protos/workflow/queue_service_pb2_grpc.py +15 -15
- flyte/_protos/workflow/run_definition_pb2.py +61 -61
- flyte/_protos/workflow/run_definition_pb2.pyi +8 -4
- flyte/_protos/workflow/run_service_pb2.py +20 -24
- flyte/_protos/workflow/run_service_pb2.pyi +2 -6
- flyte/_protos/workflow/state_service_pb2.py +36 -28
- flyte/_protos/workflow/state_service_pb2.pyi +19 -15
- flyte/_protos/workflow/state_service_pb2_grpc.py +28 -28
- flyte/_protos/workflow/task_definition_pb2.py +29 -22
- flyte/_protos/workflow/task_definition_pb2.pyi +21 -5
- flyte/_protos/workflow/task_service_pb2.py +27 -11
- flyte/_protos/workflow/task_service_pb2.pyi +29 -1
- flyte/_protos/workflow/task_service_pb2_grpc.py +34 -0
- flyte/_run.py +166 -95
- flyte/_task.py +110 -28
- flyte/_task_environment.py +55 -72
- flyte/_trace.py +6 -14
- flyte/_utils/__init__.py +6 -0
- flyte/_utils/async_cache.py +139 -0
- flyte/_utils/coro_management.py +0 -2
- flyte/_utils/helpers.py +45 -19
- flyte/_utils/org_discovery.py +57 -0
- flyte/_version.py +2 -2
- flyte/cli/__init__.py +3 -0
- flyte/cli/_abort.py +28 -0
- flyte/{_cli → cli}/_common.py +73 -23
- flyte/cli/_create.py +145 -0
- flyte/{_cli → cli}/_delete.py +4 -4
- flyte/{_cli → cli}/_deploy.py +26 -14
- flyte/cli/_gen.py +163 -0
- flyte/{_cli → cli}/_get.py +98 -23
- {union/_cli → flyte/cli}/_params.py +106 -147
- flyte/{_cli → cli}/_run.py +99 -20
- flyte/cli/main.py +166 -0
- flyte/config/__init__.py +3 -0
- flyte/config/_config.py +216 -0
- flyte/config/_internal.py +64 -0
- flyte/config/_reader.py +207 -0
- flyte/errors.py +29 -0
- flyte/extras/_container.py +33 -43
- flyte/io/__init__.py +17 -1
- flyte/io/_dir.py +2 -2
- flyte/io/_file.py +3 -4
- flyte/io/{structured_dataset → _structured_dataset}/basic_dfs.py +1 -1
- flyte/io/{structured_dataset → _structured_dataset}/structured_dataset.py +1 -1
- flyte/{_datastructures.py → models.py} +56 -7
- flyte/remote/__init__.py +2 -1
- flyte/remote/_client/_protocols.py +2 -0
- flyte/remote/_client/auth/_auth_utils.py +14 -0
- flyte/remote/_client/auth/_channel.py +34 -3
- flyte/remote/_client/auth/_token_client.py +3 -3
- flyte/remote/_client/controlplane.py +13 -13
- flyte/remote/_console.py +1 -1
- flyte/remote/_data.py +10 -6
- flyte/remote/_logs.py +89 -29
- flyte/remote/_project.py +8 -9
- flyte/remote/_run.py +228 -131
- flyte/remote/_secret.py +12 -12
- flyte/remote/_task.py +179 -15
- flyte/report/_report.py +4 -4
- flyte/storage/__init__.py +5 -0
- flyte/storage/_config.py +233 -0
- flyte/storage/_storage.py +23 -3
- flyte/syncify/__init__.py +56 -0
- flyte/syncify/_api.py +371 -0
- flyte/types/__init__.py +23 -0
- flyte/types/_interface.py +22 -7
- flyte/{io/pickle/transformer.py → types/_pickle.py} +2 -1
- flyte/types/_type_engine.py +95 -18
- flyte-0.2.0a0.dist-info/METADATA +249 -0
- flyte-0.2.0a0.dist-info/RECORD +218 -0
- {flyte-0.0.1b3.dist-info → flyte-0.2.0a0.dist-info}/entry_points.txt +1 -1
- flyte/_api_commons.py +0 -3
- flyte/_cli/__init__.py +0 -0
- flyte/_cli/_create.py +0 -42
- flyte/_cli/main.py +0 -72
- flyte/_internal/controllers/pbhash.py +0 -39
- flyte/io/_dataframe.py +0 -0
- flyte/io/pickle/__init__.py +0 -0
- flyte-0.0.1b3.dist-info/METADATA +0 -179
- flyte-0.0.1b3.dist-info/RECORD +0 -390
- union/__init__.py +0 -54
- union/_api_commons.py +0 -3
- union/_bin/__init__.py +0 -0
- union/_bin/runtime.py +0 -113
- union/_build.py +0 -25
- union/_cache/__init__.py +0 -12
- union/_cache/cache.py +0 -141
- union/_cache/defaults.py +0 -9
- union/_cache/policy_function_body.py +0 -42
- union/_cli/__init__.py +0 -0
- union/_cli/_common.py +0 -263
- union/_cli/_create.py +0 -40
- union/_cli/_delete.py +0 -23
- union/_cli/_deploy.py +0 -120
- union/_cli/_get.py +0 -162
- union/_cli/_run.py +0 -150
- union/_cli/main.py +0 -72
- union/_code_bundle/__init__.py +0 -8
- union/_code_bundle/_ignore.py +0 -113
- union/_code_bundle/_packaging.py +0 -187
- union/_code_bundle/_utils.py +0 -342
- union/_code_bundle/bundle.py +0 -176
- union/_context.py +0 -146
- union/_datastructures.py +0 -295
- union/_deploy.py +0 -185
- union/_doc.py +0 -29
- union/_docstring.py +0 -26
- union/_environment.py +0 -43
- union/_group.py +0 -31
- union/_hash.py +0 -23
- union/_image.py +0 -760
- union/_initialize.py +0 -585
- union/_interface.py +0 -84
- union/_internal/__init__.py +0 -3
- union/_internal/controllers/__init__.py +0 -77
- union/_internal/controllers/_local_controller.py +0 -77
- union/_internal/controllers/pbhash.py +0 -39
- union/_internal/controllers/remote/__init__.py +0 -40
- union/_internal/controllers/remote/_action.py +0 -131
- union/_internal/controllers/remote/_client.py +0 -43
- union/_internal/controllers/remote/_controller.py +0 -169
- union/_internal/controllers/remote/_core.py +0 -341
- union/_internal/controllers/remote/_informer.py +0 -260
- union/_internal/controllers/remote/_service_protocol.py +0 -44
- union/_internal/imagebuild/__init__.py +0 -11
- union/_internal/imagebuild/docker_builder.py +0 -416
- union/_internal/imagebuild/image_builder.py +0 -243
- union/_internal/imagebuild/remote_builder.py +0 -0
- union/_internal/resolvers/__init__.py +0 -0
- union/_internal/resolvers/_task_module.py +0 -31
- union/_internal/resolvers/common.py +0 -24
- union/_internal/resolvers/default.py +0 -27
- union/_internal/runtime/__init__.py +0 -0
- union/_internal/runtime/convert.py +0 -163
- union/_internal/runtime/entrypoints.py +0 -121
- union/_internal/runtime/io.py +0 -136
- union/_internal/runtime/resources_serde.py +0 -134
- union/_internal/runtime/task_serde.py +0 -202
- union/_internal/runtime/taskrunner.py +0 -179
- union/_internal/runtime/types_serde.py +0 -53
- union/_logging.py +0 -124
- union/_protos/__init__.py +0 -0
- union/_protos/common/authorization_pb2.py +0 -66
- union/_protos/common/authorization_pb2.pyi +0 -106
- union/_protos/common/identifier_pb2.py +0 -71
- union/_protos/common/identifier_pb2.pyi +0 -82
- union/_protos/common/identity_pb2.py +0 -48
- union/_protos/common/identity_pb2.pyi +0 -72
- union/_protos/common/identity_pb2_grpc.py +0 -4
- union/_protos/common/list_pb2.py +0 -36
- union/_protos/common/list_pb2.pyi +0 -69
- union/_protos/common/list_pb2_grpc.py +0 -4
- union/_protos/common/policy_pb2.py +0 -37
- union/_protos/common/policy_pb2.pyi +0 -27
- union/_protos/common/policy_pb2_grpc.py +0 -4
- union/_protos/common/role_pb2.py +0 -37
- union/_protos/common/role_pb2.pyi +0 -51
- union/_protos/common/role_pb2_grpc.py +0 -4
- union/_protos/common/runtime_version_pb2.py +0 -28
- union/_protos/common/runtime_version_pb2.pyi +0 -24
- union/_protos/common/runtime_version_pb2_grpc.py +0 -4
- union/_protos/logs/dataplane/payload_pb2.py +0 -96
- union/_protos/logs/dataplane/payload_pb2.pyi +0 -168
- union/_protos/logs/dataplane/payload_pb2_grpc.py +0 -4
- union/_protos/secret/definition_pb2.py +0 -49
- union/_protos/secret/definition_pb2.pyi +0 -93
- union/_protos/secret/definition_pb2_grpc.py +0 -4
- union/_protos/secret/payload_pb2.py +0 -62
- union/_protos/secret/payload_pb2.pyi +0 -94
- union/_protos/secret/payload_pb2_grpc.py +0 -4
- union/_protos/secret/secret_pb2.py +0 -38
- union/_protos/secret/secret_pb2.pyi +0 -6
- union/_protos/secret/secret_pb2_grpc.py +0 -198
- union/_protos/validate/validate/validate_pb2.py +0 -76
- union/_protos/workflow/node_execution_service_pb2.py +0 -26
- union/_protos/workflow/node_execution_service_pb2.pyi +0 -4
- union/_protos/workflow/node_execution_service_pb2_grpc.py +0 -32
- union/_protos/workflow/queue_service_pb2.py +0 -75
- union/_protos/workflow/queue_service_pb2.pyi +0 -103
- union/_protos/workflow/queue_service_pb2_grpc.py +0 -172
- union/_protos/workflow/run_definition_pb2.py +0 -100
- union/_protos/workflow/run_definition_pb2.pyi +0 -256
- union/_protos/workflow/run_definition_pb2_grpc.py +0 -4
- union/_protos/workflow/run_logs_service_pb2.py +0 -41
- union/_protos/workflow/run_logs_service_pb2.pyi +0 -28
- union/_protos/workflow/run_logs_service_pb2_grpc.py +0 -69
- union/_protos/workflow/run_service_pb2.py +0 -133
- union/_protos/workflow/run_service_pb2.pyi +0 -173
- union/_protos/workflow/run_service_pb2_grpc.py +0 -412
- union/_protos/workflow/state_service_pb2.py +0 -58
- union/_protos/workflow/state_service_pb2.pyi +0 -69
- union/_protos/workflow/state_service_pb2_grpc.py +0 -138
- union/_protos/workflow/task_definition_pb2.py +0 -72
- union/_protos/workflow/task_definition_pb2.pyi +0 -65
- union/_protos/workflow/task_definition_pb2_grpc.py +0 -4
- union/_protos/workflow/task_service_pb2.py +0 -44
- union/_protos/workflow/task_service_pb2.pyi +0 -31
- union/_protos/workflow/task_service_pb2_grpc.py +0 -104
- union/_resources.py +0 -226
- union/_retry.py +0 -32
- union/_reusable_environment.py +0 -25
- union/_run.py +0 -374
- union/_secret.py +0 -61
- union/_task.py +0 -354
- union/_task_environment.py +0 -186
- union/_timeout.py +0 -47
- union/_tools.py +0 -27
- union/_utils/__init__.py +0 -11
- union/_utils/asyn.py +0 -119
- union/_utils/file_handling.py +0 -71
- union/_utils/helpers.py +0 -46
- union/_utils/lazy_module.py +0 -54
- union/_utils/uv_script_parser.py +0 -49
- union/_version.py +0 -21
- union/connectors/__init__.py +0 -0
- union/errors.py +0 -128
- union/extras/__init__.py +0 -5
- union/extras/_container.py +0 -263
- union/io/__init__.py +0 -11
- union/io/_dataframe.py +0 -0
- union/io/_dir.py +0 -425
- union/io/_file.py +0 -418
- union/io/pickle/__init__.py +0 -0
- union/io/pickle/transformer.py +0 -117
- union/io/structured_dataset/__init__.py +0 -122
- union/io/structured_dataset/basic_dfs.py +0 -219
- union/io/structured_dataset/structured_dataset.py +0 -1057
- union/py.typed +0 -0
- union/remote/__init__.py +0 -23
- union/remote/_client/__init__.py +0 -0
- union/remote/_client/_protocols.py +0 -129
- union/remote/_client/auth/__init__.py +0 -12
- union/remote/_client/auth/_authenticators/__init__.py +0 -0
- union/remote/_client/auth/_authenticators/base.py +0 -391
- union/remote/_client/auth/_authenticators/client_credentials.py +0 -73
- union/remote/_client/auth/_authenticators/device_code.py +0 -120
- union/remote/_client/auth/_authenticators/external_command.py +0 -77
- union/remote/_client/auth/_authenticators/factory.py +0 -200
- union/remote/_client/auth/_authenticators/pkce.py +0 -515
- union/remote/_client/auth/_channel.py +0 -184
- union/remote/_client/auth/_client_config.py +0 -83
- union/remote/_client/auth/_default_html.py +0 -32
- union/remote/_client/auth/_grpc_utils/__init__.py +0 -0
- union/remote/_client/auth/_grpc_utils/auth_interceptor.py +0 -204
- union/remote/_client/auth/_grpc_utils/default_metadata_interceptor.py +0 -144
- union/remote/_client/auth/_keyring.py +0 -154
- union/remote/_client/auth/_token_client.py +0 -258
- union/remote/_client/auth/errors.py +0 -16
- union/remote/_client/controlplane.py +0 -86
- union/remote/_data.py +0 -149
- union/remote/_logs.py +0 -74
- union/remote/_project.py +0 -86
- union/remote/_run.py +0 -820
- union/remote/_secret.py +0 -132
- union/remote/_task.py +0 -193
- union/report/__init__.py +0 -3
- union/report/_report.py +0 -178
- union/report/_template.html +0 -124
- union/storage/__init__.py +0 -24
- union/storage/_remote_fs.py +0 -34
- union/storage/_storage.py +0 -247
- union/storage/_utils.py +0 -5
- union/types/__init__.py +0 -11
- union/types/_renderer.py +0 -162
- union/types/_string_literals.py +0 -120
- union/types/_type_engine.py +0 -2131
- union/types/_utils.py +0 -80
- /union/_protos/common/authorization_pb2_grpc.py → /flyte/_protos/workflow/common_pb2_grpc.py +0 -0
- /union/_protos/common/identifier_pb2_grpc.py → /flyte/_protos/workflow/environment_pb2_grpc.py +0 -0
- /flyte/io/{structured_dataset → _structured_dataset}/__init__.py +0 -0
- {flyte-0.0.1b3.dist-info → flyte-0.2.0a0.dist-info}/WHEEL +0 -0
- {flyte-0.0.1b3.dist-info → flyte-0.2.0a0.dist-info}/top_level.txt +0 -0
flyte/extras/_container.py
CHANGED
|
@@ -5,24 +5,19 @@ from typing import Any, Dict, List, Literal, Optional, Tuple, Type, Union
|
|
|
5
5
|
from flyteidl.core import tasks_pb2
|
|
6
6
|
|
|
7
7
|
from flyte import Image, storage
|
|
8
|
-
from flyte._datastructures import NativeInterface, SerializationContext
|
|
9
8
|
from flyte._logging import logger
|
|
10
9
|
from flyte._task import TaskTemplate
|
|
10
|
+
from flyte.models import NativeInterface, SerializationContext
|
|
11
11
|
|
|
12
|
-
_PRIMARY_CONTAINER_NAME_FIELD = "primary_container_name"
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
def _extract_command_key(cmd: str, **kwargs) -> Any:
|
|
13
|
+
def _extract_command_key(cmd: str, **kwargs) -> List[Any] | None:
|
|
16
14
|
"""
|
|
17
15
|
Extract the key from the command using regex.
|
|
18
16
|
"""
|
|
19
17
|
import re
|
|
20
18
|
|
|
21
|
-
input_regex = r"
|
|
22
|
-
|
|
23
|
-
if match:
|
|
24
|
-
return match.group(1)
|
|
25
|
-
return None
|
|
19
|
+
input_regex = r"\{\{\.inputs\.([a-zA-Z0-9_]+)\}\}"
|
|
20
|
+
return re.findall(input_regex, cmd)
|
|
26
21
|
|
|
27
22
|
|
|
28
23
|
def _extract_path_command_key(cmd: str, input_data_dir: Optional[str]) -> Optional[str]:
|
|
@@ -70,7 +65,7 @@ class ContainerTask(TaskTemplate):
|
|
|
70
65
|
input_data_dir: str | pathlib.Path = "/var/inputs",
|
|
71
66
|
output_data_dir: str | pathlib.Path = "/var/outputs",
|
|
72
67
|
metadata_format: MetadataFormat = "JSON",
|
|
73
|
-
local_logs: bool =
|
|
68
|
+
local_logs: bool = True,
|
|
74
69
|
**kwargs,
|
|
75
70
|
):
|
|
76
71
|
super().__init__(
|
|
@@ -106,34 +101,33 @@ class ContainerTask(TaskTemplate):
|
|
|
106
101
|
For FlyteFile and FlyteDirectory commands, e.g., "/var/inputs/inputs", we extract the key from strings that
|
|
107
102
|
begin with the specified `input_data_dir`.
|
|
108
103
|
"""
|
|
109
|
-
|
|
110
|
-
# from flytekit.types.file import FlyteFile
|
|
104
|
+
from flyte.io import Dir, File
|
|
111
105
|
|
|
112
106
|
volume_binding: Dict[str, Dict[str, str]] = {}
|
|
113
107
|
path_k = _extract_path_command_key(cmd, str(self._input_data_dir))
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
108
|
+
keys = path_k if path_k else _extract_command_key(cmd)
|
|
109
|
+
|
|
110
|
+
if keys:
|
|
111
|
+
for k in keys:
|
|
112
|
+
input_val = kwargs.get(k)
|
|
113
|
+
# TODO: Add support file and directory transformer first
|
|
114
|
+
if type(input_val) in [File, Dir]:
|
|
115
|
+
if not path_k:
|
|
116
|
+
raise AssertionError(
|
|
117
|
+
"File and Directory commands should not use the template syntax "
|
|
118
|
+
"like this: {{.inputs.infile}}\n"
|
|
119
|
+
"Please use a path-like syntax, such as: /var/inputs/infile.\n"
|
|
120
|
+
"This requirement is due to how Flyte Propeller processes template syntax inputs."
|
|
121
|
+
)
|
|
122
|
+
local_flyte_file_or_dir_path = str(input_val)
|
|
123
|
+
remote_flyte_file_or_dir_path = os.path.join(self._input_data_dir, k) # type: ignore
|
|
124
|
+
volume_binding[local_flyte_file_or_dir_path] = {
|
|
125
|
+
"bind": remote_flyte_file_or_dir_path,
|
|
126
|
+
"mode": "rw",
|
|
127
|
+
}
|
|
128
|
+
command = remote_flyte_file_or_dir_path
|
|
129
|
+
else:
|
|
130
|
+
command = cmd
|
|
137
131
|
|
|
138
132
|
return command, volume_binding
|
|
139
133
|
|
|
@@ -215,7 +209,7 @@ class ContainerTask(TaskTemplate):
|
|
|
215
209
|
output_dict[k] = self._convert_output_val_to_correct_type(output_val, output_type)
|
|
216
210
|
return output_dict
|
|
217
211
|
|
|
218
|
-
def execute(self, **kwargs) -> Any:
|
|
212
|
+
async def execute(self, **kwargs) -> Any:
|
|
219
213
|
try:
|
|
220
214
|
import docker
|
|
221
215
|
except ImportError:
|
|
@@ -235,6 +229,7 @@ class ContainerTask(TaskTemplate):
|
|
|
235
229
|
raise AssertionError(f"Only Image objects are supported, not strings. Got {self._image} instead.")
|
|
236
230
|
uri = self._image.uri
|
|
237
231
|
self._pull_image_if_not_exists(client, uri)
|
|
232
|
+
print(f"Command: {commands!r}")
|
|
238
233
|
|
|
239
234
|
container = client.containers.run(uri, command=commands, remove=True, volumes=volume_bindings, detach=True)
|
|
240
235
|
|
|
@@ -258,16 +253,11 @@ class ContainerTask(TaskTemplate):
|
|
|
258
253
|
}
|
|
259
254
|
|
|
260
255
|
return tasks_pb2.DataLoadingConfig(
|
|
261
|
-
input_path=self._input_data_dir,
|
|
262
|
-
output_path=self._output_data_dir,
|
|
256
|
+
input_path=str(self._input_data_dir),
|
|
257
|
+
output_path=str(self._output_data_dir),
|
|
263
258
|
enabled=True,
|
|
264
259
|
format=literal_to_protobuf.get(self._metadata_format, "JSON"),
|
|
265
260
|
)
|
|
266
261
|
|
|
267
262
|
def container_args(self, sctx: SerializationContext) -> List[str]:
|
|
268
263
|
return self._cmd + (self._args if self._args else [])
|
|
269
|
-
|
|
270
|
-
def config(self, sctx: SerializationContext) -> Dict[str, str]:
|
|
271
|
-
if self.pod_template is None:
|
|
272
|
-
return {}
|
|
273
|
-
return {_PRIMARY_CONTAINER_NAME_FIELD: self.primary_container_name}
|
flyte/io/__init__.py
CHANGED
|
@@ -3,9 +3,25 @@
|
|
|
3
3
|
|
|
4
4
|
This package contains additional data types beyond the primitive data types in python to abstract data flow
|
|
5
5
|
of large datasets in Union.
|
|
6
|
+
|
|
6
7
|
"""
|
|
7
8
|
|
|
8
|
-
__all__ = [
|
|
9
|
+
__all__ = [
|
|
10
|
+
"Dir",
|
|
11
|
+
"File",
|
|
12
|
+
"StructuredDataset",
|
|
13
|
+
"StructuredDatasetDecoder",
|
|
14
|
+
"StructuredDatasetEncoder",
|
|
15
|
+
"StructuredDatasetTransformerEngine",
|
|
16
|
+
"lazy_import_structured_dataset_handler",
|
|
17
|
+
]
|
|
9
18
|
|
|
10
19
|
from ._dir import Dir
|
|
11
20
|
from ._file import File
|
|
21
|
+
from ._structured_dataset import (
|
|
22
|
+
StructuredDataset,
|
|
23
|
+
StructuredDatasetDecoder,
|
|
24
|
+
StructuredDatasetEncoder,
|
|
25
|
+
StructuredDatasetTransformerEngine,
|
|
26
|
+
lazy_import_structured_dataset_handler,
|
|
27
|
+
)
|
flyte/io/_dir.py
CHANGED
|
@@ -244,7 +244,7 @@ class Dir(BaseModel, Generic[T], SerializableType):
|
|
|
244
244
|
|
|
245
245
|
shutil.copytree(self.path, local_dest, dirs_exist_ok=True)
|
|
246
246
|
|
|
247
|
-
# Figure this out when we figure out the final
|
|
247
|
+
# Figure this out when we figure out the final sync story
|
|
248
248
|
raise NotImplementedError("Sync download is not implemented for remote paths")
|
|
249
249
|
|
|
250
250
|
@classmethod
|
|
@@ -287,7 +287,7 @@ class Dir(BaseModel, Generic[T], SerializableType):
|
|
|
287
287
|
remote_dir = Dir[DataFrame].from_local_sync('/tmp/data_dir/', 's3://bucket/data/')
|
|
288
288
|
```
|
|
289
289
|
"""
|
|
290
|
-
# Implement this after we figure out the final
|
|
290
|
+
# Implement this after we figure out the final sync story
|
|
291
291
|
raise NotImplementedError("Sync upload is not implemented for remote paths")
|
|
292
292
|
|
|
293
293
|
async def exists(self) -> bool:
|
flyte/io/_file.py
CHANGED
|
@@ -22,7 +22,6 @@ from fsspec.asyn import AsyncFileSystem
|
|
|
22
22
|
from fsspec.utils import get_protocol
|
|
23
23
|
from mashumaro.types import SerializableType
|
|
24
24
|
from pydantic import BaseModel, model_validator
|
|
25
|
-
from synchronicity import Synchronizer
|
|
26
25
|
|
|
27
26
|
import flyte.storage as storage
|
|
28
27
|
from flyte._context import internal_ctx
|
|
@@ -33,8 +32,6 @@ from flyte.types import TypeEngine, TypeTransformer, TypeTransformerFailedError
|
|
|
33
32
|
# Type variable for the file format
|
|
34
33
|
T = TypeVar("T")
|
|
35
34
|
|
|
36
|
-
synced = Synchronizer()
|
|
37
|
-
|
|
38
35
|
|
|
39
36
|
class File(BaseModel, Generic[T], SerializableType):
|
|
40
37
|
"""
|
|
@@ -235,6 +232,8 @@ class File(BaseModel, Generic[T], SerializableType):
|
|
|
235
232
|
# This code is broadly similar to what storage.get_stream does, but without actually reading from the stream
|
|
236
233
|
file_handle = None
|
|
237
234
|
try:
|
|
235
|
+
if "b" not in mode:
|
|
236
|
+
raise ValueError("Mode must include 'b' for binary access, when using remote files.")
|
|
238
237
|
if isinstance(fs, AsyncFileSystem):
|
|
239
238
|
file_handle = await fs.open_async(self.path, mode)
|
|
240
239
|
yield file_handle
|
|
@@ -314,7 +313,7 @@ class File(BaseModel, Generic[T], SerializableType):
|
|
|
314
313
|
with fs.open(self.path, **open_kwargs) as f:
|
|
315
314
|
yield f
|
|
316
315
|
|
|
317
|
-
#
|
|
316
|
+
# TODO sync needs to be implemented
|
|
318
317
|
async def download(self, local_path: Optional[Union[str, Path]] = None) -> str:
|
|
319
318
|
"""
|
|
320
319
|
Asynchronously download the file to a local path.
|
|
@@ -9,7 +9,7 @@ from fsspec.core import split_protocol, strip_protocol
|
|
|
9
9
|
import flyte.storage as storage
|
|
10
10
|
from flyte._logging import logger
|
|
11
11
|
from flyte._utils import lazy_module
|
|
12
|
-
from flyte.io.
|
|
12
|
+
from flyte.io._structured_dataset.structured_dataset import (
|
|
13
13
|
CSV,
|
|
14
14
|
PARQUET,
|
|
15
15
|
StructuredDataset,
|
|
@@ -168,7 +168,7 @@ class StructuredDataset(SerializableType, DataClassJSONMixin):
|
|
|
168
168
|
return self._literal_sd
|
|
169
169
|
|
|
170
170
|
def open(self, dataframe_type: Type[DF]):
|
|
171
|
-
from flyte.io.
|
|
171
|
+
from flyte.io._structured_dataset import lazy_import_structured_dataset_handler
|
|
172
172
|
|
|
173
173
|
"""
|
|
174
174
|
Load the handler if needed. For the use case like:
|
|
@@ -5,7 +5,9 @@ import os
|
|
|
5
5
|
import pathlib
|
|
6
6
|
import tempfile
|
|
7
7
|
from dataclasses import dataclass, field, replace
|
|
8
|
-
from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Tuple, Type
|
|
8
|
+
from typing import TYPE_CHECKING, Any, Callable, ClassVar, Dict, Literal, Optional, Tuple, Type
|
|
9
|
+
|
|
10
|
+
import rich.repr
|
|
9
11
|
|
|
10
12
|
from flyte._docstring import Docstring
|
|
11
13
|
from flyte._interface import extract_return_annotation
|
|
@@ -13,6 +15,8 @@ from flyte._logging import logger
|
|
|
13
15
|
from flyte._utils.helpers import base36_encode
|
|
14
16
|
|
|
15
17
|
if TYPE_CHECKING:
|
|
18
|
+
from flyteidl.core import literals_pb2
|
|
19
|
+
|
|
16
20
|
from flyte._internal.imagebuild.image_builder import ImageCache
|
|
17
21
|
from flyte.report import Report
|
|
18
22
|
|
|
@@ -27,6 +31,7 @@ def generate_random_name() -> str:
|
|
|
27
31
|
return str(uuid4()) # Placeholder for actual random name generation logic
|
|
28
32
|
|
|
29
33
|
|
|
34
|
+
@rich.repr.auto
|
|
30
35
|
@dataclass(frozen=True, kw_only=True)
|
|
31
36
|
class ActionID:
|
|
32
37
|
"""
|
|
@@ -56,17 +61,19 @@ class ActionID:
|
|
|
56
61
|
name = generate_random_name()
|
|
57
62
|
return replace(self, name=name)
|
|
58
63
|
|
|
59
|
-
def new_sub_action_from(self,
|
|
64
|
+
def new_sub_action_from(self, task_call_seq: int, task_hash: str, input_hash: str, group: str | None) -> ActionID:
|
|
60
65
|
"""Make a deterministic name"""
|
|
61
66
|
import hashlib
|
|
62
67
|
|
|
63
|
-
components = f"{self.
|
|
68
|
+
components = f"{self.name}-{input_hash}-{task_hash}-{task_call_seq}" + (f"-{group}" if group else "")
|
|
69
|
+
logger.debug(f"----- Generating sub-action ID from components: {components}")
|
|
64
70
|
# has the components into something deterministic
|
|
65
71
|
bytes_digest = hashlib.md5(components.encode()).digest()
|
|
66
72
|
new_name = base36_encode(bytes_digest)
|
|
67
73
|
return self.new_sub_action(new_name)
|
|
68
74
|
|
|
69
75
|
|
|
76
|
+
@rich.repr.auto
|
|
70
77
|
@dataclass(frozen=True, kw_only=True)
|
|
71
78
|
class RawDataPath:
|
|
72
79
|
"""
|
|
@@ -90,6 +97,7 @@ class RawDataPath:
|
|
|
90
97
|
# Create a temporary directory for data storage
|
|
91
98
|
p = tempfile.mkdtemp()
|
|
92
99
|
logger.debug(f"Creating temporary directory for data storage: {p}")
|
|
100
|
+
pathlib.Path(p).mkdir(parents=True, exist_ok=True)
|
|
93
101
|
return RawDataPath(path=p)
|
|
94
102
|
case str():
|
|
95
103
|
return RawDataPath(path=local_folder)
|
|
@@ -132,11 +140,13 @@ class RawDataPath:
|
|
|
132
140
|
return remote_path
|
|
133
141
|
|
|
134
142
|
|
|
143
|
+
@rich.repr.auto
|
|
135
144
|
@dataclass(frozen=True)
|
|
136
145
|
class GroupData:
|
|
137
146
|
name: str
|
|
138
147
|
|
|
139
148
|
|
|
149
|
+
@rich.repr.auto
|
|
140
150
|
@dataclass(frozen=True, kw_only=True)
|
|
141
151
|
class TaskContext:
|
|
142
152
|
"""
|
|
@@ -159,6 +169,7 @@ class TaskContext:
|
|
|
159
169
|
code_bundle: CodeBundle | None = None
|
|
160
170
|
compiled_image_cache: ImageCache | None = None
|
|
161
171
|
data: Dict[str, Any] = field(default_factory=dict)
|
|
172
|
+
mode: Literal["local", "remote", "hybrid"] = "remote"
|
|
162
173
|
|
|
163
174
|
def replace(self, **kwargs) -> TaskContext:
|
|
164
175
|
if "data" in kwargs:
|
|
@@ -176,6 +187,7 @@ class TaskContext:
|
|
|
176
187
|
return self.data.get(key)
|
|
177
188
|
|
|
178
189
|
|
|
190
|
+
@rich.repr.auto
|
|
179
191
|
@dataclass(frozen=True, kw_only=True)
|
|
180
192
|
class CodeBundle:
|
|
181
193
|
"""
|
|
@@ -210,6 +222,7 @@ class CodeBundle:
|
|
|
210
222
|
return replace(self, downloaded_path=path)
|
|
211
223
|
|
|
212
224
|
|
|
225
|
+
@rich.repr.auto
|
|
213
226
|
@dataclass(frozen=True)
|
|
214
227
|
class Checkpoints:
|
|
215
228
|
"""
|
|
@@ -220,6 +233,13 @@ class Checkpoints:
|
|
|
220
233
|
checkpoint_path: str | None
|
|
221
234
|
|
|
222
235
|
|
|
236
|
+
class _has_default:
|
|
237
|
+
"""
|
|
238
|
+
A marker class to indicate that a specific input has a default value or not.
|
|
239
|
+
This is used to determine if the input is required or not.
|
|
240
|
+
"""
|
|
241
|
+
|
|
242
|
+
|
|
223
243
|
@dataclass(frozen=True)
|
|
224
244
|
class NativeInterface:
|
|
225
245
|
"""
|
|
@@ -229,7 +249,14 @@ class NativeInterface:
|
|
|
229
249
|
|
|
230
250
|
inputs: Dict[str, Tuple[Type, Any]]
|
|
231
251
|
outputs: Dict[str, Type]
|
|
232
|
-
docstring: Optional[Docstring] =
|
|
252
|
+
docstring: Optional[Docstring] = None
|
|
253
|
+
|
|
254
|
+
# This field is used to indicate that the task has a default value for the input, but already in the
|
|
255
|
+
# remote form.
|
|
256
|
+
_remote_defaults: Optional[Dict[str, literals_pb2.Literal]] = field(default=None, repr=False)
|
|
257
|
+
|
|
258
|
+
has_default: ClassVar[Type[_has_default]] = _has_default # This can be used to indicate if a specific input
|
|
259
|
+
# has a default value or not, in the case when the default value is not known. An example would be remote tasks.
|
|
233
260
|
|
|
234
261
|
def has_outputs(self) -> bool:
|
|
235
262
|
"""
|
|
@@ -237,12 +264,31 @@ class NativeInterface:
|
|
|
237
264
|
"""
|
|
238
265
|
return self.outputs is not None and len(self.outputs) > 0
|
|
239
266
|
|
|
267
|
+
def num_required_inputs(self) -> int:
|
|
268
|
+
"""
|
|
269
|
+
Get the number of required inputs for the task. This is used to determine how many inputs are required for the
|
|
270
|
+
task execution.
|
|
271
|
+
"""
|
|
272
|
+
return sum(1 for t in self.inputs.values() if t[1] is inspect.Parameter.empty)
|
|
273
|
+
|
|
240
274
|
@classmethod
|
|
241
|
-
def from_types(
|
|
275
|
+
def from_types(
|
|
276
|
+
cls,
|
|
277
|
+
inputs: Dict[str, Tuple[Type, Type[_has_default] | Type[inspect._empty]]],
|
|
278
|
+
outputs: Dict[str, Type],
|
|
279
|
+
default_inputs: Optional[Dict[str, literals_pb2.Literal]] = None,
|
|
280
|
+
) -> NativeInterface:
|
|
242
281
|
"""
|
|
243
282
|
Create a new NativeInterface from the given types. This is used to create a native interface for the task.
|
|
283
|
+
:param inputs: A dictionary of input names and their types and a value indicating if they have a default value.
|
|
284
|
+
:param outputs: A dictionary of output names and their types.
|
|
285
|
+
:param default_inputs: Optional dictionary of default inputs for remote tasks.
|
|
286
|
+
:return: A NativeInterface object with the given inputs and outputs.
|
|
244
287
|
"""
|
|
245
|
-
|
|
288
|
+
for k, v in inputs.items():
|
|
289
|
+
if v[1] is cls.has_default and (default_inputs is None or k not in default_inputs):
|
|
290
|
+
raise ValueError(f"Input {k} has a default value but no default input provided for remote task.")
|
|
291
|
+
return cls(inputs=inputs, outputs=outputs, _remote_defaults=default_inputs)
|
|
246
292
|
|
|
247
293
|
@classmethod
|
|
248
294
|
def from_callable(cls, func: Callable) -> NativeInterface:
|
|
@@ -290,7 +336,10 @@ class NativeInterface:
|
|
|
290
336
|
tp = tpe[0] if isinstance(tpe[0], str) else tpe[0].__name__
|
|
291
337
|
i += f"{key}: {tp}"
|
|
292
338
|
if tpe[1] is not inspect.Parameter.empty:
|
|
293
|
-
|
|
339
|
+
if tpe[1] is self.has_default:
|
|
340
|
+
i += " = ..."
|
|
341
|
+
else:
|
|
342
|
+
i += f" = {tpe[1]}"
|
|
294
343
|
i += ")"
|
|
295
344
|
if self.outputs:
|
|
296
345
|
initial = True
|
flyte/remote/__init__.py
CHANGED
|
@@ -11,6 +11,7 @@ __all__ = [
|
|
|
11
11
|
"Run",
|
|
12
12
|
"RunDetails",
|
|
13
13
|
"Secret",
|
|
14
|
+
"SecretTypes",
|
|
14
15
|
"Task",
|
|
15
16
|
"create_channel",
|
|
16
17
|
"upload_dir",
|
|
@@ -21,5 +22,5 @@ from ._client.auth import create_channel
|
|
|
21
22
|
from ._data import upload_dir, upload_file
|
|
22
23
|
from ._project import Project
|
|
23
24
|
from ._run import Action, ActionDetails, ActionInputs, ActionOutputs, Run, RunDetails
|
|
24
|
-
from ._secret import Secret
|
|
25
|
+
from ._secret import Secret, SecretTypes
|
|
25
26
|
from ._task import Task
|
|
@@ -58,6 +58,8 @@ class TaskService(Protocol):
|
|
|
58
58
|
self, request: task_service_pb2.GetTaskDetailsRequest
|
|
59
59
|
) -> task_service_pb2.GetTaskDetailsResponse: ...
|
|
60
60
|
|
|
61
|
+
async def ListTasks(self, request: task_service_pb2.ListTasksRequest) -> task_service_pb2.ListTasksResponse: ...
|
|
62
|
+
|
|
61
63
|
|
|
62
64
|
class RunService(Protocol):
|
|
63
65
|
async def CreateRun(self, request: run_service_pb2.CreateRunRequest) -> run_service_pb2.CreateRunResponse: ...
|
|
@@ -0,0 +1,14 @@
|
|
|
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
|
+
endpoint, client_id, client_secret, org = base64.b64decode(encoded_str.encode("utf-8")).decode("utf-8").split(":")
|
|
10
|
+
# For consistency, let's make sure org is always a non-empty string
|
|
11
|
+
if not org:
|
|
12
|
+
org = "None"
|
|
13
|
+
|
|
14
|
+
return endpoint, client_id, client_secret, org
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import os
|
|
1
2
|
import ssl
|
|
2
3
|
import typing
|
|
3
4
|
|
|
@@ -15,6 +16,11 @@ from ._authenticators.factory import (
|
|
|
15
16
|
get_async_proxy_authenticator,
|
|
16
17
|
)
|
|
17
18
|
|
|
19
|
+
# Set environment variables for gRPC, this reduces log spew and avoids unnecessary warnings
|
|
20
|
+
if "GRPC_VERBOSITY" not in os.environ:
|
|
21
|
+
os.environ["GRPC_VERBOSITY"] = "ERROR"
|
|
22
|
+
os.environ["GRPC_CPP_MIN_LOG_LEVEL"] = "ERROR"
|
|
23
|
+
|
|
18
24
|
# Initialize gRPC AIO early enough so it can be used in the main thread
|
|
19
25
|
init_grpc_aio()
|
|
20
26
|
|
|
@@ -44,7 +50,9 @@ def bootstrap_ssl_from_server(endpoint: str) -> grpc.ChannelCredentials:
|
|
|
44
50
|
|
|
45
51
|
|
|
46
52
|
async def create_channel(
|
|
47
|
-
endpoint: str,
|
|
53
|
+
endpoint: str | None,
|
|
54
|
+
api_key: str | None = None,
|
|
55
|
+
/,
|
|
48
56
|
insecure: typing.Optional[bool] = None,
|
|
49
57
|
insecure_skip_verify: typing.Optional[bool] = False,
|
|
50
58
|
ca_cert_file_path: typing.Optional[str] = None,
|
|
@@ -66,6 +74,7 @@ async def create_channel(
|
|
|
66
74
|
and create authentication interceptors that perform async operations.
|
|
67
75
|
|
|
68
76
|
:param endpoint: The endpoint URL for the gRPC channel
|
|
77
|
+
:param api_key: API key for authentication; if provided, it will be used to detect the endpoint and credentials.
|
|
69
78
|
:param insecure: Whether to use an insecure channel (no SSL)
|
|
70
79
|
:param insecure_skip_verify: Whether to skip SSL certificate verification
|
|
71
80
|
:param ca_cert_file_path: Path to CA certificate file for SSL verification
|
|
@@ -104,6 +113,18 @@ async def create_channel(
|
|
|
104
113
|
- refresh_access_token_params: Parameters to add when refreshing access token
|
|
105
114
|
:return: grpc.aio.Channel with authentication interceptors configured
|
|
106
115
|
"""
|
|
116
|
+
assert endpoint or api_key, "Either endpoint or api_key must be specified"
|
|
117
|
+
|
|
118
|
+
if api_key:
|
|
119
|
+
from flyte.remote._client.auth._auth_utils import decode_api_key
|
|
120
|
+
|
|
121
|
+
endpoint, client_id, client_secret, org = decode_api_key(api_key)
|
|
122
|
+
kwargs["auth_type"] = "ClientSecret"
|
|
123
|
+
kwargs["client_id"] = client_id
|
|
124
|
+
kwargs["client_secret"] = client_secret
|
|
125
|
+
kwargs["client_credentials_secret"] = client_secret
|
|
126
|
+
|
|
127
|
+
assert endpoint, "Endpoint must be specified by this point"
|
|
107
128
|
|
|
108
129
|
if not ssl_credentials:
|
|
109
130
|
if insecure_skip_verify:
|
|
@@ -119,7 +140,12 @@ async def create_channel(
|
|
|
119
140
|
|
|
120
141
|
# Create an unauthenticated channel first to use to get the server metadata
|
|
121
142
|
if insecure:
|
|
122
|
-
|
|
143
|
+
insecure_kwargs = {}
|
|
144
|
+
if kw_opts := kwargs.get("options"):
|
|
145
|
+
insecure_kwargs["options"] = kw_opts
|
|
146
|
+
if compression:
|
|
147
|
+
insecure_kwargs["compression"] = compression
|
|
148
|
+
unauthenticated_channel = grpc.aio.insecure_channel(endpoint, **insecure_kwargs)
|
|
123
149
|
else:
|
|
124
150
|
unauthenticated_channel = grpc.aio.secure_channel(
|
|
125
151
|
target=endpoint,
|
|
@@ -173,7 +199,12 @@ async def create_channel(
|
|
|
173
199
|
interceptors.extend(auth_interceptors)
|
|
174
200
|
|
|
175
201
|
if insecure:
|
|
176
|
-
|
|
202
|
+
insecure_kwargs = {}
|
|
203
|
+
if kw_opts := kwargs.get("options"):
|
|
204
|
+
insecure_kwargs["options"] = kw_opts
|
|
205
|
+
if compression:
|
|
206
|
+
insecure_kwargs["compression"] = compression
|
|
207
|
+
return grpc.aio.insecure_channel(endpoint, interceptors=interceptors, **insecure_kwargs)
|
|
177
208
|
|
|
178
209
|
return grpc.aio.secure_channel(
|
|
179
210
|
target=endpoint,
|
|
@@ -94,7 +94,7 @@ async def get_token(
|
|
|
94
94
|
http_proxy_url: typing.Optional[str] = None,
|
|
95
95
|
verify: typing.Optional[typing.Union[bool, str]] = None,
|
|
96
96
|
refresh_token: typing.Optional[str] = None,
|
|
97
|
-
) -> typing.Tuple[str, str, int]:
|
|
97
|
+
) -> typing.Tuple[str, str | None, int]:
|
|
98
98
|
"""
|
|
99
99
|
Retrieves an access token from the specified token endpoint.
|
|
100
100
|
|
|
@@ -165,7 +165,7 @@ async def get_token(
|
|
|
165
165
|
if "refresh_token" in j:
|
|
166
166
|
new_refresh_token = j["refresh_token"]
|
|
167
167
|
else:
|
|
168
|
-
|
|
168
|
+
logger.info("No refresh token received, this is expected for client credentials flow")
|
|
169
169
|
|
|
170
170
|
return j["access_token"], new_refresh_token, j["expires_in"]
|
|
171
171
|
|
|
@@ -213,7 +213,7 @@ async def poll_token_endpoint(
|
|
|
213
213
|
scopes: typing.Optional[typing.List[str]] = None,
|
|
214
214
|
http_proxy_url: typing.Optional[str] = None,
|
|
215
215
|
verify: typing.Optional[typing.Union[bool, str]] = None,
|
|
216
|
-
) -> typing.Tuple[str, str, int]:
|
|
216
|
+
) -> typing.Tuple[str, str | None, int]:
|
|
217
217
|
"""
|
|
218
218
|
Polls the token endpoint until authentication is complete or times out.
|
|
219
219
|
|
|
@@ -39,21 +39,21 @@ class ClientSet:
|
|
|
39
39
|
|
|
40
40
|
@classmethod
|
|
41
41
|
async def for_endpoint(cls, endpoint: str, *, insecure: bool = False, **kwargs) -> ClientSet:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
del kwargs["headless"]
|
|
46
|
-
del kwargs["command"]
|
|
47
|
-
del kwargs["client_id"]
|
|
48
|
-
del kwargs["client_credentials_secret"]
|
|
49
|
-
del kwargs["client_config"]
|
|
50
|
-
del kwargs["rpc_retries"]
|
|
51
|
-
del kwargs["http_proxy_url"]
|
|
52
|
-
return cls(await create_channel(endpoint, insecure=insecure, **kwargs), endpoint, insecure=insecure, **kwargs)
|
|
42
|
+
return cls(
|
|
43
|
+
await create_channel(endpoint, None, insecure=insecure, **kwargs), endpoint, insecure=insecure, **kwargs
|
|
44
|
+
)
|
|
53
45
|
|
|
54
46
|
@classmethod
|
|
55
|
-
async def for_api_key(cls, api_key: str, **kwargs) -> ClientSet:
|
|
56
|
-
|
|
47
|
+
async def for_api_key(cls, api_key: str, *, insecure: bool = False, **kwargs) -> ClientSet:
|
|
48
|
+
from flyte.remote._client.auth._auth_utils import decode_api_key
|
|
49
|
+
|
|
50
|
+
# Parsing the API key is done in create_channel, but cleaner to redo it here rather than getting create_channel
|
|
51
|
+
# to return the endpoint
|
|
52
|
+
endpoint, _, _, _ = decode_api_key(api_key)
|
|
53
|
+
|
|
54
|
+
return cls(
|
|
55
|
+
await create_channel(None, api_key, insecure=insecure, **kwargs), endpoint, insecure=insecure, **kwargs
|
|
56
|
+
)
|
|
57
57
|
|
|
58
58
|
@classmethod
|
|
59
59
|
async def for_serverless(cls) -> ClientSet:
|
flyte/remote/_console.py
CHANGED
|
@@ -7,7 +7,7 @@ def _get_http_domain(endpoint: str, insecure: bool) -> str:
|
|
|
7
7
|
if parsed.scheme == "dns":
|
|
8
8
|
domain = parsed.path.lstrip("/")
|
|
9
9
|
else:
|
|
10
|
-
domain = parsed.netloc
|
|
10
|
+
domain = parsed.netloc or parsed.path
|
|
11
11
|
# TODO: make console url configurable
|
|
12
12
|
if domain.split(":")[0] == "localhost":
|
|
13
13
|
domain = "localhost:8080"
|
flyte/remote/_data.py
CHANGED
|
@@ -15,8 +15,9 @@ import httpx
|
|
|
15
15
|
from flyteidl.service import dataproxy_pb2
|
|
16
16
|
from google.protobuf import duration_pb2
|
|
17
17
|
|
|
18
|
-
from flyte._initialize import CommonInit, get_client, get_common_config
|
|
19
|
-
from flyte.
|
|
18
|
+
from flyte._initialize import CommonInit, ensure_client, get_client, get_common_config
|
|
19
|
+
from flyte._logging import make_hyperlink
|
|
20
|
+
from flyte.errors import InitializationError, RuntimeSystemError
|
|
20
21
|
|
|
21
22
|
_UPLOAD_EXPIRES_IN = timedelta(seconds=60)
|
|
22
23
|
|
|
@@ -83,11 +84,13 @@ async def _upload_single_file(
|
|
|
83
84
|
raise RuntimeSystemError(
|
|
84
85
|
"PermissionDenied", f"Failed to get signed url for {fp}, please check your permissions."
|
|
85
86
|
)
|
|
87
|
+
elif e.code() == grpc.StatusCode.UNAVAILABLE:
|
|
88
|
+
raise InitializationError("EndpointUnavailable", "user", "Service is unavailable.")
|
|
86
89
|
else:
|
|
87
90
|
raise RuntimeSystemError(e.code().value, f"Failed to get signed url for {fp}.")
|
|
88
91
|
except Exception as e:
|
|
89
92
|
raise RuntimeSystemError(type(e).__name__, f"Failed to get signed url for {fp}.") from e
|
|
90
|
-
logger.debug(f
|
|
93
|
+
logger.debug(f'Uploading to {make_hyperlink("signed url", resp.signed_url)} for {fp}')
|
|
91
94
|
extra_headers = get_extra_headers_for_protocol(resp.native_url)
|
|
92
95
|
extra_headers.update(resp.headers)
|
|
93
96
|
encoded_md5 = b64encode(md5_bytes)
|
|
@@ -100,7 +103,8 @@ async def _upload_single_file(
|
|
|
100
103
|
if put_resp.status_code != 200:
|
|
101
104
|
raise RuntimeSystemError(
|
|
102
105
|
"UploadFailed",
|
|
103
|
-
f"Failed to upload {fp} to {resp.signed_url}, status code: {put_resp.status_code}"
|
|
106
|
+
f"Failed to upload {fp} to {resp.signed_url}, status code: {put_resp.status_code}, "
|
|
107
|
+
f"response: {put_resp.text}",
|
|
104
108
|
)
|
|
105
109
|
# TODO in old code we did this
|
|
106
110
|
# if self._config.platform.insecure_skip_verify is True
|
|
@@ -109,7 +113,6 @@ async def _upload_single_file(
|
|
|
109
113
|
return str_digest, resp.native_url
|
|
110
114
|
|
|
111
115
|
|
|
112
|
-
@requires_client
|
|
113
116
|
async def upload_file(fp: Path, verify: bool = True) -> Tuple[str, str]:
|
|
114
117
|
"""
|
|
115
118
|
Uploads a file to a remote location and returns the remote URI.
|
|
@@ -119,13 +122,13 @@ async def upload_file(fp: Path, verify: bool = True) -> Tuple[str, str]:
|
|
|
119
122
|
:return: A tuple containing the MD5 digest and the remote URI.
|
|
120
123
|
"""
|
|
121
124
|
# This is a placeholder implementation. Replace with actual upload logic.
|
|
125
|
+
ensure_client()
|
|
122
126
|
cfg = get_common_config()
|
|
123
127
|
if not fp.is_file():
|
|
124
128
|
raise ValueError(f"{fp} is not a single file, upload arg must be a single file.")
|
|
125
129
|
return await _upload_single_file(cfg, fp, verify=verify)
|
|
126
130
|
|
|
127
131
|
|
|
128
|
-
@requires_client
|
|
129
132
|
async def upload_dir(dir_path: Path, verify: bool = True) -> str:
|
|
130
133
|
"""
|
|
131
134
|
Uploads a directory to a remote location and returns the remote URI.
|
|
@@ -135,6 +138,7 @@ async def upload_dir(dir_path: Path, verify: bool = True) -> str:
|
|
|
135
138
|
:return: The remote URI of the uploaded directory.
|
|
136
139
|
"""
|
|
137
140
|
# This is a placeholder implementation. Replace with actual upload logic.
|
|
141
|
+
ensure_client()
|
|
138
142
|
cfg = get_common_config()
|
|
139
143
|
if not dir_path.is_dir():
|
|
140
144
|
raise ValueError(f"{dir_path} is not a directory, upload arg must be a directory.")
|