flyteplugins-union 0.4.2__tar.gz → 0.5.0__tar.gz
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.
- {flyteplugins_union-0.4.2/src/flyteplugins_union.egg-info → flyteplugins_union-0.5.0}/PKG-INFO +21 -3
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/README.md +17 -1
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/licenses/BSL.txt +1 -1
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/pyproject.toml +18 -1
- flyteplugins_union-0.5.0/src/flyteplugins/union/__init__.py +10 -0
- flyteplugins_union-0.5.0/src/flyteplugins/union/_debug.py +110 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/cli/assignment.py +15 -9
- flyteplugins_union-0.5.0/src/flyteplugins/union/cli/debug.py +260 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/errors.py +13 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/io/_base_volume.py +333 -61
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/io/_internal/_juicefs/_backend.py +33 -3
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/io/_internal/backend.py +24 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/io/_ro_volume.py +13 -14
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/io/_rw_volume.py +17 -10
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/io/_volume_transformer.py +3 -4
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/remote/__init__.py +2 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/remote/_assignment.py +51 -16
- flyteplugins_union-0.5.0/src/flyteplugins/union/remote/_ssh_debug.py +542 -0
- flyteplugins_union-0.5.0/src/flyteplugins/union/ws_proxy.py +86 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0/src/flyteplugins_union.egg-info}/PKG-INFO +21 -3
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins_union.egg-info/SOURCES.txt +6 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins_union.egg-info/entry_points.txt +4 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins_union.egg-info/requires.txt +3 -1
- flyteplugins_union-0.5.0/src/flyteplugins_union.egg-info/scm_file_list.json +173 -0
- flyteplugins_union-0.5.0/src/flyteplugins_union.egg-info/scm_version.json +8 -0
- flyteplugins_union-0.4.2/src/flyteplugins/union/__init__.py +0 -4
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/LICENSE +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/README_PYPI.md +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/licenses/APL.txt +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/setup.cfg +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/cli/__init__.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/cli/_tui/__init__.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/cli/_tui/_volume_explore.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/cli/_volume_index.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/cli/_volume_session.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/cli/api_key.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/cli/cluster.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/cli/cluster_pool.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/cli/member.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/cli/policy.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/cli/queue.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/cli/role.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/cli/user.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/cli/volume.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/__init__.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/authorizer/authorizer_connect.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/authorizer/authorizer_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/authorizer/authorizer_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/authorizer/definition_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/authorizer/definition_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/authorizer/payload_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/authorizer/payload_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/cluster/cluster_connect.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/cluster/cluster_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/cluster/cluster_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/cluster/definition_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/cluster/definition_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/cluster/payload_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/cluster/payload_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/clusterpool/clusterpool_connect.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/clusterpool/clusterpool_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/clusterpool/clusterpool_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/clusterpool/payload_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/clusterpool/payload_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/common/authorization_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/common/authorization_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/common/cluster_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/common/cluster_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/common/deployment_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/common/deployment_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/common/identifier_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/common/identifier_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/common/identity_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/common/identity_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/common/list_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/common/list_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/common/policy_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/common/policy_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/common/role_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/common/role_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/app_definition_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/app_definition_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/app_payload_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/app_payload_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/app_service_connect.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/app_service_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/app_service_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/enums_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/enums_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/member_payload_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/member_payload_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/member_service_connect.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/member_service_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/member_service_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/policy_payload_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/policy_payload_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/policy_service_connect.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/policy_service_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/policy_service_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/role_payload_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/role_payload_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/role_service_connect.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/role_service_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/role_service_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/user_payload_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/user_payload_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/user_service_connect.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/user_service_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/identity/user_service_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/queue/queue_connect.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/queue/queue_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/queue/queue_pb2.pyi +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/validate/__init__.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/validate/validate/__init__.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/internal/validate/validate/validate_pb2.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/io/__init__.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/io/_internal/__init__.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/io/_internal/_juicefs/__init__.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/io/_internal/_juicefs/_common.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/io/_internal/_juicefs/_metadata.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/io/_internal/_juicefs/_redis.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/io/_internal/_juicefs/_sqlite.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/io/_internal/_juicefs/bin/.gitkeep +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/remote/_api_key.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/remote/_cluster.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/remote/_cluster_pool.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/remote/_member.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/remote/_policy.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/remote/_queue.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/remote/_role.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/remote/_user.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/remote/_volume_explore.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/utils/__init__.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/utils/auth.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/utils/image.py +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins_union.egg-info/dependency_links.txt +0 -0
- {flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins_union.egg-info/top_level.txt +0 -0
{flyteplugins_union-0.4.2/src/flyteplugins_union.egg-info → flyteplugins_union-0.5.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flyteplugins-union
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Union SDK - Proprietary extensions for Flyte
|
|
5
5
|
Author-email: unionai <info@union.ai>
|
|
6
6
|
Project-URL: Homepage, https://www.union.ai/
|
|
@@ -19,8 +19,10 @@ Description-Content-Type: text/markdown
|
|
|
19
19
|
License-File: LICENSE
|
|
20
20
|
Requires-Dist: aiosqlite>=0.20.0
|
|
21
21
|
Requires-Dist: connectrpc>=0.9.0
|
|
22
|
-
Requires-Dist: flyte<2.
|
|
22
|
+
Requires-Dist: flyte<2.6.0,>=2.5.7
|
|
23
23
|
Requires-Dist: flyteidl2>=2.0.0
|
|
24
|
+
Requires-Dist: httpx>=0.27.0
|
|
25
|
+
Requires-Dist: httpx-ws>=0.6.0
|
|
24
26
|
Requires-Dist: kubernetes>=27.2.0
|
|
25
27
|
Requires-Dist: pyyaml>=6.0
|
|
26
28
|
Requires-Dist: rich-click>=1.8.0
|
|
@@ -43,6 +45,22 @@ For development:
|
|
|
43
45
|
uv pip install -e .
|
|
44
46
|
```
|
|
45
47
|
|
|
48
|
+
## Flyte compatibility
|
|
49
|
+
|
|
50
|
+
`flyteplugins-union` builds on `flyte` SDK internals that can change between `flyte`
|
|
51
|
+
**minor** releases, so each release pins `flyte` to a single minor line. **Install a
|
|
52
|
+
`flyteplugins-union` release built for your `flyte` minor:**
|
|
53
|
+
|
|
54
|
+
| flyteplugins-union | Requires flyte |
|
|
55
|
+
| ------------------ | ---------------- |
|
|
56
|
+
| 0.5.0+ | `>=2.5.7,<2.6.0` |
|
|
57
|
+
| 0.4.3 | `>=2.5.0,<2.6.0` |
|
|
58
|
+
| 0.4.0 – 0.4.2 | `>=2.3.0,<2.5.0` |
|
|
59
|
+
| 0.3.0 | `>=2.1.0,<2.5.0` |
|
|
60
|
+
|
|
61
|
+
See [COMPATIBILITY.md](https://github.com/unionai/flyteplugins-union/blob/main/COMPATIBILITY.md)
|
|
62
|
+
for the rationale and how the pin tracks new `flyte` releases.
|
|
63
|
+
|
|
46
64
|
## Features
|
|
47
65
|
|
|
48
66
|
### API Key Management
|
|
@@ -100,7 +118,7 @@ This makes Union commands available seamlessly through the Flyte CLI.
|
|
|
100
118
|
|
|
101
119
|
## Dependencies
|
|
102
120
|
|
|
103
|
-
- flyte
|
|
121
|
+
- flyte - The Flyte SDK v2 (pinned to a single minor; see [Flyte compatibility](#flyte-compatibility))
|
|
104
122
|
- rich-click>=1.8.0 - For CLI formatting
|
|
105
123
|
|
|
106
124
|
## Development
|
|
@@ -13,6 +13,22 @@ For development:
|
|
|
13
13
|
uv pip install -e .
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
+
## Flyte compatibility
|
|
17
|
+
|
|
18
|
+
`flyteplugins-union` builds on `flyte` SDK internals that can change between `flyte`
|
|
19
|
+
**minor** releases, so each release pins `flyte` to a single minor line. **Install a
|
|
20
|
+
`flyteplugins-union` release built for your `flyte` minor:**
|
|
21
|
+
|
|
22
|
+
| flyteplugins-union | Requires flyte |
|
|
23
|
+
| ------------------ | ---------------- |
|
|
24
|
+
| 0.5.0+ | `>=2.5.7,<2.6.0` |
|
|
25
|
+
| 0.4.3 | `>=2.5.0,<2.6.0` |
|
|
26
|
+
| 0.4.0 – 0.4.2 | `>=2.3.0,<2.5.0` |
|
|
27
|
+
| 0.3.0 | `>=2.1.0,<2.5.0` |
|
|
28
|
+
|
|
29
|
+
See [COMPATIBILITY.md](https://github.com/unionai/flyteplugins-union/blob/main/COMPATIBILITY.md)
|
|
30
|
+
for the rationale and how the pin tracks new `flyte` releases.
|
|
31
|
+
|
|
16
32
|
## Features
|
|
17
33
|
|
|
18
34
|
### API Key Management
|
|
@@ -70,7 +86,7 @@ This makes Union commands available seamlessly through the Flyte CLI.
|
|
|
70
86
|
|
|
71
87
|
## Dependencies
|
|
72
88
|
|
|
73
|
-
- flyte
|
|
89
|
+
- flyte - The Flyte SDK v2 (pinned to a single minor; see [Flyte compatibility](#flyte-compatibility))
|
|
74
90
|
- rich-click>=1.8.0 - For CLI formatting
|
|
75
91
|
|
|
76
92
|
## Development
|
|
@@ -17,7 +17,7 @@ Additional Use Grant: You may make use of the Licensed Work, provided that
|
|
|
17
17
|
computer programs whose source code are controlled by
|
|
18
18
|
such third parties.
|
|
19
19
|
|
|
20
|
-
Change Date: 2030-06-
|
|
20
|
+
Change Date: 2030-06-29
|
|
21
21
|
|
|
22
22
|
Change License: Apache License, Version 2.0
|
|
23
23
|
|
|
@@ -12,8 +12,11 @@ requires-python = ">=3.10"
|
|
|
12
12
|
dependencies = [
|
|
13
13
|
"aiosqlite>=0.20.0",
|
|
14
14
|
"connectrpc>=0.9.0",
|
|
15
|
-
"flyte>=2.
|
|
15
|
+
"flyte>=2.5.7,<2.6.0",
|
|
16
16
|
"flyteidl2>=2.0.0",
|
|
17
|
+
# ws_proxy (flyte-ws-proxy ssh ProxyCommand) speaks WebSocket over httpx.
|
|
18
|
+
"httpx>=0.27.0",
|
|
19
|
+
"httpx-ws>=0.6.0",
|
|
17
20
|
"kubernetes>=27.2.0",
|
|
18
21
|
"pyyaml>=6.0",
|
|
19
22
|
"rich-click>=1.8.0",
|
|
@@ -74,6 +77,10 @@ exclude = ["src/flyteplugins/union/internal"]
|
|
|
74
77
|
Homepage = "https://www.union.ai/"
|
|
75
78
|
Documentation = "https://docs.union.ai/"
|
|
76
79
|
|
|
80
|
+
[project.scripts]
|
|
81
|
+
# Stdio<->WebSocket proxy used as the ssh ProxyCommand for SSH-into-task debug.
|
|
82
|
+
flyte-ws-proxy = "flyteplugins.union.ws_proxy:main"
|
|
83
|
+
|
|
77
84
|
[project.entry-points."flyte.plugins.cli.commands"]
|
|
78
85
|
# CLI plugins for Union commands
|
|
79
86
|
"create.api-key" = "flyteplugins.union.cli.api_key:create_api_key"
|
|
@@ -109,6 +116,10 @@ Documentation = "https://docs.union.ai/"
|
|
|
109
116
|
# than an `explore.volume` entry because the plugin loader won't auto-create a
|
|
110
117
|
# missing `explore` verb group (it warns + skips).
|
|
111
118
|
"explore" = "flyteplugins.union.cli.volume:explore"
|
|
119
|
+
# Top-level `flyte debug <run> [action]` command: relaunch an existing run with
|
|
120
|
+
# ssh-debug (via flyte.rerun) and connect — the single ssh-into-task entry point.
|
|
121
|
+
# A single command (no group needed).
|
|
122
|
+
"debug" = "flyteplugins.union.cli.debug:debug"
|
|
112
123
|
|
|
113
124
|
[project.entry-points."flyte.plugins.types"]
|
|
114
125
|
# Custom TypeTransformer that auto-commits a still-mounted Volume at
|
|
@@ -177,3 +188,9 @@ select = [
|
|
|
177
188
|
# VolumeExplore lazy-imports flyte.io / msgpack / boto3 / the Volume model so
|
|
178
189
|
# `import flyteplugins.union.remote` stays light for non-volume callers.
|
|
179
190
|
"src/flyteplugins/union/remote/_volume_explore.py" = ["PLC0415"]
|
|
191
|
+
# ssh-debug client lazy-imports the flyte auth machinery / remote objects so
|
|
192
|
+
# `flyte debug --help` and `import flyteplugins.union.remote` stay light.
|
|
193
|
+
"src/flyteplugins/union/cli/debug.py" = ["PLC0415"]
|
|
194
|
+
# _debug defers the ssh-debug import (launch_env) so `import flyteplugins.union` stays light.
|
|
195
|
+
"src/flyteplugins/union/_debug.py" = ["PLC0415"]
|
|
196
|
+
"src/flyteplugins/union/remote/_ssh_debug.py" = ["PLC0415"]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""Union SDK - Proprietary extensions for Flyte.
|
|
2
|
+
|
|
3
|
+
This package provides Union-specific functionality on top of the open-source Flyte SDK.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from flyteplugins.union._debug import debug, with_debugcontext
|
|
7
|
+
|
|
8
|
+
# `SSHDebug` lives in `flyteplugins.union.remote` — import it from there
|
|
9
|
+
# (`from flyteplugins.union.remote import SSHDebug`).
|
|
10
|
+
__all__ = ["debug", "with_debugcontext"]
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""SSH-into-task debug helpers.
|
|
2
|
+
|
|
3
|
+
Public surface:
|
|
4
|
+
|
|
5
|
+
- ``debug(...)`` / ``with_debugcontext(...)`` — re-exported from ``flyteplugins.union``.
|
|
6
|
+
- ``SSHDebug`` — resolve the connection; import it from ``flyteplugins.union.remote``.
|
|
7
|
+
|
|
8
|
+
`debug(...)` is the convenience for the two common cases (it just wraps `with_debugcontext`):
|
|
9
|
+
|
|
10
|
+
```python
|
|
11
|
+
import flyte
|
|
12
|
+
from flyteplugins.union import debug
|
|
13
|
+
from flyteplugins.union.remote import SSHDebug
|
|
14
|
+
|
|
15
|
+
flyte.init_from_config()
|
|
16
|
+
|
|
17
|
+
run = debug(my_task, x=1) # launch a NEW task in debug mode
|
|
18
|
+
run = debug("some-prior-run") # relaunch an EXISTING run in debug mode (fetches its code+inputs)
|
|
19
|
+
|
|
20
|
+
print(SSHDebug.connect(run.name).ssh_config) # paste into ~/.ssh/config, then `ssh flyte-debug`
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The `flyte debug <run>` CLI relaunches **and** connects in one shot.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
from typing import TYPE_CHECKING, Any, Dict, Optional
|
|
29
|
+
|
|
30
|
+
import flyte
|
|
31
|
+
|
|
32
|
+
if TYPE_CHECKING:
|
|
33
|
+
from flyte import TaskTemplate
|
|
34
|
+
from flyte.remote import Run
|
|
35
|
+
|
|
36
|
+
__all__ = ["debug", "with_debugcontext"]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def with_debugcontext(
|
|
40
|
+
mode: Any = None,
|
|
41
|
+
*,
|
|
42
|
+
env_vars: Optional[Dict[str, str]] = None,
|
|
43
|
+
ssh_host_name: Optional[str] = None,
|
|
44
|
+
custom_context: Optional[Dict[str, str]] = None,
|
|
45
|
+
**kwargs,
|
|
46
|
+
):
|
|
47
|
+
"""Like `flyte.with_runcontext`, but preconfigured for ssh-into-task debug.
|
|
48
|
+
|
|
49
|
+
Ensures the auto-managed debug keypair exists and merges the ssh-debug env
|
|
50
|
+
(``_F_E_SSH`` / ``_F_SSH_PK`` / ``_F_E_VS``) into *env_vars*; all other
|
|
51
|
+
arguments are forwarded unchanged. Returns the same runner as
|
|
52
|
+
`with_runcontext`, so call ``.run(task, ...)`` / ``.rerun(run, ...)`` on it.
|
|
53
|
+
|
|
54
|
+
*ssh_host_name* records the intended ssh Host alias on the run's *custom_context*
|
|
55
|
+
(under ``ssh-host-name``); it is propagation/record only — the alias used to build
|
|
56
|
+
the local ssh-config is the one threaded directly to `SSHDebug.connect`.
|
|
57
|
+
"""
|
|
58
|
+
from flyteplugins.union.remote._ssh_debug import SSH_HOST_NAME_CONTEXT_KEY, launch_env
|
|
59
|
+
|
|
60
|
+
cc = dict(custom_context or {})
|
|
61
|
+
if ssh_host_name:
|
|
62
|
+
cc[SSH_HOST_NAME_CONTEXT_KEY] = ssh_host_name
|
|
63
|
+
return flyte.with_runcontext(mode, env_vars=launch_env(env_vars), custom_context=cc or None, **kwargs)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def debug(
|
|
67
|
+
target: "str | TaskTemplate",
|
|
68
|
+
*args: Any,
|
|
69
|
+
action_name: str = "a0",
|
|
70
|
+
name: Optional[str] = None,
|
|
71
|
+
ssh_host_name: Optional[str] = None,
|
|
72
|
+
custom_context: Optional[Dict[str, str]] = None,
|
|
73
|
+
task_template: "Optional[TaskTemplate]" = None,
|
|
74
|
+
inputs: Optional[Dict[str, Any]] = None,
|
|
75
|
+
env_vars: Optional[Dict[str, str]] = None,
|
|
76
|
+
**kwargs: Any,
|
|
77
|
+
) -> "Run":
|
|
78
|
+
"""Launch a task, or relaunch an existing run, with ssh-into-task debug enabled. Returns the `Run`.
|
|
79
|
+
|
|
80
|
+
Two forms (mirroring `flyte.run` / `flyte.rerun`), both with the ssh-debug env injected so the new
|
|
81
|
+
run comes up with sshd:
|
|
82
|
+
|
|
83
|
+
- **`debug(task, x=1)`** — run ``task`` in debug mode (= `with_debugcontext().run(task, x=1)`).
|
|
84
|
+
- **`debug("run-name")`** — relaunch an existing run in debug mode, fetching its code + inputs
|
|
85
|
+
(= `with_debugcontext().rerun("run-name")`). Pass ``inputs={...}`` to change parameters or
|
|
86
|
+
``task_template=`` to substitute code.
|
|
87
|
+
|
|
88
|
+
Then connect with `SSHDebug.connect(run.name)` (or use the `flyte debug <run>` CLI, which relaunches
|
|
89
|
+
**and** connects in one shot).
|
|
90
|
+
|
|
91
|
+
:param target: a `TaskTemplate` to launch, or a prior run name (str) to relaunch.
|
|
92
|
+
:param action_name: (run-name form) action to source the task + inputs from (default ``a0``).
|
|
93
|
+
:param name: explicit name for the new run; omit to let the platform assign one. A fixed name
|
|
94
|
+
makes the launch idempotent — relaunching with the same name re-uses the existing run.
|
|
95
|
+
:param ssh_host_name: record the intended ssh Host alias on the run's custom_context
|
|
96
|
+
(``ssh-host-name``); record/propagation only (see `with_debugcontext`).
|
|
97
|
+
:param custom_context: extra custom-context key/values to attach to the run (merged with
|
|
98
|
+
``ssh-host-name`` when *ssh_host_name* is given).
|
|
99
|
+
:param task_template: (run-name form) substitute task to run instead of the prior run's code.
|
|
100
|
+
:param inputs: (run-name form) native input overrides; omit to reuse the prior run's inputs.
|
|
101
|
+
:param env_vars: extra env vars to set on the run (merged with the ssh-debug env).
|
|
102
|
+
:param args, kwargs: (task form) inputs to pass to ``target`` (like `flyte.run(task, *args, **kwargs)`).
|
|
103
|
+
:return: the new ssh-debug Run.
|
|
104
|
+
"""
|
|
105
|
+
# TODO: add a `recover=` param (recover from the run being relaunched) once the flyteidl2
|
|
106
|
+
# RunSpec.recover field + backend support ship.
|
|
107
|
+
ctx = with_debugcontext(name=name, env_vars=env_vars, ssh_host_name=ssh_host_name, custom_context=custom_context)
|
|
108
|
+
if isinstance(target, str):
|
|
109
|
+
return ctx.rerun(target, action_name, task_template=task_template, inputs=inputs)
|
|
110
|
+
return ctx.run(target, *args, **kwargs)
|
{flyteplugins_union-0.4.2 → flyteplugins_union-0.5.0}/src/flyteplugins/union/cli/assignment.py
RENAMED
|
@@ -51,24 +51,26 @@ def create_assignment(
|
|
|
51
51
|
@click.command("assignment")
|
|
52
52
|
@click.option("--user-subject", type=str, default=None, help="User subject identifier")
|
|
53
53
|
@click.option("--creds-subject", type=str, default=None, help="Client credentials application subject")
|
|
54
|
+
@click.option("--email", type=str, default=None, help="User email for lookup")
|
|
54
55
|
@click.pass_obj
|
|
55
|
-
def get_assignment(cfg: common.CLIConfig, user_subject: str | None, creds_subject: str | None):
|
|
56
|
+
def get_assignment(cfg: common.CLIConfig, user_subject: str | None, creds_subject: str | None, email: str | None):
|
|
56
57
|
"""Get or list assignments.
|
|
57
58
|
|
|
58
|
-
Without --user-subject
|
|
59
|
+
Without --user-subject, --creds-subject, or --email, lists all assignments.
|
|
59
60
|
|
|
60
61
|
Examples:
|
|
61
62
|
|
|
62
63
|
$ flyte --org my-org get assignment
|
|
63
64
|
$ flyte --org my-org get assignment --user-subject user-123
|
|
64
65
|
$ flyte --org my-org get assignment --creds-subject app-456
|
|
66
|
+
$ flyte --org my-org get assignment --email jane@example.com
|
|
65
67
|
"""
|
|
66
68
|
cfg.init(project="", domain="")
|
|
67
69
|
console = common.get_console()
|
|
68
70
|
|
|
69
71
|
try:
|
|
70
|
-
if user_subject or creds_subject:
|
|
71
|
-
assignment = Assignment.get(user_subject=user_subject, creds_subject=creds_subject)
|
|
72
|
+
if user_subject or creds_subject or email:
|
|
73
|
+
assignment = Assignment.get(user_subject=user_subject, creds_subject=creds_subject, email=email)
|
|
72
74
|
console.print(common.format("Assignment", [assignment], "json"))
|
|
73
75
|
else:
|
|
74
76
|
assignments = list(Assignment.listall())
|
|
@@ -83,6 +85,7 @@ def get_assignment(cfg: common.CLIConfig, user_subject: str | None, creds_subjec
|
|
|
83
85
|
@click.command("assignment")
|
|
84
86
|
@click.option("--user-subject", type=str, default=None, help="User subject identifier")
|
|
85
87
|
@click.option("--creds-subject", type=str, default=None, help="Client credentials application subject")
|
|
88
|
+
@click.option("--email", type=str, default=None, help="User email for lookup")
|
|
86
89
|
@click.option("--policy", type=str, required=True, help="Policy name to unassign")
|
|
87
90
|
@click.option("--yes", is_flag=True, help="Skip confirmation prompt")
|
|
88
91
|
@click.pass_obj
|
|
@@ -90,30 +93,33 @@ def delete_assignment(
|
|
|
90
93
|
cfg: common.CLIConfig,
|
|
91
94
|
user_subject: str | None,
|
|
92
95
|
creds_subject: str | None,
|
|
96
|
+
email: str | None,
|
|
93
97
|
policy: str,
|
|
94
98
|
yes: bool,
|
|
95
99
|
):
|
|
96
100
|
"""Unassign a policy from an identity.
|
|
97
101
|
|
|
98
|
-
|
|
102
|
+
Exactly one of --user-subject, --creds-subject, or --email must be provided.
|
|
99
103
|
|
|
100
104
|
Examples:
|
|
101
105
|
|
|
102
106
|
$ flyte --org my-org delete assignment --user-subject user-123 --policy admin
|
|
103
107
|
$ flyte --org my-org delete assignment --creds-subject app-456 --policy admin
|
|
108
|
+
$ flyte --org my-org delete assignment --email jane@example.com --policy admin
|
|
104
109
|
"""
|
|
105
110
|
cfg.init(project="", domain="")
|
|
106
111
|
console = common.get_console()
|
|
107
112
|
|
|
108
|
-
|
|
109
|
-
|
|
113
|
+
provided = sum(x is not None for x in (user_subject, creds_subject, email))
|
|
114
|
+
if provided != 1:
|
|
115
|
+
raise click.ClickException("Exactly one of --user-subject, --creds-subject, or --email must be provided")
|
|
110
116
|
|
|
111
|
-
subject = user_subject or creds_subject
|
|
117
|
+
subject = user_subject or creds_subject or email
|
|
112
118
|
if not yes:
|
|
113
119
|
click.confirm(f"Are you sure you want to unassign policy '{policy}' from '{subject}'?", abort=True)
|
|
114
120
|
|
|
115
121
|
try:
|
|
116
|
-
Assignment.unassign(user_subject=user_subject, creds_subject=creds_subject, policy=policy)
|
|
122
|
+
Assignment.unassign(user_subject=user_subject, creds_subject=creds_subject, email=email, policy=policy)
|
|
117
123
|
console.print(
|
|
118
124
|
f"[green]✓[/green] Successfully unassigned policy [cyan]{policy}[/cyan] from [cyan]{subject}[/cyan]"
|
|
119
125
|
)
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"""``flyte debug <run> [action]`` — relaunch an existing run with ssh-debug, then connect.
|
|
2
|
+
|
|
3
|
+
The single ssh-into-task entry point: it re-runs the given run with the ssh-debug
|
|
4
|
+
env injected (via `flyteplugins.union.debug`, built on `flyte.rerun`), waits for the
|
|
5
|
+
new run's ``:6060`` route, and prints the ready-to-paste ``~/.ssh/config`` block.
|
|
6
|
+
|
|
7
|
+
The relaunch uses a **deterministic** name derived from the source run, so re-running
|
|
8
|
+
``flyte debug <run>`` reconnects to the live debug run instead of spawning a duplicate:
|
|
9
|
+
the backend's already-exists check short-circuits the relaunch and we just resolve the
|
|
10
|
+
route + (re)write the ssh-config.
|
|
11
|
+
|
|
12
|
+
Registered as a top-level ``debug`` command (the plugin loader won't auto-create a
|
|
13
|
+
missing verb group; a single command needs no group).
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import re
|
|
19
|
+
from typing import Optional
|
|
20
|
+
|
|
21
|
+
import rich_click as click
|
|
22
|
+
|
|
23
|
+
# Heavy imports (rich, remote objects, the SDK runner) are deferred into the command
|
|
24
|
+
# body so registering the entry point stays fast.
|
|
25
|
+
|
|
26
|
+
# Conservative cap for a derived run name (DNS-label-like: lowercase alphanumeric + hyphens).
|
|
27
|
+
_RUN_NAME_MAX = 63
|
|
28
|
+
_DEBUG_SUFFIX = "-dbg"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _sanitize_run_name(source: str, *, reserve: int = 0) -> str:
|
|
32
|
+
"""Coerce *source* into a valid run name (DNS-label-like, length-bounded).
|
|
33
|
+
|
|
34
|
+
*reserve* leaves room for a caller-appended suffix. The backend rejects user-given run
|
|
35
|
+
names starting with 'u', so a leading 'u' (and any hyphen it exposes) is dropped.
|
|
36
|
+
"""
|
|
37
|
+
stem = re.sub(r"[^a-z0-9-]+", "-", source.lower()).strip("-")
|
|
38
|
+
if stem.startswith("u"):
|
|
39
|
+
stem = stem[1:].lstrip("-")
|
|
40
|
+
return stem[: _RUN_NAME_MAX - reserve] or "dbg"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _debug_run_name(source: str) -> str:
|
|
44
|
+
"""Deterministic debug-run name for a source run.
|
|
45
|
+
|
|
46
|
+
Re-running ``flyte debug <run>`` derives the same name, so the second invocation hits the
|
|
47
|
+
backend's already-exists check and we reconnect to the live debug run instead of spawning a
|
|
48
|
+
duplicate. The suffix keeps it distinct from the source run.
|
|
49
|
+
"""
|
|
50
|
+
return _sanitize_run_name(source, reserve=len(_DEBUG_SUFFIX)) + _DEBUG_SUFFIX
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@click.command("debug")
|
|
54
|
+
@click.argument("run_name", required=True)
|
|
55
|
+
@click.argument("action_name", required=False)
|
|
56
|
+
@click.option("--user", type=str, default="root", show_default=True, help="SSH login user inside the pod.")
|
|
57
|
+
@click.option(
|
|
58
|
+
"--identity-file",
|
|
59
|
+
"identity_file",
|
|
60
|
+
type=str,
|
|
61
|
+
default=None,
|
|
62
|
+
help="Private key to authenticate with. Defaults to the auto-managed "
|
|
63
|
+
"~/.flyte/ssh-debug/id_ed25519 (created for you; no ssh-keygen needed).",
|
|
64
|
+
)
|
|
65
|
+
@click.option(
|
|
66
|
+
"--name",
|
|
67
|
+
"name",
|
|
68
|
+
type=str,
|
|
69
|
+
default=None,
|
|
70
|
+
help="Name for this debug session — used as both the ~/.ssh/config Host alias and the remote "
|
|
71
|
+
"debug run name. Re-running with the same name reconnects to the live session; a new name "
|
|
72
|
+
"starts a fresh debug run (handy if the previous one died). "
|
|
73
|
+
"Defaults to a name derived from RUN_NAME.",
|
|
74
|
+
)
|
|
75
|
+
@click.option(
|
|
76
|
+
"--api-key/--no-api-key",
|
|
77
|
+
"api_key",
|
|
78
|
+
default=False,
|
|
79
|
+
help="Authenticate the tunnel with a dedicated, long-lived API key (created/reused "
|
|
80
|
+
"as `flyte-ssh-debug`) instead of your interactive session token. Survives re-logins "
|
|
81
|
+
"and won't expire mid-session.",
|
|
82
|
+
)
|
|
83
|
+
@click.option(
|
|
84
|
+
"--refresh-token",
|
|
85
|
+
"refresh_token",
|
|
86
|
+
is_flag=True,
|
|
87
|
+
help="Force a fresh Bearer instead of reusing the cached one (use if you hit auth errors).",
|
|
88
|
+
)
|
|
89
|
+
# TODO: add a `--recover` flag (recover from the run: reuse its succeeded actions) once the
|
|
90
|
+
# flyteidl2 RunSpec.recover field + backend support ship.
|
|
91
|
+
@click.option(
|
|
92
|
+
"--write-config",
|
|
93
|
+
"write_config",
|
|
94
|
+
is_flag=True,
|
|
95
|
+
help="Write the Host block into ~/.ssh/config (replacing any prior block for the same name).",
|
|
96
|
+
)
|
|
97
|
+
@click.option(
|
|
98
|
+
"--wait/--no-wait",
|
|
99
|
+
"wait",
|
|
100
|
+
default=True,
|
|
101
|
+
show_default=True,
|
|
102
|
+
help="Wait for the debug run to start and its ssh route to become ready, then print the "
|
|
103
|
+
"ssh-config. With --no-wait, relaunch and return immediately (don't block on the pod coming "
|
|
104
|
+
"up); re-run the same command later to attach once it's running.",
|
|
105
|
+
)
|
|
106
|
+
@click.option(
|
|
107
|
+
"--timeout",
|
|
108
|
+
type=float,
|
|
109
|
+
default=300.0,
|
|
110
|
+
show_default=True,
|
|
111
|
+
help="Seconds to wait for the debug route to become ready.",
|
|
112
|
+
)
|
|
113
|
+
@click.pass_obj
|
|
114
|
+
def debug(
|
|
115
|
+
cfg,
|
|
116
|
+
run_name: str,
|
|
117
|
+
action_name: Optional[str],
|
|
118
|
+
user: str,
|
|
119
|
+
identity_file: str,
|
|
120
|
+
name: Optional[str],
|
|
121
|
+
api_key: bool,
|
|
122
|
+
refresh_token: bool,
|
|
123
|
+
write_config: bool,
|
|
124
|
+
wait: bool,
|
|
125
|
+
timeout: float,
|
|
126
|
+
) -> None:
|
|
127
|
+
"""Relaunch RUN_NAME's task with ssh-debug enabled, then connect.
|
|
128
|
+
|
|
129
|
+
Only RUN_NAME is required; ACTION_NAME defaults to the root action ``a0``. The new run
|
|
130
|
+
re-uses the original inputs and comes up with sshd; this then prints its ssh-config.
|
|
131
|
+
|
|
132
|
+
Re-running ``flyte debug RUN_NAME`` reconnects to the same debug run (it isn't relaunched
|
|
133
|
+
twice) and refreshes the token in place — so it doubles as "just reattach".
|
|
134
|
+
|
|
135
|
+
Examples:
|
|
136
|
+
|
|
137
|
+
# Relaunch a prior run in debug mode and print its ssh-config
|
|
138
|
+
$ flyte debug my-run
|
|
139
|
+
|
|
140
|
+
# Named session (ssh Host alias + remote run name), written into ~/.ssh/config
|
|
141
|
+
$ flyte debug my-run --name my-dbg --api-key --write-config
|
|
142
|
+
|
|
143
|
+
# A new --name starts a fresh debug run (e.g. the previous one died)
|
|
144
|
+
$ flyte debug my-run --name my-dbg2
|
|
145
|
+
|
|
146
|
+
# Fire-and-forget: relaunch and return without waiting for the pod to come up
|
|
147
|
+
$ flyte debug my-run --no-wait
|
|
148
|
+
|
|
149
|
+
# Then connect (or VS Code -> Remote-SSH -> <name>)
|
|
150
|
+
$ ssh my-dbg
|
|
151
|
+
"""
|
|
152
|
+
import flyte.errors
|
|
153
|
+
from flyte.cli import _common as common
|
|
154
|
+
|
|
155
|
+
from flyteplugins.union import debug as debug_run
|
|
156
|
+
from flyteplugins.union.remote import SSHDebug
|
|
157
|
+
from flyteplugins.union.remote._ssh_debug import DebugRunTerminalError
|
|
158
|
+
|
|
159
|
+
action_name = action_name or "a0"
|
|
160
|
+
cfg.init()
|
|
161
|
+
|
|
162
|
+
console = common.get_console()
|
|
163
|
+
# --name (when given) is both the remote run name and the ssh Host alias; otherwise derive a
|
|
164
|
+
# deterministic run name from RUN_NAME (so re-running reconnects) and use the default host alias.
|
|
165
|
+
debug_name = _sanitize_run_name(name) if name else _debug_run_name(run_name)
|
|
166
|
+
host_alias = name or "flyte-debug"
|
|
167
|
+
# Connect to the debug run regardless of whether we launched it now or it was already up.
|
|
168
|
+
target = debug_name
|
|
169
|
+
|
|
170
|
+
with console.status(f"[bold]Relaunching[/] {run_name}/{action_name} with ssh-debug…", spinner="dots"):
|
|
171
|
+
try:
|
|
172
|
+
# Record the chosen host alias on the run (ssh-host-name); the alias used to build the
|
|
173
|
+
# ssh-config below is still threaded directly via host_alias (no read-back).
|
|
174
|
+
new_run = debug_run(run_name, action_name=action_name, name=debug_name, ssh_host_name=name)
|
|
175
|
+
target = new_run.name
|
|
176
|
+
console.print(f"[green]Relaunched[/] as [bold]{new_run.name}[/] {new_run.url}")
|
|
177
|
+
except flyte.errors.RuntimeUserError as e:
|
|
178
|
+
# A prior `flyte debug {run_name}` already launched this debug run — reconnect to it
|
|
179
|
+
# instead of failing. (Any other user error still surfaces.)
|
|
180
|
+
if getattr(e, "code", None) != "RunAlreadyExistsError":
|
|
181
|
+
raise
|
|
182
|
+
console.print(f"[yellow]Debug run[/] [bold]{debug_name}[/] already exists — reconnecting.")
|
|
183
|
+
|
|
184
|
+
if not wait:
|
|
185
|
+
# --no-wait: don't block on the pod booting + sshd coming up. The same command, re-run
|
|
186
|
+
# once the run is going, reconnects (it won't relaunch) and prints the ssh-config.
|
|
187
|
+
reattach = f"flyte debug {run_name}" + (f" --name {name}" if name else "")
|
|
188
|
+
console.print(
|
|
189
|
+
f"[dim]--no-wait: not waiting for the ssh route on [bold]{target}[/]. "
|
|
190
|
+
f"Once it's running, re-run [bold]{reattach}[/] to attach (reconnects, won't relaunch), "
|
|
191
|
+
f"then [bold]ssh {host_alias}[/].[/dim]"
|
|
192
|
+
)
|
|
193
|
+
return
|
|
194
|
+
|
|
195
|
+
with console.status(f"[bold]Resolving[/] ssh route for {target}/a0…", spinner="dots"):
|
|
196
|
+
try:
|
|
197
|
+
info = SSHDebug.connect(
|
|
198
|
+
target,
|
|
199
|
+
"a0",
|
|
200
|
+
user=user,
|
|
201
|
+
identity_file=identity_file,
|
|
202
|
+
host_alias=host_alias,
|
|
203
|
+
use_api_key=api_key,
|
|
204
|
+
refresh_token=refresh_token,
|
|
205
|
+
timeout=timeout,
|
|
206
|
+
)
|
|
207
|
+
except (TimeoutError, PermissionError, DebugRunTerminalError) as e:
|
|
208
|
+
raise click.ClickException(str(e)) from e
|
|
209
|
+
|
|
210
|
+
_emit(console, info, run_name=target, action_name="a0", write_config=write_config)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _emit(console, info, *, run_name, action_name, write_config):
|
|
214
|
+
"""Write the Host block (optionally) and print the connect panel."""
|
|
215
|
+
import os
|
|
216
|
+
from pathlib import Path
|
|
217
|
+
|
|
218
|
+
from rich.panel import Panel
|
|
219
|
+
from rich.syntax import Syntax
|
|
220
|
+
|
|
221
|
+
if write_config:
|
|
222
|
+
ssh_config_path = Path(os.path.expanduser("~/.ssh/config"))
|
|
223
|
+
ssh_config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
224
|
+
existing = ssh_config_path.read_text() if ssh_config_path.exists() else ""
|
|
225
|
+
# Drop any prior `Host <alias>` block (header + its indented body) so
|
|
226
|
+
# re-running replaces stale config instead of shadowing it with a dup
|
|
227
|
+
# (ssh honors the first matching Host).
|
|
228
|
+
kept, skipping = [], False
|
|
229
|
+
for line in existing.splitlines():
|
|
230
|
+
if line.strip().startswith("Host ") and line.split()[1:] == [info.host_alias]:
|
|
231
|
+
skipping = True
|
|
232
|
+
continue
|
|
233
|
+
if skipping:
|
|
234
|
+
# Block body is blank or indented; ends at the next non-indented, non-blank line.
|
|
235
|
+
if line and not line[0].isspace():
|
|
236
|
+
skipping = False
|
|
237
|
+
else:
|
|
238
|
+
continue
|
|
239
|
+
kept.append(line)
|
|
240
|
+
new_text = "\n".join(kept).rstrip() + "\n\n" + info.ssh_config
|
|
241
|
+
ssh_config_path.write_text(new_text)
|
|
242
|
+
console.print(f"[green]Wrote[/] Host [bold]{info.host_alias}[/] to {ssh_config_path}")
|
|
243
|
+
|
|
244
|
+
console.print(
|
|
245
|
+
Panel.fit(
|
|
246
|
+
Syntax(info.ssh_config, "sshconfig", theme="ansi_dark", background_color="default"),
|
|
247
|
+
title=f"[bold]ssh -> {run_name}/{action_name}[/bold]",
|
|
248
|
+
subtitle=f"then: ssh {info.host_alias} (or VS Code Remote-SSH -> {info.host_alias})",
|
|
249
|
+
border_style="cyan",
|
|
250
|
+
)
|
|
251
|
+
)
|
|
252
|
+
if info.headers_file:
|
|
253
|
+
console.print(f"[dim]Bearer header written to {info.headers_file} (0600)[/dim]")
|
|
254
|
+
# Prominent next step: the literal command to run.
|
|
255
|
+
console.print(f"\n[bold green]➜ Connect now:[/] ssh {info.host_alias}")
|
|
256
|
+
console.print(
|
|
257
|
+
"[dim]Token expired / 401 on reconnect? Just re-run the same `flyte debug` command "
|
|
258
|
+
"(reconnects + refreshes in place — no --write-config needed). "
|
|
259
|
+
"For multi-day sessions use --api-key.[/dim]"
|
|
260
|
+
)
|
|
@@ -53,6 +53,19 @@ class VolumeUnmountError(VolumeError):
|
|
|
53
53
|
_code = "VolumeUnmountFailed"
|
|
54
54
|
|
|
55
55
|
|
|
56
|
+
class VolumeUnmountUnsupportedError(VolumeUnmountError):
|
|
57
|
+
"""The runtime cannot unmount the FUSE client at all because ``fusermount``
|
|
58
|
+
can't reach its mount table (``/etc/mtab`` is missing, read-only, or
|
|
59
|
+
unwritable). Unlike a transient :class:`VolumeUnmountError` (EBUSY), retrying
|
|
60
|
+
won't help — the image is missing the ``/etc/mtab`` → ``/proc/self/mounts``
|
|
61
|
+
symlink most base images carry. The terminal seal treats this as recoverable:
|
|
62
|
+
it falls back to a keep-alive (live) commit, leaving the mount in place. A
|
|
63
|
+
kind of :class:`VolumeUnmountError`, so ``except VolumeUnmountError`` catches
|
|
64
|
+
it too."""
|
|
65
|
+
|
|
66
|
+
_code = "VolumeUnmountUnsupported"
|
|
67
|
+
|
|
68
|
+
|
|
56
69
|
class VolumeCommitError(VolumeError):
|
|
57
70
|
"""A keep-alive checkpoint could not be made durable: the writeback staging
|
|
58
71
|
queue did not finish uploading to object storage (it timed out, or its
|