flyteplugins-union 0.2.1__tar.gz → 0.3.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.
Files changed (123) hide show
  1. {flyteplugins_union-0.2.1/src/flyteplugins_union.egg-info → flyteplugins_union-0.3.0}/PKG-INFO +3 -2
  2. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/licenses/BSL.txt +1 -1
  3. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/pyproject.toml +7 -5
  4. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/cli/api_key.py +11 -2
  5. flyteplugins_union-0.3.0/src/flyteplugins/union/cli/cluster.py +178 -0
  6. flyteplugins_union-0.3.0/src/flyteplugins/union/cli/queue.py +366 -0
  7. flyteplugins_union-0.3.0/src/flyteplugins/union/internal/authorizer/authorizer_connect.py +1878 -0
  8. flyteplugins_union-0.3.0/src/flyteplugins/union/internal/cluster/cluster_connect.py +513 -0
  9. flyteplugins_union-0.3.0/src/flyteplugins/union/internal/cluster/cluster_pb2.py +42 -0
  10. flyteplugins_union-0.3.0/src/flyteplugins/union/internal/cluster/cluster_pb2.pyi +6 -0
  11. flyteplugins_union-0.3.0/src/flyteplugins/union/internal/cluster/definition_pb2.py +91 -0
  12. flyteplugins_union-0.3.0/src/flyteplugins/union/internal/cluster/definition_pb2.pyi +295 -0
  13. flyteplugins_union-0.3.0/src/flyteplugins/union/internal/cluster/payload_pb2.py +55 -0
  14. flyteplugins_union-0.3.0/src/flyteplugins/union/internal/cluster/payload_pb2.pyi +109 -0
  15. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/common/authorization_pb2.py +2 -2
  16. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/common/authorization_pb2.pyi +2 -0
  17. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/common/identifier_pb2.py +35 -27
  18. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/common/identifier_pb2.pyi +12 -0
  19. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/common/identity_pb2.py +7 -3
  20. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/common/identity_pb2.pyi +12 -2
  21. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/common/role_pb2.py +2 -2
  22. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/common/role_pb2.pyi +4 -0
  23. flyteplugins_union-0.3.0/src/flyteplugins/union/internal/identity/app_payload_pb2.py +56 -0
  24. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/identity/app_payload_pb2.pyi +4 -2
  25. flyteplugins_union-0.3.0/src/flyteplugins/union/internal/identity/app_service_connect.py +383 -0
  26. flyteplugins_union-0.3.0/src/flyteplugins/union/internal/identity/member_service_connect.py +123 -0
  27. flyteplugins_union-0.3.0/src/flyteplugins/union/internal/identity/policy_service_connect.py +448 -0
  28. flyteplugins_union-0.3.0/src/flyteplugins/union/internal/identity/role_service_connect.py +513 -0
  29. flyteplugins_union-0.3.0/src/flyteplugins/union/internal/identity/user_service_connect.py +448 -0
  30. flyteplugins_union-0.3.0/src/flyteplugins/union/internal/queue/queue_connect.py +456 -0
  31. flyteplugins_union-0.3.0/src/flyteplugins/union/internal/queue/queue_pb2.py +136 -0
  32. flyteplugins_union-0.3.0/src/flyteplugins/union/internal/queue/queue_pb2.pyi +176 -0
  33. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/remote/__init__.py +3 -1
  34. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/remote/_api_key.py +24 -23
  35. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/remote/_assignment.py +9 -9
  36. flyteplugins_union-0.3.0/src/flyteplugins/union/remote/_cluster.py +234 -0
  37. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/remote/_member.py +3 -3
  38. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/remote/_policy.py +13 -13
  39. flyteplugins_union-0.3.0/src/flyteplugins/union/remote/_queue.py +421 -0
  40. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/remote/_role.py +11 -11
  41. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/remote/_user.py +9 -9
  42. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0/src/flyteplugins_union.egg-info}/PKG-INFO +3 -2
  43. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins_union.egg-info/SOURCES.txt +20 -23
  44. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins_union.egg-info/entry_points.txt +4 -0
  45. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins_union.egg-info/requires.txt +2 -1
  46. flyteplugins_union-0.2.1/src/flyteplugins/union/internal/authorizer/authorizer_pb2_grpc.py +0 -964
  47. flyteplugins_union-0.2.1/src/flyteplugins/union/internal/authorizer/definition_pb2_grpc.py +0 -4
  48. flyteplugins_union-0.2.1/src/flyteplugins/union/internal/authorizer/payload_pb2_grpc.py +0 -4
  49. flyteplugins_union-0.2.1/src/flyteplugins/union/internal/common/authorization_pb2_grpc.py +0 -4
  50. flyteplugins_union-0.2.1/src/flyteplugins/union/internal/common/deployment_pb2_grpc.py +0 -4
  51. flyteplugins_union-0.2.1/src/flyteplugins/union/internal/common/identifier_pb2_grpc.py +0 -4
  52. flyteplugins_union-0.2.1/src/flyteplugins/union/internal/common/identity_pb2_grpc.py +0 -4
  53. flyteplugins_union-0.2.1/src/flyteplugins/union/internal/common/list_pb2_grpc.py +0 -4
  54. flyteplugins_union-0.2.1/src/flyteplugins/union/internal/common/policy_pb2_grpc.py +0 -4
  55. flyteplugins_union-0.2.1/src/flyteplugins/union/internal/common/role_pb2_grpc.py +0 -4
  56. flyteplugins_union-0.2.1/src/flyteplugins/union/internal/identity/app_definition_pb2_grpc.py +0 -4
  57. flyteplugins_union-0.2.1/src/flyteplugins/union/internal/identity/app_payload_pb2.py +0 -56
  58. flyteplugins_union-0.2.1/src/flyteplugins/union/internal/identity/app_payload_pb2_grpc.py +0 -4
  59. flyteplugins_union-0.2.1/src/flyteplugins/union/internal/identity/app_service_pb2_grpc.py +0 -198
  60. flyteplugins_union-0.2.1/src/flyteplugins/union/internal/identity/enums_pb2_grpc.py +0 -4
  61. flyteplugins_union-0.2.1/src/flyteplugins/union/internal/identity/member_payload_pb2_grpc.py +0 -4
  62. flyteplugins_union-0.2.1/src/flyteplugins/union/internal/identity/member_service_pb2_grpc.py +0 -67
  63. flyteplugins_union-0.2.1/src/flyteplugins/union/internal/identity/policy_payload_pb2_grpc.py +0 -4
  64. flyteplugins_union-0.2.1/src/flyteplugins/union/internal/identity/policy_service_pb2_grpc.py +0 -232
  65. flyteplugins_union-0.2.1/src/flyteplugins/union/internal/identity/role_payload_pb2_grpc.py +0 -4
  66. flyteplugins_union-0.2.1/src/flyteplugins/union/internal/identity/role_service_pb2_grpc.py +0 -265
  67. flyteplugins_union-0.2.1/src/flyteplugins/union/internal/identity/user_payload_pb2_grpc.py +0 -4
  68. flyteplugins_union-0.2.1/src/flyteplugins/union/internal/identity/user_service_pb2_grpc.py +0 -231
  69. flyteplugins_union-0.2.1/src/flyteplugins/union/internal/validate/validate/validate_pb2_grpc.py +0 -24
  70. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/LICENSE +0 -0
  71. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/README.md +0 -0
  72. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/README_PYPI.md +0 -0
  73. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/licenses/APL.txt +0 -0
  74. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/setup.cfg +0 -0
  75. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/__init__.py +0 -0
  76. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/cli/__init__.py +0 -0
  77. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/cli/assignment.py +0 -0
  78. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/cli/member.py +0 -0
  79. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/cli/policy.py +0 -0
  80. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/cli/role.py +0 -0
  81. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/cli/user.py +0 -0
  82. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/__init__.py +0 -0
  83. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/authorizer/authorizer_pb2.py +0 -0
  84. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/authorizer/authorizer_pb2.pyi +0 -0
  85. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/authorizer/definition_pb2.py +0 -0
  86. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/authorizer/definition_pb2.pyi +0 -0
  87. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/authorizer/payload_pb2.py +0 -0
  88. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/authorizer/payload_pb2.pyi +0 -0
  89. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/common/deployment_pb2.py +0 -0
  90. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/common/deployment_pb2.pyi +0 -0
  91. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/common/list_pb2.py +0 -0
  92. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/common/list_pb2.pyi +0 -0
  93. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/common/policy_pb2.py +0 -0
  94. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/common/policy_pb2.pyi +0 -0
  95. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/identity/app_definition_pb2.py +0 -0
  96. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/identity/app_definition_pb2.pyi +0 -0
  97. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/identity/app_service_pb2.py +0 -0
  98. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/identity/app_service_pb2.pyi +0 -0
  99. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/identity/enums_pb2.py +0 -0
  100. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/identity/enums_pb2.pyi +0 -0
  101. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/identity/member_payload_pb2.py +0 -0
  102. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/identity/member_payload_pb2.pyi +0 -0
  103. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/identity/member_service_pb2.py +0 -0
  104. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/identity/member_service_pb2.pyi +0 -0
  105. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/identity/policy_payload_pb2.py +0 -0
  106. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/identity/policy_payload_pb2.pyi +0 -0
  107. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/identity/policy_service_pb2.py +0 -0
  108. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/identity/policy_service_pb2.pyi +0 -0
  109. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/identity/role_payload_pb2.py +0 -0
  110. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/identity/role_payload_pb2.pyi +0 -0
  111. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/identity/role_service_pb2.py +0 -0
  112. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/identity/role_service_pb2.pyi +0 -0
  113. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/identity/user_payload_pb2.py +0 -0
  114. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/identity/user_payload_pb2.pyi +0 -0
  115. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/identity/user_service_pb2.py +0 -0
  116. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/identity/user_service_pb2.pyi +0 -0
  117. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/validate/__init__.py +0 -0
  118. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/validate/validate/__init__.py +0 -0
  119. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/internal/validate/validate/validate_pb2.py +0 -0
  120. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/utils/__init__.py +0 -0
  121. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins/union/utils/auth.py +0 -0
  122. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins_union.egg-info/dependency_links.txt +0 -0
  123. {flyteplugins_union-0.2.1 → flyteplugins_union-0.3.0}/src/flyteplugins_union.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flyteplugins-union
