flyte 2.0.0b13__py3-none-any.whl → 2.0.0b30__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 +18 -2
- flyte/_bin/debug.py +38 -0
- flyte/_bin/runtime.py +62 -8
- flyte/_cache/cache.py +4 -2
- flyte/_cache/local_cache.py +216 -0
- flyte/_code_bundle/_ignore.py +12 -4
- flyte/_code_bundle/_packaging.py +13 -9
- flyte/_code_bundle/_utils.py +18 -10
- flyte/_code_bundle/bundle.py +17 -9
- flyte/_constants.py +1 -0
- flyte/_context.py +4 -1
- flyte/_custom_context.py +73 -0
- flyte/_debug/constants.py +38 -0
- flyte/_debug/utils.py +17 -0
- flyte/_debug/vscode.py +307 -0
- flyte/_deploy.py +235 -61
- flyte/_environment.py +20 -6
- flyte/_excepthook.py +1 -1
- flyte/_hash.py +1 -16
- flyte/_image.py +178 -81
- flyte/_initialize.py +132 -51
- flyte/_interface.py +39 -2
- flyte/_internal/controllers/__init__.py +4 -5
- flyte/_internal/controllers/_local_controller.py +70 -29
- flyte/_internal/controllers/_trace.py +1 -1
- flyte/_internal/controllers/remote/__init__.py +0 -2
- flyte/_internal/controllers/remote/_action.py +14 -16
- flyte/_internal/controllers/remote/_client.py +1 -1
- flyte/_internal/controllers/remote/_controller.py +68 -70
- flyte/_internal/controllers/remote/_core.py +127 -99
- flyte/_internal/controllers/remote/_informer.py +19 -10
- flyte/_internal/controllers/remote/_service_protocol.py +7 -7
- flyte/_internal/imagebuild/docker_builder.py +181 -69
- flyte/_internal/imagebuild/image_builder.py +0 -5
- flyte/_internal/imagebuild/remote_builder.py +155 -64
- flyte/_internal/imagebuild/utils.py +51 -2
- flyte/_internal/resolvers/_task_module.py +5 -38
- flyte/_internal/resolvers/default.py +2 -2
- flyte/_internal/runtime/convert.py +110 -21
- flyte/_internal/runtime/entrypoints.py +27 -1
- flyte/_internal/runtime/io.py +21 -8
- flyte/_internal/runtime/resources_serde.py +20 -6
- flyte/_internal/runtime/reuse.py +1 -1
- flyte/_internal/runtime/rusty.py +20 -5
- flyte/_internal/runtime/task_serde.py +34 -19
- flyte/_internal/runtime/taskrunner.py +22 -4
- flyte/_internal/runtime/trigger_serde.py +160 -0
- flyte/_internal/runtime/types_serde.py +1 -1
- flyte/_keyring/__init__.py +0 -0
- flyte/_keyring/file.py +115 -0
- flyte/_logging.py +201 -39
- flyte/_map.py +111 -14
- flyte/_module.py +70 -0
- flyte/_pod.py +4 -3
- flyte/_resources.py +213 -31
- flyte/_run.py +110 -39
- flyte/_task.py +75 -16
- flyte/_task_environment.py +105 -29
- flyte/_task_plugins.py +4 -2
- flyte/_trace.py +5 -0
- flyte/_trigger.py +1000 -0
- flyte/_utils/__init__.py +2 -1
- flyte/_utils/asyn.py +3 -1
- flyte/_utils/coro_management.py +2 -1
- flyte/_utils/docker_credentials.py +173 -0
- flyte/_utils/module_loader.py +17 -2
- flyte/_version.py +3 -3
- flyte/cli/_abort.py +3 -3
- flyte/cli/_build.py +3 -6
- flyte/cli/_common.py +78 -7
- flyte/cli/_create.py +182 -4
- flyte/cli/_delete.py +23 -1
- flyte/cli/_deploy.py +63 -16
- flyte/cli/_get.py +79 -34
- flyte/cli/_params.py +26 -10
- flyte/cli/_plugins.py +209 -0
- flyte/cli/_run.py +151 -26
- flyte/cli/_serve.py +64 -0
- flyte/cli/_update.py +37 -0
- flyte/cli/_user.py +17 -0
- flyte/cli/main.py +30 -4
- flyte/config/_config.py +10 -6
- flyte/config/_internal.py +1 -0
- flyte/config/_reader.py +29 -8
- flyte/connectors/__init__.py +11 -0
- flyte/connectors/_connector.py +270 -0
- flyte/connectors/_server.py +197 -0
- flyte/connectors/utils.py +135 -0
- flyte/errors.py +22 -2
- flyte/extend.py +8 -1
- flyte/extras/_container.py +6 -1
- flyte/git/__init__.py +3 -0
- flyte/git/_config.py +21 -0
- flyte/io/__init__.py +2 -0
- flyte/io/_dataframe/__init__.py +2 -0
- flyte/io/_dataframe/basic_dfs.py +17 -8
- flyte/io/_dataframe/dataframe.py +98 -132
- flyte/io/_dir.py +575 -113
- flyte/io/_file.py +582 -139
- flyte/io/_hashing_io.py +342 -0
- flyte/models.py +74 -15
- flyte/remote/__init__.py +6 -1
- flyte/remote/_action.py +34 -26
- flyte/remote/_client/_protocols.py +39 -4
- flyte/remote/_client/auth/_authenticators/device_code.py +4 -5
- flyte/remote/_client/auth/_authenticators/pkce.py +1 -1
- flyte/remote/_client/auth/_channel.py +10 -6
- flyte/remote/_client/controlplane.py +17 -5
- flyte/remote/_console.py +3 -2
- flyte/remote/_data.py +6 -6
- flyte/remote/_logs.py +3 -3
- flyte/remote/_run.py +64 -8
- flyte/remote/_secret.py +26 -17
- flyte/remote/_task.py +75 -33
- flyte/remote/_trigger.py +306 -0
- flyte/remote/_user.py +33 -0
- flyte/report/_report.py +1 -1
- flyte/storage/__init__.py +6 -1
- flyte/storage/_config.py +5 -1
- flyte/storage/_parallel_reader.py +274 -0
- flyte/storage/_storage.py +200 -103
- flyte/types/__init__.py +16 -0
- flyte/types/_interface.py +2 -2
- flyte/types/_pickle.py +35 -8
- flyte/types/_string_literals.py +8 -9
- flyte/types/_type_engine.py +40 -70
- flyte/types/_utils.py +1 -1
- flyte-2.0.0b30.data/scripts/debug.py +38 -0
- {flyte-2.0.0b13.data → flyte-2.0.0b30.data}/scripts/runtime.py +62 -8
- {flyte-2.0.0b13.dist-info → flyte-2.0.0b30.dist-info}/METADATA +11 -3
- flyte-2.0.0b30.dist-info/RECORD +192 -0
- {flyte-2.0.0b13.dist-info → flyte-2.0.0b30.dist-info}/entry_points.txt +3 -0
- 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 -93
- flyte/_protos/common/identifier_pb2.pyi +0 -110
- 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 -71
- 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/imagebuilder/definition_pb2.py +0 -59
- flyte/_protos/imagebuilder/definition_pb2.pyi +0 -140
- flyte/_protos/imagebuilder/definition_pb2_grpc.py +0 -4
- flyte/_protos/imagebuilder/payload_pb2.py +0 -32
- flyte/_protos/imagebuilder/payload_pb2.pyi +0 -21
- flyte/_protos/imagebuilder/payload_pb2_grpc.py +0 -4
- flyte/_protos/imagebuilder/service_pb2.py +0 -29
- flyte/_protos/imagebuilder/service_pb2.pyi +0 -5
- flyte/_protos/imagebuilder/service_pb2_grpc.py +0 -66
- flyte/_protos/logs/dataplane/payload_pb2.py +0 -100
- flyte/_protos/logs/dataplane/payload_pb2.pyi +0 -177
- 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/common_pb2.py +0 -27
- flyte/_protos/workflow/common_pb2.pyi +0 -14
- flyte/_protos/workflow/common_pb2_grpc.py +0 -4
- flyte/_protos/workflow/environment_pb2.py +0 -29
- flyte/_protos/workflow/environment_pb2.pyi +0 -12
- flyte/_protos/workflow/environment_pb2_grpc.py +0 -4
- 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 -109
- flyte/_protos/workflow/queue_service_pb2.pyi +0 -166
- flyte/_protos/workflow/queue_service_pb2_grpc.py +0 -172
- flyte/_protos/workflow/run_definition_pb2.py +0 -121
- flyte/_protos/workflow/run_definition_pb2.pyi +0 -327
- 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 -137
- flyte/_protos/workflow/run_service_pb2.pyi +0 -185
- flyte/_protos/workflow/run_service_pb2_grpc.py +0 -446
- flyte/_protos/workflow/state_service_pb2.py +0 -67
- flyte/_protos/workflow/state_service_pb2.pyi +0 -76
- flyte/_protos/workflow/state_service_pb2_grpc.py +0 -138
- flyte/_protos/workflow/task_definition_pb2.py +0 -79
- flyte/_protos/workflow/task_definition_pb2.pyi +0 -81
- flyte/_protos/workflow/task_definition_pb2_grpc.py +0 -4
- flyte/_protos/workflow/task_service_pb2.py +0 -60
- flyte/_protos/workflow/task_service_pb2.pyi +0 -59
- flyte/_protos/workflow/task_service_pb2_grpc.py +0 -138
- flyte-2.0.0b13.dist-info/RECORD +0 -239
- /flyte/{_protos → _debug}/__init__.py +0 -0
- {flyte-2.0.0b13.dist-info → flyte-2.0.0b30.dist-info}/WHEEL +0 -0
- {flyte-2.0.0b13.dist-info → flyte-2.0.0b30.dist-info}/licenses/LICENSE +0 -0
- {flyte-2.0.0b13.dist-info → flyte-2.0.0b30.dist-info}/top_level.txt +0 -0
flyte/cli/_serve.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@click.group("serve")
|
|
7
|
+
@click.pass_context
|
|
8
|
+
def serve(_: click.Context):
|
|
9
|
+
"""
|
|
10
|
+
Start the specific service. For example:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
flyte serve connector
|
|
14
|
+
```
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@serve.command()
|
|
19
|
+
@click.option(
|
|
20
|
+
"--port",
|
|
21
|
+
default="8000",
|
|
22
|
+
is_flag=False,
|
|
23
|
+
type=int,
|
|
24
|
+
help="Grpc port for the connector service",
|
|
25
|
+
)
|
|
26
|
+
@click.option(
|
|
27
|
+
"--prometheus_port",
|
|
28
|
+
default="9090",
|
|
29
|
+
is_flag=False,
|
|
30
|
+
type=int,
|
|
31
|
+
help="Prometheus port for the connector service",
|
|
32
|
+
)
|
|
33
|
+
@click.option(
|
|
34
|
+
"--worker",
|
|
35
|
+
default="10",
|
|
36
|
+
is_flag=False,
|
|
37
|
+
type=int,
|
|
38
|
+
help="Number of workers for the grpc server",
|
|
39
|
+
)
|
|
40
|
+
@click.option(
|
|
41
|
+
"--timeout",
|
|
42
|
+
default=None,
|
|
43
|
+
is_flag=False,
|
|
44
|
+
type=int,
|
|
45
|
+
help="It will wait for the specified number of seconds before shutting down grpc server. It should only be used "
|
|
46
|
+
"for testing.",
|
|
47
|
+
)
|
|
48
|
+
@click.option(
|
|
49
|
+
"--modules",
|
|
50
|
+
required=False,
|
|
51
|
+
multiple=True,
|
|
52
|
+
type=str,
|
|
53
|
+
help="List of additional files or module that defines the connector",
|
|
54
|
+
)
|
|
55
|
+
@click.pass_context
|
|
56
|
+
def connector(
|
|
57
|
+
_: click.Context, port: int, prometheus_port: int, worker: int, timeout: int | None, modules: List[str] | None
|
|
58
|
+
):
|
|
59
|
+
"""
|
|
60
|
+
Start a grpc server for the connector service.
|
|
61
|
+
"""
|
|
62
|
+
from flyte.connectors import ConnectorService
|
|
63
|
+
|
|
64
|
+
ConnectorService.run(port, prometheus_port, worker, timeout, modules)
|
flyte/cli/_update.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import rich_click as click
|
|
2
|
+
|
|
3
|
+
import flyte.remote as remote
|
|
4
|
+
|
|
5
|
+
from . import _common as common
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@click.group(name="update")
|
|
9
|
+
def update():
|
|
10
|
+
"""
|
|
11
|
+
Update various flyte entities.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@update.command("trigger", cls=common.CommandBase)
|
|
16
|
+
@click.argument("name", type=str)
|
|
17
|
+
@click.argument("task_name", type=str)
|
|
18
|
+
@click.option("--activate/--deactivate", required=True, help="Activate or deactivate the trigger.")
|
|
19
|
+
@click.pass_obj
|
|
20
|
+
def trigger(cfg: common.CLIConfig, name: str, task_name: str, activate: bool, project: str | None, domain: str | None):
|
|
21
|
+
"""
|
|
22
|
+
Update a trigger.
|
|
23
|
+
|
|
24
|
+
\b
|
|
25
|
+
Example usage:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
flyte update trigger <trigger_name> <task_name> --activate | --deactivate
|
|
29
|
+
[--project <project_name> --domain <domain_name>]
|
|
30
|
+
```
|
|
31
|
+
"""
|
|
32
|
+
cfg.init(project, domain)
|
|
33
|
+
console = common.get_console()
|
|
34
|
+
to_state = "active" if activate else "deactivate"
|
|
35
|
+
with console.status(f"Updating trigger {name} for task {task_name} to {to_state}..."):
|
|
36
|
+
remote.Trigger.update(name, task_name, activate)
|
|
37
|
+
console.print(f"Trigger updated and is set to [fuchsia]{to_state}[/fuchsia]")
|
flyte/cli/_user.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import rich_click as click
|
|
2
|
+
|
|
3
|
+
import flyte.remote as remote
|
|
4
|
+
|
|
5
|
+
from . import _common as common
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@click.command()
|
|
9
|
+
@click.pass_obj
|
|
10
|
+
def whoami(
|
|
11
|
+
cfg: common.CLIConfig,
|
|
12
|
+
):
|
|
13
|
+
"""Display the current user information."""
|
|
14
|
+
cfg.init()
|
|
15
|
+
console = common.get_console()
|
|
16
|
+
user_info = remote.User.get()
|
|
17
|
+
console.print(user_info.to_json())
|
flyte/cli/main.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import rich_click as click
|
|
2
2
|
from typing_extensions import get_args
|
|
3
3
|
|
|
4
|
-
from flyte._logging import initialize_logger, logger
|
|
4
|
+
from flyte._logging import LogFormat, initialize_logger, logger
|
|
5
5
|
|
|
6
6
|
from . import _common as common
|
|
7
7
|
from ._abort import abort
|
|
@@ -12,7 +12,11 @@ from ._delete import delete
|
|
|
12
12
|
from ._deploy import deploy
|
|
13
13
|
from ._gen import gen
|
|
14
14
|
from ._get import get
|
|
15
|
+
from ._plugins import discover_and_register_plugins
|
|
15
16
|
from ._run import run
|
|
17
|
+
from ._serve import serve
|
|
18
|
+
from ._update import update
|
|
19
|
+
from ._user import whoami
|
|
16
20
|
|
|
17
21
|
help_config = click.RichHelpConfiguration(
|
|
18
22
|
use_markdown=True,
|
|
@@ -25,7 +29,7 @@ help_config = click.RichHelpConfiguration(
|
|
|
25
29
|
},
|
|
26
30
|
{
|
|
27
31
|
"name": "Management of various objects.",
|
|
28
|
-
"commands": ["create", "get", "delete"],
|
|
32
|
+
"commands": ["create", "get", "delete", "update"],
|
|
29
33
|
},
|
|
30
34
|
{
|
|
31
35
|
"name": "Build and deploy environments, tasks and images.",
|
|
@@ -35,6 +39,10 @@ help_config = click.RichHelpConfiguration(
|
|
|
35
39
|
"name": "Documentation generation",
|
|
36
40
|
"commands": ["gen"],
|
|
37
41
|
},
|
|
42
|
+
{
|
|
43
|
+
"name": "User information",
|
|
44
|
+
"commands": ["whoami"],
|
|
45
|
+
},
|
|
38
46
|
]
|
|
39
47
|
},
|
|
40
48
|
)
|
|
@@ -116,6 +124,16 @@ def _verbosity_to_loglevel(verbosity: int) -> int | None:
|
|
|
116
124
|
show_default=True,
|
|
117
125
|
required=False,
|
|
118
126
|
)
|
|
127
|
+
@click.option(
|
|
128
|
+
"--log-format",
|
|
129
|
+
type=click.Choice(get_args(LogFormat), case_sensitive=False),
|
|
130
|
+
envvar="LOG_FORMAT",
|
|
131
|
+
default="console",
|
|
132
|
+
help="Formatting for logs, defaults to 'console' which is meant to be human readable."
|
|
133
|
+
" 'json' is meant for machine parsing.",
|
|
134
|
+
show_default=True,
|
|
135
|
+
required=False,
|
|
136
|
+
)
|
|
119
137
|
@click.rich_config(help_config=help_config)
|
|
120
138
|
@click.pass_context
|
|
121
139
|
def main(
|
|
@@ -123,6 +141,7 @@ def main(
|
|
|
123
141
|
endpoint: str | None,
|
|
124
142
|
insecure: bool,
|
|
125
143
|
verbose: int,
|
|
144
|
+
log_format: LogFormat,
|
|
126
145
|
org: str | None,
|
|
127
146
|
config_file: str | None,
|
|
128
147
|
auth_type: str | None = None,
|
|
@@ -166,8 +185,8 @@ def main(
|
|
|
166
185
|
import flyte.config as config
|
|
167
186
|
|
|
168
187
|
log_level = _verbosity_to_loglevel(verbose)
|
|
169
|
-
if log_level is not None:
|
|
170
|
-
initialize_logger(log_level)
|
|
188
|
+
if log_level is not None or log_format != "console":
|
|
189
|
+
initialize_logger(log_level=log_level, log_format=log_format)
|
|
171
190
|
|
|
172
191
|
cfg = config.auto(config_file=config_file)
|
|
173
192
|
if cfg.source:
|
|
@@ -175,6 +194,7 @@ def main(
|
|
|
175
194
|
|
|
176
195
|
ctx.obj = CLIConfig(
|
|
177
196
|
log_level=log_level,
|
|
197
|
+
log_format=log_format,
|
|
178
198
|
endpoint=endpoint,
|
|
179
199
|
insecure=insecure,
|
|
180
200
|
org=org,
|
|
@@ -193,3 +213,9 @@ main.add_command(abort) # type: ignore
|
|
|
193
213
|
main.add_command(gen) # type: ignore
|
|
194
214
|
main.add_command(delete) # type: ignore
|
|
195
215
|
main.add_command(build)
|
|
216
|
+
main.add_command(whoami) # type: ignore
|
|
217
|
+
main.add_command(update) # type: ignore
|
|
218
|
+
main.add_command(serve) # type: ignore
|
|
219
|
+
|
|
220
|
+
# Discover and register CLI plugins from installed packages
|
|
221
|
+
discover_and_register_plugins(main)
|
flyte/config/_config.py
CHANGED
|
@@ -148,6 +148,7 @@ class ImageConfig(object):
|
|
|
148
148
|
"""
|
|
149
149
|
|
|
150
150
|
builder: str | None = None
|
|
151
|
+
image_refs: typing.Dict[str, str] = field(default_factory=dict)
|
|
151
152
|
|
|
152
153
|
@classmethod
|
|
153
154
|
def auto(cls, config_file: typing.Optional[typing.Union[str, ConfigFile]] = None) -> "ImageConfig":
|
|
@@ -159,6 +160,7 @@ class ImageConfig(object):
|
|
|
159
160
|
config_file = get_config_file(config_file)
|
|
160
161
|
kwargs: typing.Dict[str, typing.Any] = {}
|
|
161
162
|
kwargs = set_if_exists(kwargs, "builder", _internal.Image.BUILDER.read(config_file))
|
|
163
|
+
kwargs = set_if_exists(kwargs, "image_refs", _internal.Image.IMAGE_REFS.read(config_file))
|
|
162
164
|
return ImageConfig(**kwargs)
|
|
163
165
|
|
|
164
166
|
|
|
@@ -192,7 +194,7 @@ class Config(object):
|
|
|
192
194
|
)
|
|
193
195
|
|
|
194
196
|
@classmethod
|
|
195
|
-
def auto(cls, config_file: typing.Union[str, ConfigFile, None] = None) -> "Config":
|
|
197
|
+
def auto(cls, config_file: typing.Union[str, pathlib.Path, ConfigFile, None] = None) -> "Config":
|
|
196
198
|
"""
|
|
197
199
|
Automatically constructs the Config Object. The order of precedence is as follows
|
|
198
200
|
1. first try to find any env vars that match the config vars specified in the FLYTE_CONFIG format.
|
|
@@ -225,16 +227,18 @@ def set_if_exists(d: dict, k: str, val: typing.Any) -> dict:
|
|
|
225
227
|
return d
|
|
226
228
|
|
|
227
229
|
|
|
228
|
-
def auto(config_file: typing.Union[str, ConfigFile, None] = None) -> Config:
|
|
230
|
+
def auto(config_file: typing.Union[str, pathlib.Path, ConfigFile, None] = None) -> Config:
|
|
229
231
|
"""
|
|
230
232
|
Automatically constructs the Config Object. The order of precedence is as follows
|
|
231
233
|
1. If specified, read the config from the provided file path.
|
|
232
234
|
2. If not specified, the config file is searched in the default locations.
|
|
233
235
|
a. ./config.yaml if it exists (current working directory)
|
|
234
|
-
b.
|
|
235
|
-
c.
|
|
236
|
-
d.
|
|
237
|
-
e.
|
|
236
|
+
b. ./.flyte/config.yaml if it exists (current working directory)
|
|
237
|
+
c. <git_root>/.flyte/config.yaml if it exists
|
|
238
|
+
d. `UCTL_CONFIG` environment variable
|
|
239
|
+
e. `FLYTECTL_CONFIG` environment variable
|
|
240
|
+
f. ~/.union/config.yaml if it exists
|
|
241
|
+
g. ~/.flyte/config.yaml if it exists
|
|
238
242
|
3. If any value is not found in the config file, the default value is used.
|
|
239
243
|
4. For any value there are environment variables that match the config variable names, those will override
|
|
240
244
|
|
flyte/config/_internal.py
CHANGED
flyte/config/_reader.py
CHANGED
|
@@ -108,7 +108,7 @@ class ConfigFile(object):
|
|
|
108
108
|
return pathlib.Path(self._location)
|
|
109
109
|
|
|
110
110
|
@staticmethod
|
|
111
|
-
def _read_yaml_config(location: str) -> typing.Optional[typing.Dict[str, typing.Any]]:
|
|
111
|
+
def _read_yaml_config(location: str | pathlib.Path) -> typing.Optional[typing.Dict[str, typing.Any]]:
|
|
112
112
|
with open(location, "r") as fh:
|
|
113
113
|
try:
|
|
114
114
|
yaml_contents = yaml.safe_load(fh)
|
|
@@ -135,20 +135,41 @@ class ConfigFile(object):
|
|
|
135
135
|
return self._yaml_config
|
|
136
136
|
|
|
137
137
|
|
|
138
|
+
def _config_path_from_git_root() -> pathlib.Path | None:
|
|
139
|
+
from flyte.git import config_from_root
|
|
140
|
+
|
|
141
|
+
config = config_from_root()
|
|
142
|
+
if config is None:
|
|
143
|
+
return None
|
|
144
|
+
return config.source
|
|
145
|
+
|
|
146
|
+
|
|
138
147
|
def resolve_config_path() -> pathlib.Path | None:
|
|
139
148
|
"""
|
|
140
149
|
Config is read from the following locations in order of precedence:
|
|
141
150
|
1. ./config.yaml if it exists
|
|
142
|
-
2.
|
|
143
|
-
3.
|
|
144
|
-
4.
|
|
145
|
-
5.
|
|
151
|
+
2. ./.flyte/config.yaml if it exists
|
|
152
|
+
3. <git_root>/.flyte/config.yaml if it exists
|
|
153
|
+
4. `UCTL_CONFIG` environment variable
|
|
154
|
+
5. `FLYTECTL_CONFIG` environment variable
|
|
155
|
+
6. ~/.union/config.yaml if it exists
|
|
156
|
+
7. ~/.flyte/config.yaml if it exists
|
|
146
157
|
"""
|
|
147
158
|
current_location_config = Path("config.yaml")
|
|
148
159
|
if current_location_config.exists():
|
|
149
160
|
return current_location_config
|
|
150
161
|
logger.debug("No ./config.yaml found")
|
|
151
162
|
|
|
163
|
+
dot_flyte_config = Path(".flyte", "config.yaml")
|
|
164
|
+
if dot_flyte_config.exists():
|
|
165
|
+
return dot_flyte_config
|
|
166
|
+
logger.debug("No ./.flyte/config.yaml found")
|
|
167
|
+
|
|
168
|
+
git_root_config = _config_path_from_git_root()
|
|
169
|
+
if git_root_config:
|
|
170
|
+
return git_root_config
|
|
171
|
+
logger.debug("No .flyte/config.yaml found in git repo root")
|
|
172
|
+
|
|
152
173
|
uctl_path_from_env = getenv(UCTL_CONFIG_ENV_VAR, None)
|
|
153
174
|
if uctl_path_from_env:
|
|
154
175
|
return pathlib.Path(uctl_path_from_env)
|
|
@@ -173,13 +194,13 @@ def resolve_config_path() -> pathlib.Path | None:
|
|
|
173
194
|
|
|
174
195
|
|
|
175
196
|
@lru_cache
|
|
176
|
-
def get_config_file(c: typing.Union[str, ConfigFile, None]) -> ConfigFile | None:
|
|
197
|
+
def get_config_file(c: typing.Union[str, pathlib.Path, ConfigFile, None]) -> ConfigFile | None:
|
|
177
198
|
"""
|
|
178
199
|
Checks if the given argument is a file or a configFile and returns a loaded configFile else returns None
|
|
179
200
|
"""
|
|
180
|
-
if isinstance(c, str):
|
|
201
|
+
if isinstance(c, (str, pathlib.Path)):
|
|
181
202
|
logger.debug(f"Using specified config file at {c}")
|
|
182
|
-
return ConfigFile(c)
|
|
203
|
+
return ConfigFile(str(c))
|
|
183
204
|
elif isinstance(c, ConfigFile):
|
|
184
205
|
return c
|
|
185
206
|
config_path = resolve_config_path()
|
flyte/connectors/__init__.py
CHANGED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from ._connector import AsyncConnector, AsyncConnectorExecutorMixin, ConnectorRegistry, Resource, ResourceMeta
|
|
2
|
+
from ._server import ConnectorService
|
|
3
|
+
|
|
4
|
+
__all__ = [
|
|
5
|
+
"AsyncConnector",
|
|
6
|
+
"AsyncConnectorExecutorMixin",
|
|
7
|
+
"ConnectorRegistry",
|
|
8
|
+
"ConnectorService",
|
|
9
|
+
"Resource",
|
|
10
|
+
"ResourceMeta",
|
|
11
|
+
]
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
import typing
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
from dataclasses import asdict, dataclass
|
|
6
|
+
from typing import Any, Dict, List, Optional
|
|
7
|
+
|
|
8
|
+
from flyteidl2.core import tasks_pb2
|
|
9
|
+
from flyteidl2.core.execution_pb2 import TaskExecution, TaskLog
|
|
10
|
+
from flyteidl2.plugins import connector_pb2
|
|
11
|
+
from flyteidl2.plugins.connector_pb2 import Connector as ConnectorProto
|
|
12
|
+
from flyteidl2.plugins.connector_pb2 import (
|
|
13
|
+
GetTaskLogsResponse,
|
|
14
|
+
GetTaskMetricsResponse,
|
|
15
|
+
TaskCategory,
|
|
16
|
+
TaskExecutionMetadata,
|
|
17
|
+
)
|
|
18
|
+
from google.protobuf import json_format
|
|
19
|
+
from google.protobuf.struct_pb2 import Struct
|
|
20
|
+
|
|
21
|
+
from flyte import Secret
|
|
22
|
+
from flyte._context import internal_ctx
|
|
23
|
+
from flyte._initialize import get_init_config
|
|
24
|
+
from flyte._internal.runtime.convert import convert_from_native_to_outputs
|
|
25
|
+
from flyte._internal.runtime.task_serde import get_proto_task
|
|
26
|
+
from flyte._logging import logger
|
|
27
|
+
from flyte._task import TaskTemplate
|
|
28
|
+
from flyte.connectors.utils import is_terminal_phase
|
|
29
|
+
from flyte.models import NativeInterface, SerializationContext
|
|
30
|
+
from flyte.types._type_engine import dataclass_from_dict
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(frozen=True)
|
|
34
|
+
class ConnectorRegistryKey:
|
|
35
|
+
task_type_name: str
|
|
36
|
+
task_type_version: int
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class ResourceMeta:
|
|
41
|
+
"""
|
|
42
|
+
This is the metadata for the job. For example, the id of the job.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def encode(self) -> bytes:
|
|
46
|
+
"""
|
|
47
|
+
Encode the resource meta to bytes.
|
|
48
|
+
"""
|
|
49
|
+
return json.dumps(asdict(self)).encode("utf-8")
|
|
50
|
+
|
|
51
|
+
@classmethod
|
|
52
|
+
def decode(cls, data: bytes) -> "ResourceMeta":
|
|
53
|
+
"""
|
|
54
|
+
Decode the resource meta from bytes.
|
|
55
|
+
"""
|
|
56
|
+
return dataclass_from_dict(cls, json.loads(data.decode("utf-8")))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dataclass
|
|
60
|
+
class Resource:
|
|
61
|
+
"""
|
|
62
|
+
This is the output resource of the job.
|
|
63
|
+
|
|
64
|
+
Attributes
|
|
65
|
+
----------
|
|
66
|
+
phase : TaskExecution.Phase
|
|
67
|
+
The phase of the job.
|
|
68
|
+
message : Optional[str]
|
|
69
|
+
The return message from the job.
|
|
70
|
+
log_links : Optional[List[TaskLog]]
|
|
71
|
+
The log links of the job. For example, the link to the BigQuery Console.
|
|
72
|
+
outputs : Optional[Union[LiteralMap, typing.Dict[str, Any]]]
|
|
73
|
+
The outputs of the job. If return python native types, the agent will convert them to flyte literals.
|
|
74
|
+
custom_info : Optional[typing.Dict[str, Any]]
|
|
75
|
+
The custom info of the job. For example, the job config.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
phase: TaskExecution.Phase
|
|
79
|
+
message: Optional[str] = None
|
|
80
|
+
log_links: Optional[List[TaskLog]] = None
|
|
81
|
+
outputs: Optional[Dict[str, Any]] = None
|
|
82
|
+
custom_info: Optional[typing.Dict[str, Any]] = None
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class AsyncConnector(ABC):
|
|
86
|
+
"""
|
|
87
|
+
This is the base class for all async connectors, and it defines the interface that all connectors must implement.
|
|
88
|
+
The connector service is responsible for invoking connectors.
|
|
89
|
+
The executor will communicate with the connector service to create tasks, get the status of tasks, and delete tasks.
|
|
90
|
+
|
|
91
|
+
All the connectors should be registered in the ConnectorRegistry.
|
|
92
|
+
Connector Service will look up the connector based on the task type and version.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
name = "Async Connector"
|
|
96
|
+
task_type_name: str
|
|
97
|
+
task_type_version: int = 0
|
|
98
|
+
metadata_type: ResourceMeta
|
|
99
|
+
|
|
100
|
+
@abstractmethod
|
|
101
|
+
async def create(
|
|
102
|
+
self,
|
|
103
|
+
task_template: tasks_pb2.TaskTemplate,
|
|
104
|
+
output_prefix: str,
|
|
105
|
+
inputs: Optional[Dict[str, typing.Any]] = None,
|
|
106
|
+
task_execution_metadata: Optional[TaskExecutionMetadata] = None,
|
|
107
|
+
**kwargs,
|
|
108
|
+
) -> ResourceMeta:
|
|
109
|
+
"""
|
|
110
|
+
Return a resource meta that can be used to get the status of the task.
|
|
111
|
+
"""
|
|
112
|
+
raise NotImplementedError
|
|
113
|
+
|
|
114
|
+
@abstractmethod
|
|
115
|
+
async def get(self, resource_meta: ResourceMeta, **kwargs) -> Resource:
|
|
116
|
+
"""
|
|
117
|
+
Return the status of the task, and return the outputs in some cases. For example, bigquery job
|
|
118
|
+
can't write the structured dataset to the output location, so it returns the output literals to the propeller,
|
|
119
|
+
and the propeller will write the structured dataset to the blob store.
|
|
120
|
+
"""
|
|
121
|
+
raise NotImplementedError
|
|
122
|
+
|
|
123
|
+
@abstractmethod
|
|
124
|
+
async def delete(self, resource_meta: ResourceMeta, **kwargs):
|
|
125
|
+
"""
|
|
126
|
+
Delete the task. This call should be idempotent. It should raise an error if fails to delete the task.
|
|
127
|
+
"""
|
|
128
|
+
raise NotImplementedError
|
|
129
|
+
|
|
130
|
+
async def get_metrics(self, resource_meta: ResourceMeta, **kwargs) -> GetTaskMetricsResponse:
|
|
131
|
+
"""
|
|
132
|
+
Return the metrics for the task.
|
|
133
|
+
"""
|
|
134
|
+
raise NotImplementedError
|
|
135
|
+
|
|
136
|
+
async def get_logs(self, resource_meta: ResourceMeta, **kwargs) -> GetTaskLogsResponse:
|
|
137
|
+
"""
|
|
138
|
+
Return the metrics for the task.
|
|
139
|
+
"""
|
|
140
|
+
raise NotImplementedError
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class ConnectorRegistry(object):
|
|
144
|
+
"""
|
|
145
|
+
This is the registry for all connectors.
|
|
146
|
+
The connector service will look up the connector registry based on the task type and version.
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
_REGISTRY: typing.ClassVar[Dict[ConnectorRegistryKey, AsyncConnector]] = {}
|
|
150
|
+
_METADATA: typing.ClassVar[Dict[str, ConnectorProto]] = {}
|
|
151
|
+
|
|
152
|
+
@staticmethod
|
|
153
|
+
def register(connector: AsyncConnector, override: bool = False):
|
|
154
|
+
key = ConnectorRegistryKey(
|
|
155
|
+
task_type_name=connector.task_type_name, task_type_version=connector.task_type_version
|
|
156
|
+
)
|
|
157
|
+
if key in ConnectorRegistry._REGISTRY and override is False:
|
|
158
|
+
raise ValueError(
|
|
159
|
+
f"Duplicate connector for task type: {connector.task_type_name}"
|
|
160
|
+
f" and version: {connector.task_type_version}"
|
|
161
|
+
)
|
|
162
|
+
ConnectorRegistry._REGISTRY[key] = connector
|
|
163
|
+
|
|
164
|
+
task_category = TaskCategory(name=connector.task_type_name, version=connector.task_type_version)
|
|
165
|
+
|
|
166
|
+
if connector.name in ConnectorRegistry._METADATA:
|
|
167
|
+
connector_metadata = ConnectorRegistry._get_connector_metadata(connector.name)
|
|
168
|
+
connector_metadata.supported_task_categories.append(task_category)
|
|
169
|
+
else:
|
|
170
|
+
connector_metadata = ConnectorProto(
|
|
171
|
+
name=connector.name,
|
|
172
|
+
supported_task_categories=[task_category],
|
|
173
|
+
)
|
|
174
|
+
ConnectorRegistry._METADATA[connector.name] = connector_metadata
|
|
175
|
+
|
|
176
|
+
@staticmethod
|
|
177
|
+
def get_connector(task_type_name: str, task_type_version: int = 0) -> AsyncConnector:
|
|
178
|
+
key = ConnectorRegistryKey(task_type_name=task_type_name, task_type_version=task_type_version)
|
|
179
|
+
if key not in ConnectorRegistry._REGISTRY:
|
|
180
|
+
raise FlyteConnectorNotFound(
|
|
181
|
+
f"Cannot find connector for task type: {task_type_name} and version: {task_type_version}"
|
|
182
|
+
)
|
|
183
|
+
return ConnectorRegistry._REGISTRY[key]
|
|
184
|
+
|
|
185
|
+
@staticmethod
|
|
186
|
+
def _list_connectors() -> List[ConnectorProto]:
|
|
187
|
+
return list(ConnectorRegistry._METADATA.values())
|
|
188
|
+
|
|
189
|
+
@staticmethod
|
|
190
|
+
def _get_connector_metadata(name: str) -> ConnectorProto:
|
|
191
|
+
if name not in ConnectorRegistry._METADATA:
|
|
192
|
+
raise FlyteConnectorNotFound(f"Cannot find connector for name: {name}.")
|
|
193
|
+
return ConnectorRegistry._METADATA[name]
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class ConnectorSecretsMixin:
|
|
197
|
+
def __init__(self, secrets: Dict[str, str]):
|
|
198
|
+
# Key is the id of the secret, value is the secret name.
|
|
199
|
+
self._secrets = secrets
|
|
200
|
+
|
|
201
|
+
@property
|
|
202
|
+
def secrets(self) -> List[Secret]:
|
|
203
|
+
return [Secret(key=k, as_env_var=v) for k, v in self._secrets.items()]
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class AsyncConnectorExecutorMixin:
|
|
207
|
+
"""
|
|
208
|
+
This mixin class is used to run the connector task locally, and it's only used for local execution.
|
|
209
|
+
Task should inherit from this class if the task can be run in the connector.
|
|
210
|
+
"""
|
|
211
|
+
|
|
212
|
+
async def execute(self, **kwargs) -> Any:
|
|
213
|
+
task = typing.cast(TaskTemplate, self)
|
|
214
|
+
connector = ConnectorRegistry.get_connector(task.task_type, task.task_type_version)
|
|
215
|
+
|
|
216
|
+
ctx = internal_ctx()
|
|
217
|
+
tctx = internal_ctx().data.task_context
|
|
218
|
+
cfg = get_init_config()
|
|
219
|
+
|
|
220
|
+
if tctx is None:
|
|
221
|
+
raise RuntimeError("Task context is not set.")
|
|
222
|
+
|
|
223
|
+
sc = SerializationContext(
|
|
224
|
+
project=tctx.action.project,
|
|
225
|
+
domain=tctx.action.domain,
|
|
226
|
+
org=tctx.action.org,
|
|
227
|
+
code_bundle=tctx.code_bundle,
|
|
228
|
+
version=tctx.version,
|
|
229
|
+
image_cache=tctx.compiled_image_cache,
|
|
230
|
+
root_dir=cfg.root_dir,
|
|
231
|
+
)
|
|
232
|
+
tt = get_proto_task(task, sc)
|
|
233
|
+
resource_meta = await connector.create(task_template=tt, output_prefix=ctx.raw_data.path, inputs=kwargs)
|
|
234
|
+
resource = Resource(phase=TaskExecution.RUNNING)
|
|
235
|
+
|
|
236
|
+
while not is_terminal_phase(resource.phase):
|
|
237
|
+
resource = await connector.get(resource_meta=resource_meta)
|
|
238
|
+
|
|
239
|
+
if resource.log_links:
|
|
240
|
+
for link in resource.log_links:
|
|
241
|
+
logger.info(f"{link.name}: {link.uri}")
|
|
242
|
+
await asyncio.sleep(1)
|
|
243
|
+
|
|
244
|
+
if resource.phase != TaskExecution.SUCCEEDED:
|
|
245
|
+
raise RuntimeError(f"Failed to run the task {task.name} with error: {resource.message}")
|
|
246
|
+
|
|
247
|
+
# TODO: Support abort
|
|
248
|
+
|
|
249
|
+
if resource.outputs is None:
|
|
250
|
+
return None
|
|
251
|
+
return tuple(resource.outputs.values())
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
async def get_resource_proto(resource: Resource) -> connector_pb2.Resource:
|
|
255
|
+
if resource.outputs:
|
|
256
|
+
interface = NativeInterface.from_types(inputs={}, outputs={k: type(v) for k, v in resource.outputs.items()})
|
|
257
|
+
outputs = await convert_from_native_to_outputs(tuple(resource.outputs.values()), interface)
|
|
258
|
+
else:
|
|
259
|
+
outputs = None
|
|
260
|
+
|
|
261
|
+
return connector_pb2.Resource(
|
|
262
|
+
phase=resource.phase,
|
|
263
|
+
message=resource.message,
|
|
264
|
+
log_links=resource.log_links,
|
|
265
|
+
outputs=outputs,
|
|
266
|
+
custom_info=(json_format.Parse(json.dumps(resource.custom_info), Struct()) if resource.custom_info else None),
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
class FlyteConnectorNotFound(ValueError): ...
|