flyte 2.0.0b25__py3-none-any.whl → 2.0.0b28__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 +2 -0
- flyte/_bin/runtime.py +8 -0
- flyte/_code_bundle/_utils.py +4 -4
- flyte/_code_bundle/bundle.py +1 -1
- flyte/_constants.py +1 -0
- flyte/_deploy.py +0 -1
- flyte/_excepthook.py +1 -1
- flyte/_initialize.py +10 -0
- flyte/_interface.py +2 -0
- flyte/_internal/imagebuild/docker_builder.py +3 -1
- flyte/_internal/imagebuild/remote_builder.py +3 -1
- flyte/_internal/resolvers/_task_module.py +4 -37
- flyte/_internal/runtime/convert.py +3 -2
- flyte/_internal/runtime/entrypoints.py +24 -1
- flyte/_internal/runtime/rusty.py +3 -3
- flyte/_internal/runtime/task_serde.py +19 -4
- flyte/_internal/runtime/trigger_serde.py +2 -2
- flyte/_map.py +2 -35
- flyte/_module.py +68 -0
- flyte/_resources.py +38 -0
- flyte/_run.py +23 -6
- flyte/_task.py +1 -2
- flyte/_task_plugins.py +4 -2
- flyte/_trigger.py +623 -5
- flyte/_utils/__init__.py +2 -1
- flyte/_utils/asyn.py +3 -1
- flyte/_utils/docker_credentials.py +173 -0
- flyte/_utils/module_loader.py +15 -0
- flyte/_version.py +3 -3
- flyte/cli/_common.py +15 -3
- flyte/cli/_create.py +100 -3
- flyte/cli/_deploy.py +38 -4
- flyte/cli/_plugins.py +208 -0
- flyte/cli/_run.py +69 -6
- flyte/cli/_serve.py +154 -0
- flyte/cli/main.py +6 -0
- flyte/connectors/__init__.py +3 -0
- flyte/connectors/_connector.py +270 -0
- flyte/connectors/_server.py +183 -0
- flyte/connectors/utils.py +26 -0
- flyte/models.py +13 -4
- flyte/remote/_client/auth/_channel.py +9 -5
- flyte/remote/_console.py +3 -2
- flyte/remote/_secret.py +6 -4
- flyte/remote/_trigger.py +2 -2
- flyte/types/_type_engine.py +1 -2
- {flyte-2.0.0b25.data → flyte-2.0.0b28.data}/scripts/runtime.py +8 -0
- {flyte-2.0.0b25.dist-info → flyte-2.0.0b28.dist-info}/METADATA +6 -2
- {flyte-2.0.0b25.dist-info → flyte-2.0.0b28.dist-info}/RECORD +54 -46
- {flyte-2.0.0b25.data → flyte-2.0.0b28.data}/scripts/debug.py +0 -0
- {flyte-2.0.0b25.dist-info → flyte-2.0.0b28.dist-info}/WHEEL +0 -0
- {flyte-2.0.0b25.dist-info → flyte-2.0.0b28.dist-info}/entry_points.txt +0 -0
- {flyte-2.0.0b25.dist-info → flyte-2.0.0b28.dist-info}/licenses/LICENSE +0 -0
- {flyte-2.0.0b25.dist-info → flyte-2.0.0b28.dist-info}/top_level.txt +0 -0
flyte/cli/_plugins.py
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"""CLI Plugin System for Flyte.
|
|
2
|
+
|
|
3
|
+
This module provides a plugin system that allows external packages to:
|
|
4
|
+
1. Register new top-level CLI commands (e.g., flyte my-command)
|
|
5
|
+
2. Register new subcommands in existing groups (e.g., flyte get my-object)
|
|
6
|
+
3. Modify behavior of existing commands via hooks
|
|
7
|
+
|
|
8
|
+
Plugins are discovered via Python entry points.
|
|
9
|
+
|
|
10
|
+
Entry Point Groups:
|
|
11
|
+
- flyte.plugins.cli.commands: Register new commands
|
|
12
|
+
- Entry point name "foo" -> flyte foo (top-level command)
|
|
13
|
+
- Entry point name "get.bar" -> flyte get bar (adds subcommand to get group)
|
|
14
|
+
- Note: At most one dot is supported. For nested groups, register the entire
|
|
15
|
+
group hierarchy as a top-level command (without dots).
|
|
16
|
+
|
|
17
|
+
- flyte.plugins.cli.hooks: Modify existing commands
|
|
18
|
+
- Entry point name "run" -> modifies flyte run
|
|
19
|
+
- Entry point name "get.project" -> modifies flyte get project
|
|
20
|
+
- Note: At most one dot is supported.
|
|
21
|
+
|
|
22
|
+
Example Plugin Package:
|
|
23
|
+
# In your-plugin/pyproject.toml
|
|
24
|
+
[project.entry-points."flyte.plugins.cli.commands"]
|
|
25
|
+
my-command = "your_plugin.cli:my_command"
|
|
26
|
+
get.my-object = "your_plugin.cli:get_my_object"
|
|
27
|
+
|
|
28
|
+
[project.entry-points."flyte.plugins.cli.hooks"]
|
|
29
|
+
run = "your_plugin.hooks:modify_run"
|
|
30
|
+
|
|
31
|
+
# In your-plugin/your_plugin/cli.py
|
|
32
|
+
import rich_click as click
|
|
33
|
+
|
|
34
|
+
@click.command()
|
|
35
|
+
def my_command():
|
|
36
|
+
'''My custom top-level command.'''
|
|
37
|
+
click.echo("Hello from plugin!")
|
|
38
|
+
|
|
39
|
+
@click.command()
|
|
40
|
+
def get_my_object():
|
|
41
|
+
'''Get my custom object.'''
|
|
42
|
+
click.echo("Getting my object...")
|
|
43
|
+
|
|
44
|
+
# In your-plugin/your_plugin/hooks.py
|
|
45
|
+
def modify_run(command):
|
|
46
|
+
'''Add behavior to flyte run command.'''
|
|
47
|
+
# Wrap invoke() instead of callback to ensure Click's full machinery runs
|
|
48
|
+
original_invoke = command.invoke
|
|
49
|
+
|
|
50
|
+
def wrapper(ctx):
|
|
51
|
+
# Do something before
|
|
52
|
+
click.echo("Plugin: Starting task...")
|
|
53
|
+
|
|
54
|
+
result = original_invoke(ctx)
|
|
55
|
+
|
|
56
|
+
# Do something after
|
|
57
|
+
click.echo("Plugin: Task completed!")
|
|
58
|
+
return result
|
|
59
|
+
|
|
60
|
+
command.invoke = wrapper
|
|
61
|
+
return command
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
from importlib.metadata import entry_points
|
|
65
|
+
from typing import Callable
|
|
66
|
+
|
|
67
|
+
import rich_click as click
|
|
68
|
+
|
|
69
|
+
from flyte._logging import logger
|
|
70
|
+
|
|
71
|
+
# Type alias for command hooks
|
|
72
|
+
CommandHook = Callable[[click.Command], click.Command]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def discover_and_register_plugins(root_group: click.Group):
|
|
76
|
+
"""
|
|
77
|
+
Discover all CLI plugins from installed packages and register them.
|
|
78
|
+
|
|
79
|
+
This function:
|
|
80
|
+
1. Discovers command plugins and adds them to the CLI
|
|
81
|
+
2. Discovers hook plugins and applies them to existing commands
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
root_group: The root Click command group (main CLI group)
|
|
85
|
+
"""
|
|
86
|
+
_load_command_plugins(root_group)
|
|
87
|
+
_load_hook_plugins(root_group)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _load_command_plugins(root_group: click.Group):
|
|
91
|
+
"""Load and register command plugins."""
|
|
92
|
+
for ep in entry_points(group="flyte.plugins.cli.commands"):
|
|
93
|
+
try:
|
|
94
|
+
command = ep.load()
|
|
95
|
+
if not isinstance(command, click.Command):
|
|
96
|
+
logger.warning(f"Plugin {ep.name} did not return a click.Command, got {type(command).__name__}")
|
|
97
|
+
continue
|
|
98
|
+
|
|
99
|
+
# Check if this is a subcommand (contains dot notation)
|
|
100
|
+
if "." in ep.name:
|
|
101
|
+
group_name, command_name = ep.name.split(".", 1)
|
|
102
|
+
|
|
103
|
+
# Validate: only support one level of nesting (group.command)
|
|
104
|
+
if "." in command_name:
|
|
105
|
+
logger.error(
|
|
106
|
+
f"Plugin {ep.name} uses multiple dots, which is not supported. "
|
|
107
|
+
f"Use at most one dot (e.g., 'group.command'). "
|
|
108
|
+
f"For nested groups, register the entire group hierarchy as a top-level command."
|
|
109
|
+
)
|
|
110
|
+
continue
|
|
111
|
+
|
|
112
|
+
_add_subcommand_to_group(root_group, group_name, command_name, command)
|
|
113
|
+
else:
|
|
114
|
+
# Top-level command
|
|
115
|
+
root_group.add_command(command, name=ep.name)
|
|
116
|
+
logger.info(f"Registered plugin command: flyte {ep.name}")
|
|
117
|
+
|
|
118
|
+
except Exception as e:
|
|
119
|
+
logger.error(f"Failed to load plugin command {ep.name}: {e}")
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _load_hook_plugins(root_group: click.Group):
|
|
123
|
+
"""Load and apply hook plugins to existing commands."""
|
|
124
|
+
for ep in entry_points(group="flyte.plugins.cli.hooks"):
|
|
125
|
+
try:
|
|
126
|
+
hook = ep.load()
|
|
127
|
+
if not callable(hook):
|
|
128
|
+
logger.warning(f"Plugin hook {ep.name} is not callable")
|
|
129
|
+
continue
|
|
130
|
+
|
|
131
|
+
# Check if this is a subcommand hook (contains dot notation)
|
|
132
|
+
if "." in ep.name:
|
|
133
|
+
group_name, command_name = ep.name.split(".", 1)
|
|
134
|
+
|
|
135
|
+
# Validate: only support one level of nesting (group.command)
|
|
136
|
+
if "." in command_name:
|
|
137
|
+
logger.error(
|
|
138
|
+
f"Plugin hook {ep.name} uses multiple dots, which is not supported. "
|
|
139
|
+
f"Use at most one dot (e.g., 'group.command')."
|
|
140
|
+
)
|
|
141
|
+
continue
|
|
142
|
+
|
|
143
|
+
_apply_hook_to_subcommand(root_group, group_name, command_name, hook)
|
|
144
|
+
else:
|
|
145
|
+
# Top-level command hook
|
|
146
|
+
_apply_hook_to_command(root_group, ep.name, hook)
|
|
147
|
+
|
|
148
|
+
except Exception as e:
|
|
149
|
+
logger.error(f"Failed to apply hook {ep.name}: {e}")
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _add_subcommand_to_group(root_group: click.Group, group_name: str, command_name: str, command: click.Command):
|
|
153
|
+
"""Add a subcommand to an existing command group."""
|
|
154
|
+
if group_name not in root_group.commands:
|
|
155
|
+
logger.warning(f"Cannot add plugin subcommand '{command_name}' - group '{group_name}' does not exist")
|
|
156
|
+
return
|
|
157
|
+
|
|
158
|
+
group = root_group.commands[group_name]
|
|
159
|
+
if not isinstance(group, click.Group):
|
|
160
|
+
logger.warning(f"Cannot add plugin subcommand '{command_name}' - '{group_name}' is not a command group")
|
|
161
|
+
return
|
|
162
|
+
|
|
163
|
+
group.add_command(command, name=command_name)
|
|
164
|
+
# lower to debug later
|
|
165
|
+
logger.info(f"Registered plugin subcommand: flyte {group_name} {command_name}")
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _apply_hook_to_command(root_group: click.Group, command_name: str, hook: CommandHook):
|
|
169
|
+
"""Apply a hook to a top-level command."""
|
|
170
|
+
if command_name not in root_group.commands:
|
|
171
|
+
logger.warning(f"Cannot apply hook - command '{command_name}' does not exist")
|
|
172
|
+
return
|
|
173
|
+
|
|
174
|
+
original_command = root_group.commands[command_name]
|
|
175
|
+
try:
|
|
176
|
+
modified_command = hook(original_command)
|
|
177
|
+
root_group.commands[command_name] = modified_command
|
|
178
|
+
# lower to debug later
|
|
179
|
+
logger.info(f"Applied hook to command: flyte {command_name}")
|
|
180
|
+
except Exception as e:
|
|
181
|
+
logger.error(f"Hook failed for command {command_name}: {e}")
|
|
182
|
+
root_group.commands[command_name] = original_command
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _apply_hook_to_subcommand(root_group: click.Group, group_name: str, command_name: str, hook: CommandHook):
|
|
186
|
+
"""Apply a hook to a subcommand within a group."""
|
|
187
|
+
if group_name not in root_group.commands:
|
|
188
|
+
logger.warning(f"Cannot apply hook - group '{group_name}' does not exist")
|
|
189
|
+
return
|
|
190
|
+
|
|
191
|
+
group = root_group.commands[group_name]
|
|
192
|
+
if not isinstance(group, click.Group):
|
|
193
|
+
logger.warning(f"Cannot apply hook - '{group_name}' is not a command group")
|
|
194
|
+
return
|
|
195
|
+
|
|
196
|
+
if command_name not in group.commands:
|
|
197
|
+
logger.warning(f"Cannot apply hook - subcommand '{command_name}' does not exist in group '{group_name}'")
|
|
198
|
+
return
|
|
199
|
+
|
|
200
|
+
original_command = group.commands[command_name]
|
|
201
|
+
original_command.callback
|
|
202
|
+
try:
|
|
203
|
+
modified_command = hook(original_command)
|
|
204
|
+
group.commands[command_name] = modified_command
|
|
205
|
+
logger.info(f"Applied hook to subcommand: flyte {group_name} {command_name}")
|
|
206
|
+
except Exception as e:
|
|
207
|
+
logger.error(f"Hook failed for subcommand {group_name}.{command_name}: {e}")
|
|
208
|
+
group.commands[command_name] = original_command
|
flyte/cli/_run.py
CHANGED
|
@@ -86,6 +86,26 @@ class RunArguments:
|
|
|
86
86
|
)
|
|
87
87
|
},
|
|
88
88
|
)
|
|
89
|
+
raw_data_path: str | None = field(
|
|
90
|
+
default=None,
|
|
91
|
+
metadata={
|
|
92
|
+
"click.option": click.Option(
|
|
93
|
+
["--raw-data-path"],
|
|
94
|
+
type=str,
|
|
95
|
+
help="Override the output prefix used to store offloaded data types. e.g. s3://bucket/",
|
|
96
|
+
)
|
|
97
|
+
},
|
|
98
|
+
)
|
|
99
|
+
service_account: str | None = field(
|
|
100
|
+
default=None,
|
|
101
|
+
metadata={
|
|
102
|
+
"click.option": click.Option(
|
|
103
|
+
["--service-account"],
|
|
104
|
+
type=str,
|
|
105
|
+
help="Kubernetes service account. If not provided, the configured default will be used",
|
|
106
|
+
)
|
|
107
|
+
},
|
|
108
|
+
)
|
|
89
109
|
name: str | None = field(
|
|
90
110
|
default=None,
|
|
91
111
|
metadata={
|
|
@@ -119,10 +139,24 @@ class RunArguments:
|
|
|
119
139
|
)
|
|
120
140
|
},
|
|
121
141
|
)
|
|
142
|
+
no_sync_local_sys_paths: bool = field(
|
|
143
|
+
default=True,
|
|
144
|
+
metadata={
|
|
145
|
+
"click.option": click.Option(
|
|
146
|
+
["--no-sync-local-sys-paths"],
|
|
147
|
+
is_flag=True,
|
|
148
|
+
flag_value=True,
|
|
149
|
+
default=False,
|
|
150
|
+
help="Disable synchronization of local sys.path entries under the root directory "
|
|
151
|
+
"to the remote container.",
|
|
152
|
+
)
|
|
153
|
+
},
|
|
154
|
+
)
|
|
122
155
|
|
|
123
156
|
@classmethod
|
|
124
157
|
def from_dict(cls, d: Dict[str, Any]) -> RunArguments:
|
|
125
|
-
|
|
158
|
+
modified = {k: v for k, v in d.items() if k in {f.name for f in fields(cls)}}
|
|
159
|
+
return cls(**modified)
|
|
126
160
|
|
|
127
161
|
@classmethod
|
|
128
162
|
def options(cls) -> List[click.Option]:
|
|
@@ -142,7 +176,12 @@ class RunTaskCommand(click.RichCommand):
|
|
|
142
176
|
|
|
143
177
|
def invoke(self, ctx: click.Context):
|
|
144
178
|
obj: CLIConfig = initialize_config(
|
|
145
|
-
ctx,
|
|
179
|
+
ctx,
|
|
180
|
+
self.run_args.project,
|
|
181
|
+
self.run_args.domain,
|
|
182
|
+
self.run_args.root_dir,
|
|
183
|
+
tuple(self.run_args.image) or None,
|
|
184
|
+
not self.run_args.no_sync_local_sys_paths,
|
|
146
185
|
)
|
|
147
186
|
|
|
148
187
|
async def _run():
|
|
@@ -153,6 +192,8 @@ class RunTaskCommand(click.RichCommand):
|
|
|
153
192
|
copy_style=self.run_args.copy_style,
|
|
154
193
|
mode="local" if self.run_args.local else "remote",
|
|
155
194
|
name=self.run_args.name,
|
|
195
|
+
raw_data_path=self.run_args.raw_data_path,
|
|
196
|
+
service_account=self.run_args.service_account,
|
|
156
197
|
).run.aio(self.obj, **ctx.params)
|
|
157
198
|
if self.run_args.local:
|
|
158
199
|
console.print(
|
|
@@ -219,11 +260,23 @@ class TaskPerFileGroup(common.ObjectsPerFileGroup):
|
|
|
219
260
|
return {k: v for k, v in module.__dict__.items() if isinstance(v, TaskTemplate)}
|
|
220
261
|
|
|
221
262
|
def list_commands(self, ctx):
|
|
222
|
-
common.initialize_config(
|
|
263
|
+
common.initialize_config(
|
|
264
|
+
ctx,
|
|
265
|
+
self.run_args.project,
|
|
266
|
+
self.run_args.domain,
|
|
267
|
+
self.run_args.root_dir,
|
|
268
|
+
sync_local_sys_paths=not self.run_args.no_sync_local_sys_paths,
|
|
269
|
+
)
|
|
223
270
|
return super().list_commands(ctx)
|
|
224
271
|
|
|
225
272
|
def get_command(self, ctx, obj_name):
|
|
226
|
-
common.initialize_config(
|
|
273
|
+
common.initialize_config(
|
|
274
|
+
ctx,
|
|
275
|
+
self.run_args.project,
|
|
276
|
+
self.run_args.domain,
|
|
277
|
+
self.run_args.root_dir,
|
|
278
|
+
sync_local_sys_paths=not self.run_args.no_sync_local_sys_paths,
|
|
279
|
+
)
|
|
227
280
|
return super().get_command(ctx, obj_name)
|
|
228
281
|
|
|
229
282
|
def _get_command_for_obj(self, ctx: click.Context, obj_name: str, obj: Any) -> click.Command:
|
|
@@ -246,7 +299,12 @@ class RunReferenceTaskCommand(click.RichCommand):
|
|
|
246
299
|
|
|
247
300
|
def invoke(self, ctx: click.Context):
|
|
248
301
|
obj: CLIConfig = common.initialize_config(
|
|
249
|
-
ctx,
|
|
302
|
+
ctx,
|
|
303
|
+
self.run_args.project,
|
|
304
|
+
self.run_args.domain,
|
|
305
|
+
self.run_args.root_dir,
|
|
306
|
+
tuple(self.run_args.image) or None,
|
|
307
|
+
not self.run_args.no_sync_local_sys_paths,
|
|
250
308
|
)
|
|
251
309
|
|
|
252
310
|
async def _run():
|
|
@@ -284,7 +342,12 @@ class RunReferenceTaskCommand(click.RichCommand):
|
|
|
284
342
|
import flyte.remote
|
|
285
343
|
from flyte._internal.runtime.types_serde import transform_native_to_typed_interface
|
|
286
344
|
|
|
287
|
-
common.initialize_config(
|
|
345
|
+
common.initialize_config(
|
|
346
|
+
ctx,
|
|
347
|
+
self.run_args.project,
|
|
348
|
+
self.run_args.domain,
|
|
349
|
+
sync_local_sys_paths=not self.run_args.no_sync_local_sys_paths,
|
|
350
|
+
)
|
|
288
351
|
|
|
289
352
|
task = flyte.remote.Task.get(self.task_name, auto_version="latest")
|
|
290
353
|
task_details = task.fetch()
|
flyte/cli/_serve.py
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
from concurrent import futures
|
|
5
|
+
|
|
6
|
+
import grpc
|
|
7
|
+
import rich_click as click
|
|
8
|
+
from flyteidl2.service import connector_pb2
|
|
9
|
+
from flyteidl2.service.connector_pb2_grpc import (
|
|
10
|
+
add_AsyncConnectorServiceServicer_to_server,
|
|
11
|
+
add_ConnectorMetadataServiceServicer_to_server,
|
|
12
|
+
)
|
|
13
|
+
from rich.console import Console
|
|
14
|
+
from rich.table import Table
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@click.group("serve")
|
|
18
|
+
@click.pass_context
|
|
19
|
+
def serve(_: click.Context):
|
|
20
|
+
"""
|
|
21
|
+
Start the specific service. For example:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
flyte serve connector
|
|
25
|
+
```
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@serve.command()
|
|
30
|
+
@click.option(
|
|
31
|
+
"--port",
|
|
32
|
+
default="8000",
|
|
33
|
+
is_flag=False,
|
|
34
|
+
type=int,
|
|
35
|
+
help="Grpc port for the connector service",
|
|
36
|
+
)
|
|
37
|
+
@click.option(
|
|
38
|
+
"--prometheus_port",
|
|
39
|
+
default="9090",
|
|
40
|
+
is_flag=False,
|
|
41
|
+
type=int,
|
|
42
|
+
help="Prometheus port for the connector service",
|
|
43
|
+
)
|
|
44
|
+
@click.option(
|
|
45
|
+
"--worker",
|
|
46
|
+
default="10",
|
|
47
|
+
is_flag=False,
|
|
48
|
+
type=int,
|
|
49
|
+
help="Number of workers for the grpc server",
|
|
50
|
+
)
|
|
51
|
+
@click.option(
|
|
52
|
+
"--timeout",
|
|
53
|
+
default=None,
|
|
54
|
+
is_flag=False,
|
|
55
|
+
type=int,
|
|
56
|
+
help="It will wait for the specified number of seconds before shutting down grpc server. It should only be used "
|
|
57
|
+
"for testing.",
|
|
58
|
+
)
|
|
59
|
+
@click.option(
|
|
60
|
+
"--modules",
|
|
61
|
+
required=False,
|
|
62
|
+
multiple=True,
|
|
63
|
+
type=str,
|
|
64
|
+
help="List of additional files or module that defines the connector",
|
|
65
|
+
)
|
|
66
|
+
@click.pass_context
|
|
67
|
+
def connector(_: click.Context, port, prometheus_port, worker, timeout, modules):
|
|
68
|
+
"""
|
|
69
|
+
Start a grpc server for the connector service.
|
|
70
|
+
"""
|
|
71
|
+
import asyncio
|
|
72
|
+
|
|
73
|
+
working_dir = os.getcwd()
|
|
74
|
+
if all(os.path.realpath(path) != working_dir for path in sys.path):
|
|
75
|
+
sys.path.append(working_dir)
|
|
76
|
+
for m in modules:
|
|
77
|
+
importlib.import_module(m)
|
|
78
|
+
|
|
79
|
+
asyncio.run(_start_grpc_server(port, prometheus_port, worker, timeout))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
async def _start_grpc_server(port: int, prometheus_port: int, worker: int, timeout: int):
|
|
83
|
+
try:
|
|
84
|
+
from flyte.connectors._server import (
|
|
85
|
+
AsyncConnectorService,
|
|
86
|
+
ConnectorMetadataService,
|
|
87
|
+
)
|
|
88
|
+
except ImportError as e:
|
|
89
|
+
raise ImportError(
|
|
90
|
+
"Flyte connector dependencies are not installed. Please install it using `pip install flyte[connector]`"
|
|
91
|
+
) from e
|
|
92
|
+
|
|
93
|
+
click.secho("🚀 Starting the connector service...")
|
|
94
|
+
_start_http_server(prometheus_port)
|
|
95
|
+
|
|
96
|
+
print_metadata()
|
|
97
|
+
|
|
98
|
+
server = grpc.aio.server(futures.ThreadPoolExecutor(max_workers=worker))
|
|
99
|
+
|
|
100
|
+
add_AsyncConnectorServiceServicer_to_server(AsyncConnectorService(), server)
|
|
101
|
+
add_ConnectorMetadataServiceServicer_to_server(ConnectorMetadataService(), server)
|
|
102
|
+
_start_health_check_server(server, worker)
|
|
103
|
+
|
|
104
|
+
server.add_insecure_port(f"[::]:{port}")
|
|
105
|
+
await server.start()
|
|
106
|
+
await server.wait_for_termination(timeout)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _start_http_server(prometheus_port: int):
|
|
110
|
+
try:
|
|
111
|
+
from prometheus_client import start_http_server
|
|
112
|
+
|
|
113
|
+
click.secho("Starting up the server to expose the prometheus metrics...")
|
|
114
|
+
start_http_server(prometheus_port)
|
|
115
|
+
except ImportError as e:
|
|
116
|
+
click.secho(f"Failed to start the prometheus server with error {e}", fg="red")
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _start_health_check_server(server: grpc.Server, worker: int):
|
|
120
|
+
try:
|
|
121
|
+
from grpc_health.v1 import health, health_pb2, health_pb2_grpc
|
|
122
|
+
|
|
123
|
+
health_servicer = health.HealthServicer(
|
|
124
|
+
experimental_non_blocking=True,
|
|
125
|
+
experimental_thread_pool=futures.ThreadPoolExecutor(max_workers=worker),
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
for service in connector_pb2.DESCRIPTOR.services_by_name.values():
|
|
129
|
+
health_servicer.set(service.full_name, health_pb2.HealthCheckResponse.SERVING)
|
|
130
|
+
health_servicer.set(health.SERVICE_NAME, health_pb2.HealthCheckResponse.SERVING)
|
|
131
|
+
|
|
132
|
+
health_pb2_grpc.add_HealthServicer_to_server(health_servicer, server)
|
|
133
|
+
|
|
134
|
+
except ImportError as e:
|
|
135
|
+
click.secho(f"Failed to start the health check servicer with error {e}", fg="red")
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def print_metadata():
|
|
139
|
+
from flyte.connectors import ConnectorRegistry
|
|
140
|
+
|
|
141
|
+
connectors = ConnectorRegistry.list_connectors()
|
|
142
|
+
|
|
143
|
+
table = Table(title="Connector Metadata")
|
|
144
|
+
table.add_column("Connector Name", style="cyan", no_wrap=True)
|
|
145
|
+
table.add_column("Support Task Types", style="cyan")
|
|
146
|
+
|
|
147
|
+
for connector in connectors:
|
|
148
|
+
categories = ""
|
|
149
|
+
for category in connector.supported_task_categories:
|
|
150
|
+
categories += f"{category.name} ({category.version}) "
|
|
151
|
+
table.add_row(connector.name, categories)
|
|
152
|
+
|
|
153
|
+
console = Console()
|
|
154
|
+
console.print(table)
|
flyte/cli/main.py
CHANGED
|
@@ -12,7 +12,9 @@ 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
|
|
16
18
|
from ._update import update
|
|
17
19
|
from ._user import whoami
|
|
18
20
|
|
|
@@ -201,3 +203,7 @@ main.add_command(delete) # type: ignore
|
|
|
201
203
|
main.add_command(build)
|
|
202
204
|
main.add_command(whoami) # type: ignore
|
|
203
205
|
main.add_command(update) # type: ignore
|
|
206
|
+
main.add_command(serve) # type: ignore
|
|
207
|
+
|
|
208
|
+
# Discover and register CLI plugins from installed packages
|
|
209
|
+
discover_and_register_plugins(main)
|
flyte/connectors/__init__.py
CHANGED