3
- Version: 0.2.1
3
+ Version: 0.3.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/
@@ -17,7 +17,8 @@ Classifier: Topic :: Software Development
17
17
  Requires-Python: >=3.10
18
18
  Description-Content-Type: text/markdown
19
19
  License-File: LICENSE
20
- Requires-Dist: flyte<2.1.0,>=2.0.0
20
+ Requires-Dist: connectrpc>=0.9.0
21
+ Requires-Dist: flyte<2.5.0,>=2.1.0
21
22
  Requires-Dist: flyteidl2>=2.0.0
22
23
  Requires-Dist: pyyaml>=6.0
23
24
  Requires-Dist: rich-click>=1.8.0
@@ -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-03-05
20
+ Change Date: 2030-05-06
21
21
 
22
22
  Change License: Apache License, Version 2.0
23
23
 
@@ -10,7 +10,8 @@ description = "Union SDK - Proprietary extensions for Flyte"
10
10
  readme = "README.md"
11
11
  requires-python = ">=3.10"
12
12
  dependencies = [
13
- "flyte>=2.0.0,<2.1.0",
13
+ "connectrpc>=0.9.0",
14
+ "flyte>=2.1.0,<2.5.0",
14
15
  "flyteidl2>=2.0.0",
15
16
  "pyyaml>=6.0",
16
17
  "rich-click>=1.8.0",
@@ -29,7 +30,6 @@ classifiers = [
29
30
 
30
31
  [dependency-groups]
31
32
  dev = [
32
- "grpcio-tools>=1.71.0",
33
33
  "build>=1.2.2.post1",
34
34
  "mock>=5.2.0",
35
35
  "pytest>=8.3.5",
@@ -63,6 +63,10 @@ Documentation = "https://docs.union.ai/"
63
63
  "get.user" = "flyteplugins.union.cli.user:get_user"
64
64
  "delete.user" = "flyteplugins.union.cli.user:delete_user"
65
65
  "get.member" = "flyteplugins.union.cli.member:get_member"
66
+ "get.cluster" = "flyteplugins.union.cli.cluster:get_cluster"
67
+ "create.queue" = "flyteplugins.union.cli.queue:create_queue"
68
+ "get.queue" = "flyteplugins.union.cli.queue:get_queue"
69
+ "update.queue" = "flyteplugins.union.cli.queue:update_queue"
66
70
  "create.assignment" = "flyteplugins.union.cli.assignment:create_assignment"
67
71
  "get.assignment" = "flyteplugins.union.cli.assignment:get_assignment"
68
72
  "delete.assignment" = "flyteplugins.union.cli.assignment:delete_assignment"
@@ -76,9 +80,7 @@ include = ["flyteplugins*"]
76
80
 
77
81
  [tool.ruff]
78
82
  line-length = 120
79
- extend-exclude = [
80
- "src/flyteplugins/union/internal",
81
- ]
83
+ extend-exclude = ["src/flyteplugins/union/internal"]
82
84
 
83
85
  [tool.ruff.lint]
84
86
  select = [
@@ -7,8 +7,14 @@ from flyteplugins.union.remote import ApiKey
7
7
 
8
8
  @click.command("api-key")
9
9
  @click.option("--name", type=str, help="Name for API key", required=True)
10
+ @click.option(
11
+ "--no-default-policies",
12
+ "skip_default_policy_assignments",
13
+ is_flag=True,
14
+ help="Skip attaching the server's default policies. Grant access explicitly via 'flyte create assignment'.",
15
+ )
10
16
  @click.pass_obj
11
- def create_api_key(cfg: common.CLIConfig, name: str):
17
+ def create_api_key(cfg: common.CLIConfig, name: str, skip_default_policy_assignments: bool):
12
18
  """
13
19
  Create an API key for headless authentication.
14
20
 
@@ -22,6 +28,9 @@ def create_api_key(cfg: common.CLIConfig, name: str):
22
28
  # Create an API key named "ci-pipeline"
23
29
  $ flyte create api-key --name ci-pipeline
24
30
 
31
+ # Create a locked-down key with no default policy attachments
32
+ $ flyte create api-key --name ci-pipeline --no-default-policies
33
+
25
34
  # The output will include an export command like:
26
35
  # export FLYTE_API_KEY="<base64-encoded-credentials>"
27
36
  """
@@ -29,7 +38,7 @@ def create_api_key(cfg: common.CLIConfig, name: str):
29
38
  cfg.init(project="", domain="")
30
39
 
31
40
  try:
32
- api_key = ApiKey.create(name=name)
41
+ api_key = ApiKey.create(name=name, skip_default_policy_assignments=skip_default_policy_assignments)
33
42
 
34
43
  console = common.get_console()
35
44
 
@@ -0,0 +1,178 @@
1
+ import rich_click as click
2
+ from flyte.cli import _common as common
3
+
4
+ from flyteplugins.union.remote import Cluster
5
+
6
+
7
+ @click.command("cluster")
8
+ @click.argument("name", type=str, required=False)
9
+ @click.option("--limit", type=int, default=100, help="Maximum number of clusters to return.")
10
+ @click.pass_obj
11
+ def get_cluster(cfg: common.CLIConfig, name: str | None, limit: int):
12
+ """Get a cluster or list all clusters.
13
+
14
+ If NAME is provided, fetch that specific cluster and render a detailed view.
15
+ Otherwise list all clusters.
16
+
17
+ Examples:
18
+
19
+ $ flyte --org my-org get cluster
20
+
21
+ $ flyte --org my-org get cluster my-cluster
22
+ """
23
+ cfg.init(project="", domain="")
24
+ console = common.get_console()
25
+
26
+ try:
27
+ if name:
28
+ cluster = Cluster.get(name)
29
+ if cfg.output_format in ("json", "json-raw"):
30
+ console.print(common.format(f"Cluster {name}", [cluster], cfg.output_format))
31
+ else:
32
+ console.print(_render_cluster_detail(cluster))
33
+ else:
34
+ clusters = list(Cluster.listall(limit=limit))
35
+ if not clusters:
36
+ console.print("[yellow]No clusters found.[/yellow]")
37
+ return
38
+ enabled = [c for c in clusters if c.state == "enabled"]
39
+ disabled = [c for c in clusters if c.state != "enabled"]
40
+ if enabled:
41
+ console.print(common.format("Enabled Clusters", enabled, cfg.output_format))
42
+ if disabled:
43
+ if enabled:
44
+ console.print()
45
+ console.print(common.format("Disabled Clusters", disabled, cfg.output_format))
46
+ except Exception as e:
47
+ raise click.ClickException(f"Unable to get clusters: {e}") from e
48
+
49
+
50
+ # ---------------------------------------------------------------------------
51
+ # Detailed single-cluster rendering
52
+ # ---------------------------------------------------------------------------
53
+
54
+
55
+ def _render_cluster_detail(cluster: Cluster):
56
+ """Render a multi-section Panel for a single cluster."""
57
+ from rich.panel import Panel # noqa: PLC0415
58
+ from rich.table import Table # noqa: PLC0415
59
+
60
+ outer = Table.grid(padding=(0, 0))
61
+ outer.add_column(ratio=1)
62
+
63
+ sections = [
64
+ ("Identity", _kv_table(cluster, [("name", "name"), ("organization", "organization")])),
65
+ (
66
+ "State",
67
+ _kv_table(
68
+ cluster,
69
+ [
70
+ ("state", "state"),
71
+ ("health", "health_display"),
72
+ ("unhealthy reasons", "_unhealthy_reasons_str"),
73
+ ],
74
+ ),
75
+ ),
76
+ (
77
+ "Cloud",
78
+ _kv_table(
79
+ cluster,
80
+ [
81
+ ("host", "cloud_host_name"),
82
+ ("storage", "storage_type"),
83
+ ("bucket", "bucket_name"),
84
+ ("region", "bucket_region"),
85
+ ("metadata prefix", "metadata_bucket_prefix"),
86
+ ("gcp project", "gcp_project_id"),
87
+ ("user role", "user_role"),
88
+ ("operator app", "operator_app_id"),
89
+ ],
90
+ skip_empty=True,
91
+ ),
92
+ ),
93
+ (
94
+ "Config",
95
+ _kv_table(
96
+ cluster,
97
+ [
98
+ ("assigned", "assigned_config_id"),
99
+ ("synced", "synced_config_id"),
100
+ ("synced at", "synced_at"),
101
+ ("drift", "_drift_display"),
102
+ ],
103
+ skip_empty=True,
104
+ ),
105
+ ),
106
+ (
107
+ "Capacity",
108
+ _kv_table(cluster, [("resources", "capacity"), ("ingress (dataplane)", "dataplane_ingress_enabled")]),
109
+ ),
110
+ (
111
+ "Tunnel",
112
+ _kv_table(
113
+ cluster,
114
+ [("status", "tunnel_status_display"), ("public url", "tunnel_url")],
115
+ skip_empty=True,
116
+ ),
117
+ ),
118
+ ]
119
+
120
+ for title, body in sections:
121
+ if body is None:
122
+ continue
123
+ outer.add_row(Panel(body, title=f"[bold]{title}[/bold]", border_style="dim"))
124
+
125
+ if cluster.queues:
126
+ outer.add_row(_render_queues_panel(cluster.queues))
127
+
128
+ return outer
129
+
130
+
131
+ def _kv_table(cluster: Cluster, fields: list[tuple[str, str]], *, skip_empty: bool = False):
132
+ """Render label / value rows. Returns None when skip_empty and every value is empty."""
133
+ from rich.table import Table # noqa: PLC0415
134
+ from rich.text import Text # noqa: PLC0415
135
+
136
+ rows = []
137
+ for label, accessor in fields:
138
+ value = _accessor(cluster, accessor)
139
+ if skip_empty and not value and value is not False:
140
+ continue
141
+ rows.append((label, value))
142
+
143
+ if not rows:
144
+ return None
145
+
146
+ t = Table.grid(padding=(0, 2))
147
+ t.add_column(style="dim", no_wrap=True)
148
+ t.add_column()
149
+ for label, value in rows:
150
+ t.add_row(label, Text.from_markup(str(value)) if value != "" else Text("—", style="dim"))
151
+ return t
152
+
153
+
154
+ def _accessor(cluster: Cluster, name: str):
155
+ """Pull a property/value from the cluster, or compute a small helper string."""
156
+ if name == "_unhealthy_reasons_str":
157
+ return "; ".join(cluster.unhealthy_reasons) if cluster.unhealthy_reasons else ""
158
+ if name == "_drift_display":
159
+ if not cluster.assigned_config_id:
160
+ return ""
161
+ return "[bright_red]drifted[/bright_red]" if cluster.config_drift else "[green]in sync[/green]"
162
+ return getattr(cluster, name)
163
+
164
+
165
+ def _render_queues_panel(queues: list[dict]):
166
+ """Render the cluster's bound queues as a small panel."""
167
+ from rich.panel import Panel # noqa: PLC0415
168
+ from rich.table import Table # noqa: PLC0415
169
+
170
+ t = Table.grid(padding=(0, 2))
171
+ t.add_column(style="dim", no_wrap=True)
172
+ t.add_column()
173
+ for q in queues:
174
+ scope_parts = [s for s in (q.get("project"), q.get("domain")) if s]
175
+ scope = "/".join(scope_parts) if scope_parts else "—"
176
+ t.add_row(q["name"], scope)
177
+
178
+ return Panel(t, title=f"[bold]Queues ({len(queues)})[/bold]", border_style="dim")
@@ -0,0 +1,366 @@
1
+ import rich_click as click
2
+
3
+ # All heavy imports (yaml, rich.*, remote objects) are deferred to inside
4
+ # command functions so that registering CLI entry points stays fast.
5
+ from flyteplugins.union.remote._queue import FAIRNESS_VALUES, PRIORITY_VALUES
6
+
7
+ _PRIORITY_CHOICES = click.Choice(list(PRIORITY_VALUES), case_sensitive=False)
8
+ _FAIRNESS_CHOICES = click.Choice(list(FAIRNESS_VALUES), case_sensitive=False)
9
+
10
+
11
+ # ---------------------------------------------------------------------------
12
+ # create queue
13
+ # ---------------------------------------------------------------------------
14
+
15
+
16
+ @click.command("queue")
17
+ @click.argument("name", type=str)
18
+ @click.option("--run-concurrency", type=int, required=True, help="Max concurrent runs (required)")
19
+ @click.option("--action-concurrency", type=int, required=True, help="Max concurrent actions (required)")
20
+ @click.option("--depth", type=int, default=10000, show_default=True, help="Max queue depth")
21
+ @click.option("--priority", type=_PRIORITY_CHOICES, default="medium", show_default=True, help="Queue priority")
22
+ @click.option(
23
+ "--fairness",
24
+ type=_FAIRNESS_CHOICES,
25
+ default="round_robin",
26
+ show_default=True,
27
+ help="Fairness algorithm",
28
+ )
29
+ @click.option("--cluster", "clusters", type=str, multiple=True, help="Target cluster(s). Repeat for multiple.")
30
+ @click.option("--project", type=str, default="", help="Scope queue to a project")
31
+ @click.option("--domain", type=str, default="", help="Scope queue to a domain")
32
+ @click.pass_obj
33
+ def create_queue(
34
+ cfg,
35
+ name: str,
36
+ run_concurrency: int,
37
+ action_concurrency: int,
38
+ depth: int,
39
+ priority: str,
40
+ fairness: str,
41
+ clusters: tuple[str, ...],
42
+ project: str,
43
+ domain: str,
44
+ ):
45
+ """Create a scheduling queue.
46
+
47
+ Examples:
48
+
49
+ $ flyte create queue my-queue --run-concurrency 100 --action-concurrency 1000
50
+
51
+ $ flyte create queue gpu-queue --run-concurrency 50 --action-concurrency 500 \\
52
+ --priority min --cluster gpu-cluster-1
53
+
54
+ $ flyte create queue backfill --run-concurrency 10 --action-concurrency 100 \\
55
+ --depth 5000 --priority max
56
+
57
+ $ flyte create queue team-queue --run-concurrency 100 --action-concurrency 1000 \\
58
+ --project my-project --domain production
59
+ """
60
+ from flyte.cli import _common as common # noqa: PLC0415
61
+
62
+ from flyteplugins.union.remote import Queue # noqa: PLC0415
63
+
64
+ cfg.init(project="", domain="")
65
+ console = common.get_console()
66
+
67
+ try:
68
+ queue = Queue.create(
69
+ name,
70
+ org=cfg.org,
71
+ domain=domain,
72
+ project=project,
73
+ run_concurrency=run_concurrency,
74
+ action_concurrency=action_concurrency,
75
+ depth=depth,
76
+ priority=priority,
77
+ fairness=fairness,
78
+ clusters=list(clusters) if clusters else None,
79
+ )
80
+ console.print(f"[green]✓[/green] Successfully created queue: [cyan]{queue.name}[/cyan]")
81
+ console.print(common.format(f"Queue {queue.name}", [queue], "json"))
82
+ except Exception as e:
83
+ raise click.ClickException(f"Unable to create queue: {e}") from e
84
+
85
+
86
+ # ---------------------------------------------------------------------------
87
+ # get queue
88
+ # ---------------------------------------------------------------------------
89
+
90
+
91
+ @click.command("queue")
92
+ @click.argument("name", type=str, required=False)
93
+ @click.option("--project", type=str, default="", help="Scope to a project")
94
+ @click.option("--domain", type=str, default="", help="Scope to a domain")
95
+ @click.option("--limit", type=int, default=100, help="Maximum number of queues to return")
96
+ @click.option("--watch", is_flag=True, help="Stream live queue metrics (requires NAME)")
97
+ @click.pass_obj
98
+ def get_queue(cfg, name: str | None, project: str, domain: str, limit: int, watch: bool):
99
+ """Get a queue or list all queues.
100
+
101
+ If NAME is provided, fetch that specific queue with its current metrics.
102
+ Use --watch to stream live metrics with progress bars.
103
+ Otherwise list all queues.
104
+
105
+ Examples:
106
+
107
+ $ flyte get queue
108
+
109
+ $ flyte get queue my-queue
110
+
111
+ $ flyte get queue my-queue --watch
112
+ """
113
+ if watch and not name:
114
+ raise click.ClickException("--watch requires a queue NAME")
115
+
116
+ from flyte.cli import _common as common # noqa: PLC0415
117
+
118
+ from flyteplugins.union.remote import Queue # noqa: PLC0415
119
+
120
+ cfg.init(project="", domain="")
121
+ console = common.get_console()
122
+
123
+ try:
124
+ if name and watch:
125
+ _watch_queue(console, name, cfg.org, domain, project)
126
+ elif name:
127
+ queue = Queue.get(name, org=cfg.org, domain=domain, project=project)
128
+ console.print(common.format(f"Queue {name}", [queue], "json"))
129
+
130
+ # Show metrics snapshot
131
+ try:
132
+ metrics = Queue.details(name, org=cfg.org, domain=domain, project=project)
133
+ console.print(_render_metrics(metrics, queue))
134
+ except Exception:
135
+ pass # Metrics are best-effort
136
+ else:
137
+ queues = list(Queue.listall(org=cfg.org, limit=limit))
138
+ if not queues:
139
+ console.print("[yellow]No queues found.[/yellow]")
140
+ return
141
+ console.print(common.format("Queues", queues, cfg.output_format))
142
+ except Exception as e:
143
+ raise click.ClickException(f"Unable to get queue(s): {e}") from e
144
+
145
+
146
+ # ---------------------------------------------------------------------------
147
+ # Watch display helpers
148
+ # ---------------------------------------------------------------------------
149
+
150
+
151
+ def _pct_color(ratio: float) -> str:
152
+ if ratio >= 0.9:
153
+ return "bright_red"
154
+ if ratio >= 0.7:
155
+ return "yellow"
156
+ return "medium_purple1"
157
+
158
+
159
+ def _bar_style(ratio: float) -> str:
160
+ if ratio >= 0.9:
161
+ return "bright_red"
162
+ if ratio >= 0.7:
163
+ return "yellow"
164
+ return "medium_purple1"
165
+
166
+
167
+ def _render_metric_block(label: str, used: int, cap: int):
168
+ """Render a single metric: header row with label/value/pct, then a slim bar."""
169
+ from rich.progress_bar import ProgressBar # noqa: PLC0415
170
+ from rich.table import Table # noqa: PLC0415
171
+ from rich.text import Text # noqa: PLC0415
172
+
173
+ ratio = used / cap if cap > 0 else 0
174
+ pct = ratio * 100
175
+ color = _pct_color(ratio)
176
+ style = _bar_style(ratio)
177
+
178
+ block = Table.grid(padding=0)
179
+ block.add_column("label", ratio=1)
180
+ block.add_column("value", justify="right")
181
+ block.add_column("pct", justify="right", min_width=6)
182
+
183
+ block.add_row(
184
+ Text(label, style="bold"),
185
+ Text(f"{used:,} / {cap:,}", style="dim"),
186
+ Text(f"{pct:.0f}%", style=color),
187
+ )
188
+ bar = ProgressBar(
189
+ total=max(cap, 1),
190
+ completed=min(used, cap),
191
+ width=None,
192
+ style="bar.back",
193
+ complete_style=style,
194
+ finished_style=style,
195
+ )
196
+ # bar spans all columns
197
+ block.add_row(bar, "", "")
198
+ return block
199
+
200
+
201
+ def _render_metrics(metrics: dict, queue=None):
202
+ """Build a rich Panel showing queue metrics with slim progress bars."""
203
+ from datetime import datetime, timezone # noqa: PLC0415
204
+
205
+ from rich.panel import Panel # noqa: PLC0415
206
+ from rich.table import Table # noqa: PLC0415
207
+
208
+ outer = Table.grid(padding=(1, 2))
209
+ outer.add_column(ratio=1)
210
+
211
+ outer.add_row(_render_metric_block("Runs in-flight", metrics["runs_in_flight"], metrics["run_concurrency"]))
212
+ outer.add_row(
213
+ _render_metric_block("Actions in-flight", metrics["actions_in_flight"], metrics["action_concurrency"])
214
+ )
215
+ outer.add_row(_render_metric_block("Queue depth", metrics["current_depth"], metrics["depth"]))
216
+
217
+ ts = datetime.fromtimestamp(metrics["observed_at"], tz=timezone.utc).strftime("%H:%M:%S UTC")
218
+ subtitle_parts = [f"last updated {ts}"]
219
+
220
+ status_str = ""
221
+ if queue:
222
+ status_color = {"active": "green", "draining": "yellow", "drained": "dim"}.get(queue.status, "white")
223
+ status_str = f" [{status_color}]{queue.status}[/{status_color}]"
224
+ subtitle_parts.append(f"priority: {queue.priority}")
225
+ subtitle_parts.append(f"fairness: {queue.fairness}")
226
+ if queue.clusters:
227
+ subtitle_parts.append(f"clusters: {', '.join(queue.clusters)}")
228
+
229
+ title = f"[bold]{metrics['name']}[/bold]{status_str}"
230
+ subtitle = " | ".join(subtitle_parts)
231
+ return Panel(outer, title=title, subtitle=f"[dim]{subtitle}[/dim]", border_style="dim")
232
+
233
+
234
+ def _watch_queue(console, name: str, org: str, domain: str, project: str) -> None:
235
+ """Stream live queue metrics using rich Live display."""
236
+ from rich.console import Console # noqa: PLC0415
237
+ from rich.live import Live # noqa: PLC0415
238
+
239
+ from flyteplugins.union.remote import Queue # noqa: PLC0415
240
+
241
+ queue = Queue.get(name, org=org, domain=domain, project=project)
242
+ console.print(f"Watching queue [cyan]{name}[/cyan] (Ctrl+C to stop)\n")
243
+
244
+ # Use a console that auto-detects the real terminal width so that
245
+ # Live's cursor-up count matches the actual number of physical lines.
246
+ live_console = Console(force_terminal=True)
247
+ try:
248
+ with Live(console=live_console, refresh_per_second=4) as live:
249
+ for metrics in Queue.watch(name, org=org, domain=domain, project=project):
250
+ # Re-fetch queue config occasionally to pick up status changes
251
+ try:
252
+ queue = Queue.get(name, org=org, domain=domain, project=project)
253
+ except Exception:
254
+ pass
255
+ live.update(_render_metrics(metrics, queue))
256
+ except KeyboardInterrupt:
257
+ console.print("\n[dim]Watch stopped.[/dim]")
258
+
259
+
260
+ # ---------------------------------------------------------------------------
261
+ # YAML helpers for interactive editing
262
+ # ---------------------------------------------------------------------------
263
+
264
+
265
+ def _queue_to_yaml(queue) -> str:
266
+ """Serialize a Queue to YAML for interactive editing."""
267
+ import yaml as _yaml # noqa: PLC0415
268
+
269
+ data = {
270
+ "name": queue.name,
271
+ "run_concurrency": queue.run_concurrency,
272
+ "action_concurrency": queue.action_concurrency,
273
+ "depth": queue.depth,
274
+ "priority": queue.priority,
275
+ "fairness": queue.fairness,
276
+ "clusters": queue.clusters,
277
+ }
278
+ if queue.project:
279
+ data["project"] = queue.project
280
+ if queue.domain:
281
+ data["domain"] = queue.domain
282
+ comment = (
283
+ "# Edit queue configuration below.\n"
284
+ "# Priority: min, medium, max\n"
285
+ "# Fairness: round_robin, shuffle_interleave\n"
286
+ "# Clusters: list of cluster names, or ['*'] for all\n"
287
+ )
288
+ return comment + "\n" + _yaml.safe_dump(data, default_flow_style=False, sort_keys=False)
289
+
290
+
291
+ # ---------------------------------------------------------------------------
292
+ # update queue
293
+ # ---------------------------------------------------------------------------
294
+
295
+
296
+ @click.command("queue")
297
+ @click.argument("name", type=str)
298
+ @click.option("--project", type=str, default="", help="Scope to a project")
299
+ @click.option("--domain", type=str, default="", help="Scope to a domain")
300
+ @click.option("--drain", is_flag=True, help="Begin draining the queue")
301
+ @click.option("--activate", is_flag=True, help="Re-activate a draining or drained queue")
302
+ @click.option("--edit", "edit_mode", is_flag=True, help="Open an editor to modify queue settings")
303
+ @click.pass_obj
304
+ def update_queue(cfg, name: str, project: str, domain: str, drain: bool, activate: bool, edit_mode: bool):
305
+ """Update a queue.
306
+
307
+ Use --drain to begin draining (stops new submissions).
308
+ Use --activate to re-activate a draining or drained queue.
309
+ Use --edit to interactively modify queue configuration.
310
+
311
+ Examples:
312
+
313
+ $ flyte update queue my-queue --drain
314
+
315
+ $ flyte update queue my-queue --activate
316
+
317
+ $ flyte update queue my-queue --edit
318
+ """
319
+ if drain and activate:
320
+ raise click.ClickException("Cannot specify both --drain and --activate")
321
+ if not drain and not activate and not edit_mode:
322
+ raise click.ClickException("Must specify --drain, --activate, or --edit")
323
+
324
+ import yaml as _yaml # noqa: PLC0415, I001
325
+ from flyte.cli import _common as common # noqa: PLC0415
326
+ from flyteplugins.union.cli import edit_with_retry # noqa: PLC0415
327
+ from flyteplugins.union.remote import Queue # noqa: PLC0415
328
+
329
+ cfg.init(project="", domain="")
330
+ console = common.get_console()
331
+
332
+ try:
333
+ if drain:
334
+ queue = Queue.drain(name, org=cfg.org, domain=domain, project=project)
335
+ console.print(f"[green]✓[/green] Queue [cyan]{name}[/cyan] is now draining")
336
+ console.print(common.format(f"Queue {name}", [queue], "json"))
337
+ elif activate:
338
+ queue = Queue.activate(name, org=cfg.org, domain=domain, project=project)
339
+ console.print(f"[green]✓[/green] Queue [cyan]{name}[/cyan] is now active")
340
+ console.print(common.format(f"Queue {name}", [queue], "json"))
341
+ else:
342
+ # Interactive edit
343
+ current = Queue.get(name, org=cfg.org, domain=domain, project=project)
344
+ yaml_text = _queue_to_yaml(current)
345
+
346
+ def _apply(edited_yaml):
347
+ data = _yaml.safe_load(edited_yaml)
348
+ clusters = data.get("clusters")
349
+ return Queue.update(
350
+ name,
351
+ org=cfg.org,
352
+ domain=domain,
353
+ project=project,
354
+ run_concurrency=data.get("run_concurrency"),
355
+ action_concurrency=data.get("action_concurrency"),
356
+ depth=data.get("depth"),
357
+ priority=data.get("priority"),
358
+ fairness=data.get("fairness"),
359
+ clusters=clusters,
360
+ )
361
+
362
+ result = edit_with_retry(yaml_text, _apply, console=console, noun="queue")
363
+ if result and result != "no_changes":
364
+ console.print(f"[green]✓[/green] Successfully updated queue: [cyan]{name}[/cyan]")
365
+ except Exception as e:
366
+ raise click.ClickException(f"Unable to update queue: {e}") from e